You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by Rory Winston <rw...@eircom.net> on 2004/01/20 14:53:17 UTC

[net] Patch for NNTP to facilitate threading of messages

Here is a patch for the NNTP component to facilitate message threading -
i.e. organizing messages in a structured 'threaded' format based on the
References: value in their headers. The algorithm for this was originally
devised by Jamie Zawinski (see his explanation at
http://www.jwz.org/doc/threading.html). The implementation is based on his
original implementation for Grendel, a defunct Mozilla component. In order
for an Article object to be threadable, it must implement the Threadable
interface, and contain some pointers to other messages that will be
populated when the message tree is constructed. Here is a sample of how this
works (This uses the Extended NNTP command XOVER that I added in a previous
patch, which was commited by Daniel):

// get the article headers
Article[] articles = getArticleInfo(lowArticleNumber, highArticleNumber);

// Now thread the articles and display them
Threader threader = new Threader();

// This will return a tree of articles in threaded format
Article theRootArticle = (Article) threader.thread(articles);
Article.printThread(theRootArticle, 0);

This simple console-based example, when invoked on a group like
comp.lang.ada, will result in an output like:

...
ctags for ada	"Xenos" <do...@spamhate.com>
==>Re: ctags for ada	Stephen Leake <St...@nasa.gov>
==>Re: ctags for ada	Preben Randhol <ra...@pvv.org>
New, fixed, version of AdaGraph	Jerry van Dijk <so...@nospam.demon.nl>
ANN: New SPARK (including RavenSPARK) definition now available
rod.chapman@praxis-cs.co.uk (Rod Chapman)
==>Re: ANN: New SPARK (including RavenSPARK) definition now available
snarflemike@yahoo.com (Mike Silva)
CString	Szymon Guz <al...@skynetSMIECI.VONorg.NOJUSZpl>
==>Re: CString	"Luke A. Guest"
<la...@n_o_p_o_r_k_a_n_d_h_a_m.abyss2.demon.co.uk>
==>Re: CString	Szymon Guz <al...@skynetSMIECI.VONorg.NOJUSZpl>
==>Re: CString	Duncan Sands <ba...@free.fr>
==>Re: CString	David Marceau <da...@sympatico.ca>
==>Re: CString	"Frank J. Lhota" <NO...@rcn.com>
httpng	"Andrew Carroll" <an...@carroll-tech.net>
==>Re: httpng	"amado.alves" <am...@netcabo.pt>
==>Re: httpng	"Stephane Richard" <st...@verizon.net>
==>Re: httpng	Pascal Obry <p....@wanadoo.fr>
Latin_1 and portability	xavier.serrand@free.fr (Xavier Serrand)
==>RE: Latin_1 and portability	Kilgallen@SpamCop.net (Larry Kilgallen)
==>RE: Latin_1 and portability	"amado.alves" <am...@netcabo.pt>
==>RE: Latin_1 and portability	"amado.alves" <am...@netcabo.pt>
==>Re: Latin_1 and portability	=?ISO-8859-1?Q?Bj=F6rn?= Persson
<bj...@sverige.nu>
==>Re: Latin_1 and portability	Pascal Obry <p....@wanadoo.fr>
==>Re: Latin_1 and portability	=?ISO-8859-1?Q?Bj=F6rn?= Persson
<bj...@sverige.nu>
==>Re: Latin_1 and portability	"Jeff C,"
<no...@userealemailsniff.com>
==>Re: Latin_1 and portability	Georg Bauhaus
<sb...@l1-hrz.uni-duisburg.de>
...


The functionality is an optional add-on to the existing NNTP functionality.
With this in mind, I have provided the implementation in the examples
package. I have modified the
ExtendedNNTPOps class to demonstrate this in action.

I have also refactored the NNTP examples package from examples to
examples.nntp, as the proliferation of separate classes in a single examples
package was getting confusing.

Cheers,
Rory


Index: .cvsignore
===================================================================
RCS file: /home/cvspublic/jakarta-commons/net/.cvsignore,v
retrieving revision 1.1
diff -u -r1.1 .cvsignore
--- .cvsignore	17 Jul 2002 08:04:50 -0000	1.1
+++ .cvsignore	20 Jan 2004 13:09:25 -0000
@@ -1,3 +1,4 @@
 .project
 target
-dist
\ No newline at end of file
+dist
+.classpath
Index: src/java/examples/ExtendedNNTPOps.java
===================================================================
RCS file: src/java/examples/ExtendedNNTPOps.java
diff -N src/java/examples/ExtendedNNTPOps.java
--- src/java/examples/ExtendedNNTPOps.java	2 Jan 2004 03:39:03 -0000	1.5
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,266 +0,0 @@
-/* ====================================================================
- * The Apache Software License, Version 1.1
- *
- * Copyright (c) 2001-2004 The Apache Software Foundation.  All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- *    if any, must include the following acknowledgment:
- *       "This product includes software developed by the
- *        Apache Software Foundation (http://www.apache.org/)."
- *    Alternately, this acknowledgment may appear in the software itself,
- *    if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Apache" and "Apache Software Foundation" and
- *    "Apache Commons" must not be used to endorse or promote products
- *    derived from this software without prior written permission. For
- *    written permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- *    nor may "Apache" appear in their name, without
- *    prior written permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- */
-
-package examples;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.PrintWriter;
-import java.util.StringTokenizer;
-
-import org.apache.commons.net.io.DotTerminatedMessageReader;
-import org.apache.commons.net.nntp.NNTPClient;
-import org.apache.commons.net.nntp.NewsgroupInfo;
-
-
-public class ExtendedNNTPOps {
-
-    // simple class that encapsulates some basic info about an NNTP article
-    class Article {
-        private int articleNumber;
-        private String subject;
-        private String date;
-        private String articleId;
-
-        private String from;
-        private StringBuffer header;
-
-        public Article()
-        {
-            header = new StringBuffer();
-        }
-
-        public void addHeaderField(String name, String val) {
-            header.append(name);
-            header.append(": ");
-            header.append(val);
-            header.append('\n');
-        }
-
-        public String getArticleId() {
-            return articleId;
-        }
-
-        public int getArticleNumber() {
-            return articleNumber;
-        }
-
-        public String getDate() {
-            return date;
-        }
-
-        public String getFrom() {
-            return from;
-        }
-
-        public String getSubject() {
-            return subject;
-        }
-
-        public void setArticleId(String string) {
-            articleId = string;
-        }
-
-        public void setArticleNumber(int i) {
-            articleNumber = i;
-        }
-
-        public void setDate(String string) {
-            date = string;
-        }
-
-        public void setFrom(String string) {
-            from = string;
-        }
-
-        public void setSubject(String string) {
-            subject = string;
-        }
-    }
-
-    NNTPClient client;
-
-    public ExtendedNNTPOps() {
-        client = new NNTPClient();
-        client.addProtocolCommandListener(new PrintCommandListener(
-                                          new PrintWriter(System.out)));
-    }
-
-    private Article[] getArticleInfo(int lowArticleNumber,
-                                     int highArticleNumber)
-        throws IOException
-    {
-        Reader reader = null;
-        Article[] articles = new Article[0];
-        reader = (DotTerminatedMessageReader)
-            client.retrieveArticleInfo(lowArticleNumber,
highArticleNumber);
-
-        if (reader != null) {
-            String theInfo = readerToString(reader);
-            StringTokenizer st = new StringTokenizer(theInfo, "\n");
-
-            // Extract the article information
-            // Mandatory format (from NNTP RFC 2980) is :
-            // Subject\tAuthor\tDate\tID\tReference(s)\tByte Count\tLine
Count
-
-            int count = st.countTokens();
-            articles = new Article[count];
-            int index = 0;
-
-            while (st.hasMoreTokens()) {
-                StringTokenizer stt = new StringTokenizer(st.nextToken(),
"\t");
-                Article article = new Article();
-
article.setArticleNumber(Integer.parseInt(stt.nextToken()));
-                article.setSubject(stt.nextToken());
-                article.setFrom(stt.nextToken());
-                article.setDate(stt.nextToken());
-                article.setArticleId(stt.nextToken());
-                article.addHeaderField("References", stt.nextToken());
-                articles[index++] = article;
-            }
-        } else {
-            return null;
-        }
-
-        return articles;
-    }
-
-    private String readerToString(Reader reader)
-    {
-        String temp;
-        StringBuffer sb = null;
-        BufferedReader bufReader = new BufferedReader(reader);
-
-        sb = new StringBuffer();
-        try
-            {
-                temp = bufReader.readLine();
-                while (temp != null) {
-                    sb.append(temp);
-                    sb.append("\n");
-                    temp = bufReader.readLine();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-
-        return sb.toString();
-    }
-
-    public void demo(String host, String user, String password) {
-        try {
-            client.connect(host);
-
-            // AUTHINFO USER/AUTHINFO PASS
-            boolean success = client.authenticate(user, password);
-            if(success)
-		{
-                    System.out.println("Authentication succeeded");
-		}
-            else
-		{
-                    System.out.println("Authentication failed, error =" +
-                                       client.getReplyString());
-		}
-
-	    // XOVER
-	    NewsgroupInfo testGroup = new NewsgroupInfo();
-            client.selectNewsgroup("alt.test", testGroup);
-            int lowArticleNumber = testGroup.getFirstArticle();
-            int highArticleNumber = testGroup.getLastArticle();
-	    Article[] articles =
-                getArticleInfo(lowArticleNumber, highArticleNumber);
-
-	    for(int i =0; i < articles.length; ++i)
-                {
-                    System.out.println(articles[i].getSubject());
-                }
-
-	    // LIST ACTIVE
-	    NewsgroupInfo[] fanGroups = client.listNewsgroups("alt.fan.*");
-	    for(int i = 0; i < fanGroups.length; ++i)
-                {
-                    System.out.println(fanGroups[i].getNewsgroup());
-                }
-
-
-        }
-        catch(IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    public static void main(String[] args) {
-        ExtendedNNTPOps ops;
-
-        if(args.length != 3) {
-            System.err.println(
-                   "usage: ExtendedNNTPOps nntpserver username password");
-            System.exit(1);
-        }
-
-        ops = new ExtendedNNTPOps();
-        ops.demo(args[0], args[1], args[2]);
-    }
-
-}
-
-/* Emacs configuration
- * Local variables:        **
- * mode:             java  **
- * c-basic-offset:   4     **
- * indent-tabs-mode: nil   **
- * End:                    **
- */
Index: src/java/examples/newsgroups.java
===================================================================
RCS file: src/java/examples/newsgroups.java
diff -N src/java/examples/newsgroups.java
--- src/java/examples/newsgroups.java	2 Jan 2004 03:39:03 -0000	1.7
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,126 +0,0 @@
-package examples;
-
-/* ====================================================================
- * The Apache Software License, Version 1.1
- *
- * Copyright (c) 2001-2004 The Apache Software Foundation.  All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- *    if any, must include the following acknowledgment:
- *       "This product includes software developed by the
- *        Apache Software Foundation (http://www.apache.org/)."
- *    Alternately, this acknowledgment may appear in the software itself,
- *    if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Apache" and "Apache Software Foundation" and
- *    "Apache Commons" must not be used to endorse or promote products
- *    derived from this software without prior written permission. For
- *    written permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- *    nor may "Apache" appear in their name, without
- *    prior written permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- */
-
-import java.io.IOException;
-import org.apache.commons.net.nntp.NNTPClient;
-import org.apache.commons.net.nntp.NewsgroupInfo;
-
-/***
- * This is a trivial example using the NNTP package to approximate the
- * Unix newsgroups command.  It merely connects to the specified news
- * server and issues fetches the list of newsgroups stored by the server.
- * On servers that store a lot of newsgroups, this command can take a very
- * long time (listing upwards of 30,000 groups).
- * <p>
- ***/
-
-public final class newsgroups
-{
-
-    public final static void main(String[] args)
-    {
-        NNTPClient client;
-        NewsgroupInfo[] list;
-
-        if (args.length < 1)
-        {
-            System.err.println("Usage: newsgroups newsserver");
-            System.exit(1);
-        }
-
-        client = new NNTPClient();
-
-        try
-        {
-            client.connect(args[0]);
-
-            list = client.listNewsgroups();
-
-            if (list != null)
-            {
-                for (int i = 0; i < list.length; i++)
-                    System.out.println(list[i].getNewsgroup());
-            }
-            else
-            {
-                System.err.println("LIST command failed.");
-                System.err.println("Server reply: " +
client.getReplyString());
-            }
-        }
-        catch (IOException e)
-        {
-            e.printStackTrace();
-        }
-        finally
-        {
-            try
-            {
-                if (client.isConnected())
-                    client.disconnect();
-            }
-            catch (IOException e)
-            {
-                System.err.println("Error disconnecting from server.");
-                e.printStackTrace();
-                System.exit(1);
-            }
-        }
-
-    }
-
-}
-
-
Index: src/java/examples/post.java
===================================================================
RCS file: src/java/examples/post.java
diff -N src/java/examples/post.java
--- src/java/examples/post.java	2 Jan 2004 03:39:03 -0000	1.7
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,205 +0,0 @@
-package examples;
-
-/* ====================================================================
- * The Apache Software License, Version 1.1
- *
- * Copyright (c) 2001-2004 The Apache Software Foundation.  All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- *    if any, must include the following acknowledgment:
- *       "This product includes software developed by the
- *        Apache Software Foundation (http://www.apache.org/)."
- *    Alternately, this acknowledgment may appear in the software itself,
- *    if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Apache" and "Apache Software Foundation" and
- *    "Apache Commons" must not be used to endorse or promote products
- *    derived from this software without prior written permission. For
- *    written permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- *    nor may "Apache" appear in their name, without
- *    prior written permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- */
-
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.io.Writer;
-import org.apache.commons.net.io.Util;
-import org.apache.commons.net.nntp.NNTPClient;
-import org.apache.commons.net.nntp.NNTPReply;
-import org.apache.commons.net.nntp.SimpleNNTPHeader;
-
-/***
- * This is an example program using the NNTP package to post an article
- * to the specified newsgroup(s).  It prompts you for header information
and
- * a filename to post.
- * <p>
- ***/
-
-public final class post
-{
-
-    public final static void main(String[] args)
-    {
-        String from, subject, newsgroup, filename, server, organization;
-        String references;
-        BufferedReader stdin;
-        FileReader fileReader = null;
-        SimpleNNTPHeader header;
-        NNTPClient client;
-
-        if (args.length < 1)
-        {
-            System.err.println("Usage: post newsserver");
-            System.exit(1);
-        }
-
-        server = args[0];
-
-        stdin = new BufferedReader(new InputStreamReader(System.in));
-
-        try
-        {
-            System.out.print("From: ");
-            System.out.flush();
-
-            from = stdin.readLine();
-
-            System.out.print("Subject: ");
-            System.out.flush();
-
-            subject = stdin.readLine();
-
-            header = new SimpleNNTPHeader(from, subject);
-
-            System.out.print("Newsgroup: ");
-            System.out.flush();
-
-            newsgroup = stdin.readLine();
-            header.addNewsgroup(newsgroup);
-
-            while (true)
-            {
-                System.out.print("Additional Newsgroup <Hit enter to end>:
");
-                System.out.flush();
-
-                // Of course you don't want to do this because readLine()
may be null
-                newsgroup = stdin.readLine().trim();
-
-                if (newsgroup.length() == 0)
-                    break;
-
-                header.addNewsgroup(newsgroup);
-            }
-
-            System.out.print("Organization: ");
-            System.out.flush();
-
-            organization = stdin.readLine();
-
-            System.out.print("References: ");
-            System.out.flush();
-
-            references = stdin.readLine();
-
-            if (organization != null && organization.length() > 0)
-                header.addHeaderField("Organization", organization);
-
-            if (references != null && organization.length() > 0)
-                header.addHeaderField("References", references);
-
-            header.addHeaderField("X-Newsreader", "NetComponents");
-
-            System.out.print("Filename: ");
-            System.out.flush();
-
-            filename = stdin.readLine();
-
-            try
-            {
-                fileReader = new FileReader(filename);
-            }
-            catch (FileNotFoundException e)
-            {
-                System.err.println("File not found. " + e.getMessage());
-                System.exit(1);
-            }
-
-            client = new NNTPClient();
-            client.addProtocolCommandListener(new PrintCommandListener(
-                                                  new
PrintWriter(System.out)));
-
-            client.connect(server);
-
-            if (!NNTPReply.isPositiveCompletion(client.getReplyCode()))
-            {
-                client.disconnect();
-                System.err.println("NNTP server refused connection.");
-                System.exit(1);
-            }
-
-            if (client.isAllowedToPost())
-            {
-                Writer writer = client.postArticle();
-
-                if (writer != null)
-                {
-                    writer.write(header.toString());
-                    Util.copyReader(fileReader, writer);
-                    writer.close();
-                    client.completePendingCommand();
-                }
-            }
-
-            fileReader.close();
-
-            client.logout();
-
-            client.disconnect();
-        }
-        catch (IOException e)
-        {
-            e.printStackTrace();
-            System.exit(1);
-        }
-    }
-}
-
-