NNTP Implementation


There are two JSP pages used to display the NNTP client information. The code for the group list, nntp_groups.jsp (shown rendered in the browser in Figure 4-4), can be found in Listing 4-3.

Listing 4-3. NNTP Group List JSP
 <%@ page contentType="text/html; charset=iso-8859-1"     language="java" import="com.cascadetg.ch04.*" %> <% String newsgroup_pattern = "comp.lang.java.*"; NNTPConnection myNNTPConnection = new NNTPConnection(); if(request.getParameter("refresh") != null)     myNNTPConnection.refreshNewsgroupList(newsgroup_pattern); org.apache.commons.net.nntp.NewsgroupInfo[] newsgroups =     myNNTPConnection.getNewsgroupList(newsgroup_pattern); %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Java Newsgroups</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <link href="../ch03/default.css" rel="stylesheet" type="text/css" /> </head> <body> <p><strong><%= newsgroup_pattern %></strong></p><hr /> <table width="100%"  border="0" cellspacing="3" cellpadding="3">      <tr>          <td><strong>Newsgroup</strong></td>          <td><strong>Articles</strong></td>          <td>&nbsp;</td>      </tr>  <%  for(int i = 0; i < newsgroups.length; i++) { %>      <tr>          <td><%= newsgroups[i].getNewsgroup() %></td>          <td><%= newsgroups[i].getArticleCount() %></td>          <td><a href="nntp_message.jsp?newsgroup=<%=              newsgroups[i].getNewsgroup() %>&article=<%=              newsgroups[i].getLastArticle()              %>">Most Recent Article</a></td>      </tr>  <% } %>  </table><hr />  <form name="form1"  method="post" action="">    <input name="refresh" type="submit"         value="Refresh Newsgroup List" />  </form>  </body>  </html> 

The code in Listing 4-3 relies on the underlying org.apache.commons.net. nntp.NewsgroupInfo class for the information about the groups. Figure 4-6 shows the methods of this class.

Figure 4-6. NewsgroupInfo class diagram.


Clicking on the link for the Most Recent Article displays the nntp_message.jsp page. The listing for nntp_message.jsp is shown in Listing 4-4.

