1 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 ; 38 import java.io.FileOutputStream ; 39 import java.io.InputStream ; 40 import java.io.IOException ; 41 import java.io.PrintStream ; 42 import java.util.ArrayList ; 43 import java.util.Date ; 44 import java.util.HashMap ; 45 import java.util.Iterator ; 46 import java.util.List ; 47 import java.util.Set ; 48 49 52 public class NNTPRepositoryImpl extends AbstractLogEnabled 53 implements NNTPRepository, Contextualizable, Configurable, Initializable { 54 55 58 private Context context; 59 60 63 private Configuration configuration; 64 65 68 private boolean readOnly; 69 70 73 private File rootPath; 74 75 78 private File tempPath; 79 80 83 private NNTPSpooler spool; 84 85 88 private ArticleIDRepository articleIDRepo; 89 90 93 private HashMap groupNameMap = null; 94 95 98 private boolean definedGroupsOnly = false; 99 100 103 private String rootPathString = null; 104 105 108 private String tempPathString = null; 109 110 113 private String articleIdPathString = null; 114 115 118 private String articleIDDomainSuffix = null; 119 120 124 private String [] overviewFormat = { "Subject:", 125 "From:", 126 "Date:", 127 "Message-ID:", 128 "References:", 129 "Bytes:", 130 "Lines:" 131 }; 132 133 138 private HashMap repositoryGroups = new HashMap (); 139 140 143 public void contextualize(Context context) 144 throws ContextException { 145 this.context = context; 146 } 147 148 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 (); 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 groupName = children[i].getValue(); 186 groupNameMap.put(groupName, groupName); 187 } 188 } 189 } 190 getLogger().debug("Repository configuration done"); 191 } 192 193 196 public void initialize() throws Exception { 197 198 getLogger().debug("Starting initialize"); 199 File 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 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 errorBuffer = 228 new StringBuffer (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 groups = groupNameMap.keySet(); 236 Iterator groupIterator = groups.iterator(); 237 while( groupIterator.hasNext() ) { 238 String groupName = (String )groupIterator.next(); 239 File groupFile = new File (rootPath,groupName); 240 if ( groupFile.exists() == false ) { 241 groupFile.mkdirs(); 242 } else if (!(groupFile.isDirectory())) { 243 StringBuffer errorBuffer = 244 new StringBuffer (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 errorBuffer = 257 new StringBuffer (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 270 public boolean isReadOnly() { 271 return readOnly; 272 } 273 274 277 public NNTPGroup getGroup(String 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 groupFile = new File (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 e) { 296 getLogger().error("Couldn't create group object.", e); 297 groupToReturn = null; 298 } 299 } 300 } 301 return groupToReturn; 302 } 303 304 307 public NNTPArticle getArticleFromID(String id) { 308 try { 309 return articleIDRepo.getArticle(this,id); 310 } catch(Exception ex) { 311 getLogger().error("Couldn't get article " + id + ": ", ex); 312 return null; 313 } 314 } 315 316 319 public void createArticle(InputStream in) { 320 StringBuffer fileBuffer = 321 new StringBuffer (32) 322 .append(System.currentTimeMillis()) 323 .append(".") 324 .append(Math.random()); 325 File f = new File (tempPath, fileBuffer.toString()); 326 FileOutputStream fout = null; 327 try { 328 fout = new FileOutputStream (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 (spool.getSpoolPath(),f.getName())); 338 if (!renamed) { 339 throw new IOException ("Could not create article on the spool."); 340 } 341 } catch(IOException ex) { 342 throw new NNTPException("create article failed",ex); 343 } finally { 344 if (fout != null) { 345 try { 346 fout.close(); 347 } catch (IOException ioe) { 348 } 350 } 351 } 352 } 353 354 class GroupFilter implements java.io.FilenameFilter { 355 public boolean accept(java.io.File dir, String 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 367 public Iterator getMatchedGroups(String wildmat) { 368 File [] f = rootPath.listFiles(new AndFileFilter(new GroupFilter(), new AndFileFilter 369 (new DirectoryFileFilter(),new GlobFilenameFilter(wildmat)))); 370 return getGroups(f); 371 } 372 373 381 private Iterator getGroups(File [] f) { 382 List list = new ArrayList (); 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 394 public Iterator getGroupsSince(Date dt) { 395 File [] f = rootPath.listFiles(new AndFileFilter(new GroupFilter(), new AndFileFilter 396 (new DirectoryFileFilter(),new DateSinceFileFilter(dt.getTime())))); 397 return getGroups(f); 398 } 399 400 404 407 public Iterator getArticlesSince(final Date dt) { 408 final Iterator giter = getGroupsSince(dt); 409 return new Iterator () { 410 411 private Iterator 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 next() { 432 return iter.next(); 433 } 434 435 public void remove() { 436 throw new UnsupportedOperationException ("remove not supported"); 437 } 438 }; 439 } 440 441 444 public String [] getOverviewFormat() { 445 return overviewFormat; 446 } 447 448 453 private NNTPSpooler createSpooler() 454 throws ConfigurationException { 455 String className = "org.apache.james.nntpserver.repository.NNTPSpooler"; 456 Configuration spoolerConfiguration = configuration.getChild("spool"); 457 try { 458 className = spoolerConfiguration.getAttribute("class"); 460 } catch(ConfigurationException ce) { 461 } 463 try { 464 Object obj = getClass().getClassLoader().loadClass(className).newInstance(); 465 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 cce) { 472 StringBuffer errorBuffer = 473 new StringBuffer (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 errorString = errorBuffer.toString(); 478 getLogger().error(errorString, cce); 479 throw new ConfigurationException(errorString, cce); 480 } catch(Exception ex) { 481 getLogger().error("Spooler initialization failed",ex); 482 throw new ConfigurationException("Spooler initialization failed",ex); 483 } 484 } 485 } 486
| Popular Tags
|