KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > mail > providers > nntp > NNTPFolder


1 /*
2  * NNTPFolder.java
3  * Copyright (C) 2002 dog <dog@gnu.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * You also have permission to link it with the Sun Microsystems, Inc.
11  * JavaMail(tm) extension and run that combination.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  */

22
23 package gnu.mail.providers.nntp;
24
25 import java.io.ByteArrayInputStream JavaDoc;
26 import java.io.ByteArrayOutputStream JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.LinkedList JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Map JavaDoc;
32
33 import javax.mail.FetchProfile JavaDoc;
34 import javax.mail.Flags JavaDoc;
35 import javax.mail.Folder JavaDoc;
36 import javax.mail.FolderNotFoundException JavaDoc;
37 import javax.mail.IllegalWriteException JavaDoc;
38 import javax.mail.Message JavaDoc;
39 import javax.mail.MessageRemovedException JavaDoc;
40 import javax.mail.MessagingException JavaDoc;
41 import javax.mail.MethodNotSupportedException JavaDoc;
42 import javax.mail.event.ConnectionEvent JavaDoc;
43
44 import gnu.inet.nntp.ArticleResponse;
45 import gnu.inet.nntp.GroupResponse;
46 import gnu.inet.nntp.HeaderEntry;
47 import gnu.inet.nntp.HeaderIterator;
48 import gnu.inet.nntp.NNTPConstants;
49 import gnu.inet.nntp.StatusResponse;
50
51 /**
52  * A JavaMail folder delegate for an NNTP newsgroup.
53  *
54  * @author <a HREF='mailto:dog@gnu.org'>Chris Burdess</a>
55  * @version 2.0
56  */