Listing 4-4. NNTP Message Display JSP
 <%@ page contentType="text/html; charset=iso-8859-1"     language="java" import="com.cascadetg.ch04.*" %> <% NNTPConnection myNNTPConnection = new NNTPConnection(); String newsgroup = request.getParameter("newsgroup"); String article = request.getParameter("article"); String header = myNNTPConnection.getArticleHeader(newsgroup, article); String body = myNNTPConnection.getArticleBody(newsgroup, article); String previousArticle = myNNTPConnection.previousArticle(newsgroup, article); String nextArticle = myNNTPConnection.nextArticle(newsgroup, article); %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title><%= newsgroup %> <%= article %></title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <link href="../ch03/default.css" rel="stylesheet" type="text/css" /> <style type="text/css"> <!-- .header {     background-color: #EEEEEE;     border: thin solid #CCCCCC; } .body {     font-family: "Courier New", Courier, mono;     font-size: small; } --> </style> </head> <body> <table width="100%"  border="0" cellspacing="3" cellpadding="3">   <tr>     <td><strong><%= newsgroup %> #<%= article %></strong></td>   </tr> </table> <hr /> <table width="100%"  border="0" cellspacing="0" cellpadding="0">   <tr>     <td width="33%"><%     if(previousArticle != null)     { %>         <a href="nntp_message.jsp?newsgroup=<%=newsgroup%>&article=<%=             previousArticle %>">&lt;- Previous</a> <%  } %>&nbsp;</td>     <td align="center"><a         href="nntp_groups.jsp"><strong>Return To Group List</strong></a></td>     <td width="33%" align="right"><%     if(nextArticle != null)     { %>         <a href="nntp_message.jsp?newsgroup=<%=newsgroup%>&article=<%=             nextArticle %>">Next -&gt;</a> <%    } %>&nbsp;</td>   </tr> </table> <hr /> <table width="100%"  border="0" cellpadding="3"     cellspacing="3" >   <tr>     <td ><%= header %></td>   </tr> </table> <br /> <p >     <%= body %><br /> </p> </body> </html> 

Now that you've seen the JSP presentation code, let's look at the underlying Java code. A diagram is shown in Figure 4-7.

Figure 4-7. NNTP Connection example classes.


Looking at the NNTPConnection code shown in Listing 4-5, you'll notice that several java.util.Hashtable objects are used to cache the content retrieved. As NNTP is typically read-and-post, but messages are rarely deleted, this is a reasonable approach. Other NNTP clients cache data on disk or in a database.

You'll also notice the liberal use of the synchronized keyword to avoid contention over the use of the NNTPClient objectit's important for certain commands to be issued in sequence. For example, the NNTPConnection.nextArticle() method issues a client.selectNewsgroup() call and then immediately issues client.selectNextArticle() and client.selectArticle(). If these calls were to be intermixed with other connections, the wrong data might be returned.

Listing 4-5. NNTP Connection Example
 package com.cascadetg.ch04; import org.apache.commons.net.nntp.*; // http://nagoya.apache.org/bugzilla/show_bug.cgi?id=26282 public class NNTPConnection {     /**      * This class attempts to improve performance over the      * FTPConnection class by using a single static client to      * retrieve data. It caches some of the data in-memory. You'll      * notice that this cache code adds significant complexity,      * however.      */     static org.apache.commons.net.nntp.NNTPClient client =         new org.apache.commons.net.nntp.NNTPClient();     /** These serve as our in-memory cache of retrived data */     static NewsgroupInfo[] newsgroups = null;     static java.util.Hashtable article_bodies =         new java.util.Hashtable();     static java.util.Hashtable article_headers =         new java.util.Hashtable();     static java.util.Hashtable article_pointers =         new java.util.Hashtable();     /** Gets the latest newsgroup list, ignoring the cache */     public void refreshNewsgroupList(String newsgroup_pattern)     {         checkConnection();         try         {             synchronized (client)             {                 newsgroups =                     client.listNewsgroups(newsgroup_pattern);             }         } catch (Exception e) { e.printStackTrace(); }     }     /** Gets the latest articleID, given a newsgroup name */     public int getRecentArticleID(String newsgroup)     {         for (int i = 0; i < newsgroups.length; i++)         {             if (newsgroups[i].getNewsgroup().compareTo(newsgroup)                 == 0)                 return newsgroups[i].getLastArticle();         }         return -1;     }     /** Gets the list of newsgroups, from the cache if possible */     public NewsgroupInfo[] getNewsgroupList(String newsgroup_list)     {         if (newsgroups == null)             refreshNewsgroupList(newsgroup_list);         return newsgroups;     }     /** Gets an article pointer given a newsgroup and articleID. */     ArticlePointer getArticlePointer(         String newsgroup,         String articleID)     {         if (article_pointers.containsKey(newsgroup + articleID))             return (ArticlePointer)article_pointers.get(                 newsgroup + articleID);         checkConnection();         ArticlePointer newPointer = new ArticlePointer();         String result = "";         try         {             synchronized (client)             {                 client.selectNewsgroup(newsgroup);                 client.selectArticle(articleID);                 getAsString(                     client.retrieveArticleBody(                         articleID,                         newPointer),                     false);             }             article_pointers.put(newsgroup + articleID, newPointer);             return newPointer;         } catch (Exception e)         {             result = "Unable to retrive.";             e.printStackTrace();         }         return null;     }     /**      * Gets the next article given a current article. Note that      * this method relies on the server to provide the previous and      * next article information.      */     public String nextArticle(String newsgroup, String articleID)     {         if (articleID == null)             return null;         ArticlePointer myPointer = new ArticlePointer();         checkConnection();         try         {             synchronized (client)             {                 client.selectNewsgroup(newsgroup);                 client.selectArticle(Integer.parseInt(articleID));                 if (!client.selectNextArticle(myPointer))                     return null;                 client.selectArticle(                     myPointer.articleNumber,                     myPointer);             }         } catch (Exception e)         {             e.printStackTrace();         }         return myPointer.articleNumber + "";     }     /**   See nextArticle for more information.      */     public String previousArticle(         String newsgroup,         String articleID)     {         if (articleID == null)             return null;         ArticlePointer myPointer = new ArticlePointer();         checkConnection();         try         {             synchronized (client)             {                 client.selectNewsgroup(newsgroup);                 client.selectArticle(Integer.parseInt(articleID));                 if (!client.selectPreviousArticle(myPointer))                     return null;                 client.selectArticle(                     myPointer.articleNumber,                     myPointer);             }         } catch (Exception e)         {             e.printStackTrace();         }         return myPointer.articleNumber + "";     }     /** Gets the body of an article, preferably from the cache */     public String getArticleBody(         String newsgroup,         String articleID)     {         if (article_bodies.containsKey(newsgroup + articleID))             return (String)article_bodies.get(                 newsgroup + articleID);         checkConnection();         String result = "";         try         {             synchronized (client)             {                 client.selectNewsgroup(newsgroup);                 client.selectArticle(articleID);                 result =                     getAsString(                         client.retrieveArticleBody(articleID),                         true);             }             article_bodies.put(newsgroup + articleID, result);         } catch (Exception e)         {             result = "Unable to retrive.";             e.printStackTrace();         }         return result;     }     /** Gets the header of an article, preferably from the cache */     public String getArticleHeader(         String newsgroup,         String articleID)     {         if (article_headers.containsKey(newsgroup + articleID))             return (String)article_headers.get(                 newsgroup + articleID);         String result = "";         try         {             checkConnection();             synchronized (client)             {                 client.selectNewsgroup(newsgroup);                 client.selectArticle(articleID);                 result =                     getAsString(                         client.retrieveArticleHeader(articleID),                         false);             }             java.util.StringTokenizer myTokenizer =                 new java.util.StringTokenizer(result, "\n", true);             result = "";             while (myTokenizer.hasMoreTokens())             {                 String current = myTokenizer.nextToken();                 current =                     Utilities.replaceToken(current, "<", "&lt;");                 current =                     Utilities.replaceToken(current, ">", "&gt;");                 if (current.startsWith("From:"))                     result = result + current + "<br />";                 if (current.startsWith("Subject:"))                     result = result + current + "<br />";                 if (current.startsWith("Date:"))                      result = result + current + "<br />";             }             article_headers.put(newsgroup + articleID, result);         } catch (Exception e)         {             result = "Unable to retrive.";             e.printStackTrace();         }         return result;     }     /**   The various body & header methods return Readers, not      * Strings. This utiltiy method converts from a Reader to a      * String, and also performs some basic HTML formatting if      * requested.  */     public String getAsString(java.io.Reader in, boolean HTML)     {         if (in == null)             return "";         java.io.BufferedReader bufferedReader =             new java.io.BufferedReader(in);         StringBuffer temp = new StringBuffer();         boolean read = true;         String current = "";         while (read)         {             try             {                 current = bufferedReader.readLine();             } catch (Exception e)             { read = false; }             if (current != null)             {                 current = current + "\n";                 if (HTML)                 {                     current =                         Utilities.replaceToken(                             current, "<", "&lt;");                     current =                         Utilities.replaceToken(                             current, ">", "&gt;");                     temp.append(current);                     temp.append("<br />");                 } else                 { temp.append(current); }             } else read = false;         }         return temp.toString();     }     /**   Verifies that the NNTP connection is valid, and attempts to      * reestablish if not. */     protected synchronized void checkConnection()     {         if (client.isConnected())         {             synchronized (client)             {                 try                 {                     client.stat();                     return;                 } catch (Exception e)                 {                     // Ok, failed, so let's try to reconnect.                     try                     {                         client.disconnect();                     } catch (Exception e1)                     {  // No need to report this                     }                 }             }         }         try         {             synchronized (client)             {                 client.connect(NetConnectionTokens.nntp_server);                 client.getReplyCode();                 client.authenticate(                     NetConnectionTokens.nntp_username,                     NetConnectionTokens.nntp_password);             }         } catch (Exception e)         {             System.err.println("Unable to connect to NNTP server!");             e.printStackTrace();         }     }     public synchronized void close()     {         try         {             synchronized (client)             { client.logout(); }         } catch (Exception e)         {   // Silent failure.         }         try         {             synchronized (client)             { client.disconnect(); }         } catch (Exception e) { // Silent failure.         }     } } 

In a "real" NNTP client application, one can think of all sorts of additional features, such as preloading and sorting the headers on another thread, perhaps providing a threaded view of the messages, and interweaving the replies (for more information on NNTP message threading, see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=26282).

Tip:

If you are interested in setting up your own NNTP server, you may want to investigate James, http://james.apache.org/, a Java-based SMTP, POP3, and NNTP server.




    Apache Jakarta Commons(c) Reusable Java Components
    Real World Web Services
    ISBN: N/A
    EAN: 2147483647
    Year: 2006
    Pages: 137
    Authors: Will Iverson

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net