Nov 3, 2000 12:00 AM PT

Applications, applets, and hybrids

Java 101 charts a new course, and explores applications, applets, and hybrids

If you've been following Java 101, you know that Jacob Weintraub has created an excellent series of articles designed to help Java newbies become Java developers. However, as Jacob can no longer continue this column, JavaWorld has passed the torch to me.

You'll quickly discover that my route to becoming a Java developer follows a different path. For example, I prefer to talk about the programming aspects of Java that are not object-oriented (such as types, variables, operators, expressions, and statements) before delving into its object-oriented side. I believe that approach will enhance Java 101's natural flow from one topic to another -- a flow that seamlessly moves from start to finish. To facilitate the discussion, occasionally I will present an advanced concept before I fully explain it. As a result, you'll encounter brief explanations about more advanced topics as you work your way through this and future articles.

Java 101 will introduce many example programs. Each program is compiled with Sun's Java 2 Platform, Standard Edition version 1.4 (also known as SDK 1.4) compiler and tested on the Windows 98 SE platform. Although I'll try to keep Windows references to a minimum, that won't always be possible, so I'll warn you when an article strays into the world of Windows.

Because I haven't spent much time dealing with Java from the server perspective, this column will focus on client-side Java. That doesn't mean we won't explore topics such as Remote Method Invocation and JDBC, that bridge the client and server sides. However, I will not present servlets, Enterprise JavaBeans, Java Server Pages, and other server-side topics. To get a better idea of the subjects I will cover, see the sidebar, The Road Ahead, for the Java 101 course outline.

In this month's article, I discuss the structure of application, applet, and hybrid programs. From an application perspective, you might find some overlap from Jacob's earlier articles, but I will also introduce quite a bit of new content.

Program categories

Java gives you the ability to create four kinds of programs: applications, applets, hybrids, and servlets. I discuss the first three programs in this article. To learn more about servlets, please consult the Java 2 Platform, Enterprise Edition documentation.

Applications

An application is a standalone program consisting of at least one class with a main() method. That method features the following signature:

public static void main (String [] args)

The public keyword means main() is callable from outside the class in which it's declared. The static keyword means main() is callable without an object reference. In other words, the JVM does not need to create an object from the class that declares main() before calling main(). Finally, the void keyword means main() doesn't return a value.

As with other methods, main() has a parameter list, a list of types and variable names. In main()'s case, only one parameter appears -- args. That parameter is declared a reference to -- also known as address of -- an array of String objects. Each object contains the contents of an argument passed to the application by way of the program's command line. Note: You do not have to use args as that parameter's name. You can just as easily specify chickens, as in String [] chickens.

Listing 1 presents source code for an application called App. That application prints out a list of arguments that are passed to itself by way of the command line.

Listing 1. App.java

// App.java
class App 
{
   public static void main (String [] args)
   {
      System.out.println ("Command arguments\n");
      for (int i = 0; i < args.length; i++)
           System.out.println (args [i]);
   }
}

TEXTBOX:

TEXTBOX_HEAD: Comments

Listing 1 illustrates a technique that I use to identify programs -- at the top of a source file, I place a comment that identifies the source file's name. I find that comment useful in keeping track of programs. If you're not familiar with the concept of a comment, it's nothing more than source code documentation that only has meaning at the source level. When source code is compiled, the comment is thrown away. We'll look at comments again next month.

:END_TEXTBOX

Code within App's main() method calls one of the out object reference variable's println() methods to output information to the standard output device. Typically, that device is a command window such as the Microsoft Windows DOS window, although the device can be redirected to a file. (I will demonstrate that redirection in a subsequent article.) The period character separates the println() method call from the out object reference variable. In turn, out is declared in a class called System and separated from System by a period character. An object reference variable closely resembles a C or C++ pointer: It's a variable that contains the address of another variable. You'll receive quite a bit of exposure to object reference variables in upcoming articles.

If you've worked with C or C++, you're probably familiar with the structure of the for loop statement (that appears in source code via keyword for). The for loop statement repetitively executes one or more statements for either a specified number of times or indefinitely. (In future articles, I will explore the for loop statement and other statements in detail.) In App's case, for executes a System.out.println method call for each argument that was passed to that program on the command line. Notice args.length. In Java, length is a property of an array, and it returns a count of array elements.

