001: /*
002: * Copyright 2004 Hippo Webworks.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package nl.hippo.slide.repository.impl;
017:
018: import java.beans.PropertyChangeEvent;
019: import java.beans.PropertyChangeListener;
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.lang.reflect.Constructor;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.EventListener;
026: import java.util.HashMap;
027: import java.util.HashSet;
028: import java.util.Hashtable;
029: import java.util.Iterator;
030: import java.util.Map;
031:
032: import nl.hippo.slide.index.LuceneIndexer;
033: import nl.hippo.slide.repository.RepositoryListener;
034: import nl.hippo.slide.repository.SlideRepository;
035:
036: import nl.hippo.slide.replication.ReplicatorCollection;
037:
038: import org.apache.avalon.excalibur.monitor.Monitor;
039: import org.apache.avalon.excalibur.monitor.Resource;
040: import org.apache.avalon.excalibur.monitor.SourceResource;
041: import org.apache.avalon.framework.activity.Disposable;
042: import org.apache.avalon.framework.activity.Initializable;
043: import org.apache.avalon.framework.configuration.Configurable;
044: import org.apache.avalon.framework.configuration.Configuration;
045: import org.apache.avalon.framework.configuration.ConfigurationException;
046: import org.apache.avalon.framework.configuration.ConfigurationUtil;
047: import org.apache.avalon.framework.configuration.DefaultConfiguration;
048: import org.apache.avalon.framework.configuration.SAXConfigurationHandler;
049: import org.apache.avalon.framework.container.ContainerUtil;
050: import org.apache.avalon.framework.context.Context;
051: import org.apache.avalon.framework.context.ContextException;
052: import org.apache.avalon.framework.context.Contextualizable;
053: import org.apache.avalon.framework.logger.AbstractLogEnabled;
054: import org.apache.avalon.framework.logger.Logger;
055: import org.apache.avalon.framework.parameters.Parameters;
056: import org.apache.avalon.framework.service.ServiceException;
057: import org.apache.avalon.framework.service.ServiceManager;
058: import org.apache.avalon.framework.service.Serviceable;
059: import org.apache.excalibur.source.Source;
060: import org.apache.excalibur.source.SourceResolver;
061: import org.apache.excalibur.xml.sax.SAXParser;
062: import org.apache.slide.common.Domain;
063: import org.apache.slide.common.Namespace;
064: import org.apache.slide.common.NamespaceAccessToken;
065: import org.apache.slide.common.Scope;
066: import org.apache.slide.common.SlideAccess;
067: import org.apache.slide.event.EventDispatcher;
068: import org.apache.slide.extractor.Extractor;
069: import org.apache.slide.extractor.ExtractorManager;
070: import org.apache.slide.store.ExtendedStore;
071: import org.apache.slide.store.ExtendedStoreAccess;
072: import org.apache.slide.store.Store;
073: import org.apache.slide.util.ObjectCache;
074: import org.xml.sax.InputSource;
075: import org.xml.sax.SAXException;
076:
077: /**
078: * Default SlideRepository implementation.
079: *
080: * @avalon.component
081: * @avalon.service type=SlideRepository
082: * @x-avalon.info name=slide
083: * @x-avalon.lifestyle type=singleton
084: *
085: * @author <a href="mailto:unico@hippo.nl">Unico Hommes</a>
086: */
087: public class SlideRepositoryImpl extends AbstractLogEnabled implements
088: SlideRepository, Contextualizable, Serviceable, Configurable,
089: Initializable, Disposable {
090:
091: // ---------------------------------------------------- Constants
092:
093: private static final String DOMAIN_ELEMENT = "domain";
094: //private static final String NAMESPACES_ELEMENT = "namespaces";
095: private static final String NAMESPACE_ELEMENT = "namespace";
096: private static final String SRC_ATTR = "src";
097:
098: private static final String NAME_ATTR = "name";
099: private static final String DEFINITION_ELEMENT = "definition";
100: private static final String CONFIGURATION_ELEMENT = "configuration";
101: private static final String DATA_ELEMENT = "data";
102:
103: private static final String EXTRACTORS_ELEMENT = "extractors";
104: private static final String EVENTS_ELEMENT = "events";
105:
106: private static final String EXTRACTOR_ELEMENT = "extractor";
107: private static final String LISTENER_ELEMENT = "listener";
108:
109: private static final String REPLICATORS_ELEMENT = "replicators";
110:
111: private static final String INDEXER_ELEMENT = "indexer";
112: private static final String INDEXER_CLASSNAME_ATTR = "class";
113: private static final String DEFAULT_INDEXER_CLASSNAME_ATTR = "nl.hippo.slide.index.LuceneIndexerImpl";
114:
115: private static final String ROLE_ATTR = "role";
116:
117: private static final String DEFAULT_DOMAIN_RESOURCE = "resource://nl/hippo/slide/repository/impl/domain.xml";
118:
119: private static final String LAST_MODIFIED_PROPERTY = "last-modified";
120:
121: // ---------------------------------------------------- Instance variables
122:
123: private String m_contextpath;
124: private String m_workdir;
125:
126: private ServiceManager m_manager;
127: private Monitor m_monitor;
128: private SourceResolver m_resolver;
129:
130: private Configuration m_configuration;
131: private SlideAccess m_slide;
132:
133: // namespace names -> NamespaceConfigurations
134: private final Map m_namespaces = new HashMap();
135:
136: // source uris -> Resources
137: private final Map m_resources = new HashMap();
138:
139: private final Collection m_listeners = new HashSet();
140:
141: // ---------------------------------------------------- Lifecycle
142:
143: public SlideRepositoryImpl() {
144: }
145:
146: public void contextualize(Context context) throws ContextException {
147: m_contextpath = context.get("context-root").toString();
148: m_workdir = context.get("impl.workDir").toString();
149: }
150:
151: /**
152: * @avalon.dependency type=SAXParser
153: * @avalon.dependency type=Monitor
154: * @avalon.dependency type=SourceResolver
155: */
156: public void service(final ServiceManager manager)
157: throws ServiceException {
158: m_manager = manager;
159: m_monitor = (Monitor) manager.lookup(Monitor.ROLE);
160: m_resolver = (SourceResolver) manager
161: .lookup(SourceResolver.ROLE);
162: }
163:
164: public void configure(final Configuration configuration)
165: throws ConfigurationException {
166: m_configuration = configuration;
167: }
168:
169: public void initialize() throws Exception {
170:
171: m_slide = new SlideAccess();
172: SlideLoggerAdapter logger = new SlideLoggerAdapter(getLogger()
173: .getChildLogger("domain"));
174: m_slide.setLogger(logger);
175:
176: DomainConfigurationChangeListener dccl = new DomainConfigurationChangeListener();
177: m_configuration = loadConfiguration(m_configuration
178: .getChild(DOMAIN_ELEMENT), DEFAULT_DOMAIN_RESOURCE,
179: dccl);
180:
181: final Hashtable parameters = Parameters.toProperties(Parameters
182: .fromConfiguration(m_configuration));
183: parameters.put("contextpath", m_contextpath);
184: parameters.put("workdir", m_workdir);
185: m_slide.setParameters(parameters);
186:
187: loadAllNamespaces(m_configuration
188: .getChildren(NAMESPACE_ELEMENT));
189:
190: // global extractors
191: final DefaultConfiguration extractors = new DefaultConfiguration(
192: EXTRACTORS_ELEMENT);
193:
194: Configuration configuration = m_configuration.getChild(
195: EXTRACTORS_ELEMENT, true);
196: // TODO: reconfigure global extractors?
197: configuration = loadConfiguration(configuration, null, null);
198: Configuration[] children = configuration.getChildren();
199: for (int i = 0; i < children.length; i++) {
200: final String role = children[i].getAttribute(ROLE_ATTR,
201: null);
202: if (role != null
203: && children[i].getName().equals(EXTRACTOR_ELEMENT)) {
204: final Extractor extractor = (Extractor) m_manager
205: .lookup(role);
206: ExtractorManager.getInstance().addExtractor(extractor);
207: } else {
208: extractors.addChild(children[i]);
209: }
210: }
211: ExtractorManager.getInstance().configure(
212: new SlideConfigurationAdapter(extractors));
213:
214: // events
215: final DefaultConfiguration events = new DefaultConfiguration(
216: EVENTS_ELEMENT);
217:
218: configuration = m_configuration.getChild(EVENTS_ELEMENT, true);
219: // TODO: reconfigure global events?
220: configuration = loadConfiguration(configuration, null, null);
221: children = configuration.getChildren();
222: for (int i = 0; i < children.length; i++) {
223: final String role = children[i].getAttribute(ROLE_ATTR,
224: null);
225: if (role != null
226: && children[i].getName().equals(LISTENER_ELEMENT)) {
227: final EventListener listener = (EventListener) m_manager
228: .lookup(role);
229: EventDispatcher.getInstance()
230: .addEventListener(listener);
231: } else {
232: events.addChild(children[i]);
233: }
234: }
235: EventDispatcher.getInstance().configure(
236: new SlideConfigurationAdapter(events));
237:
238: Domain.setInitialized(true);
239:
240: }
241:
242: public void dispose() {
243: final String[] namespaces = m_slide.getNamespaceNames();
244: for (int i = 0; i < namespaces.length; i++) {
245: disposeNamespace(namespaces[i]);
246: }
247: }
248:
249: // ---------------------------------------------------- SlideRepository
250:
251: public NamespaceAccessToken getNamespaceToken(
252: final String namespaceName) {
253: return m_slide.getNamespaceToken(namespaceName);
254: }
255:
256: public NamespaceAccessToken[] getNamespaceTokens() {
257: final String[] names = m_slide.getNamespaceNames();
258: final NamespaceAccessToken[] tokens = new NamespaceAccessToken[names.length];
259: for (int i = 0; i < names.length; i++) {
260: tokens[i] = getNamespaceToken(names[i]);
261: }
262: return tokens;
263: }
264:
265: public Namespace[] getNamespaces() {
266: return m_slide.getNamespaces();
267: }
268:
269: public LuceneIndexer getLuceneIndexer(String namespaceName) {
270: final NamespaceConfiguration nc = (NamespaceConfiguration) m_namespaces
271: .get(namespaceName);
272: return nc != null ? nc.getIndexer() : null;
273: }
274:
275: public ReplicatorCollection getReplicator(String namespaceName) {
276: final NamespaceConfiguration nc = (NamespaceConfiguration) m_namespaces
277: .get(namespaceName);
278: return nc != null ? nc.getReplicator() : null;
279: }
280:
281: public synchronized void registerRepositoryListener(
282: RepositoryListener listener) {
283: m_listeners.add(listener);
284: }
285:
286: public synchronized void unregisterRepositoryListener(
287: RepositoryListener listener) {
288: m_listeners.remove(listener);
289: }
290:
291: // ---------------------------------------------------- Implementation
292:
293: private Configuration loadConfiguration(Configuration config,
294: String defaultSource, ConfigurationChangeListener listener)
295: throws SAXException, IOException, ServiceException,
296: Exception {
297:
298: if (config == null)
299: return null;
300:
301: final String src = config.getAttribute(SRC_ATTR, defaultSource);
302: if (src != null) {
303: Source source = null;
304: try {
305: source = m_resolver.resolveURI(src);
306: return loadConfiguration(source, config, listener);
307: } finally {
308: if (source != null) {
309: m_resolver.release(source);
310: }
311: }
312: } else {
313: return config;
314: }
315: }
316:
317: private Configuration loadConfiguration(Source source,
318: Configuration config, ConfigurationChangeListener listener)
319: throws SAXException, IOException, ServiceException,
320: Exception {
321: InputStream in = null;
322: SAXParser parser = null;
323: try {
324:
325: // parse the configuration
326: parser = (SAXParser) m_manager.lookup(SAXParser.ROLE);
327: in = source.getInputStream();
328: final SAXConfigurationHandler handler = new SAXConfigurationHandler();
329: parser.parse(new InputSource(in), handler);
330:
331: // start monitoring the configuration resource
332: if (listener != null) {
333: // make sure that only one resource per source is being monitored
334: // (the same configuration file may be used by several namespaces)
335: SourceResource resource = (SourceResource) m_resources
336: .get(source.getURI());
337: if (resource == null) {
338: resource = new SourceResource(source);
339: m_resources.put(source.getURI(), resource);
340: m_monitor.addResource(resource);
341: }
342: listener.addResource(resource);
343: resource.addPropertyChangeListener(listener);
344: }
345:
346: // re-add the src attribute for future reference
347: DefaultConfiguration result = new DefaultConfiguration(
348: config.getName());
349: result.setAttribute(SRC_ATTR, source.getURI());
350: result.addAll(handler.getConfiguration());
351: result.makeReadOnly();
352:
353: return result;
354: } finally {
355: if (in != null) {
356: try {
357: in.close();
358: } catch (IOException e) {
359: }
360: }
361: m_manager.release(parser);
362: }
363: }
364:
365: private void loadAllNamespaces(final Configuration[] configurations)
366: throws IOException, ServiceException, Exception {
367: for (int i = 0; i < configurations.length; i++) {
368: loadNamespace(configurations[i]);
369: }
370: }
371:
372: private void loadNamespace(final Configuration namespace)
373: throws ServiceException, SAXException, IOException,
374: Exception {
375:
376: if (namespace.getChild(EVENTS_ELEMENT, false) != null) {
377: throw new ConfigurationException("The <" + EVENTS_ELEMENT
378: + "> should not be present in the <"
379: + NAMESPACE_ELEMENT + "> configuration (of '"
380: + namespace.getAttribute(NAME_ATTR) + "')! <"
381: + EVENTS_ELEMENT
382: + "> should be a child of <slide>.", namespace
383: .getChild(EVENTS_ELEMENT));
384: }
385:
386: final NamespaceConfiguration nc = new NamespaceConfiguration(
387: namespace);
388: m_namespaces.put(nc.getNamespaceName(), nc);
389:
390: final NamespaceConfigurationChangeListener listener = new NamespaceConfigurationChangeListener(
391: nc.getNamespaceName());
392: nc.setListener(listener);
393:
394: Configuration definition = namespace
395: .getChild(DEFINITION_ELEMENT);
396: definition = loadConfiguration(definition, null, listener);
397:
398: Configuration configuration = namespace
399: .getChild(CONFIGURATION_ELEMENT);
400: configuration = loadConfiguration(configuration, null, listener);
401:
402: Configuration data = namespace.getChild(DATA_ELEMENT);
403: data = loadConfiguration(data, null, listener);
404:
405: Configuration extractors = namespace
406: .getChild(EXTRACTORS_ELEMENT);
407: extractors = loadConfiguration(extractors, null, listener);
408:
409: Configuration indexer = namespace.getChild(INDEXER_ELEMENT,
410: false);
411: indexer = loadConfiguration(indexer, null, listener);
412:
413: Configuration replicator = namespace.getChild(
414: REPLICATORS_ELEMENT, false);
415: replicator = loadConfiguration(replicator, null, listener);
416:
417: if (getLogger().isInfoEnabled()) {
418: getLogger().info(
419: "Initializing namespace \"" + nc.getNamespaceName()
420: + "\"");
421: }
422:
423: final SlideLoggerAdapter logger = new SlideLoggerAdapter(
424: getLogger().getChildLogger(nc.getNamespaceName()));
425: final SlideConfigurationAdapter def = new SlideConfigurationAdapter(
426: definition);
427: final SlideConfigurationAdapter config = new SlideConfigurationAdapter(
428: configuration);
429: final SlideConfigurationAdapter dat = new SlideConfigurationAdapter(
430: data);
431: final SlideConfigurationAdapter extr = new SlideConfigurationAdapter(
432: extractors);
433:
434: m_slide.addNamespace(nc.getNamespaceName(), logger, def,
435: config, dat, extr);
436:
437: if (indexer != null) {
438: loadIndexer(nc, indexer);
439: }
440:
441: if (replicator != null) {
442: loadReplicator(nc, replicator);
443: }
444:
445: fireNamespaceAddedEvent(nc.getNamespaceName());
446: }
447:
448: private void loadReplicator(final NamespaceConfiguration nc,
449: final Configuration configuration) throws Exception {
450: final Logger logger = getLogger().getChildLogger(
451: nc.getNamespaceName()).getChildLogger("replicator");
452:
453: final ReplicatorCollection replicator = ReplicatorCollection
454: .getReplicatorCollection(configuration, logger);
455:
456: nc.setReplicator(replicator);
457: }
458:
459: private void loadIndexer(final NamespaceConfiguration nc,
460: final Configuration configuration) throws Exception {
461:
462: final Logger logger = getLogger().getChildLogger(
463: nc.getNamespaceName()).getChildLogger("indexer");
464: final String indexpath = m_workdir + "/slide_index/"
465: + nc.getNamespaceName();
466:
467: final String indexerClassname = configuration.getAttribute(
468: INDEXER_CLASSNAME_ATTR, DEFAULT_INDEXER_CLASSNAME_ATTR);
469: final Class indexerClass = Class.forName(indexerClassname);
470: final Constructor indexerConstructor = indexerClass
471: .getConstructor(new Class[] {
472: NamespaceAccessToken.class, String.class });
473: final LuceneIndexer indexer = (LuceneIndexer) indexerConstructor
474: .newInstance(new Object[] {
475: getNamespaceToken(nc.getNamespaceName()),
476: indexpath });
477:
478: ContainerUtil.enableLogging(indexer, logger);
479: ContainerUtil.service(indexer, m_manager);
480: ContainerUtil.configure(indexer, configuration);
481: ContainerUtil.initialize(indexer);
482:
483: nc.setIndexer(indexer);
484:
485: }
486:
487: /** dispose the namespace and its resources */
488: private void disposeNamespace(String namespaceName) {
489: try {
490: final NamespaceConfiguration nc = (NamespaceConfiguration) m_namespaces
491: .get(namespaceName);
492: if (nc != null) {
493: getLogger().info(
494: "Disposing namespace '" + namespaceName + "'");
495:
496: // clear caches
497: clearCachesOfNamespace(namespaceName);
498:
499: // stop listening and remove the resource if there are no longer any
500: // interested parties
501: final Iterator resources = nc.resources();
502: while (resources.hasNext()) {
503: final SourceResource resource = (SourceResource) resources
504: .next();
505: resource.removePropertyChangeListener(nc
506: .getListener());
507: if (!resource.hasListeners()) {
508: m_monitor.removeResource(resource);
509: m_resources.remove(resource.getSource()
510: .getURI());
511: }
512: }
513:
514: // then shut down the indexer
515: if (nc.getIndexer() != null) {
516: ContainerUtil.dispose(nc.getIndexer());
517: }
518:
519: // remove the namespace from slide
520: m_slide.removeNamespace(namespaceName);
521:
522: // forget about this namespace
523: m_namespaces.remove(namespaceName);
524:
525: // notify the listeners
526: fireNamespaceRemovedEvent(namespaceName);
527: }
528: } catch (Exception e) {
529: getLogger()
530: .error(
531: "Error disposing namespace '"
532: + namespaceName + "'", e);
533: }
534: }
535:
536: public synchronized void reloadNamespace(String namespaceName) {
537: if (getLogger().isInfoEnabled()) {
538: getLogger().info(
539: "Reloading namespace : '" + namespaceName + "'...");
540: }
541: try {
542: final NamespaceConfiguration nc = (NamespaceConfiguration) m_namespaces
543: .get(namespaceName);
544: disposeNamespace(namespaceName);
545: loadNamespace(nc.getConfiguration());
546: getLogger().info(
547: "Reloading namespace: '" + namespaceName
548: + "' finished.");
549: } catch (RuntimeException e) {
550: final String msg = "Error reloading namespace: '"
551: + namespaceName + "'.";
552: getLogger().error(msg, e);
553: } catch (Exception e) {
554: final String msg = "Error reloading namespace: '"
555: + namespaceName + "'.";
556: getLogger().error(msg, e);
557: }
558:
559: }
560:
561: public synchronized void reloadAllNamespaces() {
562: // remove old
563: dispose();
564:
565: try {
566: final String src = m_configuration.getAttribute(SRC_ATTR);
567: m_resolver.resolveURI(src);
568: final Configuration newconfiguration = loadConfiguration(
569: m_configuration, null, null);
570: final Configuration[] namespaces = newconfiguration
571: .getChildren(NAMESPACE_ELEMENT);
572: final HashSet namespaceNames = new HashSet();
573: // check for added and changed namespaces
574: for (int i = 0; i < namespaces.length; i++) {
575: final String namespaceName = namespaces[i]
576: .getAttribute(NAME_ATTR, null);
577: namespaceNames.add(namespaceName);
578: try {
579: loadNamespace(namespaces[i]);
580: } catch (Exception e) {
581: getLogger().error(
582: "Error reloading namespace '"
583: + namespaceName + "'", e);
584: }
585: }
586:
587: m_configuration = newconfiguration;
588: } catch (ServiceException e) {
589: getLogger().error("Error reloading domain config", e);
590: } catch (SAXException e) {
591: getLogger().error("Error reloading domain config", e);
592: } catch (IOException e) {
593: getLogger().error("Error reloading domain config", e);
594: } catch (Exception e) {
595: getLogger().error("Error reloading domain config", e);
596: }
597: }
598:
599: public void clearCachesOfNamespace(String namespaceName) {
600:
601: getLogger().info(
602: "Clearing caches of namespace '" + namespaceName + "'");
603: Namespace[] namespaces = m_slide.getNamespaces();
604: Namespace namespace = null;
605: boolean found = false;
606: for (int i = 0; i < namespaces.length && !found; i++) {
607: Namespace namespaceTemp = namespaces[i];
608: if (namespaceTemp.getName().equals(namespaceName)) {
609: namespace = namespaceTemp;
610: found = true;
611: }
612: }
613: if (!found) {
614: getLogger()
615: .error(
616: "Error clearing caches of namespace '"
617: + namespaceName
618: + "': namespace not found.");
619: return;
620: }
621: Store store = namespace.getStore(Scope.ROOT);
622: if (store != null && store instanceof ExtendedStore) {
623: ExtendedStore extendedStore = (ExtendedStore) store;
624: ExtendedStoreAccess storeAccess = new ExtendedStoreAccess(
625: extendedStore);
626:
627: ObjectCache cache = storeAccess.getObjectsCache();
628: if (cache != null) {
629: cache.clear();
630: }
631:
632: cache = storeAccess.getPermissionsCache();
633: if (cache != null) {
634: cache.clear();
635: }
636:
637: cache = storeAccess.getDescriptorsCache();
638: if (cache != null) {
639: cache.clear();
640: }
641:
642: cache = storeAccess.getDescriptorCache();
643: if (cache != null) {
644: cache.clear();
645: }
646:
647: cache = storeAccess.getLocksCache();
648: if (cache != null) {
649: cache.clear();
650: }
651:
652: cache = storeAccess.getContentCache();
653: if (cache != null) {
654: cache.clear();
655: }
656: }
657:
658: }
659:
660: private void fireNamespaceRemovedEvent(String namespaceName) {
661: final Iterator listeners = new ArrayList(m_listeners)
662: .iterator();
663: while (listeners.hasNext()) {
664: ((RepositoryListener) listeners.next())
665: .namespaceRemoved(namespaceName);
666: }
667: }
668:
669: private void fireNamespaceAddedEvent(String namespaceName) {
670: final Iterator listeners = new ArrayList(m_listeners)
671: .iterator();
672: while (listeners.hasNext()) {
673: ((RepositoryListener) listeners.next())
674: .namespaceAdded(namespaceName);
675: }
676: }
677:
678: abstract class ConfigurationChangeListener implements
679: PropertyChangeListener {
680: abstract void addResource(Resource resource);
681: }
682:
683: /**
684: * Class that handles changes in the top level Domain configuration.
685: * Currently only handles loading and reloading of namespaces.
686: *
687: * @author unico
688: */
689: final class DomainConfigurationChangeListener extends
690: ConfigurationChangeListener {
691:
692: void addResource(Resource resource) {
693: // no need to remember this one
694: }
695:
696: public void propertyChange(PropertyChangeEvent event) {
697: if (event.getPropertyName().equals(LAST_MODIFIED_PROPERTY)) {
698: if (getLogger().isInfoEnabled()) {
699: getLogger()
700: .info(
701: "Domain configuration changed. Checking for changes...");
702: }
703: boolean didStuff = false;
704: Source source = null;
705: try {
706: final String src = m_configuration
707: .getAttribute(SRC_ATTR);
708: source = m_resolver.resolveURI(src);
709: final Configuration newconfiguration = loadConfiguration(
710: m_configuration, null, null);
711: final Configuration[] namespaces = newconfiguration
712: .getChildren(NAMESPACE_ELEMENT);
713: final HashSet namespaceNames = new HashSet();
714: // check for added and changed namespaces
715: for (int i = 0; i < namespaces.length; i++) {
716: final String namespaceName = namespaces[i]
717: .getAttribute(NAME_ATTR, null);
718: namespaceNames.add(namespaceName);
719: final NamespaceConfiguration oldnc = (NamespaceConfiguration) m_namespaces
720: .get(namespaceName);
721: if (oldnc != null) {
722: if (!ConfigurationUtil.equals(
723: namespaces[i], oldnc
724: .getConfiguration())) {
725: didStuff = true;
726: if (getLogger().isInfoEnabled()) {
727: getLogger()
728: .info(
729: "Configuration changed for namespace '"
730: + namespaceName
731: + "'. Reloading ...");
732: }
733: disposeNamespace(namespaceName);
734: try {
735: loadNamespace(namespaces[i]);
736: } catch (Exception e) {
737: getLogger().error(
738: "Error reloading namespace '"
739: + namespaceName
740: + "'", e);
741: }
742: } else {
743: if (getLogger().isDebugEnabled()) {
744: getLogger().debug(
745: "Nothing changed to namespace '"
746: + namespaceName
747: + "'");
748: }
749: }
750: } else {
751: didStuff = true;
752: if (getLogger().isInfoEnabled()) {
753: getLogger().info(
754: "Configuration added for namespace '"
755: + namespaceName
756: + "'. Loading ...");
757: }
758: try {
759: loadNamespace(namespaces[i]);
760: } catch (Exception e) {
761: getLogger().error(
762: "Error reloading namespace '"
763: + namespaceName + "'",
764: e);
765: }
766: }
767: }
768: // check for removed namespaces
769: final Iterator oldNamespaceNames = new ArrayList(
770: m_namespaces.keySet()).iterator();
771: while (oldNamespaceNames.hasNext()) {
772: final String namespaceName = (String) oldNamespaceNames
773: .next();
774: if (!namespaceNames.contains(namespaceName)) {
775: didStuff = true;
776: if (getLogger().isInfoEnabled()) {
777: getLogger().info(
778: "Configuration removed for namespace '"
779: + namespaceName
780: + "'. Disposing ...");
781: }
782: disposeNamespace(namespaceName);
783: }
784: }
785: if (didStuff) {
786: m_configuration = newconfiguration;
787: } else {
788: getLogger()
789: .warn(
790: "Something definitely changed in the Domain configuration "
791: + "but I'm too stupid to figure out what it was and take appropriate action."
792: + "If it's important, you'll need to restart the container manually.");
793: }
794: } catch (RuntimeException e) {
795: final String msg = "Error handling domain configuration change";
796: getLogger().error(msg, e);
797: } catch (Exception e) {
798: final String msg = "Error handling domain configuration change";
799: getLogger().error(msg, e);
800: } finally {
801: if (source != null) {
802: m_resolver.release(source);
803: }
804: }
805: }
806: }
807:
808: }
809:
810: /**
811: * Handles changes to an existing namespace. Regardless of what part of the configuration changes
812: * it just disposes and restarts the entire namespace.
813: *
814: * @author unico
815: */
816: final class NamespaceConfigurationChangeListener extends
817: ConfigurationChangeListener {
818:
819: final String m_namespaceName;
820:
821: NamespaceConfigurationChangeListener(String namespaceName) {
822: m_namespaceName = namespaceName;
823: }
824:
825: public synchronized void propertyChange(
826: PropertyChangeEvent event) {
827: if (event.getPropertyName().equals(LAST_MODIFIED_PROPERTY)) {
828: if (getLogger().isInfoEnabled()) {
829: getLogger().info(
830: "Configuration changed for namespace : '"
831: + m_namespaceName
832: + "'. Reloading...");
833: }
834: reloadNamespace(m_namespaceName);
835: }
836: }
837:
838: void addResource(Resource resource) {
839: final NamespaceConfiguration nc = (NamespaceConfiguration) m_namespaces
840: .get(m_namespaceName);
841: nc.addResource(resource);
842: }
843:
844: }
845:
846: /**
847: * A holder object for stuff we need to remember about a namespace.
848: */
849: private final static class NamespaceConfiguration {
850:
851: private final Configuration m_configuration;
852: private final Collection m_resources;
853: private LuceneIndexer m_indexer;
854: private ReplicatorCollection m_replicator;
855: private NamespaceConfigurationChangeListener m_listener;
856:
857: NamespaceConfiguration(Configuration configuration) {
858: m_configuration = configuration;
859: m_resources = new HashSet(10);
860: }
861:
862: String getNamespaceName() {
863: return m_configuration.getAttribute(NAME_ATTR, null);
864: }
865:
866: Configuration getConfiguration() {
867: return m_configuration;
868: }
869:
870: void addResource(Resource resource) {
871: m_resources.add(resource);
872: }
873:
874: Iterator resources() {
875: return m_resources.iterator();
876: }
877:
878: LuceneIndexer getIndexer() {
879: return m_indexer;
880: }
881:
882: void setIndexer(LuceneIndexer indexer) {
883: m_indexer = indexer;
884: }
885:
886: ReplicatorCollection getReplicator() {
887: return m_replicator;
888: }
889:
890: void setReplicator(ReplicatorCollection replicator) {
891: m_replicator = replicator;
892: }
893:
894: NamespaceConfigurationChangeListener getListener() {
895: return m_listener;
896: }
897:
898: void setListener(NamespaceConfigurationChangeListener listener) {
899: m_listener = listener;
900: }
901: }
902:
903: }
|