HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

The conventional way to present up-to-date information is to keep it on your Web site or a Web site you have some access to or control over so you can modify the information as needed. This article describes a way to create newsfeeds using Java applets so that the applet can be embedded anywhere in any page, and the applet distributor can keep the presented information up to date by modifying information on the site. Ways to extend the ability of the applet using CGI programming are also examined. This solves problems for theaters, clubs and other organizations that want their information on many pages but want to maintain it in just one spot. Establishments that want to distribute information about sales and events, and, of course, news organizations, can also benefit from distributing newsfeeds. This article also discusses a way to ensure that the information isn't loaded from cache (anticaching).

Conceptually, this applet connects back to the server and retrieves information - from a flat file or a script - as a stream of bytes that are cast to chars and appended to a StringBuffer. The toString() method in StringBuffer is called, and the resulting string is parsed to create a Vector containing the news and any other information returned by the server. When you want to use the applet as a newsfeed, you can permit other sites to embed the applet in their pages (using the CODEBASE="URL" attribute, with the URL being the directory on your server where the class files are, just as you can embed an image loaded from another server in another page, as shown in Figure 1). The applet then connects back to the server from which it was downloaded and gets the information, irrespective of the page in which it's embedded.

Figure 1
Figure 1:

The Code
The source code for the applet, contained in the file newsfeed.java, is less than 300 lines, yet it contains all the functionality necessary to create a simple newsfeed. It uses Java 1.0 event handling to keep the event handling simple and to work with older browsers. To compile it using a JDK 1.1 compiler, you'll need to use deprecation: javac newsfeed.java -deprecation.

The included packages and classes are needed for painting (awt.*), storing information in a vector (Vector), parsing the information returned from the server (StringTokenizer), networking (net.*), input/output classes and required exceptions (io.*), and working with dates (Date).

The integer variables that have global scope within the class -speed, iHeight, iTimesThrough and iCurrentPosition -store the speed of the thread and the font height and keep track of the number of times the retrieved news has been presented and the current position in the list of news items. The initial value of iTimesThrough, 11, is explained in the paint() method. The Thread variable, called newsthread, is used to implement the Runnable interface. An Image object is used for the background image. The String objects - strCurrentString and strCurrentURL - are used to store the current news item and the associated URL. The boolean bCalledFromRun, if true, increments iCurrentPosition when paint() is called. The Vector veInformation is used to contain the news items retrieved from the server.

The class extends Applet and can be run either in a browser or in AppletViewer. Implementing Runnable permits the class to spawn and run in its own thread. To implement the Runnable interface, we need to implement start(), stop(), and run() methods (discussed below).

Unlike most applets, newsfeed doesn't override init(); all the initialization is done in the initialize() method. This method is called from paint() the first time that paint() is called and every 10 times thereafter to refresh the news.

The initialize() method is the key method of the applet. The Date object that's created is used to get the current time in milliseconds (stored in lUnique), which is used to circumvent caching (anticaching is discussed later in this article). The passed-in graphics object is used to draw a string indicating that the news is being downloaded and to get the font metrics, from which the font height is determined and stored in iHeight.

The strType variable stores the type of news that's going to be requested. This information is used only if a script will process the request. Setting iCurrentPosition and strCurrentURL variables to 0 and ".", respectively, clears them.

Listings 1 contains five examples of URLs that can be read from. If you're experimenting with the applet using AppletViewer, you'd include the file called news.txt in the same directory as the applet (the file format of the news file is discussed later) and then run the applet in AppletViewer. Remember, when running in a browser, an applet can generally connect back only to the server from which it was downloaded. The URL string to use depends on whether you're using a flat file or a script, and the sophistication of the script. If you're using a flat file, you'd use "/news/news.txt". The applet gets the host name using getCodeBase().getHost() and creates a URL with the returned host and the path "/news/news.txt". If the applet was downloaded from www.lithic.com and you used the path "/news/news.txt", the URL would be http://www.lithic.com/news/news.txt. The "?"+lUnique circumvents caching. You can, of course, use other paths and file names, but the file must be a publicly accessible ASCII file in the correct format. The last three examples of URL are scripts, discussed later in this article.

