KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > nntpserver > repository > NNTPRepositoryImpl


1 /***********************************************************************
2  * Copyright (c) 2000-2004 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.excalibur.io.AndFileFilter;
21 import org.apache.avalon.excalibur.io.DirectoryFileFilter;
22 import org.apache.avalon.framework.activity.Initializable;
23 import org.apache.avalon.framework.configuration.Configurable;
24 import org.apache.avalon.framework.configuration.Configuration;
25 import org.apache.avalon.framework.configuration.ConfigurationException;
26 import org.apache.avalon.framework.container.ContainerUtil;
27 import org.apache.avalon.framework.context.Context;
28 import org.apache.avalon.framework.context.ContextException;
29 import org.apache.avalon.framework.context.Contextualizable;
30 import org.apache.avalon.framework.logger.AbstractLogEnabled;
31 import org.apache.avalon.framework.logger.LogEnabled;
32 import org.apache.james.context.AvalonContextUtilities;
33 import org.apache.james.nntpserver.DateSinceFileFilter;
34 import org.apache.james.nntpserver.NNTPException;
35 import org.apache.oro.io.GlobFilenameFilter;
36
37 import java.io.File JavaDoc;
38 import java.io.FileOutputStream JavaDoc;
39 import java.io.InputStream JavaDoc;
40 import java.io.IOException JavaDoc;
41 import java.io.PrintStream JavaDoc;
42 import java.util.ArrayList JavaDoc;
43 import java.util.Date JavaDoc;
44 import java.util.HashMap JavaDoc;
45 import java.util.Iterator JavaDoc;
46 import java.util.List JavaDoc;
47 import java.util.Set JavaDoc;
48
49 /**
50  * NNTP Repository implementation.
51  */

