| 1 16 package org.outerj.daisy.frontend.components.siteconf; 17 18 import org.apache.avalon.framework.service.Serviceable; 19 import org.apache.avalon.framework.service.ServiceManager; 20 import org.apache.avalon.framework.service.ServiceException; 21 import org.apache.avalon.framework.thread.ThreadSafe; 22 import org.apache.avalon.framework.activity.Disposable; 23 import org.apache.avalon.framework.activity.Initializable; 24 import org.apache.avalon.framework.logger.AbstractLogEnabled; 25 import org.apache.avalon.framework.configuration.Configurable; 26 import org.apache.avalon.framework.configuration.Configuration; 27 import org.apache.avalon.framework.configuration.ConfigurationException; 28 import org.apache.avalon.framework.context.Contextualizable; 29 import org.apache.avalon.framework.context.Context; 30 import org.apache.avalon.framework.context.ContextException; 31 import org.apache.avalon.excalibur.monitor.ActiveMonitor; 32 import org.apache.avalon.excalibur.monitor.FileResource; 33 import org.apache.avalon.excalibur.monitor.DirectoryResource; 34 import org.apache.excalibur.source.SourceResolver; 35 import org.apache.excalibur.source.Source; 36 import org.apache.excalibur.source.impl.FileSource; 37 import org.apache.cocoon.ResourceNotFoundException; 38 import org.apache.cocoon.xml.SaxBuffer; 39 import org.apache.cocoon.xml.IncludeXMLConsumer; 40 import org.apache.xmlbeans.XmlOptions; 41 import org.outerx.daisy.x10Siteconf.SiteconfDocument; 42 import org.outerj.daisy.repository.VersionState; 43 import org.outerj.daisy.repository.LockType; 44 import org.outerj.daisy.repository.Repository; 45 import org.outerj.daisy.repository.RepositoryException; 46 import org.outerj.daisy.repository.variant.Branch; 47 import org.outerj.daisy.repository.variant.Language; 48 import org.outerj.daisy.configutil.PropertyResolver; 49 import org.outerj.daisy.frontend.WikiHelper; 50 import org.outerj.daisy.frontend.util.WikiDataDirHelper; 51 import org.outerj.daisy.xmlutil.LocalSAXParserFactory; 52 import org.xml.sax.XMLReader ; 53 import org.xml.sax.InputSource ; 54 55 import java.io.InputStream ; 56 import java.io.File ; 57 import java.io.FileInputStream ; 58 import java.util.*; 59 import java.beans.PropertyChangeListener ; 60 import java.beans.PropertyChangeEvent ; 61 62 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap; 63 import EDU.oswego.cs.dl.util.concurrent.Mutex; 64 65 import javax.xml.parsers.SAXParserFactory ; 66 import javax.xml.parsers.SAXParser ; 67 68 77 public class SitesManagerImpl extends AbstractLogEnabled implements SitesManager, Serviceable, ThreadSafe, 78 Disposable, Configurable, Initializable, Contextualizable { 79 private ServiceManager serviceManager; 80 private Context context; 81 private ActiveMonitor monitor; 82 83 private Map siteConfs = new ConcurrentReaderHashMap(); 84 85 private Mutex updateMutex = new Mutex(); 86 private String siteConfDirName; 87 private File sitesDir; 88 private DirectoryResource sitesDirResource; 89 private boolean running = false; 90 private String globalSitemapPath; 91 92 private File globalSiteConfFile; 93 private String globalSkinName; 94 private String globalPublisherRequestSet; 95 private FileResource globalSiteConfResource; 96 97 private File globalSkinConfFile; 98 private SaxBuffer globalSkinConf; 99 private FileResource globalSkinConfResource; 100 101 private Repository repository; 102 private String repoLogin; 103 private String repoPassword; 104 105 public void service(ServiceManager serviceManager) throws ServiceException { 106 this.serviceManager = serviceManager; 107 monitor = (ActiveMonitor)serviceManager.lookup(ActiveMonitor.ROLE); 108 } 109 110 public void contextualize(Context context) throws ContextException { 111 this.context = context; 112 } 113 114 public void configure(Configuration configuration) throws ConfigurationException { 115 siteConfDirName = PropertyResolver.resolveProperties(configuration.getChild("directory").getValue(), WikiDataDirHelper.getResolveProperties(context)); 116 repoLogin = configuration.getChild("repositoryUser").getAttribute("login"); 117 repoPassword = configuration.getChild("repositoryUser").getAttribute("password"); 118 } 119 120 public void initialize() throws Exception { 121 repository = WikiHelper.getRepository(repoLogin, repoPassword, serviceManager); 122 SourceResolver sourceResolver = null; 123 Source source = null; 124 try { 125 sourceResolver = (SourceResolver)serviceManager.lookup(SourceResolver.ROLE); 126 source = sourceResolver.resolveURI(siteConfDirName); 127 if (source instanceof FileSource) { 128 sitesDir = ((FileSource)source).getFile(); 129 if (!sitesDir.exists()) { 130 throw new Exception ("Specified sites directory does not exist: " + siteConfDirName); 131 } 132 if (!sitesDir.isDirectory()) { 133 throw new Exception ("Specified sites directory is not a directory: " + siteConfDirName); 134 } 135 } else { 136 throw new Exception ("Specified sites directory does not point to a filesystem location: " + siteConfDirName); 137 } 138 } finally { 139 if (source != null) 140 sourceResolver.release(source); 141 if (sourceResolver != null) 142 serviceManager.release(sourceResolver); 143 } 144 this.globalSitemapPath = new File (new File (sitesDir, "cocoon"), "sitemap.xmap").toURI().toString(); 145 initialSitesDirectoryScan(); 146 sitesDirResource = new DirectoryResource(sitesDir.getAbsolutePath()); 147 sitesDirResource.addPropertyChangeListener(new SitesDirListener()); 148 monitor.addResource(sitesDirResource); 149 running = true; 150 } 151 152 public void dispose() { 153 running = false; 154 155 if (sitesDirResource != null) { 156 monitor.removeResource(sitesDirResource); 157 } 158 159 CachedEntry[] cachedEntry = (CachedEntry[])siteConfs.values().toArray(new CachedEntry[0]); 160 for (int i = 0; i < cachedEntry.length; i++) { 161 monitor.removeResource(cachedEntry[i].siteConfResource); 162 monitor.removeResource(cachedEntry[i].skinConfResource); 163 } 164 165 monitor.removeResource(globalSkinConfResource); 166 167 serviceManager.release(monitor); 168 } 169 170 private void initialSitesDirectoryScan() throws Exception { 171 updateMutex.acquire(); 172 try { 173 File subdirs[] = sitesDir.listFiles(); 175 for (int i = 0; i < subdirs.length; i++) { 176 File subdir = subdirs[i]; 177 if (subdir.isDirectory() && !subdir.isHidden() && !subdir.getName().equals("cocoon")) { 178 File siteConfFile = new File (subdir, "siteconf.xml"); 179 SiteConfImpl siteConf = getSiteConf(siteConfFile); 180 181 185 makeFileListeners(subdir, siteConf); 186 } 187 } 188 189 this.globalSkinConfFile = new File (sitesDir, "skinconf.xml"); 190 this.globalSkinConf = loadSkinConf(globalSkinConfFile); 191 this.globalSkinConfResource = new FileResource(globalSkinConfFile); 192 this.globalSkinConfResource.addPropertyChangeListener(new GlobalSkinConfListener()); 193 monitor.addResource(globalSkinConfResource); 194 195 this.globalSiteConfFile = new File (sitesDir, "siteconf.xml"); 196 loadGlobalParameters(globalSiteConfFile); 197 this.globalSiteConfResource = new FileResource(globalSiteConfFile); 198 this.globalSiteConfResource.addPropertyChangeListener(new GlobalSiteConfListener()); 199 monitor.addResource(globalSiteConfResource); 200 } finally { 201 updateMutex.release(); 202 } 203 } 204 205 private void makeFileListeners(File siteDir, SiteConf siteConf) throws Exception { 206 String siteName = siteDir.getName(); 207 File siteConfFile = new File (siteDir, "siteconf.xml"); 208 FileResource siteConfResource = new FileResource(siteConfFile); 209 FileResource skinConfResource = new FileResource(new File (siteDir, "skinconf.xml")); 210 SiteConfListener siteConfListener = new SiteConfListener(siteName, siteConfFile, siteConfResource, skinConfResource); 211 siteConfResource.addPropertyChangeListener(siteConfListener); 212 skinConfResource.addPropertyChangeListener(siteConfListener); siteConfs.put(siteName, new CachedEntry(siteConf, siteConfResource, skinConfResource)); 214 monitor.addResource(siteConfResource); 215 monitor.addResource(skinConfResource); 216 } 217 218 private SiteConfImpl getSiteConf(File siteConfFile) { 219 try { 220 SiteconfDocument siteConfDocument; 221 if (siteConfFile.exists()) { 222 InputStream is = null; 223 try { 224 is = new FileInputStream (siteConfFile); 225 XmlOptions xmlOptions = new XmlOptions().setLoadUseXMLReader(LocalSAXParserFactory.newXmlReader()); 226 siteConfDocument = SiteconfDocument.Factory.parse(is, xmlOptions); 227 } catch (Exception e) { 228 getLogger().error("Could not read or parse the following siteconf.xml file: " + siteConfFile.getAbsolutePath()); 229 return null; 230 } finally { 231 if (is != null) { 232 try { 233 is.close(); 234 } catch (Exception e) { 235 getLogger().error("Error closing inputstream on " + siteConfFile.getAbsolutePath()); 236 } 237 } 238 } 239 240 if (!siteConfDocument.validate()) { 241 getLogger().error("The following siteconf.xml file does not validate against the schema: " + siteConfFile.getAbsolutePath()); 242 return null; 243 } 244 245 SiteconfDocument.Siteconf siteConfXml = siteConfDocument.getSiteconf(); 246 247 long branchId = Branch.MAIN_BRANCH_ID; 248 if (siteConfXml.isSetBranch()) { 249 String branch = siteConfXml.getBranch(); 250 try { 251 branchId = repository.getVariantManager().getBranch(branch, false).getId(); 252 } catch (RepositoryException e) { 253 getLogger().error("Problem with branch name \"" + branch + "\" in " + siteConfFile.getAbsolutePath(), e); 254 return null; 255 } 256 } 257 258 long languageId = Language.DEFAULT_LANGUAGE_ID; 259 if (siteConfXml.isSetLanguage()) { 260 String language = siteConfXml.getLanguage(); 261 try { 262 languageId = repository.getVariantManager().getLanguage(language, false).getId(); 263 } catch (RepositoryException e) { 264 getLogger().error("Problem with language name \"" + language + "\" in " + siteConfFile.getAbsolutePath(), e); 265 return null; 266 } 267 } 268 269 String defaultDocumentType = siteConfXml.getDefaultDocumentType(); 270 long defaultDocumentTypeId = -1; 271 if (defaultDocumentType != null) { 272 try { 273 defaultDocumentTypeId = repository.getRepositorySchema().getDocumentType(defaultDocumentType, false).getId(); 274 } catch (RepositoryException e) { 275 getLogger().error("Problem with default document type \"" + defaultDocumentType + "\" in " + siteConfFile.getAbsolutePath(), e); 276 return null; 277 } 278 } 279 280 SiteSwitchingMode siteSwitchingMode = SiteSwitchingMode.ALL; 281 String [] switchSites = null; 282 if (siteConfXml.isSetSiteSwitching()) { 283 String mode = siteConfXml.getSiteSwitching().getMode(); 284 if (mode == null) { 285 } else if (mode.equals("stay")) { 287 siteSwitchingMode = SiteSwitchingMode.STAY; 288 } else if (mode.equals("all")) { 289 siteSwitchingMode = SiteSwitchingMode.ALL; 290 } else if (mode.equals("selected")) { 291 siteSwitchingMode = SiteSwitchingMode.SELECTED; 292 } 293 294 if (siteSwitchingMode == SiteSwitchingMode.SELECTED) { 295 switchSites = siteConfXml.getSiteSwitching().getSiteArray(); 296 } 297 } 298 299 if (!siteConfXml.isSetHomepageDocId() && !siteConfXml.isSetHomepage()) { 300 getLogger().error("At least one of <homepageDocId> or <homepage> must be present in siteconf.xml at " + siteConfFile.getAbsolutePath()); 301 return null; 302 } 303 304 long homepageDocId = siteConfXml.isSetHomepageDocId() ? siteConfXml.getHomepageDocId() : -1; 305 306 File confDir = siteConfFile.getParentFile(); 307 SiteConfImpl siteConf = new SiteConfImpl(confDir, confDir.getName(), siteConfXml.getTitle(), 308 siteConfXml.getDescription(), siteConfXml.getSkin(), siteConfXml.getNavigationDocId(), 309 homepageDocId, siteConfXml.getHomepage(), siteConfXml.getCollectionId(), siteConfXml.getContextualizedTree(), 310 VersionState.fromString(siteConfXml.getNewVersionStateDefault().toString()), branchId, languageId, 311 defaultDocumentTypeId, siteConfXml.getPublisherRequestSet(), siteSwitchingMode, switchSites, this); 312 313 if (siteConfXml.getLocking().isSetAutomatic()) { 314 SiteconfDocument.Siteconf.Locking.Automatic automatic = siteConfXml.getLocking().getAutomatic(); 315 siteConf.setAutomaticLocking(true); 316 siteConf.setDefaultLockTime(automatic.getDefaultTime() * 60 * 1000); siteConf.setLockType(LockType.fromString(automatic.getLockType().toString())); 318 siteConf.setAutoExtendLock(automatic.getAutoExtend()); 319 } 320 321 File skinConfFile = new File (confDir, "skinconf.xml"); 322 siteConf.setSkinConf(loadSkinConf(skinConfFile)); 323 324 return siteConf; 325 } else { 326 getLogger().error("The following directory does not contain a siteconf.xml file: " + siteConfFile.getParentFile().getAbsolutePath()); 327 return null; 328 } 329 } catch (Exception e) { 330 getLogger().error("Problem with siteconf definition in " + siteConfFile.getAbsolutePath(), e); 331 return null; 332 } 333 } 334 335 private SaxBuffer loadSkinConf(File file) { 336 if (file.exists()) { 337 try { 338 SAXParserFactory parserFactory = SAXParserFactory.newInstance(); 339 parserFactory.setNamespaceAware(true); 340 parserFactory.setValidating(false); 341 SAXParser parser = parserFactory.newSAXParser(); 342 XMLReader xmlReader = parser.getXMLReader(); 343 SaxBuffer buffer = new SaxBuffer(); 344 xmlReader.setContentHandler(new IncludeXMLConsumer(buffer)); 345 InputSource skinConfInputSource = new InputSource (new FileInputStream (file)); 346 xmlReader.parse(skinConfInputSource); 347 return buffer; 348 } catch (Throwable e) { 349 getLogger().error("Error parsing skinconf file at " + file.getAbsolutePath(), e); 350 } 351 } 352 return null; 353 } 354 355 static class CachedEntry { 356 public SiteConf siteConf; 357 public FileResource siteConfResource; 358 public FileResource skinConfResource; 359 360 public CachedEntry(SiteConf siteConf, FileResource siteConfResource, FileResource skinConfResource) { 361 this.siteConf = siteConf; 362 this.siteConfResource = siteConfResource; 363 this.skinConfResource = skinConfResource; 364 } 365 } 366 367 370 class SiteConfListener implements PropertyChangeListener { 371 private String name; 372 private File siteConfFile; 373 private FileResource siteConfResource; 374 private FileResource skinConfResource; 375 376 public SiteConfListener(String name, File siteConfFile, FileResource siteConfResource, FileResource skinConfResource) { 377 this.name = name; 378 this.siteConfFile = siteConfFile; 379 this.siteConfResource = siteConfResource; 380 this.skinConfResource = skinConfResource; 381 } 382 383 public void propertyChange(PropertyChangeEvent evt) { 384 if (!running) 385 return; 386 387 if (getLogger().isDebugEnabled()) { 388 getLogger().debug("Received change event for " + ((FileResource)evt.getSource()).getResourceKey()); 389 } 390 391 try { 392 updateMutex.acquire(); 393 try { 394 399 SiteConfImpl siteConf = getSiteConf(siteConfFile); 400 if (siteConf == null) { 401 siteConfs.put(name, new CachedEntry(null, siteConfResource, skinConfResource)); 402 } else { 403 siteConfs.put(name, new CachedEntry(siteConf, siteConfResource, skinConfResource)); 404 } 405 } finally { 406 updateMutex.release(); 407 } 408 } catch (Exception e) { 409 getLogger().error("Error processing siteconf.xml change event for " + siteConfFile.getAbsolutePath(), e); 410 } 411 } 412 } 413 414 417 class SitesDirListener implements PropertyChangeListener { 418 public void propertyChange(PropertyChangeEvent evt) { 419 if (!running) 420 return; 421 422 if (getLogger().isDebugEnabled()) { 423 getLogger().debug("Received a change event on the SitesDirListener of type " + evt.getPropertyName()); 424 } 425 426 try { 427 updateMutex.acquire(); 428 try { 429 if (evt.getPropertyName().equals(DirectoryResource.REMOVED)) { 430 Iterator it = ((Set)evt.getNewValue()).iterator(); 431 while (it.hasNext()) { 432 File file = (File )it.next(); 433 String name = file.getName(); 434 CachedEntry cachedEntry = (CachedEntry)siteConfs.get(name); 435 if (cachedEntry != null) { 436 monitor.removeResource(cachedEntry.siteConfResource); 437 monitor.removeResource(cachedEntry.skinConfResource); 438 siteConfs.remove(name); 439 } 440 } 441 } else if (evt.getPropertyName().equals(DirectoryResource.ADDED)) { 442 Iterator it = ((Set)evt.getNewValue()).iterator(); 443 while (it.hasNext()) { 444 File file = (File )it.next(); 445 446 if (file.isDirectory() && !file.isHidden()) { 447 String siteName = file.getName(); 448 449 CachedEntry cachedEntry = (CachedEntry)siteConfs.get(siteName); 451 if (cachedEntry != null) { 452 monitor.removeResource(cachedEntry.siteConfResource); 453 monitor.removeResource(cachedEntry.skinConfResource); 454 siteConfs.remove(siteName); 455 } 456 457 File siteConfFile = new File (file, "siteconf.xml"); 459 SiteConfImpl siteConf = getSiteConf(siteConfFile); 460 makeFileListeners(file, siteConf); 461 } 462 } 463 } 464 } finally { 465 updateMutex.release(); 466 } 467 } catch (Throwable e) { 468 getLogger().error("Error handling sites dir change event.", e); 469 } 470 } 471 } 472 473 476 class GlobalSkinConfListener implements PropertyChangeListener { 477 public void propertyChange(PropertyChangeEvent evt) { 478 if (!running) 479 return; 480 481 if (getLogger().isDebugEnabled()) { 482 getLogger().debug("Received change event for " + ((FileResource)evt.getSource()).getResourceKey()); 483 } 484 485 SitesManagerImpl.this.globalSkinConf = loadSkinConf(SitesManagerImpl.this.globalSkinConfFile); 486 } 487 } 488 489 public String getGlobalSkinName() { 490 return globalSkinName; 491 } 492 493 class GlobalSiteConfListener implements PropertyChangeListener { 494 public void propertyChange(PropertyChangeEvent evt) { 495 if (!running) 496 return; 497 498 if (getLogger().isDebugEnabled()) { 499 getLogger().debug("Received change event for " + ((FileResource)evt.getSource()).getResourceKey()); 500 } 501 502 loadGlobalParameters(SitesManagerImpl.this.globalSiteConfFile); 503 } 504 } 505 506 private void loadGlobalParameters(File file) { 507 try { 508 if (file.exists()) { 509 XmlOptions xmlOptions = new XmlOptions().setLoadUseXMLReader(LocalSAXParserFactory.newXmlReader()); 510 SiteconfDocument siteconfDoc = SiteconfDocument.Factory.parse(file, xmlOptions); 511 512 this.globalSkinName = siteconfDoc.getSiteconf().getSkin(); 513 if (this.globalSkinName == null || this.globalSkinName.trim().length() == 0) 514 this.globalSkinName = "default"; 515 516 this.globalPublisherRequestSet = siteconfDoc.getSiteconf().getPublisherRequestSet(); 517 if (this.globalPublisherRequestSet == null || this.globalPublisherRequestSet.trim().length() == 0) 518 this.globalPublisherRequestSet = "default"; 519 } 520 } catch (Exception e) { 521 getLogger().error("Error reading global siteconf.xml at " + file.getAbsolutePath(), e); 522 } 523 } 524 525 public SiteConf getSiteConf(String name) throws Exception { 526 CachedEntry cachedEntry = (CachedEntry)siteConfs.get(name); 527 SiteConf siteConf = null; 528 529 if (cachedEntry != null) 530 siteConf = cachedEntry.siteConf; 531 532 if (siteConf == null) 533 throw new ResourceNotFoundException("There is no site called \"" + name + "\"."); 534 535 return siteConf; 536 } 537 538 public SiteConf getSiteConfSoftly(String name) { 539 CachedEntry cachedEntry = (CachedEntry)siteConfs.get(name); 540 SiteConf siteConf = null; 541 542 if (cachedEntry != null) 543 siteConf = cachedEntry.siteConf; 544 545 return siteConf; 546 } 547 548 public SaxBuffer getGlobalSkinConf() { 549 return globalSkinConf; 550 } 551 552 public List getSiteConfs() { 553 List snapshot = new ArrayList(siteConfs.values()); 554 List result = new ArrayList(snapshot.size()); 555 556 Iterator it = snapshot.iterator(); 557 while (it.hasNext()) { 558 CachedEntry entry = (CachedEntry)it.next(); 559 if (entry.siteConf != null) 560 result.add(entry.siteConf); 561 } 562 563 return result; 564 } 565 566 public String getGlobalCocoonSitemapLocation() { 567 return globalSitemapPath; 568 } 569 570 public String getGlobalPublisherRequestSet() { 571 return globalPublisherRequestSet; 572 } 573 } 574 | Popular Tags |