At the command line, type javac App.java to compile App. If you've entered everything as shown, you should end up with a class file called App.class that contains App's byte code instructions. So how do you run App? Take a look at Figure 1. That figure shows App running from the command line with three arguments: one, two, and three.

Note: Figure 1 shows App running under Windows 98 SE. Unix and Linux run App similarly. However, when run under the Mac, you'll probably have to complete a little more work. I'd love to show you how to do that, but I've never used Java on a Mac.

Figure 1. Running App with three arguments

The java program executes an application. Under Windows, that program is stored in an executable file called java.exe. As with javac, java is specified at the command line. The name of the class file that contains the main() method then follows java.

The java program looks for the main() method in the class file. If it doesn't find main(), it outputs an error message. (As you can see from Figure 1, you DON'T specify the .class file extension.)

Arguments can follow the class name, but they are optional. In Figure 1, those arguments are one, two, and three. java creates a String array (by way of the Java Native Interface -- JNI), and populates each element with a reference to a String object containing the characters composing an argument. Once complete, the main() method is called (by way of the JNI) and passed a reference to the String array.

Suppose you were to type java App * at the command line. What do you think the command window would display? If you think the answer is an asterisk, check out Figure 2.

Figure 2. Using a wildcard character to specify an argument

Figure 2 shows App displaying the names of files located in the same directory as App.class. It turns out that the asterisk character represents a wildcard. In other words, it represents all filenames in the current directory. When java builds the String array, it obtains a list of all the current directory's filenames, and places each filename in a separate String object, which is then stored as an element in the array.

Try running java App * *. Guess what will display. Because each asterisk causes java to obtain a list of all file names, you'll see two copies of all file names in the current directory.

Suppose you write a Calculator application that multiplies two numbers with the asterisk, as in java Calculator 4 * 3. Based on the previous discussion, 4 and 3 will not multiply. If you want the asterisk to be interpreted as itself -- and not a wildcard -- you must surround it with a pair of double quote characters. For example, you would type java Calculator 4 "*" 3. Furthermore, if your argument contains embedded space characters, and you want to include those space characters as part of the argument, you must use double quotes. For example, type java App "my compound argument" at the command line to specify my compound argument as App's single argument.

Our first Java application consisted of a single class. However, you can also create applications that consist of multiple classes. Furthermore, each class can have its own main() method. To see what that situation looks like, check out Listing 2.

Listing 2. Fred.java

// Fred.java
class A
{
   public static void main (String [] dogs)
   {
      System.out.println ("Class A main() method");
   }
}
class B
{
   public static void main (String [] chickens)
   {
      System.out.println ("Class B main() method");
      System.out.println ("Num args: " + chickens.length);
   }
}

Listing 2 presents source code stored in a file called Fred.java. That source code consists of two classes: A and B. When compiled (as in javac Fred.java), you end up with two class files: A.class and B.class. If you were to type java A, you would see Class A main() method in the command window. However, if you were to type java B, the command window would display Class B main() method, followed by a line that begins with Num args: and identifies the number of arguments passed on the command line.

Is Fred one application or two applications? The answer depends on your perspective. Normally, an application consists of a single class with a main() method. As you've seen, you run the application by specifying java and the name of the class that contains main(). However, you might find yourself placing a main() method in other classes (for debugging purposes). To prevent confusion for anyone using your program, either remove all main() methods except the main() method that starts the application, or identify the class file containing the official main() method before deploying the application.

In addition to java, the Java 2 SDK includes a javaw program that you can use to run applications. That program is almost identical to java, except that javaw does not display a command window when running an application (unless you run the application through a Windows batch file, which automatically opens a command window). For example, suppose your class file called GUIDemo is stored in the c:\jdk1.4\projects directory (assuming Windows). You decide to create a Windows shortcut for running that program, and choose the following command line: java -cp c:\jdk1.4\projects GUIDemo. (The -cp option tells java where to find a class file called GUIDemo.class.) When you select the shortcut, a command window pops up along with GUIDemo's GUI window. However, if you change java to javaw, you won't see the command window.

Now that you've had a chance to play with applications, let's take a look at the second category of Java programs -- applets.

Applets