52 public class NNTPRepositoryImpl extends AbstractLogEnabled
53     implements NNTPRepository, Contextualizable, Configurable, Initializable {
54
55     /**
56      * The context employed by this repository
57      */

58     private Context context;
59
60     /**
61      * The configuration employed by this repository
62      */

63     private Configuration configuration;
64
65     /**
66      * Whether the repository is read only
67      */

68     private boolean readOnly;
69
70     /**
71      * The groups are located under this path.
72      */

73     private File JavaDoc rootPath;
74
75     /**
76      * Articles are temporarily written here and then sent to the spooler.
77      */

78     private File JavaDoc tempPath;
79
80     /**
81      * The spooler for this repository.
82      */

83     private NNTPSpooler spool;
84
85     /**
86      * The article ID repository associated with this NNTP repository.
87      */

88     private ArticleIDRepository articleIDRepo;
89
90     /**
91      * A map to allow lookup of valid newsgroup names
92      */

93     private HashMap JavaDoc groupNameMap = null;
94
95     /**
96      * Restrict use to newsgroups specified in config only
97      */

98     private boolean definedGroupsOnly = false;
99
100     /**
101      * The root path as a String.
102      */

103     private String JavaDoc rootPathString = null;
104
105     /**
106      * The temp path as a String.
107      */

108     private String JavaDoc tempPathString = null;
109
110     /**
111      * The article ID path as a String.
112      */

113     private String JavaDoc articleIdPathString = null;
114
115     /**
116      * The domain suffix used for files in the article ID repository.
117      */

118     private String JavaDoc articleIDDomainSuffix = null;
119
120     /**
121      * The ordered list of fields returned in the overview format for
122      * articles stored in this repository.
123      */

124     private String JavaDoc[] overviewFormat = { "Subject:",
125                                         "From:",
126                                         "Date:",
127                                         "Message-ID:",
128                                         "References:",
129                                         "Bytes:",
130                                         "Lines:"
131                                       };
132
133     /**
134      * This is a mapping of group names to NNTP group objects.
135      *
136      * TODO: This needs to be addressed so it scales better
137      */

138     private HashMap JavaDoc repositoryGroups = new HashMap JavaDoc();
139
140     /**
141      * @see org.apache.avalon.framework.context.Contextualizable#contextualize(Context)
142      */

143     public void contextualize(Context context)
144             throws ContextException {
145         this.context = context;
146     }
147
148     /**
149      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
150      */

151     public void configure( Configuration aConfiguration ) throws ConfigurationException {
152         configuration = aConfiguration;
153         readOnly = configuration.getChild("readOnly").getValueAsBoolean(false);
154         articleIDDomainSuffix = configuration.getChild("articleIDDomainSuffix")
155             .getValue("foo.bar.sho.boo");
156         rootPathString = configuration.getChild("rootPath").getValue(null);
157         if (rootPathString == null) {
158             throw new ConfigurationException("Root path URL is required.");
159         }
160         tempPathString = configuration.getChild("tempPath").getValue(null);
161         if (tempPathString == null) {
162             throw new ConfigurationException("Temp path URL is required.");
163         }
164         articleIdPathString = configuration.getChild("articleIDPath").getValue(null);
165         if (articleIdPathString == null) {
166             throw new ConfigurationException("Article ID path URL is required.");
167         }
168         if (getLogger().isDebugEnabled()) {
169             if (readOnly) {
170                 getLogger().debug("NNTP repository is read only.");
171             } else {
172                 getLogger().debug("NNTP repository is writeable.");
173             }
174             getLogger().debug("NNTP repository root path URL is " + rootPathString);
175             getLogger().debug("NNTP repository temp path URL is " + tempPathString);
176             getLogger().debug("NNTP repository article ID path URL is " + articleIdPathString);
177         }
178         Configuration newsgroupConfiguration = configuration.getChild("newsgroups");
179         definedGroupsOnly = newsgroupConfiguration.getAttributeAsBoolean("only", false);
180         groupNameMap = new HashMap JavaDoc();
181         if ( newsgroupConfiguration != null ) {
182             Configuration[] children = newsgroupConfiguration.getChildren("newsgroup");
183             if ( children != null ) {
184                 for ( int i = 0 ; i < children.length ; i++ ) {
185                     String JavaDoc groupName = children[i].getValue();
186                     groupNameMap.put(groupName, groupName);
187                 }
188             }
189         }
190         getLogger().debug("Repository configuration done");
191     }
192
193     /**
194      * @see org.apache.avalon.framework.activity.Initializable#initialize()
195      */

196     public void initialize() throws Exception JavaDoc {
197
198         getLogger().debug("Starting initialize");
199         File JavaDoc articleIDPath = null;
200
201         try {
202             rootPath = AvalonContextUtilities.getFile(context, rootPathString);
203             tempPath = AvalonContextUtilities.getFile(context, tempPathString);
204             articleIDPath = AvalonContextUtilities.getFile(context, articleIdPathString);
205         } catch (Exception JavaDoc e) {
206             getLogger().fatalError(e.getMessage(), e);
207             throw e;
208         }
209
210         if ( articleIDPath.exists() == false ) {
211             articleIDPath.mkdirs();
212         }
213
214         articleIDRepo = new ArticleIDRepository(articleIDPath, articleIDDomainSuffix);
215         spool = (NNTPSpooler)createSpooler();
216         spool.setRepository(this);
217         spool.setArticleIDRepository(articleIDRepo);
218         if (getLogger().isDebugEnabled()) {
219             getLogger().debug("repository:readOnly=" + readOnly);
220             getLogger().debug("repository:rootPath=" + rootPath.getAbsolutePath());
221             getLogger().debug("repository:tempPath=" + tempPath.getAbsolutePath());
222         }
223
224         if ( rootPath.exists() == false ) {
225             rootPath.mkdirs();
226         } else if (!(rootPath.isDirectory())) {
227             StringBuffer JavaDoc errorBuffer =
228                 new StringBuffer JavaDoc(128)
229                     .append("NNTP repository root directory is improperly configured. The specified path ")
230                     .append(rootPathString)
231                     .append(" is not a directory.");
232             throw new ConfigurationException(errorBuffer.toString());
233         }
234
235         Set JavaDoc groups = groupNameMap.keySet();
236         Iterator JavaDoc groupIterator = groups.iterator();
237         while( groupIterator.hasNext() ) {
238             String JavaDoc groupName = (String JavaDoc)groupIterator.next();
239             File JavaDoc groupFile = new File JavaDoc(rootPath,groupName);
240             if ( groupFile.exists() == false ) {
241                 groupFile.mkdirs();
242             } else if (!(groupFile.isDirectory())) {
243                 StringBuffer JavaDoc errorBuffer =
244                     new StringBuffer JavaDoc(128)
245                         .append("A file exists in the NNTP root directory with the same name as a newsgroup. File ")
246                         .append(groupName)
247                         .append("in directory ")
248                         .append(rootPathString)
249                         .append(" is not a directory.");
250                 throw new ConfigurationException(errorBuffer.toString());
251             }
252         }
253         if ( tempPath.exists() == false ) {
254             tempPath.mkdirs();
255         } else if (!(tempPath.isDirectory())) {
256             StringBuffer JavaDoc errorBuffer =
257                 new StringBuffer JavaDoc(128)
258                     .append("NNTP repository temp directory is improperly configured. The specified path ")
259                     .append(tempPathString)
260                     .append(" is not a directory.");
261             throw new ConfigurationException(errorBuffer.toString());
262         }
263
264         getLogger().debug("repository initialization done");
265     }
266
267     /**
268      * @see org.apache.james.nntpserver.repository.NNTPRepository#isReadOnly()
269      */

270     public boolean isReadOnly() {
271         return readOnly;
272     }
273
274     /**
275      * @see org.apache.james.nntpserver.repository.NNTPRepository#getGroup(String)
276      */

277     public NNTPGroup getGroup(String JavaDoc groupName) {
278         if (definedGroupsOnly && groupNameMap.get(groupName) == null) {
279             if (getLogger().isDebugEnabled()) {
280                 getLogger().debug(groupName + " is not a newsgroup hosted on this server.");
281             }
282             return null;
283         }
284         File JavaDoc groupFile = new File JavaDoc(rootPath,groupName);
285         NNTPGroup groupToReturn = null;
286         synchronized(this) {
287             groupToReturn = (NNTPGroup)repositoryGroups.get(groupName);
288             if ((groupToReturn == null) && groupFile.exists() && groupFile.isDirectory() ) {
289                 try {
290                     groupToReturn = new NNTPGroupImpl(groupFile);
291                     ContainerUtil.enableLogging(groupToReturn, getLogger());
292                     ContainerUtil.contextualize(groupToReturn, context);
293                     ContainerUtil.initialize(groupToReturn);
294                     repositoryGroups.put(groupName, groupToReturn);
295                 } catch (Exception JavaDoc e) {
296                     getLogger().error("Couldn't create group object.", e);
297                     groupToReturn = null;
298                 }
299             }
300         }
301         return groupToReturn;
302     }
303
304     /**
305      * @see org.apache.james.nntpserver.repository.NNTPRepository#getArticleFromID(String)
306      */

307     public NNTPArticle getArticleFromID(String JavaDoc id) {
308         try {
309             return articleIDRepo.getArticle(this,id);
310         } catch(Exception JavaDoc ex) {
311             getLogger().error("Couldn't get article " + id + ": ", ex);
312             return null;
313         }
314     }
315
316     /**
317      * @see org.apache.james.nntpserver.repository.NNTPRepository#createArticle(InputStream)
318      */

319     public void createArticle(InputStream JavaDoc in) {
320         StringBuffer JavaDoc fileBuffer =
321             new StringBuffer JavaDoc(32)
322                     .append(System.currentTimeMillis())
323                     .append(".")
324                     .append(Math.random());
325         File JavaDoc f = new File JavaDoc(tempPath, fileBuffer.toString());
326         FileOutputStream JavaDoc fout = null;
327         try {
328             fout = new FileOutputStream JavaDoc(f);
329             byte[] readBuffer = new byte[1024];
330             int bytesRead = 0;
331             while ( ( bytesRead = in.read(readBuffer, 0, 1024) ) > 0 ) {
332                 fout.write(readBuffer, 0, bytesRead);
333             }
334             fout.flush();
335             fout.close();
336             fout = null;
337             boolean renamed = f.renameTo(new File JavaDoc(spool.getSpoolPath(),f.getName()));
338             if (!renamed) {
339                 throw new IOException JavaDoc("Could not create article on the spool.");
340             }
341         } catch(IOException JavaDoc ex) {
342             throw new NNTPException("create article failed",ex);
343         } finally {
344             if (fout != null) {
345                 try {
346                     fout.close();
347                 } catch (IOException JavaDoc ioe) {
348                     // Ignored
349
}
350             }
351         }
352     }
353
354     class GroupFilter implements java.io.FilenameFilter JavaDoc {
355         public boolean accept(java.io.File JavaDoc dir, String JavaDoc name) {
356             if (getLogger().isDebugEnabled()) {
357                 getLogger().debug(((definedGroupsOnly ? groupNameMap.containsKey(name) : true) ? "Accepting ": "Rejecting") + name);
358             }
359
360             return definedGroupsOnly ? groupNameMap.containsKey(name) : true;
361         }
362     }
363
364     /**
365      * @see org.apache.james.nntpserver.repository.NNTPRepository#getMatchedGroups(String)
366      */

367     public Iterator JavaDoc getMatchedGroups(String JavaDoc wildmat) {
368         File JavaDoc[] f = rootPath.listFiles(new AndFileFilter(new GroupFilter(), new AndFileFilter
369             (new DirectoryFileFilter(),new GlobFilenameFilter(wildmat))));
370         return getGroups(f);
371     }
372
373     /**
374      * Gets an iterator of all news groups represented by the files
375      * in the parameter array.
376      *
377      * @param f the array of files that correspond to news groups
378      *
379      * @return an iterator of news groups
380      */

381     private Iterator JavaDoc getGroups(File JavaDoc[] f) {
382         List JavaDoc list = new ArrayList JavaDoc();
383         for ( int i = 0 ; i < f.length ; i++ ) {
384             if (f[i] != null) {
385                 list.add(getGroup(f[i].getName()));
386             }
387         }
388         return list.iterator();
389     }
390
391     /**
392      * @see org.apache.james.nntpserver.repository.NNTPRepository#getGroupsSince(Date)
393      */

394     public Iterator JavaDoc getGroupsSince(Date JavaDoc dt) {
395         File JavaDoc[] f = rootPath.listFiles(new AndFileFilter(new GroupFilter(), new AndFileFilter
396             (new DirectoryFileFilter(),new DateSinceFileFilter(dt.getTime()))));
397         return getGroups(f);
398     }
399
400     // gets the list of groups.
401
// creates iterator that concatenates the article iterators in the list of groups.
402
// there is at most one article iterator reference for all the groups
403

404     /**
405      * @see org.apache.james.nntpserver.repository.NNTPRepository#getArticlesSince(Date)
406      */

407     public Iterator JavaDoc getArticlesSince(final Date JavaDoc dt) {
408         final Iterator JavaDoc giter = getGroupsSince(dt);
409         return new Iterator JavaDoc() {
410
411                 private Iterator JavaDoc iter = null;
412
413                 public boolean hasNext() {
414                     if ( iter == null ) {
415                         if ( giter.hasNext() ) {
416                             NNTPGroup group = (NNTPGroup)giter.next();
417                             iter = group.getArticlesSince(dt);
418                         }
419                         else {
420                             return false;
421                         }
422                     }
423                     if ( iter.hasNext() ) {
424                         return true;
425                     } else {
426                         iter = null;
427                         return hasNext();
428                     }
429                 }
430
431                 public Object JavaDoc next() {
432                     return iter.next();
433                 }
434
435                 public void remove() {
436                     throw new UnsupportedOperationException JavaDoc("remove not supported");
437                 }
438             };
439     }
440
441     /**
442      * @see org.apache.james.nntpserver.repository.NNTPRepository#getOverviewFormat()
443      */

444     public String JavaDoc[] getOverviewFormat() {
445         return overviewFormat;
446     }
447
448     /**
449      * Creates an instance of the spooler class.
450      *
451      * TODO: This method doesn't properly implement the Avalon lifecycle.
452      */

453     private NNTPSpooler createSpooler()
454             throws ConfigurationException {
455         String JavaDoc className = "org.apache.james.nntpserver.repository.NNTPSpooler";
456         Configuration spoolerConfiguration = configuration.getChild("spool");
457         try {
458             // Must be a subclass of org.apache.james.nntpserver.repository.NNTPSpooler
459
className = spoolerConfiguration.getAttribute("class");
460         } catch(ConfigurationException ce) {
461             // Use the default class.
462
}
463         try {
464             Object JavaDoc obj = getClass().getClassLoader().loadClass(className).newInstance();
465             // TODO: Need to support service
466
ContainerUtil.enableLogging(obj, getLogger());
467             ContainerUtil.contextualize(obj, context);
468             ContainerUtil.configure(obj, spoolerConfiguration.getChild("configuration"));
469             ContainerUtil.initialize(obj);
470             return (NNTPSpooler)obj;
471         } catch(ClassCastException JavaDoc cce) {
472             StringBuffer JavaDoc errorBuffer =
473                 new StringBuffer JavaDoc(128)
474                     .append("Spooler initialization failed because the spooler class ")
475                     .append(className)
476                     .append(" was not a subclass of org.apache.james.nntpserver.repository.NNTPSpooler");
477             String JavaDoc errorString = errorBuffer.toString();
478             getLogger().error(errorString, cce);
479             throw new ConfigurationException(errorString, cce);
480         } catch(Exception JavaDoc ex) {
481             getLogger().error("Spooler initialization failed",ex);
482             throw new ConfigurationException("Spooler initialization failed",ex);
483         }
484     }
485 }
486
Popular Tags