View Javadoc

1   /************************************************************************
2    * Copyright (c) 2000-2006 The Apache Software Foundation.             *
3    * All rights reserved.                                                *
4    * ------------------------------------------------------------------- *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you *
6    * may not use this file except in compliance with the License. You    *
7    * may obtain a copy of the License at:                                *
8    *                                                                     *
9    *     http://www.apache.org/licenses/LICENSE-2.0                      *
10   *                                                                     *
11   * Unless required by applicable law or agreed to in writing, software *
12   * distributed under the License is distributed on an "AS IS" BASIS,   *
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
14   * implied.  See the License for the specific language governing       *
15   * permissions and limitations under the License.                      *
16   ***********************************************************************/
17  
18  package org.apache.james.nntpserver.repository;
19  
20  import org.apache.avalon.framework.logger.AbstractLogEnabled;
21  import org.apache.james.nntpserver.DateSinceFileFilter;
22  import org.apache.james.util.io.AndFileFilter;
23  import org.apache.james.util.io.ExtensionFileFilter;
24  import org.apache.james.util.io.IOUtil;
25  import org.apache.james.util.io.InvertedFileFilter;
26  
27  import java.io.File;
28  import java.io.FileOutputStream;
29  import java.io.InputStream;
30  import java.io.IOException;
31  import java.util.ArrayList;
32  import java.util.Date;
33  import java.util.Iterator;
34  import java.util.List;
35  
36  /***
37   * Group is represented by a directory.
38   * Articles are stored in files with the name of file == article number
39   *
40   */
41  class NNTPGroupImpl extends AbstractLogEnabled implements NNTPGroup {
42  
43      /***
44       * The directory to which this group maps.
45       */
46      private final File root;
47  
48      /***
49       * The last article number in the group
50       */
51      private int lastArticle;
52  
53      /***
54       * The last article number in the group
55       */
56      private int firstArticle;
57  
58      /***
59       * The number of articles in the group.
60       */
61      private int numOfArticles;
62  
63      /***
64       * Whether the first, last, and total number of articles in the
65       * group have been read from disk.
66       * An instance may collect range info once. This involves disk I/O
67       */
68      private boolean articleRangeInfoCollected = false;
69  
70      /***
71       * The sole constructor for this particular NNTPGroupImpl.
72       *
73       * @param root the directory containing the articles
74       */
75      NNTPGroupImpl(File root) {
76          this.root = root;
77      }
78  
79      /***
80       * @see org.apache.james.nntpserver.NNTPGroup#getName()
81       */
82      public String getName() {
83          return root.getName();
84      }
85  
86      /***
87       * @see org.apache.james.nntpserver.NNTPGroup#getDescription()
88       */
89      public String getDescription() {
90          return getName();
91      }
92  
93      /***
94       * @see org.apache.james.nntpserver.NNTPGroup#isPostAllowed()
95       */
96      public boolean isPostAllowed() {
97          return true;
98      }
99  
100     /***
101      * Generates the first, last, and number of articles from the
102      * information in the group directory.
103      */
104     private void collectArticleRangeInfo() {
105         if ( articleRangeInfoCollected ) {
106             return;
107         }
108         String[] list = root.list();
109         int first = -1;
110         int last = -1;
111         for ( int i = 0 ; i < list.length ; i++ ) {
112             int num = Integer.parseInt(list[i]);
113             if ( first == -1 || num < first ) {
114                 first = num;
115             }
116             if ( num > last ) {
117                 last = num;
118             }
119         }
120         numOfArticles = list.length;
121         firstArticle = Math.max(first,0);
122         lastArticle = Math.max(last,0);
123         articleRangeInfoCollected = true;
124     }
125 
126     /***
127      * @see org.apache.james.nntpserver.NNTPGroup#getNumberOfArticles()
128      */
129     public int getNumberOfArticles() {
130         collectArticleRangeInfo();
131         return numOfArticles;
132     }
133 
134     /***
135      * @see org.apache.james.nntpserver.NNTPGroup#getFirstArticleNumber()
136      */
137     public int getFirstArticleNumber() {
138         collectArticleRangeInfo();
139         return firstArticle;
140     }
141 
142     /***
143      * @see org.apache.james.nntpserver.NNTPGroup#getLastArticleNumber()
144      */
145     public int getLastArticleNumber() {
146         collectArticleRangeInfo();
147         return lastArticle;
148     }
149 
150     /***
151      * @see org.apache.james.nntpserver.NNTPGroup#getArticle(int)
152      */
153     public NNTPArticle getArticle(int number) {
154         File f = new File(root,number + "");
155         return f.exists() ? new NNTPArticleImpl(this, f) : null;
156     }
157 
158     /***
159      * @see org.apache.james.nntpserver.NNTPGroup#getArticlesSince(Date)
160      */
161     public Iterator getArticlesSince(Date dt) {
162         File[] f = root.listFiles(new AndFileFilter
163             (new DateSinceFileFilter(dt.getTime()),
164              new InvertedFileFilter(new ExtensionFileFilter(".id"))));
165         List list = new ArrayList();
166         for ( int i = 0 ; i < f.length ; i++ ) {
167             list.add(new NNTPArticleImpl(this, f[i]));
168         }
169         return list.iterator();
170     }
171 
172     /***
173      * @see org.apache.james.nntpserver.NNTPGroup#getArticles()
174      */
175     public Iterator getArticles() {
176         File[] f = root.listFiles();
177         List list = new ArrayList();
178         for ( int i = 0 ; i < f.length ; i++ )
179             list.add(new NNTPArticleImpl(this, f[i]));
180         return list.iterator();
181     }
182 
183     /***
184      * @see org.apache.james.nntpserver.repository.NNTPGroup#getListFormat()
185      */
186     public String getListFormat() {
187         StringBuffer showBuffer =
188             new StringBuffer(128)
189                 .append(getName())
190                 .append(" ")
191                 .append(getLastArticleNumber())
192                 .append(" ")
193                 .append(getFirstArticleNumber())
194                 .append(" ")
195                 .append((isPostAllowed() ? "y":"n"));
196         return showBuffer.toString();
197     }
198 
199     /***
200      * @see org.apache.james.nntpserver.repository.NNTPGroup#getListNewsgroupsFormat()
201      */
202     public String getListNewsgroupsFormat() {
203         StringBuffer showBuffer =
204             new StringBuffer(128)
205                 .append(getName())
206                 .append(" ")
207                 .append(getDescription());
208          return showBuffer.toString();
209     }
210 
211     /***
212      * @see org.apache.james.nntpserver.repository.NNTPGroup#addArticle(InputStream)
213      */
214     public NNTPArticle addArticle(InputStream newsStream)
215             throws IOException {
216         File articleFile = null;
217         synchronized (this) {
218             int artNum;
219             if (numOfArticles < 0)
220                 throw new IllegalStateException("NNTP Group is corrupt (articles < 0).");
221             else if (numOfArticles == 0) {
222                 firstArticle = 1;
223                 artNum = 1;
224             } else {
225                 artNum = getLastArticleNumber() + 1;
226             }
227             
228             articleFile = new File(root, artNum + "");
229             articleFile.createNewFile();
230             lastArticle = artNum;
231             numOfArticles++;
232         }
233         if (getLogger().isDebugEnabled()) {
234             getLogger().debug("Copying message to: "+articleFile.getAbsolutePath());
235         }
236         FileOutputStream fout = null;
237         try {
238             fout = new FileOutputStream(articleFile);
239             IOUtil.copy(newsStream,fout);
240             fout.flush();
241         } finally {
242             try {
243                 if (fout != null) {
244                     fout.close();
245                 }
246             } catch (IOException ioe) {
247                 // Ignore this exception so we don't
248                 // trash any "real" exceptions
249             }
250         }
251         return new NNTPArticleImpl(this, articleFile);
252     }
253 
254 //     public NNTPArticle getArticleFromID(String id) {
255 //         if ( id == null )
256 //             return null;
257 //         int idx = id.indexOf('@');
258 //         if ( idx != -1 )
259 //             id = id.substring(0,idx);
260 //         File f = new File(root,id + ".id");
261 //         if ( f.exists() == false )
262 //             return null;
263 //         try {
264 //             FileInputStream fin = new FileInputStream(f);
265 //             int count = fin.available();
266 //             byte[] ba = new byte[count];
267 //             fin.read(ba);
268 //             fin.close();
269 //             String str = new String(ba);
270 //             int num = Integer.parseInt(str);
271 //             return getArticle(num);
272 //         } catch(IOException ioe) {
273 //             throw new NNTPException("could not fectch article: "+id,ioe);
274 //         }
275 //     }
276 
277 }