An applet is an application that runs in the context of a Web browser that controls the applet. Because a rectangular area of the Webpage displays an applet's output, applets are described as being embedded in Webpages. Furthermore, by calling certain methods -- which we'll shortly investigate -- the browser manages an applet's life cycle.

An applet's class files download automatically to a user's machine when the user surfs to a Webpage containing the applet. Once downloaded, the browser's virtual machine or the Java Plug-in software executes those class files. (See Resources for an article that explores Java Plug-in.)

Imagine a malicious person creating an applet that deletes files, wastes reams of printer paper, steals passwords or other sensitive information, and so on. An applet with unrestricted access to a user's machine could perform all of those misdeeds. For that reason, applets can only execute limited functions. For example, an applet cannot perform any file-related activities.

Sun has established a specific (and involved) procedure for turning restricted applets into unrestricted applets. However, unrestricted applets can only run under the user's permission. (We will explore that subject in a future article.) To be an applet, one -- and only one -- of the applet's classes must conform to the following pattern:

public class class_name extends java.applet.Applet
{
}

The required public keyword gives the Web browser access to the applet. The extends keyword indicates the object-oriented programming concept of inheritance and suggests that the class_name class inherits applet capabilities from a class called Applet (located in the java.applet package -- an organizational mechanism for classes and class files -- to be explored in a future article).

Each applet has a life cycle. An applet is initialized (once and only once), started and stopped (one or more times during its life), and destroyed (once and only once). The browser calls one of four methods at significant points during that life cycle to indicate that the applet has entered another phase of existence. Those methods are init(), start(), stop(), and destroy().

The browser calls init() method after creating a class_name object (a task that occurs behind the scenes). Generally, you would perform one-time initialization, such as creating the program's GUI, in that method. init() features the following signature: public void init ().

The start() method is called immediately after init() and whenever the Webpage containing the applet is revisited. That method represents a good location to begin a background thread for performing animation (which we'll investigate in a future article). It has the following signature: public void start ().

The browser calls the stop() method prior to leaving a Webpage and just before calling the destroy() method. You would normally place code that terminates a background thread in stop(). It has the following signature: public void stop ().

Finally, the browser calls destroy() before exiting or replacing a stopped applet in its cache. That method presents an appropriate spot for canceling any global initialization such as closing a file that was opened in init() (unrestricted applets only). destroy() has the following signature: public void destroy ().

To give you an idea of what a simple applet looks like, I've written a small applet called Lifecycle. The source code is presented in Listing 3.

Listing 3. Lifecycle.java

// Lifecycle.java
// To run applet under Netscape Communicator 4.7 browser or Internet
// Explorer 5.0 internal JVM, compile using javac -target 1.1 Lifecycle.java
import java.awt.*;
public class Lifecycle extends java.applet.Applet
{
   String msg = "Hello World";
   public void init ()
   {
      System.out.println ("init called");
      String s = getParameter ("message");
      if (s != null)
          msg = s;
   }
   public void start ()
   {
      System.out.println ("start called");
   }
   public void paint (Graphics g)
   {
      g.drawString (msg, 10, 30);
   }
   public void stop ()
   {
      System.out.println ("stop called");
   }
   public void destroy ()
   {
      System.out.println ("destroy called");
   }
}

When the browser calls either init(), start(), stop(), or destroy(), those methods in turn call System.out.println to send a message to the standard output device.

I've declared a String variable, called msg, and initialized that variable to Hello World (all the characters between the double quote characters). Behind the scenes, an object of type String is created, and the characters between the double quotes are copied into that object. (You'll learn more about strings in a future article.)

The init() method calls the getParameter() method (inherited from Applet) to retrieve the value of an applet parameter. An external name/value pair, an applet parameter dynamically configures an applet without recompilation. The name of the parameter is identified as message -- the sole String argument to getParameter(). If message exists, getParameter() returns its value. If it doesn't exist, a special null value returns. Java supplies the null reserved word to indicate a null reference -- similar to a zero pointer in C or C++. We'll look more closely at null in next month's article.

After calling init(), the browser calls the paint() method to tell the applet to draw something in its rectangular area. The address of a graphics context -- an object that represents a video screen or printer -- is passed to paint(), and the applet calls various graphics context methods, such as drawString(), to perform drawing. The drawString() method takes three arguments -- a String object that specifies the characters to be drawn, an x coordinate (10) that indicates the number of pixels to the right of the rectangular area's left edge, and a y coordinate (30) that distinguishes the number of pixels below the rectangular area's top edge. The (x, y) coordinates -- (10, 30) in the program -- identify the location of the left-most character in the rectangular area.

