View Javadoc

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