One problem in retrieving the current news is that the requested URL may be loaded from cache. This can be overcome by creating a unique URL each time, which can be done by including a unique number in the query string. One way to accomplish this is to include the current time in the query string. If the time between queries is greater than the resolution of the system clock, this should overcome the caching problem. Using a Date to get the current time in milliseconds and appending it to the query strings of the script URLs is an effective anticaching strategy. The same strategy can be used with URLs that are not scripts, because they generally ignore the query string (an HTTP server will generally serve the same page if you type in index.html or index.html?123). The key thing is to present the browser with a URL that is unique so it won't find it in cache.

After the URL object is instantiated, openStream() is called, which returns an InputStream for reading from that connection. The InputStream object is used to instantiate a DataInputStream, which we read from until we reach the end of file, represented by an EOFException. The bytes returned from the DataInputStream are cast to chars, then appended to a StringBuffer. The string representing the contents of the URL is recovered by using the toString() method in StringBuffer. This is a general-purpose method for creating a string from the content of a URL - if you try this using (String)url.getContent(), you may get a ClassCastException. The vectorize() method takes the retrieved String and a token String as arguments and returns a Vector containing the news. In the information retrieved from the file or script, we expect the zeroth line - which becomes the zeroth node in veInformation - to be the name of an image stored in the codebase. After this image name is stored in strImage, the zeroth is removed, leaving as many nodes in the Vector as there are news items. After the image is loaded, the variable that tracks the number of times the new items have been presented is set to 0.

To simplify working with the information returned from the server, the vectorize() method creates a Vector from it. The two parameters passed in are the String to parse and the String on which to tokenize it. If the String to parse ended with the token used to parse, we'd end up with a final piece of zero length. To avoid this, if the string ends with the parse character, it's pulled off. Because most files or streams of bytes sent via a server are punctuated by a <cr><lf>, both are checked for and removed if they end the strIn. Most of the work is done using a stringTokenizer. After a Vector is instantiated, the stringTokenizer is instantiated with the string to parse on and the string to parse. In our model the string to parse on is always a new line, but passing in the string to parse on makes the method more flexible. The code then tokenizes the string, adding each token to the Vector.

