View Javadoc

1   package org.modeshape.jcr;
2   
3   import java.io.FileInputStream;
4   import java.io.IOException;
5   import java.io.InputStream;
6   import java.util.Hashtable;
7   import java.util.concurrent.TimeUnit;
8   import javax.jcr.Repository;
9   import javax.jcr.RepositoryException;
10  import javax.naming.Context;
11  import javax.naming.Name;
12  import javax.naming.NamingException;
13  import javax.naming.RefAddr;
14  import javax.naming.Reference;
15  import javax.naming.event.EventContext;
16  import javax.naming.event.NamespaceChangeListener;
17  import javax.naming.event.NamingEvent;
18  import javax.naming.event.NamingExceptionEvent;
19  import javax.naming.spi.ObjectFactory;
20  import org.modeshape.common.collection.Problem;
21  import org.modeshape.common.collection.Problems;
22  import org.modeshape.common.util.Logger;
23  import org.xml.sax.SAXException;
24  
25  /**
26   * The {@code JndiRepositoryFactory} class provides a means of initializing and accessing {@link Repository repositories} in a
27   * JNDI tree. <h2>Example JNDI Configurations</h2> <h3>Tomcat 5.5</h3>
28   * <p>
29   * The following configuration can be added to added to server.xml in Tomcat 5.5 to initialize a {@code JcrRepository} and add it
30   * to the JNDI tree.
31   * 
32   * <pre>
33   *   &lt;GlobalNamingResources&gt;
34   *         &lt;!-- Other configuration omitted --&gt;
35   *      &lt;Resource name=&quot;jcr/local&quot; auth=&quot;Container&quot;
36   *           type=&quot;javax.jcr.Repository&quot;
37   *           factory=&quot;org.modeshape.jcr.JndiRepositoryFactory&quot;
38   *           configFile=&quot;/tck/default/configRepository.xml&quot;
39   *           repositoryName=&quot;Test Repository Source&quot;
40   *           /&gt;
41   *   &lt;/GlobalNamingResources&gt;
42   * </pre>
43   * 
44   * This will create a repository loaded from the or file &quot;/tck/default/configRepository.xml&quot; and return the JCR
45   * repository named &quot;Test Repository Source&quot;. The name of the repository is important as a single configuration file may
46   * contain configuration information for many JCR repositories.
47   * </p>
48   */
49  public class JndiRepositoryFactory implements ObjectFactory {
50  
51      private static final String CONFIG_FILE = "configFile";
52      private static final String REPOSITORY_NAME = "repositoryName";
53  
54      private static JcrEngine engine;
55      protected static final Logger log = Logger.getLogger(JndiRepositoryFactory.class);
56  
57      /**
58       * {@link JcrConfiguration#loadFrom(java.io.InputStream) Initializes} and {@link JcrEngine#start() starts} the {@code
59       * JcrEngine} managed by this factory.
60       * 
61       * @param configFileName the name of the file containing the configuration information for the {@code JcrEngine}; may not be
62       *        null. This method will first attempt to load this file as a resource from the classpath. If no resource with the
63       *        given name exists, the name will be treated as a file name and loaded from the file system.
64       * @throws IOException if there is an error or problem reading the configuration resource at the supplied path
65       * @throws SAXException if the contents of the configuration resource are not valid XML
66       * @throws RepositoryException if the {@link JcrEngine#start() JcrEngine could not be started}
67       * @see JcrConfiguration#loadFrom(java.io.InputStream)
68       * @see Class#getResourceAsStream(String)
69       */
70      private static synchronized void initializeEngine( String configFileName )
71          throws IOException, SAXException, RepositoryException {
72          if (engine != null) return;
73  
74          log.info(JcrI18n.engineStarting);
75          long start = System.currentTimeMillis();
76  
77          JcrConfiguration config = new JcrConfiguration();
78          InputStream configStream = JndiRepositoryFactory.class.getResourceAsStream(configFileName);
79  
80          if (configStream == null) {
81              try {
82                  configStream = new FileInputStream(configFileName);
83              } catch (IOException ioe) {
84                  throw new RepositoryException(ioe);
85              }
86          }
87  
88          engine = config.loadFrom(configStream).build();
89          engine.start();
90  
91          Problems problems = engine.getProblems();
92          for (Problem problem : problems) {
93              switch (problem.getStatus()) {
94                  case ERROR:
95                      log.error(problem.getThrowable(), problem.getMessage(), problem.getParameters());
96                      break;
97                  case WARNING:
98                      log.warn(problem.getThrowable(), problem.getMessage(), problem.getParameters());
99                      break;
100                 case INFO:
101                     log.info(problem.getThrowable(), problem.getMessage(), problem.getParameters());
102                     break;
103             }
104         }
105 
106         if (problems.hasErrors()) {
107             throw new RepositoryException(JcrI18n.couldNotStartEngine.text());
108         }
109         log.info(JcrI18n.engineStarted, (System.currentTimeMillis() - start));
110     }
111 
112     /**
113      * Creates an {@code JcrRepository} using the reference information specified.
114      * <p>
115      * This method first attempts to convert the {@code obj} parameter into a {@link Reference reference to JNDI configuration
116      * information}. If that is successful, a {@link JcrEngine} will be created (if not previously created by a call to this
117      * method) and it will be configured from the resource or file at the location specified by the {@code configFile} key in the
118      * reference. After the configuration is successful, the {@link JcrEngine#getRepository(String) JcrEngine will be queried} for
119      * the repository with the name specified by the value of the @{code repositoryName} key in the reference.
120      * </p>
121      * 
122      * @param obj the reference to the JNDI configuration information; must be a non-null instance of {@link Reference}
123      * @param name ignored
124      * @param nameCtx ignored
125      * @param environment ignored
126      * @return the repository; never null
127      * @throws IOException if there is an error or problem reading the configuration resource at the supplied path
128      * @throws SAXException if the contents of the configuration resource are not valid XML
129      * @throws NamingException if there is an error registering the namespace listener
130      * @throws RepositoryException if the {@link JcrEngine#start() JcrEngine could not be started}, the named repository does not
131      *         exist in the given configuration resource, or the named repository could not be created
132      */
133     public JcrRepository getObjectInstance( Object obj,
134                                             Name name,
135                                             Context nameCtx,
136                                             Hashtable<?, ?> environment )
137         throws IOException, SAXException, RepositoryException, NamingException {
138         if (!(obj instanceof Reference)) return null;
139 
140         Reference ref = (Reference)obj;
141 
142         if (engine == null) {
143             RefAddr configFile = ref.get(CONFIG_FILE);
144             assert configFile != null;
145 
146             initializeEngine(configFile.getContent().toString());
147 
148             if (nameCtx instanceof EventContext) {
149                 EventContext evtCtx = (EventContext)nameCtx;
150 
151                 NamespaceChangeListener listener = new NamespaceChangeListener() {
152 
153                     public void namingExceptionThrown( NamingExceptionEvent evt ) {
154                         evt.getException().printStackTrace();
155                     }
156 
157                     public void objectAdded( NamingEvent evt ) {
158                     }
159 
160                     public void objectRemoved( NamingEvent evt ) {
161                         Object oldObject = evt.getOldBinding().getObject();
162                         if (!(oldObject instanceof JcrEngine)) return;
163 
164                         JcrEngine engine = (JcrEngine)oldObject;
165 
166                         log.info(JcrI18n.engineStopping);
167                         long start = System.currentTimeMillis();
168                         engine.shutdown();
169                         try {
170                             engine.awaitTermination(30, TimeUnit.SECONDS);
171                             log.info(JcrI18n.engineStopped, (System.currentTimeMillis() - start));
172                         } catch (InterruptedException ie) {
173                             // Thread.interrupted();
174                         }
175                     }
176 
177                     public void objectRenamed( NamingEvent evt ) {
178                     }
179 
180                 };
181 
182                 evtCtx.addNamingListener(name, EventContext.OBJECT_SCOPE, listener);
183             }
184         }
185 
186         assert engine != null;
187 
188         RefAddr repositoryName = ref.get(REPOSITORY_NAME);
189         assert repositoryName != null;
190 
191         return engine.getRepository(repositoryName.getContent().toString());
192     }
193 
194 }