57 public final class NNTPFolder
58   extends Folder JavaDoc
59 {
60
61   String JavaDoc name;
62   int first = -1;
63   int last = -1;
64   int count = -1;
65   boolean open;
66
67   Map JavaDoc articleCache; // cache of article-number to NNTPMessage
68

69   NNTPFolder(NNTPStore store, String JavaDoc name)
70   {
71     super(store);
72     this.name = name;
73   }
74   
75   /**
76    * Returns the name of the newsgroup, e.g. <code>alt.test</code>.
77    */

78   public String JavaDoc getName()
79   {
80     return name;
81   }
82
83   /**
84    * @see #getName
85    */

86   public String JavaDoc getFullName()
87   {
88     return name;
89   }
90
91   /**
92    * This implementation uses a flat namespace, so the parent of any
93    * NNTPFolder is the NNTP root folder.
94    */

95   public Folder JavaDoc getParent()
96     throws MessagingException JavaDoc
97   {
98     NNTPStore ns = (NNTPStore)store;
99     return ns.root;
100   }
101
102   /**
103    * Returns the type of this folder.
104    * This folder type only holds messages.
105    */

106   public int getType()
107     throws MessagingException JavaDoc
108   {
109     return HOLDS_MESSAGES;
110   }
111
112   public boolean isOpen()
113   {
114     return open;
115   }
116
117   /**
118    * This folder type is always read-only.
119    */

120   public int getMode()
121   {
122     return READ_ONLY;
123   }
124
125   /**
126    * Returns the flags supported by this folder.
127    */

128   public Flags JavaDoc getPermanentFlags()
129   {
130     NNTPStore ns = (NNTPStore)store;
131     return new Flags JavaDoc(ns.permanentFlags);
132   }
133
134   /**
135    * This method has no particular meaning in NNTP.
136    * However, we will use it to send a GROUP command and refresh our article
137    * stats.
138    */

139   public void open(int mode)
140     throws MessagingException JavaDoc
141   {
142     // NB this should probably throw an exception if READ_WRITE is
143
// specified, but this tends to cause problems with existing clients.
144
if (open)
145       throw new IllegalStateException JavaDoc();
146     try
147     {
148       NNTPStore ns = (NNTPStore)store;
149       synchronized (ns.connection) {
150         GroupResponse response = ns.connection.group(name);
151         count = response.count;
152         first = response.first;
153         last = response.last;
154       }
155
156       articleCache = new HashMap JavaDoc(1024); // TODO make configurable
157
open = true;
158       notifyConnectionListeners(ConnectionEvent.OPENED);
159     }
160     catch (StatusResponse e)
161     {
162       if (e.getStatus()==NNTPConstants.NO_SUCH_GROUP)
163         throw new FolderNotFoundException JavaDoc(e.getMessage(), this);
164       else
165         throw new MessagingException JavaDoc(e.getMessage(), e);
166     }
167     catch (IOException JavaDoc e)
168     {
169       throw new MessagingException JavaDoc(e.getMessage(), e);
170     }
171   }
172
173   /**
174    * This method has no particular meaning in NNTP.
175    */

176   public void close(boolean expunge)
177     throws MessagingException JavaDoc
178   {
179     if (!open)
180       throw new IllegalStateException JavaDoc();
181     
182     articleCache = null;
183     open = false;
184     notifyConnectionListeners(ConnectionEvent.CLOSED);
185   }
186
187   /**
188    * Indicates whether the newsgroup is present on the server.
189    */

190   public boolean exists()
191     throws MessagingException JavaDoc
192   {
193     try
194     {
195       NNTPStore ns = (NNTPStore)store;
196       synchronized (ns.connection)
197       {
198         GroupResponse response = ns.connection.group(name);
199         count = response.count;
200         first = response.first;
201         last = response.last;
202       }
203       return true;
204     }
205     catch (StatusResponse e)
206     {
207       if (e.getStatus()==NNTPConstants.NO_SUCH_GROUP)
208         return false;
209       else
210         throw new MessagingException JavaDoc(e.getMessage(), e);
211     }
212     catch (IOException JavaDoc e)
213     {
214       throw new MessagingException JavaDoc(e.getMessage(), e);
215     }
216   }
217
218   /**
219    * Indicates whether there are new articles in this newsgroup.
220    */

221   public boolean hasNewMessages()
222     throws MessagingException JavaDoc
223   {
224     try
225     {
226       NNTPStore ns = (NNTPStore)store;
227       boolean hasNew = false;
228       synchronized (ns.connection)
229       {
230         GroupResponse response = ns.connection.group(name);
231         if (response.last>last)
232           hasNew = true;
233         count = response.count;
234         first = response.first;
235         last = response.last;
236       }
237       return hasNew;
238     }
239     catch (StatusResponse e)
240     {
241       if (e.getStatus()==NNTPConstants.NO_SUCH_GROUP)
242         throw new FolderNotFoundException JavaDoc(e.getMessage(), this);
243       else
244         throw new MessagingException JavaDoc(e.getMessage(), e);
245     }
246     catch (IOException JavaDoc e)
247     {
248       throw new MessagingException JavaDoc(e.getMessage(), e);
249     }
250   }
251
252   /**
253    * Returns the number of articles in this newsgroup.
254    */

255   public int getMessageCount()
256     throws MessagingException JavaDoc
257   {
258     return count;
259   }
260
261   /**
262    * Returns the article corresponding to the specified article
263    * number.
264    * @throws MessageRemovedException often ;-)
265    */

266   public Message JavaDoc getMessage(int msgnum)
267     throws MessagingException JavaDoc
268   {
269     if (!open)
270       throw new IllegalStateException JavaDoc();
271
272     // Cache lookup
273
Integer JavaDoc key = new Integer JavaDoc(msgnum);
274     NNTPMessage m = (NNTPMessage)articleCache.get(key);
275     if (m!=null)
276       return m;
277     
278     try
279     {
280       NNTPStore ns = (NNTPStore)store;
281       synchronized (ns.connection)
282       {
283         // Ensure group selected
284
GroupResponse gr = ns.connection.group(name);
285         first = gr.first;
286         last = gr.last;
287         count = gr.count;
288         // Get article
289
m = getMessageImpl(msgnum);
290         // Cache store
291
articleCache.put(key, m);
292         return m;
293       }
294     }
295     catch (StatusResponse e)
296     {
297       switch (e.getStatus())
298       {
299         case NNTPConstants.NO_ARTICLE_SELECTED:
300         case NNTPConstants.NO_SUCH_ARTICLE_NUMBER:
301         case NNTPConstants.NO_SUCH_ARTICLE:
302           throw new MessageRemovedException JavaDoc(e.getMessage());
303         default:
304           throw new MessagingException JavaDoc(e.getMessage(), e);
305       }
306     }
307     catch (IOException JavaDoc e)
308     {
309       throw new MessagingException JavaDoc(e.getMessage(), e);
310     }
311   }
312
313   /*
314    * Perform article STAT.
315    * NB not synchronized against the connection!
316    */

317   NNTPMessage getMessageImpl(int msgnum)
318     throws IOException JavaDoc
319   {
320     NNTPStore ns = (NNTPStore)store;
321     // Issue STAT
322
ArticleResponse response = ns.connection.stat(msgnum);
323     String JavaDoc messageId = response.messageId;
324     return new NNTPMessage(this, msgnum, messageId);
325   }
326
327   /**
328    * Returns all articles in this group.
329    * This tries XHDR first to retrieve Message-IDs for the articles.
330    * If this fails we fall back to statting each article.
331    */

332   public Message JavaDoc[] getMessages()
333     throws MessagingException JavaDoc
334   {
335     NNTPStore ns = (NNTPStore)store;
336     List JavaDoc acc = new LinkedList JavaDoc();
337     synchronized (ns.connection)
338     {
339       try
340       {
341         // Ensure group selected
342
GroupResponse gr = ns.connection.group(name);
343         first = gr.first;
344         last = gr.last;
345         count = gr.count;
346         // Get Message-IDs for all article numbers
347
StringBuffer JavaDoc rb = new StringBuffer JavaDoc();
348         rb.append(Integer.toString(first));
349         rb.append('-');
350         rb.append(Integer.toString(last));
351         HeaderIterator i = ns.connection.xhdr("Message-ID", rb.toString());
352         while (i.hasNext())
353         {
354           HeaderEntry entry = i.nextHeaderEntry();
355           Integer JavaDoc key = new Integer JavaDoc(entry.getArticleId());
356           // Cache lookup
357
NNTPMessage m = (NNTPMessage)articleCache.get(key);
358           if (m==null)
359           {
360             int msgnum = key.intValue();
361             String JavaDoc messageId = entry.getHeader();
362             m = new NNTPMessage(this, msgnum, messageId);
363             // Cache store
364
articleCache.put(key, m);
365           }
366           acc.add(m);
367         }
368       }
369       catch (StatusResponse e)
370       {
371         // Perhaps the server does not understand XHDR.
372
// We'll do it the slow way.
373
for (int i=first; i<=last; i++)
374         {
375           Integer JavaDoc key = new Integer JavaDoc(i);
376           // Cache lookup
377
Message JavaDoc m = (NNTPMessage)articleCache.get(key);
378           if (m==null)
379           {
380             try
381             {
382               m = getMessageImpl(i);
383               // Cache store
384
articleCache.put(key, m);
385               acc.add(m);
386             }
387             catch (StatusResponse se)
388             {
389               switch (se.getStatus())
390               {
391                 case NNTPConstants.NO_ARTICLE_SELECTED:
392                 case NNTPConstants.NO_SUCH_ARTICLE_NUMBER:
393                 case NNTPConstants.NO_SUCH_ARTICLE:
394                   break; // article does not exist, ignore
395
default:
396                   throw new MessagingException JavaDoc(se.getMessage(), se);
397               }
398             }
399             catch (IOException JavaDoc ie)
400             {
401               throw new MessagingException JavaDoc(ie.getMessage(), ie);
402             }
403           }
404         }
405       }
406       catch (IOException JavaDoc e)
407       {
408         throw new MessagingException JavaDoc(e.getMessage(), e);
409       }
410     }
411     int len = acc.size();
412     Message JavaDoc[] messages = new Message JavaDoc[len];
413     acc.toArray(messages);
414     return messages;
415   }
416
417   /**
418    * Prefetch.
419    */

420   public void fetch(Message JavaDoc[] msgs, FetchProfile JavaDoc fp)
421     throws MessagingException JavaDoc
422   {
423     boolean head = fp.contains(FetchProfile.Item.ENVELOPE);
424     head = head || (fp.getHeaderNames().length>0);
425     boolean body = fp.contains(FetchProfile.Item.CONTENT_INFO);
426     int op = (head && body) ? 3 : head ? 2 : body ? 1 : 0;
427     try
428     {
429       NNTPStore ns = (NNTPStore)store;
430       for (int i=0; i<msgs.length; i++)
431       {
432         Message JavaDoc msg = msgs[i];
433         if (msg==null || !(msg instanceof NNTPMessage))
434           continue;
435         NNTPMessage message = (NNTPMessage)msg;
436         String JavaDoc messageId = message.getMessageId();
437         
438         ArticleResponse response = null;
439         synchronized (ns.connection)
440         {
441           switch (op)
442           {
443             case 3: // head & body
444
response = ns.connection.article(messageId);
445               break;
446             case 2: // head
447
response = ns.connection.head(messageId);
448               break;
449             case 1: // body
450
response = ns.connection.body(messageId);
451               break;
452           }
453           ByteArrayOutputStream JavaDoc out = new ByteArrayOutputStream JavaDoc();
454           byte[] buf = new byte[4096];
455           for (int len = response.in.read(buf);
456               len>-1;
457               len = response.in.read(buf))
458             out.write(buf, 0, len);
459           switch (op)
460           {
461             case 3: // head & body
462
ByteArrayInputStream JavaDoc hbin =
463                 new ByteArrayInputStream JavaDoc(out.toByteArray());
464               message.updateHeaders(hbin);
465               int len = hbin.available();
466               byte[] content = new byte[len];
467               hbin.read(content);
468               message.updateContent(content);
469               break;
470             case 2: // head
471
ByteArrayInputStream JavaDoc hin =
472                 new ByteArrayInputStream JavaDoc(out.toByteArray());
473               message.updateHeaders(hin);
474               break;
475             case 1: // body
476
message.updateContent(out.toByteArray());
477               break;
478           }
479         }
480       }
481     }
482     catch (StatusResponse e)
483     {
484       switch (e.getStatus())
485       {
486         case NNTPConstants.NO_GROUP_SELECTED:
487           throw new IllegalStateException JavaDoc(e.getMessage());
488         case NNTPConstants.NO_ARTICLE_SELECTED:
489         case NNTPConstants.NO_SUCH_ARTICLE_NUMBER:
490         case NNTPConstants.NO_SUCH_ARTICLE:
491           throw new MessageRemovedException JavaDoc(e.getMessage());
492           default:
493           throw new MessagingException JavaDoc(e.getMessage(), e);
494       }
495     }
496     catch (IOException JavaDoc e)
497     {
498       throw new MessagingException JavaDoc(e.getMessage(), e);
499     }
500   }
501   
502   // -- Subscription --
503

504   /**
505    * Indicates if the newsgroup is subscribed.
506    * This uses the newsrc mechanism associated with this folder's store.
507    */

508   public boolean isSubscribed()
509   {
510     NNTPStore ns = (NNTPStore)store;
511     return ns.newsrc.isSubscribed(name);
512   }
513
514   /**
515    * Subscribes or unsubscribes to this newsgroup.
516    * This uses the newsrc mechanism associated with this folder's store.
517    */

518   public void setSubscribed(boolean flag)
519     throws MessagingException JavaDoc
520   {
521     NNTPStore ns = (NNTPStore)store;
522     ns.newsrc.setSubscribed(name, flag);
523   }
524
525   boolean isSeen(int articleNumber)
526   {
527     NNTPStore ns = (NNTPStore)store;
528     return ns.newsrc.isSeen(name, articleNumber);
529   }
530   
531   void setSeen(int articleNumber, boolean flag)
532   {
533     NNTPStore ns = (NNTPStore)store;
534     ns.newsrc.setSeen(name, articleNumber, flag);
535   }
536
537   // -- Stuff we can't do --
538

539   /**
540    * This folder type does not contain other folders.
541    */

542   public Folder JavaDoc getFolder(String JavaDoc name)
543     throws MessagingException JavaDoc
544   {
545     throw new MethodNotSupportedException JavaDoc();
546   }
547
548   /**
549    * This folder type does not contain other folders.
550    */

551   public Folder JavaDoc[] list(String JavaDoc pattern)
552     throws MessagingException JavaDoc
553   {
554     throw new MethodNotSupportedException JavaDoc();
555   }
556
557   /**
558    * This folder type does not contain other folders.
559    */

560   public Folder JavaDoc[] listSubscribed(String JavaDoc pattern)
561     throws MessagingException JavaDoc
562   {
563     return list(pattern);
564   }
565
566   /**
567    * If we move away from a flat namespace, this might be useful.
568    */

569   public char getSeparator()
570     throws MessagingException JavaDoc
571   {
572     return '.';
573   }
574
575   /**
576    * NNTP servers are read-only.
577    */

578   public boolean create(int type)
579     throws MessagingException JavaDoc
580   {
581     throw new MethodNotSupportedException JavaDoc();
582   }
583
584   /**
585    * NNTP servers are read-only.
586    */

587   public boolean delete(boolean recurse)
588     throws MessagingException JavaDoc
589   {
590     throw new MethodNotSupportedException JavaDoc();
591   }
592
593   /**
594    * NNTP servers are read-only.
595    */

596   public boolean renameTo(Folder JavaDoc folder)
597     throws MessagingException JavaDoc
598   {
599     throw new MethodNotSupportedException JavaDoc();
600   }
601
602   /**
603    * NNTP servers are read-only.
604    */

605   public void appendMessages(Message JavaDoc[] messages)
606     throws MessagingException JavaDoc
607   {
608     throw new IllegalWriteException JavaDoc();
609   }
610
611   /**
612    * NNTP servers are read-only.
613    */

614   public Message JavaDoc[] expunge()
615     throws MessagingException JavaDoc
616   {
617     throw new IllegalWriteException JavaDoc();
618   }
619
620 }
621
Popular Tags