Source code: org/apache/slide/common/Namespace.java
1 /*
2 * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/common/Namespace.java,v 1.65.2.1 2004/09/29 15:01:26 unico Exp $
3 * $Revision: 1.65.2.1 $
4 * $Date: 2004/09/29 15:01:26 $
5 *
6 * ====================================================================
7 *
8 * Copyright 1999-2002 The Apache Software Foundation
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 *
22 */
23
24 package org.apache.slide.common;
25
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.Method;
28 import java.util.Enumeration;
29 import java.util.Hashtable;
30 import java.util.Vector;
31 import javax.transaction.Status;
32 import javax.transaction.SystemException;
33 import javax.transaction.TransactionManager;
34 import org.apache.slide.authenticate.CredentialsToken;
35 import org.apache.slide.content.ContentInterceptor;
36 import org.apache.slide.extractor.Extractor;
37 import org.apache.slide.extractor.ExtractorManager;
38 import org.apache.slide.store.ContentStore;
39 import org.apache.slide.store.DefaultIndexer;
40 import org.apache.slide.store.IndexStore;
41 import org.apache.slide.store.LockStore;
42 import org.apache.slide.store.NodeStore;
43 import org.apache.slide.store.RevisionDescriptorStore;
44 import org.apache.slide.store.RevisionDescriptorsStore;
45 import org.apache.slide.store.SecurityStore;
46 import org.apache.slide.store.SequenceStore;
47 import org.apache.slide.store.Store;
48 import org.apache.slide.structure.ObjectAlreadyExistsException;
49 import org.apache.slide.structure.SubjectNode;
50 import org.apache.slide.transaction.SlideTransactionManager;
51 import org.apache.slide.util.conf.Configurable;
52 import org.apache.slide.util.conf.Configuration;
53 import org.apache.slide.util.conf.ConfigurationException;
54 import org.apache.slide.util.logger.Logger;
55
56 /**
57 * A Namespace contains a hierarchically organized tree of information.
58 *
59 * <p>
60 * Objects in the namespace are generally referred to as <i>Nodes</i>. Nodes
61 * may have a parent, children, content and meta-data. They can also be
62 * versioned (so that multiple revisions of the object's content and
63 * metadata are stored) and locked (so that only specific principals are
64 * allowed to read or modify the object). In addition, access control
65 * information can be assigned to every node.
66 * </p>
67 * <p>
68 * Nodes in the hierarchy are identified by their URI (Unique Resource
69 * Identifier). A URI is analogous to a file path in traditional file
70 * systems. For example:
71 * <pre>
72 * /users/john/documents/my_document.txt
73 * </pre>
74 * As you can see, the slash ("/") is used to separate nodes in the path.
75 * </p>
76 * <p>
77 * Client applications can not access a Namespace object directly. Instead,
78 * access must be requested from the {@link Domain Domain}, which will hand
79 * out a proxy object ({@link NamespaceAccessToken NamespaceAccessToken})
80 * that enables the client application to access the namespace using the
81 * helpers.
82 * </p>
83 * <p>
84 * Namespaces are necessarily self-contained. What this means is that a
85 * namespace cannot reference or contain links to another namespace. A
86 * namespace is typically assigned per-application, which effectively
87 * isolates it's data and security context from those of other applications.
88 * </p>
89 *
90 * @version $Revision: 1.65.2.1 $
91 */
92 public final class Namespace {
93
94
95 // -------------------------------------------------------------- Constants
96
97
98 public static final String REFERENCE = "reference";
99 public static final String NODE_STORE = "nodestore";
100 public static final String SECURITY_STORE = "securitystore";
101 public static final String LOCK_STORE = "lockstore";
102 public static final String REVISION_DESCRIPTORS_STORE =
103 "revisiondescriptorsstore";
104 public static final String REVISION_DESCRIPTOR_STORE =
105 "revisiondescriptorstore";
106 public static final String CONTENT_STORE = "contentstore";
107 public static final String PROPERTIES_INDEX_STORE = "propertiesindexer";
108 public static final String CONTENT_INDEX_STORE = "contentindexer";
109 public static final String SEQUENCE_STORE = "sequencestore";
110
111
112 /**
113 * Log channel for logger
114 */
115 private static final String LOG_CHANNEL = Namespace.class.getName();
116
117 protected static final String I_CREATESTORELISTENERCLASS = "createStoreListenerClass";
118 protected static final String I_CREATESTORELISTENERCLASS_DEFAULT = "org.apache.slide.webdav.util.UriHandler";
119
120 protected static Class createStoreListenerClass;
121
122
123 static {
124 try {
125 String createStoreListenerClassName = Domain.getParameter(I_CREATESTORELISTENERCLASS, I_CREATESTORELISTENERCLASS_DEFAULT);
126 createStoreListenerClass = Class.forName( createStoreListenerClassName );
127 }
128 catch( Exception x ) {
129 Domain.warn( "Loading of create_store_listener class failed: "+x.getMessage() );
130 }
131 }
132
133 // ----------------------------------------------------- Instance Variables
134
135
136 /**
137 * Namespace name.
138 */
139 private String name;
140
141
142 /**
143 * classname of the search implementation
144 */
145 private String searchClassName;
146
147
148 /**
149 * Static Vector which holds a reference, and provides access to all
150 * the services instances used by the Slide namespace.
151 */
152 private transient Vector connectedServices;
153
154
155 /**
156 * Registered DescriptorStores on this Namespace.
157 */
158 private transient Hashtable stores;
159
160
161 /**
162 * Current namespace configuration.
163 */
164 private NamespaceConfig config;
165
166
167 /**
168 * Uri cache.
169 */
170 private Hashtable uriCache;
171
172
173 /**
174 * Default descriptors store classname.
175 */
176 private String defaultStoreClassname =
177 "org.apache.slide.store.ExtendedStore";
178
179
180 /**
181 * Transaction manager associated with this namespace.
182 */
183 private TransactionManager transactionManager =
184 new SlideTransactionManager();
185
186
187 /**
188 * Logger.
189 */
190 private Logger logger;
191
192
193 /**
194 * Application logger.
195 */
196 private Logger applicationLogger;
197
198
199 // ------------------------------------------------------------ Constructor
200
201
202 /**
203 * Constructor.
204 */
205 Namespace() {
206 stores = new Hashtable();
207 connectedServices = new Vector();
208 name = new String();
209 uriCache = new Hashtable();
210 }
211
212
213 // ------------------------------------------------------------- Properties
214
215
216 /**
217 * Sets the qualified name of the namespace.
218 *
219 * @param name Name of the namespace
220 */
221 public void setName(String name) {
222 this.name = name;
223 }
224
225
226 /**
227 * Gets the qulified name of the namespace.
228 *
229 * @return String Namespace name
230 */
231 public String getName() {
232 return name;
233 }
234
235
236 /**
237 * Method setSearchClassName
238 *
239 * @param searchClassName classname of the search implementation
240 */
241 public void setSearchClassName (String searchClassName) {
242 this.searchClassName = searchClassName;
243 }
244
245
246 /**
247 * Method getSearchClassName
248 *
249 * @return classname of the search implementation
250 */
251 public String getSearchClassName() {
252 return searchClassName;
253 }
254
255
256 /**
257 * Returns the namespace configuration.
258 *
259 * @return NamespaceConfig Namespace configuration
260 */
261 public NamespaceConfig getConfig() {
262 return config;
263 }
264
265
266 /**
267 * Enumerate all scopes managed by this namespace.
268 *
269 * @return return an enumeration of all scopes
270 */
271 public Enumeration enumerateScopes() {
272 return stores.keys();
273 }
274
275
276
277 /**
278 * Transaction manager accessor.
279 */
280 public TransactionManager getTransactionManager() {
281 return transactionManager;
282 }
283
284
285 /**
286 * Return the current logger.
287 */
288 public Logger getLogger() {
289 if (logger != null)
290 return logger;
291 else
292 return Domain.getLogger();
293 }
294
295
296 /**
297 * Set the logger used by this namespace.
298 */
299 public void setLogger(Logger logger) {
300 this.logger = logger;
301 if (transactionManager instanceof SlideTransactionManager) {
302 ((SlideTransactionManager) transactionManager).setLogger(logger);
303 }
304 }
305
306
307 /**
308 * Return the current application logger.
309 */
310 public Logger getApplicationLogger() {
311 if (applicationLogger != null)
312 return applicationLogger;
313 else if (logger != null)
314 return logger;
315 else
316 return Domain.getLogger();
317 }
318
319
320 /**
321 * Set the logger used by this namespace.
322 */
323 public void setApplicationLogger(Logger logger) {
324 this.applicationLogger = logger;
325 }
326
327
328 // --------------------------------------------------------- Public Methods
329
330
331 /**
332 * Used to register a Store in the namespace for the specified scope.
333 * First, the function instantiate the Store, then gives it
334 * its init parameters. It is then stored in the stores
335 * Hashtable, associated with the given scope.
336 *
337 * @param storeClass Class of the Data Source
338 * @param parameters Init parameters for the Data Source
339 * @param scope Scope for which the Data Source is registered
340 * @param childStores Instances of the typed stores
341 * @exception ServiceRegistrationFailed An error occured during
342 * instantiation of the service
343 * @exception ServiceParameterErrorException Incorrect service parameter
344 * @exception ServiceParameterMissingException Service parameter missing
345 */
346 public void registerStore(String storeName, Class storeClass,
347 Hashtable parameters, Scope scope,
348 Hashtable childStores)
349 throws ServiceRegistrationFailedException,
350 ServiceParameterErrorException, ServiceParameterMissingException {
351 if (!stores.containsKey(scope)) {
352 try {
353 Store store = (Store) storeClass.newInstance();
354 store.setName(storeName);
355 store.setParameters(parameters);
356 stores.put(scope, store);
357
358 // assign NodeStore
359 NodeStore nodeStore =
360 (NodeStore) dereferenceStore (NODE_STORE, childStores);
361
362 store.setNodeStore (nodeStore);
363
364 // assign SecurityStore
365 SecurityStore securityStore =
366 (SecurityStore) dereferenceStore (SECURITY_STORE, childStores);
367
368 store.setSecurityStore (securityStore);
369
370 // assign LockStore
371 LockStore lockStore =
372 (LockStore) dereferenceStore (LOCK_STORE, childStores);
373
374 store.setLockStore (lockStore);
375
376 // assign RevisionDescriptorsStore
377 RevisionDescriptorsStore revisionDescriptorsStore =
378 (RevisionDescriptorsStore) dereferenceStore
379 (REVISION_DESCRIPTORS_STORE, childStores);
380
381 store.setRevisionDescriptorsStore (revisionDescriptorsStore);
382
383 // assign RevisionDescriptorStore
384 RevisionDescriptorStore revisionDescriptorStore =
385 (RevisionDescriptorStore) dereferenceStore
386 (REVISION_DESCRIPTOR_STORE, childStores);
387
388 store.setRevisionDescriptorStore (revisionDescriptorStore);
389
390 // assign ContentStore
391 ContentStore contentStore =
392 (ContentStore) dereferenceStore (CONTENT_STORE, childStores);
393
394 store.setContentStore (contentStore);
395
396 // assign PropertiesIndexStore
397 IndexStore propertiesIndexer =
398 (IndexStore) dereferenceStore (PROPERTIES_INDEX_STORE, childStores);
399
400 // if not configured, take the default indexer
401 if (propertiesIndexer == null) {
402 propertiesIndexer = new DefaultIndexer (revisionDescriptorStore);
403 childStores.put (PROPERTIES_INDEX_STORE, propertiesIndexer);
404 }
405
406 store.setPropertiesIndexer (propertiesIndexer);
407
408 // assign ContentIndexStore
409 IndexStore contentIndexer =
410 (IndexStore) dereferenceStore (CONTENT_INDEX_STORE, childStores);
411
412 // if not configured, take the default indexer
413 if (contentIndexer == null) {
414 contentIndexer = new DefaultIndexer (contentStore);
415 childStores.put (CONTENT_INDEX_STORE, contentIndexer);
416 }
417
418 store.setContentIndexer (contentIndexer);
419
420 // assign SequenceStore
421 SequenceStore sequenceStore =
422 (SequenceStore) dereferenceStore (SEQUENCE_STORE, childStores);
423
424 store.setSequenceStore(sequenceStore);
425
426 // set the scope in the father and child stores
427 store.setScope(scope);
428
429 // call the create_store_listener
430 notifyStoreCreated( this.name, scope.toString(), storeName );
431
432 } catch(InstantiationException e) {
433 throw new ServiceRegistrationFailedException
434 (storeClass);
435 } catch(IllegalAccessException e) {
436 throw new ServiceRegistrationFailedException
437 (storeClass);
438 } catch(NullPointerException e) {
439 throw new ServiceRegistrationFailedException
440 (storeClass);
441 } catch(ClassCastException e) {
442 // TEMP
443 getLogger().log(e,LOG_CHANNEL, Logger.ERROR);
444 // --TEMP
445 throw new ServiceRegistrationFailedException
446 (storeClass);
447 }
448
449 }
450 }
451
452
453 Object dereferenceStore (String storeType, Hashtable childStores) {
454 Object result;
455
456 Object o = childStores.get(storeType);
457 if (o instanceof String) {
458 result = childStores.get(o);
459 } else {
460 result = o;
461 }
462 return result;
463 }
464
465
466 /**
467 * At the end of the service registration, this service is called to
468 * perform any required initialization task.
469 *
470 * @exception ServicesInitializationFailedException One or more
471 * exception occured while initializing services
472 */
473 public void initializeServices()
474 throws ServicesInitializationFailedException {
475
476 // We create the nested exception which will hold all thrown exception
477 // during the initialization process.
478 ServicesInitializationFailedException nestedException
479 = new ServicesInitializationFailedException();
480
481 // Initializing DesciptorsStores
482 Enumeration serviceList = stores.elements();
483 while (serviceList.hasMoreElements()) {
484 Service service = (Service) serviceList.nextElement();
485 try {
486 getLogger().log("Initializing Store " + service,LOG_CHANNEL,Logger.INFO);
487 service.setNamespace(this);
488 service.initialize(new NamespaceAccessTokenImpl(this));
489 } catch (ServiceInitializationFailedException e) {
490 // We add the exception which just occured to the
491 // nested exception
492 nestedException.addException(e);
493 }
494 }
495
496 // If the nested exception is not empty, we throw it.
497 if (!nestedException.isEmpty()) {
498 throw nestedException;
499 }
500
501 }
502
503
504 /**
505 * Reinitialize namespace.
506 */
507 public void clearNamespace() {
508 stores.clear();
509 }
510
511
512 /**
513 * Connects a data source on demand.
514 *
515 * @param service Service on which a connection attempt will be made
516 * @param token the credentials token containing e.g. the credential
517 * @exception ServiceConnectionFailedException Error connecting service
518 * @exception ServiceAccessException Unspecified low level service
519 * access exception
520 */
521 public void connectService(Service service, CredentialsToken token)
522 throws ServiceConnectionFailedException, ServiceAccessException {
523 // Try to connect ...
524 boolean newConnection = service.connectIfNeeded(token);
525
526 // If successfull (ie, no exception was thrown), we add it to the list
527 // of the connected components.
528 if (newConnection) {
529 connectedServices.addElement(service);
530 }
531 }
532
533
534 /**
535 * Disconnects all services.
536 *
537 * @exception ServicesShutDownFailedException Error disconnecting one or
538 * more services
539 */
540 public void disconnectServices()
541 throws ServicesShutDownFailedException {
542
543 // We create the nested exception which will hold all thrown exception
544 // during shut down of services.
545 ServicesShutDownFailedException nestedException
546 = new ServicesShutDownFailedException();
547
548 for (int i=0; i<connectedServices.size(); i++) {
549 try {
550 Service service = (Service) connectedServices.elementAt(i);
551 if (service.isConnected()) {
552 getLogger().log("Shutting down service " + service,LOG_CHANNEL,Logger.INFO);
553 service.disconnect();
554 }
555 } catch (ServiceDisconnectionFailedException e) {
556 nestedException.addException(e);
557 } catch (ServiceAccessException e) {
558 nestedException.addException(e);
559 }
560 }
561 connectedServices.removeAllElements();
562
563 // If the nested exception is not empty, we throw it.
564 if (!nestedException.isEmpty()) {
565 throw nestedException;
566 }
567
568 }
569
570
571 /**
572 * Remove a Store from the registry.
573 *
574 * @param scope Scope to disconnect
575 * @exception ServiceDisconnctionFailedException Error disconnecting
576 * DescriptorsStore
577 * @exception ServiceAccessException Unspecified error during
578 * service access
579 */
580 public void unregisterStore(Scope scope)
581 throws ServiceDisconnectionFailedException, ServiceAccessException {
582 if (stores.containsKey(scope)) {
583 Store store = (Store) stores.get(scope);
584 if (store.isConnected()) {
585 store.disconnect();
586 connectedServices.removeElement(store);
587 }
588 stores.remove(scope);
589 store = null;
590 }
591 }
592
593
594 /**
595 * Get the Data Source associated with the given scope, if any.
596 * In contrary to the retrieveStore method, this methos does not
597 * perform a connection.
598 *
599 * @param scope Scope to match
600 */
601 public Store getStore(Scope scope) {
602 Store store = null;
603 if (stores.containsKey(scope)) {
604 store = (Store) stores.get(scope);
605 }
606 return store;
607 }
608
609
610 /**
611 * Get the Data Source associated with the given scope, if any and
612 * connect to the store.
613 *
614 * @param scope Scope to match
615 * @param token the Credeantials token containing e.g. the credential
616 * @exception ServiceConnectionFailedException Connection to Store failed
617 * @exception ServiceAccessException Unspecified service access exception
618 */
619 public Store retrieveStore(Scope scope, CredentialsToken token)
620 throws ServiceConnectionFailedException, ServiceAccessException {
621 Store store = getStore(scope);
622 if (store != null) {
623 connectService(store, token);
624 }
625 return store;
626 }
627
628
629 /**
630 * Builds a new uri object to access this namespace. This call will
631 * return a Uri which doesn't have its token field set. The store should
632 * accept such Uri as valid, and bypass any check that is made based on the
633 * state.
634 *
635 * @param uri Requested Uri
636 * @return Uri
637 */
638 public Uri getUri(String uri) {
639 return getUri(null, uri);
640 }
641
642
643 /**
644 * Builds a new uri object to access this namespace.
645 *
646 * @param token SlideToken
647 * @param uri Requested Uri
648 * @return Uri
649 */
650 public Uri getUri(SlideToken token, String uri) {
651 return getUri(token, uri, token==null
652 ?false
653 :token.isForceStoreEnlistment());
654 }
655
656
657 /**
658 * Builds a new uri object to access this namespace.
659 *
660 * @param token SlideToken
661 * @param uri Requested Uri
662 * @param forcedEnlistment may differ from the value set in token
663 * @return Uri
664 */
665 public Uri getUri(SlideToken token, String uri, boolean forcedEnlistment) {
666
667 Uri result = null;
668 Object temp = null;
669 temp = uriCache.get(uri);
670 if (temp == null) {
671 result = new Uri(token, this, uri);
672 uriCache.put(uri, result);
673 if (uriCache.size() > 10000) {
674 clearUriCache();
675 }
676 } else {
677 result = (Uri) temp;
678 result = result.cloneObject();
679 result.setToken(token);
680 result.reconnectServices();
681 }
682
683 // if a different forceEnlistment value want to be used
684 // wrap the used token to reflect the different value
685 if (token != null && token.isForceStoreEnlistment() != forcedEnlistment) {
686 SlideToken wToken = new SlideTokenWrapper(token);
687 wToken.setForceStoreEnlistment(forcedEnlistment);
688 result.setToken(wToken);
689 }
690
691 return result;
692
693 }
694
695
696 /**
697 * Clear uri cache.
698 */
699 void clearUriCache() {
700 uriCache.clear();
701 }
702
703
704 /**
705 * Get content interceptors associated with this namespace.
706 */
707 public ContentInterceptor[] getContentInterceptors() {
708 return config.getContentInterceptors();
709 }
710
711
712 // -------------------------------------------------------- Package Methods
713
714
715 /**
716 * Parses the contents of the specified definition object, and uses that
717 * info to initialize the namespace.
718 *
719 * @param definition Definiton of the scopes and stores of
720 * the namespace
721 * @exception SlideException Something went wrong during registry or
722 * services initialization
723 * @exception ConfigurationException Error parsing configuration file
724 */
725 void loadDefinition(Configuration definition)
726 throws SlideException, ConfigurationException {
727
728 getLogger().log("Loading namespace definition",LOG_CHANNEL,Logger.INFO);
729
730 // Loading stores
731
732 Hashtable storesClass = new Hashtable();
733 Hashtable storesParameters = new Hashtable();
734 Hashtable childStores = new Hashtable();
735
736 Enumeration storeDefinitions =
737 definition.getConfigurations("store");
738
739 while (storeDefinitions.hasMoreElements()) {
740 loadStoreDefinition
741 ((Configuration) storeDefinitions.nextElement(),
742 storesClass, storesParameters, childStores);
743 }
744
745 Enumeration scopeDefinitions =
746 definition.getConfigurations("scope");
747
748 while (scopeDefinitions.hasMoreElements()) {
749 loadScopeDefinition
750 ((Configuration) scopeDefinitions.nextElement(),
751 storesClass, storesParameters, childStores);
752 }
753
754 // Initialize all loaded services.
755 initializeServices();
756
757 }
758
759
760 /**
761 * Parses the contents of the specified reader, and uses that info to
762 * initialize the specified Slide namespace.
763 *
764 * @param namespaceBaseDataDefinition Namespace base data
765 * @exception SlideException Something went wrong during registry or
766 * services initialization
767 */
768 void loadBaseData(Configuration namespaceBaseDataDefinition)
769 throws SlideException, ConfigurationException {
770
771 getLogger().log("Loading namespace " + getName() + " base data",LOG_CHANNEL,Logger.INFO);
772
773 // Load Namespace Base Data
774 try {
775 // start transaction for temp object creation
776 getTransactionManager().begin();
777
778 SlideToken slideToken = new SlideTokenImpl(new CredentialsToken(""));
779 slideToken.setForceStoreEnlistment(true);
780
781 // First, we create the root node
782 Uri rootUri = getUri(slideToken, "/");
783 SubjectNode rootNode = new SubjectNode("/");
784 try {
785 rootUri.getStore().createObject(rootUri, rootNode);
786 } catch (ObjectAlreadyExistsException e) {
787 // abort the failed transaction
788 getTransactionManager().rollback();
789 // start a new one to continue processing
790 getTransactionManager().begin();
791 }
792
793 // end transaction for temp object creation
794 getTransactionManager().commit();
795
796 getLogger().log("Init namespace " + getName() + " configuration",LOG_CHANNEL,Logger.INFO);
797
798 // Create the dummy configuration
799 config.initializeAsDummyConfig(this);
800
801 // Create the Access token
802 NamespaceAccessToken token = new NamespaceAccessTokenImpl(this);
803
804 // start the transaction, NOTE some operations are outside this TA
805 token.begin();
806
807 getLogger().log("Import data into namespace " + getName(),LOG_CHANNEL,Logger.INFO);
808 token.importData(slideToken, namespaceBaseDataDefinition);
809
810 // end the transaction, NOTE some operations are outside this TA
811 token.commit();
812
813 // start transaction for temp object removal
814 getTransactionManager().begin();
815
816 getLogger().log("Finish init namespace " + getName() + " configuration",LOG_CHANNEL,Logger.INFO);
817
818 // And remove the all permission from the root node
819 rootNode =
820 (SubjectNode) rootUri.getStore().retrieveObject(rootUri);
821 rootUri.getStore().storeObject(rootUri, rootNode);
822
823 // end transaction for temp object removal
824 getTransactionManager().commit();
825
826 } catch (SlideException e) {
827 // If that occurs, then most likely the base config was
828 // already done before
829 e.printStackTrace();
830 getLogger().log("Namespace base configuration was already done before",LOG_CHANNEL,Logger.INFO);
831 try {
832 if (getTransactionManager().getStatus()==Status.STATUS_ACTIVE)
833 getTransactionManager().rollback();
834 }
835 catch (SystemException ex) {
836 getLogger().log("Could not rollback namespace base configuration: " + ex.toString(),LOG_CHANNEL,Logger.WARNING);
837 }
838 } catch (Exception e) {
839 getLogger().log("Unable to read Namespace base configuration file : ",LOG_CHANNEL,Logger.ERROR);
840 getLogger().log(e,LOG_CHANNEL, Logger.ERROR);
841 // Unable to load the base configuration XML file.
842 // Log the event, and hope it was already done before.
843 try {
844 if (getTransactionManager().getStatus()==Status.STATUS_ACTIVE)
845 getTransactionManager().rollback();
846 }
847 catch (SystemException ex) {
848 getLogger().log("Could not rollback namespace base configuration after load error: " + ex.toString(),LOG_CHANNEL,Logger.WARNING);
849 }
850 }
851 }
852
853
854 /**
855 * Parses the contents of the specified reader, and uses that info to
856 * initialize the specified Slide namespace.
857 *
858 * @param namespaceConfigurationDefinition The configuration to load.
859 * @exception SlideException Something went wrong during registry or
860 * services initialization
861 */
862 void loadConfiguration(Configuration namespaceConfigurationDefinition)
863 throws SlideException {
864
865 getLogger().log("Loading namespace " + getName() + " configuration",LOG_CHANNEL,Logger.INFO);
866
867 // Load Namespace Config
868 config = new NamespaceConfig();
869 config.initializeNamespaceConfig(this,
870 namespaceConfigurationDefinition);
871
872 }
873
874
875 /**
876 * Parses the contents of the specified reader, and uses that info to
877 * initialize the specified Slide namespace.
878 *
879 * @param namespaceConfigurationDefinition Namespace configuration
880 * @exception SlideException Something went wrong during registry or
881 * services initialization
882 */
883 void loadParameters(Configuration namespaceConfigurationDefinition)
884 throws SlideException {
885
886 getLogger().log("Loading namespace " + getName() + " parameters",LOG_CHANNEL,Logger.INFO);
887
888 // Load Namespace Config
889 config = new NamespaceConfig();
890 config.initializeNamespaceParameters(this,
891 namespaceConfigurationDefinition);
892
893 }
894
895
896 void loadExtractors(Configuration namespaceExtractorsDefinition)
897 throws SlideException {
898
899 getLogger().log("Loading namespace " + getName() + " extractors",LOG_CHANNEL,Logger.INFO);
900
901 Enumeration extractorConfigs = namespaceExtractorsDefinition.getConfigurations("extractor");
902 while (extractorConfigs.hasMoreElements()) {
903 Configuration extractorConfig = (Configuration) extractorConfigs.nextElement();
904 String classname = extractorConfig.getAttribute("classname");
905 String uri = extractorConfig.getAttribute("uri", null);
906 String contentType = extractorConfig.getAttribute("content-type", null);
907 String namespace = getName();
908 try {
909 Class extractorClass = Class.forName(classname);
910 Extractor extractor = null;
911 Constructor extractorConstructor = extractorClass.getConstructor(new Class[] { String.class, String.class, String.class } );
912 extractor = (Extractor)extractorConstructor.newInstance(new String[] { uri, contentType, namespace });
913 if ( extractor instanceof Configurable ) {
914 ((Configurable)extractor).configure(extractorConfig.getConfiguration("configuration"));
915 }
916 ExtractorManager.getInstance().addExtractor(extractor);
917 } catch (ClassCastException e) {
918 throw new ConfigurationException("Extractor '"+classname+"' is not of type Extractor", namespaceExtractorsDefinition);
919 } catch (ConfigurationException e) {
920 throw e;
921 } catch (Exception e) {
922 throw new ConfigurationException("Extractor '"+classname+"' could not be loaded", namespaceExtractorsDefinition);
923 }
924 }
925 }
926
927 // -------------------------------------------------------- Private Methods
928
929
930 /**
931 * Parse the store definition.
932 *
933 * @param storeDefinition store definition
934 * @param storesClass Class names of the stores
935 * @param storesParameters Parameters of the stores
936 * @param childStores Child stores
937 * @exception ConfigurationException Error parsing configuration file
938 * @exception SlideException Error loading the specified class
939 */
940 private void loadStoreDefinition
941 (Configuration storeDefinition,
942 Hashtable storesClass,
943 Hashtable storesParameters,
944 Hashtable childStores)
945 throws ConfigurationException, SlideException {
946
947 String storeName = storeDefinition.getAttribute("name");
948 String storeClassname = defaultStoreClassname;
949
950 try {
951 storeClassname = storeDefinition.getAttribute("classname");
952 } catch (ConfigurationException e) {
953 }
954
955 Enumeration storeParametersDefinitions =
956 storeDefinition.getConfigurations("parameter");
957
958 // Load descriptors store class
959 Class storeClass = null;
960 try {
961 storeClass = Class.forName(storeClassname);
962 } catch (Exception e) {
963 getLogger().log(e,LOG_CHANNEL, Logger.ERROR);
964 throw new SlideException(e.getMessage());
965 }
966 storesClass.put(storeName, storeClass);
967
968 // Load descriptor store parameters
969 Hashtable storeParameters = new Hashtable();
970 while (storeParametersDefinitions.hasMoreElements()) {
971 Configuration parameterDefinition = (Configuration)
972 storeParametersDefinitions.nextElement();
973 String parameterName = parameterDefinition.getAttribute("name");
974 String parameterValue = parameterDefinition.getValue();
975 storeParameters.put(parameterName, parameterValue);
976 }
977
978 storesParameters.put(storeName, storeParameters);
979
980 // Now reading the "child" stores
981
982 Hashtable currentStoreChildStores = new Hashtable();
983
984 // Loading node store (if any)
985 getChildStore (storeDefinition, NODE_STORE, currentStoreChildStores, storeParameters);
986
987 // Loading security store (if any)
988 getChildStore (storeDefinition, SECURITY_STORE, currentStoreChildStores, storeParameters);
989
990 // Loading lock store (if any)
991 getChildStore (storeDefinition, LOCK_STORE, currentStoreChildStores, storeParameters);
992
993 // Loading revision descriptors store (if any)
994 getChildStore (storeDefinition, REVISION_DESCRIPTORS_STORE, currentStoreChildStores, storeParameters);
995
996 // Loading revision descriptor store (if any)
997 getChildStore (storeDefinition, REVISION_DESCRIPTOR_STORE, currentStoreChildStores, storeParameters);
998
999 // Loading content store (if any)
1000 getChildStore (storeDefinition, CONTENT_STORE, currentStoreChildStores, storeParameters);
1001
1002 // Loading descriptorindexstore store (if any)
1003 getChildStore (storeDefinition, PROPERTIES_INDEX_STORE, currentStoreChildStores, storeParameters);
1004
1005 // Loading contentindexstore store (if any)
1006 getChildStore (storeDefinition, CONTENT_INDEX_STORE, currentStoreChildStores, storeParameters);
1007
1008 // load default indexer, if no indexer defined
1009
1010 // Loading sequence store (if any)
1011 getChildStore (storeDefinition, SEQUENCE_STORE, currentStoreChildStores, storeParameters);
1012
1013 childStores.put(storeName, currentStoreChildStores);
1014
1015 }
1016
1017 private void getChildStore(Configuration storeDefinition, String key, Hashtable currentStoreChildStores, Hashtable storeParameters) throws SlideException
1018 {
1019 Configuration localStoreDefinition;
1020 try {
1021 localStoreDefinition = storeDefinition.getConfiguration(key);
1022 } catch (ConfigurationException e) {
1023 return;
1024 // silently ignore as this only indicates there is no such store defined
1025 }
1026 try {
1027 try {
1028 Configuration referenceDefinition =
1029 localStoreDefinition.getConfiguration(REFERENCE);
1030 currentStoreChildStores.put
1031 (key, referenceDefinition.getAttribute("store"));
1032 getLogger().log(key + " references " + referenceDefinition.getAttribute("store"),LOG_CHANNEL,Logger.INFO);
1033 } catch (ConfigurationException ex) {
1034 getLogger().log(key + ": " + localStoreDefinition.getAttribute("classname"),LOG_CHANNEL,Logger.INFO);
1035 Service store =
1036 loadChildStore(localStoreDefinition,
1037 storeParameters);
1038 if (store != null) {
1039 currentStoreChildStores.put(key, store);
1040 }
1041 }
1042 } catch (ConfigurationException e) {
1043 getLogger().log("Exception while loading "+key+"!", e, LOG_CHANNEL, Logger.WARNING);
1044 }
1045 }
1046
1047
1048 /**
1049 * Load a child descriptors store.
1050 *
1051 * @param childStoreDefinition XML definition of the child store
1052 * @param fatherParameters XML parameters defined for the father
1053 * @return Service Instance of the child store
1054 * @exception ConfigurationException Error parsing configuration file
1055 * @exception SlideException Error loading the specified class
1056 */
1057 private Service loadChildStore(Configuration childStoreDefinition,
1058 Hashtable fatherParameters)
1059 throws ConfigurationException, SlideException {
1060
1061 // Load classname
1062 String childStoreClassname =
1063 childStoreDefinition.getAttribute("classname");
1064
1065 // Load descriptors store class
1066 Service childStore = null;
1067 try {
1068 Class childStoreClass =
1069 Class.forName(childStoreClassname);
1070 childStore = (Service) childStoreClass.newInstance();
1071 } catch (Exception e) {
1072 getLogger().log(e,LOG_CHANNEL, Logger.ERROR);
1073 return null;
1074 }
1075
1076 // Retrieve parent parameters
1077 Hashtable childStoreParameters = new Hashtable();
1078 Enumeration fatherParametersKeys = fatherParameters.keys();
1079 while (fatherParametersKeys.hasMoreElements()) {
1080 Object key = fatherParametersKeys.nextElement();
1081 Object value = fatherParameters.get(key);
1082 childStoreParameters.put(key, value);
1083 }
1084
1085 // Load parameters
1086 Enumeration childStoreParametersDefinitions =
1087 childStoreDefinition.getConfigurations("parameter");
1088 while (childStoreParametersDefinitions.hasMoreElements()) {
1089 Configuration parameterDefinition = (Configuration)
1090 childStoreParametersDefinitions.nextElement();
1091 String parameterName = parameterDefinition.getAttribute("name");
1092 String parameterValue = parameterDefinition.getValue();
1093 childStoreParameters.put(parameterName, parameterValue);
1094 }
1095 childStore.setParameters(childStoreParameters);
1096
1097 return childStore;
1098
1099 }
1100
1101
1102 /**
1103 * Parse the content store definition.
1104 *
1105 * @param storesClass Class names of the descriptors stores
1106 * @param storesParameters Parameters of the descriptors stores
1107 * @param childStores Child stores instances
1108 * @exception ConfigurationException Error parsing configuration file
1109 * @exception UnknownServiceDeclarationException Reference to
1110 * unknown service
1111 * @exception ServiceParameterErrorException Service parameter error
1112 * @exception ServiceParameterMissingException Service parameter missing
1113 * @exception ServiceRegistrationFailedException Error registering service
1114 */
1115 private void loadScopeDefinition(Configuration scopeDefinition,
1116 Hashtable storesClass,
1117 Hashtable storesParameters,
1118 Hashtable childStores)
1119 throws ConfigurationException, UnknownServiceDeclarationException,
1120 ServiceParameterErrorException, ServiceParameterMissingException,
1121 ServiceRegistrationFailedException {
1122
1123 String match = scopeDefinition.getAttribute("match");
1124
1125 // First, we get the correct class and parameters from the Hashtables.
1126 String storeName = scopeDefinition.getAttribute("store");
1127
1128 if (storeName != null) {
1129 if ((!storesClass.containsKey(storeName)) ||
1130 (!storesParameters.containsKey(storeName))) {
1131 throw new UnknownServiceDeclarationException(storeName);
1132 }
1133 registerStore(storeName,
1134 (Class) storesClass.get(storeName),
1135 (Hashtable) storesParameters.get(storeName),
1136 new Scope(match),
1137 (Hashtable) childStores.get(storeName));
1138 getLogger().log("Registering Store "
1139 + storeName
1140 + " ("
1141 + storesClass.get(storeName)
1142 + ") with parameters "
1143 + storesParameters.get(storeName)
1144 + " on scope " + match,LOG_CHANNEL,Logger.INFO);
1145 }
1146
1147 }
1148
1149 /**
1150 *
1151 */
1152 private void notifyStoreCreated( String namespaceName, String scope, String storeName ) {
1153
1154 if( createStoreListenerClass != null ) {
1155 try {
1156 Method nsc = createStoreListenerClass.getMethod(
1157 "notifyStoreCreated", new Class[]{String.class, String.class, String.class} );
1158 nsc.invoke( null, new Object[]{namespaceName, scope, storeName} ); // obj=null since method is static
1159 }
1160 catch( Exception x ) {
1161 Domain.warn( "Notification of store creation "+
1162 "(namespace="+namespaceName+", scope="+scope+", store="+storeName+") failed: "+x.getMessage() );
1163 }
1164 }
1165 }
1166
1167
1168 // --------------------------------------------------------- Object Methods
1169
1170
1171 /**
1172 * Get a String representation of this namespace.
1173 */
1174 public String toString() {
1175 return getName();
1176 }
1177
1178
1179}
1180