By now, you're probably itching to try out Lifecycle. Before you can do that, you'll need to compile the source code (as in javac Lifecycle.java). Then, you'll need some HTML code to present to the browser. Listing 4 provides that code.

Listing 4. Lifecycle.html

<applet code="Lifecycle.class" width=250 height=50>
  <param name="message" value="What an applet!">
</applet>

To run an applet under the control of the browser's virtual machine, you use an HTML tag called <applet>. That tag requires you to specify the name of the class file, whose class extends Applet (by way of the code attribute). Furthermore, you indicate the width and height of the applet's rectangular area by stating the width and height attributes. In this case, Lifecycle.class constitutes the applet, and the dimensions of the rectangular area equal 250 by 50 pixels.

Each <applet> tag must have a matching </applet> closing tag. As you can see, another tag <param>, which describes an applet parameter, can sandwich between those tags. The value of the <param> tag's name attribute matches the argument passed to the getParameter() method. In both cases, message is specified. getParameter() returns the value of the <param> tag's value attribute. In this case, What an applet! is identified. By using <param> tags, you save yourself the trouble of recompiling your applet to change hard-coded items (such as image filenames and sound filenames).

When it comes time to run the applet, you can either fire up your browser and specify the location and name of Lifecycle.html in the browser's location text field, or you can use the appletviewer program that comes with the Java 2 SDK.

The appletviewer program takes a single argument -- the applet's HTML file. To run Lifecycle, type appletviewer Lifecycle.html. After a few moments, a window should appear with the required output. The first time you run appletviewer, you'll probably receive a welcome screen. Select Ok (or whatever button is presented), and you'll see your applet. That welcome screen will only appear during appletviewer's first run.

I could go on and on about applets, but I don't want to overwhelm you. I will discuss additional features in future articles. Right now, let's take a look at the third category of Java programs -- hybrids.

Hybrids

A hybrid combines application and applet characteristics, and can run as either a standalone application or a browser-dependent applet. In application mode, support code is provided to mimic a browser environment. That support code creates a GUI window for the applet, creates a stub, and creates a context. Our own appletviewer program will result. (For those who want to learn more about stubs and context, I recommend checking out the Java 2 SDK documentation on the AppletStub and AppletContext interfaces, and the Applet class.)

Listing 5 presents source code to a hybrid program called Hybrid. Don't be alarmed by all the source code. Our objective is to compile that code and run it as either an application or an applet. The code will make much more sense after you've progressed through this column.

Listing 5. Hybrid.java

// Hybrid.java
// To run applet under Netscape Communicator 4.7 browser or Internet
// Explorer 5.0 internal JVM, compile using javac -target 1.1 Hybrid.java
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
public class Hybrid extends Applet implements ActionListener
{
   static Hybrid h;
   static Stub s;
   Image im;
   public void init ()
   {
      System.out.println ("init () called");
      System.out.println ("isActive () returns " + isActive ());
      // Create simple GUI.
      Button b = new Button ("Visit Javasoft");
      b.addActionListener (this);
      add (b);
      b = new Button ("Giggle");
      b.addActionListener (this);
      add (b);
      // Obtain an Image object in preparation for loading.
      String imageName = getParameter ("image");
      im = getImage (getCodeBase (), imageName);
   }
   public void start ()
   {
      System.out.println ("start () called");
      System.out.println ("isActive () returns " + isActive ());
   }
   public void paint (Graphics g)
   {
      // Load and draw an image.
      if (im != null)
          g.drawImage (im, 0, 0, this);
   }
   public void actionPerformed (ActionEvent e)
   {
      if (e.getActionCommand ().equals ("Giggle"))
      {
          String soundName = getParameter ("sound");
          if (soundName != null)
          {
              AudioClip ac = getAudioClip (getDocumentBase (),
                                           soundName);
              ac.play ();
          }
          return;
      }
      try
      {
          URL u = new URL ("http://www.javasoft.com");
          getAppletContext ().showDocument (u);
      }
      catch (MalformedURLException exc) { System.out.println (e); }
   }
   public void stop ()
   {
      System.out.println ("stop () called");
      System.out.println ("isActive () returns " + isActive ());
   }
   public void destroy ()
   {
      System.out.println ("destroy () called");
      System.out.println ("isActive () returns " + isActive ());
   }
   public static void main (String [] args)
   {
      Frame frame = new Frame ("Hybrid as an Application");
      h = new Hybrid ();
      frame.add (new Panel ().add (h));
      // Create the frame's peer.  Peer is not visible.
      frame.addNotify ();
      h.setStub (s = new Stub (args));
      h.init ();
      frame.setSize (300, 200);
      frame.setVisible (true);
      s.setActive (true);
      h.start ();
      frame.addWindowListener (new WindowAdapter ()
                               {
                                   public void windowClosing
                                                       (WindowEvent w)
                                   {
                                      s.setActive (false);
                                      h.stop ();
                                      h.destroy ();
                                      System.exit (0);
                                   }
                               });
   }
}
/* The Stub class provides a mechanism for obtaining information from
   the run-time environment.  Typically, this environment is maintained
   by a Web browser.  For this program, a Web browser environment is
   being simulated. */