The code that handles tokenizing by "\n" checks to see if a \r or \n is present and pulls them off the end of each token (they're assumed to be on the end; if your code needs these characters for anything, you may need to modify this code). The Vector is then trimmed to size and returned.

The paint() method, which is called repeatedly from run(), is used as the engine of this applet. The size of the applet is determined first, to be used to clear the applet's graphics context. If this is the first time through (iTimesthrough variable is initially set to 11, so initialize() will be called the first time through paint()), or if the number of times the news has been presented exceeds some arbitrary number of times (here 10), the news is refreshed using the initialize() method.

If an image is available, it's drawn on the background. The text is offset 45 pixels to be to the left of the image (it could also be drawn on top of the image). The examples in Figures 2 and 3 are just names for a company or club, but in practice they could be an advertisement or something associated with the news item. The image name is downloaded with the news, so it can be changed with each refresh.

Figure 2
Figure 2
Figure 3
Figure 3

Each line of news is in two parts: the URL is everything up to the first space, and the news item is everything after. After the current line of news is retrieved, the iCurrentPosition variable is incremented if bCalledFromRun is true. Initially the paint method is called repeatedly as the background image is drawn. To prevent an initial race through the news, the current position increments only if the paint() method was called from run(). New news is then drawn, in blue, 45 pixels from the left side of the applet and iHeight down (the iHeight variable was set to the font height in the initialize() method). Checking on whether the iCurrentPosition variable is bigger than veInformation, the Vector, which holds all the news items, permits the news to "wrap" back to the first news item if the current news item is the last. The iTimesThrough variable is incremented if the news wrapped, tracking the number of times that the current news has been displayed.

The mouseDown(), mouseEnter() and mouseExit() methods work together to create a mouse interface for the applet. In mouseDown(), if the current URL (set in paint()) isn't a ".", a URL object is instantiated and passed to the showDocument() method in the applet context for this applet. Using a "." permits presenting news items for which there is no link. The mouseEnter() method draws the current news item in red and shows the link in the status bar with showStatus(), both of which are common in applets that show menus or image maps. The thread is stopped in mouseEnter() so the current news item will remain displayed. The mouseExit() method undoes what the mouseEnter() method did by converting the text color back to blue, clearing the status and then starting the thread again.

The start(), stop() and run() methods are used to implement the runnable interface. A Thread object called newsthread is instantiated and started in the start() method. The run() method is called repeatedly, and the thread is paused for speed milliseconds; then repaint() is called. The stop() method is called when the applet stops.

To update the news in real time, just upload a new flat file or change the data in the database if you're using a database with a script. Flat files can also be changed by using CGI scripts that allow an authenticated user to modify, delete or add to the contents of the flat file. The next time the applet accesses the flat file or script, it'll get the current information (sometimes flat files are cached by the server, but in our experience this hasn't been a problem).

Working with CGI Scripts vs Flat Files
Whether using a flat file, a CGI script that uses a flat file for data or a CGI script that uses a DSN and a database, the applet expects the data to be presented in a certain format:

line1: Image<optional \r><\n >
line2: URL1[space]Text for news item 1<optional \r><\n >
line3: URL2[space]Text for news item 2<optional \r><\n >
.
.
.
Line: URLn-1[space]Text for news item n-1<optional \r><optional \n>

Any data source that can supply text in this format can act as a data source for the newsfeed. In practice, the designer would probably choose something with more flexibility and capability, but this works fine for our example. In any case, if you use a flat file, remember to upload it using ASCII mode.

The news.txt file has information in the format above, so it can be parsed directly by the applet. The information in cginews.txt must be supplied to the applet via newsfeed.pl, a Perl script. If you use the script and the cginews.txt, be sure that the permission of the script is executable, the path to the Perl interpreter is correct and the cginews.txt is world readable. Note that \n is read from the cginews.txt file; if the terminal character on each line is not a \n , the information won't parse correctly when it's received by the applet. (Perl programming is beyond the scope of this article, but if you have some experience, these scripts should be a starting point. If not, the flat file can provide most of the same functionality.)

The Perl script newsfeed.pl first retrieves the query string (this is what's after the "?"). If the URL accessed was http://www.lithic.com/cgi-local/newsfeed.pl?ls930353830, the query string would be ls930353830. The numbers after the Is represent the anticaching strategy discussed above. The first two characters are passed to the PrintNews function, which prints characters 2 through the last character of every line in the cginews.txt file that start with these two letters.

In the example case the two letters are "ls". The first two characters of each line, which are the code compared to the type passed in, are removed before the line is printed. Using a code permits a single applet with a single data source to supply different news depending on the query string. This could be combined with cookies on the page the applet was embedded in, so user preferences could dictate what news was sent.

A guardian is set so that if no lines were printed for a given two-letter combination, a "." and a line indicating there was no news would be printed out.

Modifications to the Perl script and minor modifications to the applet would permit a more sophisticated interaction with the user. For example, if the URL http://www.lithic,com/cgi-local/moresubstantialnewsfeed.pl?type=Is&docbase=[doc base]&time=[time] were constructed and opened, where [doc base] and [time] are understood to be the page that the applet is embedded in and the current time, and the information was parsed by the script, the type and codebase would be known (knowing the codebase would permit returning an appropriate message to unauthorized newsfeeds, rather than the news). The time is still used for anticaching, but it may also be interesting statistically.

Conclusion
The newsfeed is easy to set up and distribute, but if you have some difficulty, here are some things to check.

  • In the newsfeed.java, did you choose the right string for the URL (for example, if you have a news.txt in a feed directory, make sure you create a string that points at /feed/news.txt)?
  • Check if the URL creation code was changed to url=new URL(str) (this is indicated in comments in the code).
  • Did you upload the news file in ASCII mode and the class files in binary mode?
  • You should use the Perl script only if you have some experience with Perl.
  • You may want to remove the ?=code after the .txt if you're using a flat file and having problems.

This implementation of a newsfeed, though limited, created a very small class file that displays news, responds to button clicks when there is a link associated with a news event, and circumvents caching. Many improvements are possible: adding more colors, offsets, speed, audio clips and so forth. Additionally, double buffering would offer smoother graphics. The applet offers a functional framework in a small package.

Using a newsfeed solves many of the problems associated with the static nature of Web pages and solves access issues. For example, a document on a CD-ROM could present current information (provided that the person reading the HTML document in which the newsfeed was embedded was attached to the Internet). Used correctly, newsfeeds based on the applet described in this article can be a way to make the net more relevant, provide better service to your customers and substantially decrease the time it takes to disseminate information.

AUTHOR BIO
John Keogh is president of Lithic Software Corporation, an Internet software and services company. He has a background in computer science, chemistry and technical writing. John programs in C, C++, Java, Perl, SQL and several other languages. You can visit his Web site at www.Lithic.com/.
He can be reaced at: [email protected]

	

Listing 1:  newsfeed.java

//newsfeed.java a class for creating a newsfeed

//includes
import java.awt.*;
import java.util.Vector;
import java.util.StringTokenizer;
import java.net.*;
import java.io.*;
import java.util.Date;


//class that implements a simple newsfeed
public class newsfeed extends java.applet.Applet
                              implements Runnable
  {
  int speed=2000, iHeight=0, iTimesThrough=11,
      iCurrentPosition;
  Thread newsthread;
  Image image;
  String strCurrentURL,  strCurrentString;
  boolean bCalledFromRun=false;
  Vector veInformation;


  //called from paint when needed, don't want to
  //call from init, because we need a graphics
  //object for this
  private void initialize(Graphics g)
    {
    String strType=getParameter("type");
    if(strType==null)
      strType="ls";


    //a date is used to prevent caching
    Date date=new Date();
    long lUnique=date.getTime();


    iHeight=g.getFontMetrics().getHeight()+2;


    g.setColor(Color.black);
    g.drawString("Getting current news...", 45,
                  iHeight);


    strCurrentURL=".";
    iCurrentPosition=0;
    //first one for use on your own system, with
    //appletviewer, second third, fourth, or fifth
    //for use on the Web, depending on if you use
    //a flat file in a directory called news, or a
    //script in a cgi-local or cgi-bin directory, and
    //how the script parses the query string
    String str="news.txt";
    //String str="http://"+getCodeBase().getHost()+
    //           "/news/news.txt?"+lUnique;
    //String str="http://"+getCodeBase().getHost()+
    //           "/cgi-local/newsfeed.pl?"+
    //           strType+iUnique;
    //String str="http://"+getCodeBase().getHost()+
    //           "/cgi-bin/newsfeed.pl?"+
    //           strType+lUnique;
    //String str="http://"+getCodeBase().getHost()+
    //          "/cgi-bin/newsfeed.pl?type="+
    //           strType+"&time="+lUnique+
    //           "&docbase="+getDocbase();


    //a url object is created, then a stream is created
    //to retrieve the content of the url
    URL url=null;
    String strContent="";
    try
      {
      //first one for use on your own system, with
      //AppletViewer, second for use on the Web
      url=new URL(getCodeBase(), str);
      //url=new URL(str);
      }
    catch(MalformedURLException m){return;}
    try
      {
      StringBuffer sb=new String-
      Buffer("");
      InputStream is=url.openStream();
      DataInputStream dis=new DataIn-
      putStream(is);
      while (true)
        {
        try{sb.append((char)dis.read-
        Byte());}
        catch(EOFException f){break;}
        }
      strContent=sb.toString();
      }
    catch(IOException e){return;}


    //after the contents of the url are
    //retrieved, they are parsed to
    //create a Vector which contains
    //the information
    veInformation=vectorize(strContent, "");


    //get the background image, then
    //remove it
    String strImage=(String)veInforma-
    tion.elementAt(0);
    veInformation.removeElementAt(0);


    //load the image, if there is one
    if(strImage.compareTo(".")!=0)
      image=getImage(getCodeBase(),
      strImage);


    iTimesThrough=0;
    }


  //parses the returned information
  //based on <CR>
  private Vector vectorize(String strIn,
                      String strParse)
    {
    //if it ends with our parse charac-
    ter, pull it off the end
if(strIn.endsWith("\r")||strIn.endsWith("")||strIn.endsWith(strParse))
      strIn=strIn.substring(0,
      strIn.length()-1);    if(strIn.endsWith("\r")||strIn.endsWith("\n")
      strIn=strIn.substring(0,
      strIn.length()-1);


    Vector ve=new Vector(1);


    //get ready to tokenize the string
       StringTokenizer t = new StringTo-
      kenizer(strIn, strParse);
    int iLines = t.countTokens();


    if(strParse.compareTo("\n")==0)
      {
      for(int i=0; i<iLines; i++)
        {
        String str=t.nextToken();
        if(str.indexOf("\n")!=-1)
          str=str.substring(0,
          str.length()-1);
        if(str.indexOf("\n")!=-1)
          str=str.substring(0,
          str.length()-1);
        ve.addElement(str);
        }
      }
    else
      {
      for(int 1=0; i<iLines; i++)
        ve.addElement(t.nextToken());
      }


    ve.trimToSize();
    return ve;
    }



  public void paint(Graphics g)
    {
    Dimension d=this.size();


    //the first time through, and each
    //ten thereafter, get the news
    if(iTimesThrough>10)
      {
      //clear this
      g.setColor(Color.white);
      g.fillRect(0, 0, d.width, d.height);


      initialize(g);
      }


    //clear
    g.setColor(Color.white);
    g.fillRect(0, 0, d.width, d.height);


    //draw the background image
    if(image!=null)
      g.drawImage(image, 0, 0, this);


    //get the current text and url,
    //then draw the text
    String strTemp=(String)veInforma-
    tion.elementAt(iCurrentPosition);
    strCurrentURL=strTemp.substring(0,
    strTemp.indexOf(" "));
    strCurrentString=strTemp.sub-
    string(strTemp.indexOf(" ")+1);
    g.setColor(Color.blue);
    g.drawString(strCurrentString, 45,
    iHeight);


    //only advance if called from run
    if(bCalledFromRun)
      iCurrentPosition++;
    bCalledFromRun=false;


    if(iCurrentPosition>=veInfor-
    mation.size())
      {
      iCurrentPosition=0;
      iTimesThrough++;
      }
    }


  //overide update for smoother graphics
  public void update(Graphics g)
    {
    paint(g);
    }


  //If there is a mousedown go to the url
  //associated with this news event.  A
  //"." is used if there is no url
  //associated with the news item
  public boolean mouseDown(Event e, int
  x, int y)
    {
    if(strCurrentURL.compareTo(".")!=0)
      {
      URL url;
      try{url=new URL(strCurrentURL);}
      catch(MalformedURLException
      m){return true;}
      this.getAppletContext().showDocu-
      ment(url);
      }
    return true;
    }


  //if the mouse enters the applet,
  //repaint the text red and stop run-
  //ning. Set the status to the url
  //associated with the news item
  public boolean mouseEnter(Event e,
  int x, int y)
    {
    Graphics g=this.getGraphics();


    //clear
    Dimension d=this.size();
    g.setColor(Color.white);
    g.fillRect(0, 0, d.width,
    d.height);


    //draw the background image
    if(image!=null)
      g.drawImage(image, 0, 0, this);


    g.setColor(Color.red);


    g.drawString(strCurrentString, 45,
    iHeight);
    if(strCurrentURL.compareTo(".")!=0)
      showStatus(strCurrentURL);
    else
      showStatus("No Link");
    this.stop();
    return true;
    }


  //if the mouse exits the applet,
  //repaint the text blue and start
  //running again. Clear the status
  public boolean mouseExit(Event e, int
  x, int y)
    {
    Graphics g=this.getGraphics();
    g.setColor(Color.blue);
    g.drawString(strCurrentString, 45,
    iHeight);
    this.start();
    showStatus("");
    return true;
    }



  //start stop and run are required to
  //implement the Runnable interface.
  //Speed can be adjusted to present
  //information faster or slower.
  public void start()
    {
         newsthread = new Thread(this);
         newsthread.start();
    }


  public void stop()
    {
         newsthread.stop();
    }


  public void run()
    {
         while (true)
      {
      bCalledFromRun=true;
         try {Thread.currentThread()
        .sleep (speed);}
      catch (InterruptedException e){}
           repaint();
      }
    }
}


Listing 2:  news.txt

lithic.gif
tmtm.gif
lshttp://www.lithic.com/java/PersonalChatware.html Have a look at the
plugin API in lpc
lshttp://www.lithic.com/java/calculator.html See Lithic Software's newest
applet
lshttp://www.lithic.com/onlinesystems/index.html Check out LSC Online
Systems
tmhttp://www.toastmasters.org/ Toastmasters main page is more useful than
ever
tmhttp://www.lithic.com/tm/tm.html Grand Junction Clubs now have a page



<HTML>
<HEAD>
<title>Lithic Newsfeed</title>

<!newsfeed.java a page with the newsfeed.class embedded>

</HEAD>

<BODY BGCOLOR=White>

<CENTER>
<APPLET CODE="newsfeed.class" width=300 height=20>
<PARAM NAME=type VALUE="1S">
</APPLET>
</CENTER>

<BODY>
</HTML>







Listing 4:  newsfeed.pl

#!/usr/bin/perl
# newsfeed.pl


#newsfeed.pl a script for sending out news
MAIN:
  {
  $file="cginews.txt";
  $querystring=$ENV{"QUERY_STRING"} ;
  print("Content-type: text/html");
  if($querystring)
    {
    $query=substr($querystring, 0, 2);
    &PrintNews($file, $query);
    }
  }


#prints out all the lines that begin with the
#appropriate letters the letter is the token
#sent in the query string
sub PrintNews
  {
  local($file, $control)[email protected]_;
  local($guardian);


  $guardian="";


  open(TIMES, $file);
  while(<TIMES>)
    {
    $type=substr($_, 0, 2);
    if($type=~/$control/)
      {
      $content=substr($_, 2);
      print($content);
      $guardian="t";
      }
    }
  close(TIMES);


  #if nothing was found, just print out
  that no news is currently available
  if(!$guardian)
    {
    print("");
    print(" no news currently available");
    }
  }


Listing 5:  cginews.txt

lslithic.gif
tmtm.gif
lshttp://www.lithic.com/java/PersonalChatware.html Have a look at the
plugin API in lpc
lshttp://www.lithic.com/java/calculator.html See Lithic Software's newest
applet
lshttp://www.lithic.com/onlinesystems/index.html Check out LSC Online
Systems
tmhttp://www.toastmasters.org/ Toastmasters main page is more useful than
ever
tmhttp://www.lithic.com/tm/tm.html Grand Junction Clubs now have a page



      
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.