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