class Stub implements AppletStub
{
   private boolean active = false;
   private Hashtable ht = new Hashtable ();
   private Context c;
   // Create a new Stub object.  The application's array of command
   // arguments are passed to this constructor, where they are saved
   // in a Hashtable object, for later retrieval by the getParameter
   // method.
   Stub (String [] args)
   {
      c = new Context (); // Create an applet context.
      // Make sure an even number of arguments has been passed.
      if ((args.length & 1) != 0)
          return;
      for (int i = 0; i < args.length; i += 2)
           ht.put (args [i], args [i + 1]);
   }
   // Return the current state of an applet.  During initialization,
   // the applet is not active (and this method returns false).  The
   // applet's active state is set to true just before the start
   // method is called.
   public boolean isActive ()
   {
      return active;
   }
   // Return the complete URL of the HTML document containing the
   // applet.  This URL includes the name of the document's file.
   public URL getDocumentBase ()
   {
      URL u = null;
      try
      {
          u = new URL ("file:///" + (new File ("").getAbsolutePath ()) +
                       "/x.html"); // Use a fake document.
      }
      catch (MalformedURLException e) {}
      return u;
   }
   // Return the complete URL of the applet's .class file(s).  This
   // method is often used with the getImage and getAudioClip
   // methods to load image/audio files relative to the .class files.
   public URL getCodeBase ()
   {
      URL u = null;
      try
      {
          u = new URL ("file:///" + new File ("").getAbsolutePath () + "/");
      }
      catch (MalformedURLException e) {}
      return u;
   }
   // Return the value of the applet parameter, identified by the
   // name argument.  If not present, null is returned.  The Applet
   // class contains a getParameter method that calls this method.
   public String getParameter (String name)
   {
      return (String) ht.get (name);
   }
   // Return a reference to the applet's context.  The Applet class
   // contains a getAppletContext method that calls this method.
   public AppletContext getAppletContext ()
   {
      return c; // Return current applet context.
   }
   // Resize the applet.  The Applet class contains a pair of resize
   // methods that call this method. Note: Web browsers don't permit
   // applets from being resized.
   public void appletResize (int width, int height)
   {
   }
   // The following method is an extra method that is called to set
   // the value of the private active variable.
   public void setActive (boolean active)
   {
      this.active = active;
   }
}
/* The Context class provides a mechanism to control the environment
   in which the program is running.  Typically, this environment is
   maintained by a Web browser.  For this program, a Web browser
   environment is being simulated. */
class Context implements AppletContext
{
   // Load the file located by the url argument.  The Applet
   // class contains a pair of getAudioClip methods that call
   // this method.
   public AudioClip getAudioClip (URL url)
   {
      return Applet.newAudioClip (url);
   }
   // Prepare to load the image located by the url argument.  The
   // image is loaded when needed (by one of Graphics' drawImage
   // methods).  The Applet class contains a pair of getImage
   // methods that call this method.
   public Image getImage (URL url)
   {
      Toolkit tk = Toolkit.getDefaultToolkit ();
      return tk.getImage (url);
   }
   // Fetch the Applet (identified by name) from the current HTML
   // document.
   public Applet getApplet (String name)
   {
      return null;
   }
   // Return an enumeration to all Applets located on the current HTML
   // page.
   public Enumeration getApplets ()
   {
      return null;
   }
   // Show the HTML document, located by the url argument, in the
   // current Web browser window.
   public void showDocument (URL url)
   {
      System.out.println ("Showing document " + url);
   }
   // Show the HTML document, located by the url argument, in the
   // Web browser window, identified by the frame argument.
   public void showDocument (URL url, String frame)
   {
      try
      {
         showDocument (new URL (url.toString () + frame));
      }
      catch (MalformedURLException e) {}
   }
   // Show a status message, identified by the message argument, in
   // the Web browser's status bar.  The Applet class contains a
   // showStatus method that calls this method.
   public void showStatus (String message)
   {
      System.out.println (message);
   }
   // The following three methods are required by SDK 1.4.  To learn
   // about those methods, please refer to the SDK 1.4 documentation.
   public InputStream getStream (String key)
   {
      return null;
   }
   public Iterator getStreamKeys ()
   {
      return null;
   }
   public void setStream (String key, InputStream stream)
   {
   }
}

As you study the source code, you'll encounter methods for loading images (getImage()), loading audio clips (getAudioClip() and newAudioClip()), showing a Webpage (showDocument()), and showing status information (showStatus()). I'll talk about those methods in a future Java 101 article. For now, you might want to check out the Java 2 SDK documentation to learn more about them.

To compile Hybrid, type javac Hybrid.java. Compiling that source file results in the creation of several class files. However, you only need to focus on the Hybrid.class file. After compiling successfully, we'll first run Hybrid as an application.

The Hybrid program requires an image file and a sound file. Any image and sound file can be specified as command arguments to Hybrid. I've chosen an image of a cartoon character that serves as Sun's Java mascot. I've stored that character, known as Duke (who looks like a drunken penguin), and its image in duke.gif. In addition, I've selected a giggling sound, which I imagine sounds like Duke after a few cocktails. Type either of the following lines below to run Hybrid as an application. Make sure the image and sound files are located in the same directory as Hybrid.class and the other class files.

java Hybrid image duke.gif sound giggle.wav
java Hybrid sound giggle.wav image duke.gif

When run, Hybrid displays a GUI consisting of an image of Duke and a pair of buttons. If you press the Giggle button, Duke will giggle. If you press the Visit Javasoft button, you won't jump to Javasoft's Webpage because showDocument has not been written to do that. Figure 3 illustrates Hybrid's GUI.

Figure 3. Hybrid's GUI

Now run Hybrid as an applet. To do that, you'll need an HTML file containing code similar to that shown in Listing 6.

Listing 6. Hybrid.html

<applet code="Hybrid.class" width=300 height=200>
  <param name="image" value="duke.gif">
  <param name="sound" value="giggle.wav">
</applet>

If you choose to run this applet by using appletviewer, you still will not advance to Javasoft's Webpage. The appletviewer program is not a real browser; it only recognizes <applet> (and a few other tags). However, if you choose to run this applet via a Web browser, you will arrive at that Webpage, from which you can obtain the latest information on Java. (Note: If you run Hybrid in a Web browser, you will probably not hear Duke giggle. That is because many browsers contain older JVMs that do not recognize the WAV file format -- so they will not recognize giggle.wav. You can rectify that problem by using Java Plug-in.)

Review

Java 101 is changing. This article has set the tone for a column that will completely explore Java 2 Standard Edition Version 1.4 over the next few years. The sidebar, A Road Map, details where we'll go and what we'll discover.

In this article, I presented applications, applets, and hybrid programs, and described their architectures and how to run them.

Next month, I start exploring Java's non-object-oriented language basics.

Jeff Friesen has been involved with computers for the past 20 years. He holds a degree in computer science and has worked with many computer languages. Jeff has also taught introductory Java programming at the college level. In addition to writing for JavaWorld, he wrote his own Java book for beginners -- Java 2 By Example, Second Edition (Que Publishing, 2001) -- and helped write a second Java book, Special Edition Using Java 2 Platform (Que Publishing, 2001). Jeff goes by the nickname Java Jeff (or JavaJeff). To see what he's working on, check out his Website at http://www.javajeff.com.

Learn more about this topic