View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.jcr;
25  
26  import java.io.File;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.net.URL;
30  import java.util.Collections;
31  import java.util.EnumMap;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.Map;
35  import java.util.Set;
36  import net.jcip.annotations.NotThreadSafe;
37  import org.modeshape.cnd.CndImporter;
38  import org.modeshape.common.collection.Problem;
39  import org.modeshape.common.collection.Problem.Status;
40  import org.modeshape.common.component.ClassLoaderFactory;
41  import org.modeshape.common.util.CheckArg;
42  import org.modeshape.graph.ExecutionContext;
43  import org.modeshape.graph.Graph;
44  import org.modeshape.graph.Location;
45  import org.modeshape.graph.Node;
46  import org.modeshape.graph.Subgraph;
47  import org.modeshape.graph.connector.RepositorySource;
48  import org.modeshape.graph.io.Destination;
49  import org.modeshape.graph.io.GraphBatchDestination;
50  import org.modeshape.graph.property.Name;
51  import org.modeshape.graph.property.Path;
52  import org.modeshape.graph.property.PathNotFoundException;
53  import org.modeshape.graph.property.Property;
54  import org.modeshape.graph.property.NamespaceRegistry.Namespace;
55  import org.modeshape.jcr.JcrRepository.Option;
56  import org.modeshape.repository.ModeShapeConfiguration;
57  import org.modeshape.repository.ModeShapeConfigurationException;
58  import org.xml.sax.SAXException;
59  
60  /**
61   * A configuration builder for a {@link JcrEngine}. This class is an internal domain-specific language (DSL), and is designed to
62   * be used in a traditional way or in a method-chained manner:
63   * 
64   * <pre>
65   * configuration.repositorySource(&quot;Source1&quot;).setClass(InMemoryRepositorySource.class).setDescription(&quot;description&quot;);
66   * configuration.mimeTypeDetector(&quot;detector&quot;).setClass(ExtensionBasedMimeTypeDetector.class).setDescription(&quot;default detector&quot;);
67   * configuration.sequencer(&quot;MicrosoftDocs&quot;)
68   *              .setClass(&quot;org.modeshape.sequencer.msoffice.MSOfficeMetadataSequencer&quot;)
69   *              .setDescription(&quot;Our primary sequencer for all .doc files&quot;)
70   *              .sequencingFrom(&quot;/public//(*.(doc|xml|ppt)[*]/jcr:content[@jcr:data]&quot;)
71   *              .andOutputtingTo(&quot;/documents/$1&quot;);
72   * configuration.repository(&quot;MyRepository&quot;).setSource(&quot;Source1&quot;);
73   * configuration.save();
74   * </pre>
75   */
76  @NotThreadSafe
77  public class JcrConfiguration extends ModeShapeConfiguration {
78  
79      /**
80       * Interface used to define a JCR Repository that's accessible from the JcrEngine.
81       * 
82       * @param <ReturnType>
83       */
84      public interface RepositoryDefinition<ReturnType>
85          extends Returnable<ReturnType>, Removable<ReturnType>, SetDescription<RepositoryDefinition<ReturnType>> {
86  
87          /**
88           * Specify the name of the repository source that is to be used by this JCR repository.
89           * 
90           * @param sourceName the name of the repository source that should be exposed by this JCR repository
91           * @return the interface used to set the value for the property; never null
92           * @throws IllegalArgumentException if the source name parameter is null
93           */
94          RepositoryDefinition<ReturnType> setSource( String sourceName );
95  
96          /**
97           * Get the name of the repository source that is to be used by this JCR repository.
98           * 
99           * @return the source name, or null if it has not yet been set
100          */
101         String getSource();
102 
103         /**
104          * Specify the repository option that is to be set.
105          * 
106          * @param option the option to be set
107          * @param value the new value for the option
108          * @return the interface used to set the value for the property; never null
109          * @throws IllegalArgumentException if either parameter is null
110          */
111         RepositoryDefinition<ReturnType> setOption( JcrRepository.Option option,
112                                                     String value );
113 
114         /**
115          * Specify the repository option that is to be set.
116          * 
117          * @param option the option to be set
118          * @param value the new value for the option
119          * @return the interface used to set the value for the property; never null
120          * @throws IllegalArgumentException if either parameter is null
121          */
122         RepositoryDefinition<ReturnType> setOption( JcrRepository.Option option,
123                                                     boolean value );
124 
125         /**
126          * Specify the repository option that is to be set.
127          * 
128          * @param option the option to be set
129          * @param value the new value for the option
130          * @return the interface used to set the value for the property; never null
131          * @throws IllegalArgumentException if either parameter is null
132          */
133         RepositoryDefinition<ReturnType> setOption( JcrRepository.Option option,
134                                                     int value );
135 
136         /**
137          * Specify the repository option that is to be set.
138          * 
139          * @param option the option to be set
140          * @param value the new value for the option
141          * @return the interface used to set the value for the property; never null
142          * @throws IllegalArgumentException if either parameter is null
143          */
144         RepositoryDefinition<ReturnType> setOption( JcrRepository.Option option,
145                                                     long value );
146 
147         /**
148          * Specify the repository option that is to be set.
149          * 
150          * @param option the option to be set
151          * @param value the new value for the option
152          * @return the interface used to set the value for the property; never null
153          * @throws IllegalArgumentException if either parameter is null
154          */
155         RepositoryDefinition<ReturnType> setOption( JcrRepository.Option option,
156                                                     float value );
157 
158         /**
159          * Specify the repository option that is to be set.
160          * 
161          * @param option the option to be set
162          * @param value the new value for the option
163          * @return the interface used to set the value for the property; never null
164          * @throws IllegalArgumentException if either parameter is null
165          */
166         RepositoryDefinition<ReturnType> setOption( JcrRepository.Option option,
167                                                     double value );
168 
169         /**
170          * Get the value for the repository option.
171          * 
172          * @param option the option
173          * @return the current option value, which may be null if the option has not been set (and its default would be used)
174          * @throws IllegalArgumentException if the option parameter is null
175          */
176         String getOption( JcrRepository.Option option );
177 
178         /**
179          * Specify that the CND file located at the supplied path should be loaded into the repository.
180          * <p>
181          * This method may be called multiple times, once per CND file.
182          * </p>
183          * 
184          * @param pathToCndFile the path to the CND file
185          * @return this object for chained method invocation
186          * @throws IllegalArgumentException if the string is null or empty
187          * @throws ModeShapeConfigurationException if there is an error reading the CND file
188          */
189         RepositoryDefinition<ReturnType> addNodeTypes( String pathToCndFile );
190 
191         /**
192          * Specify that the CND file is to be loaded into the repository.
193          * 
194          * @param cndFile the CND file
195          * @return this object for chained method invocation
196          * @throws IllegalArgumentException if the file is null
197          * @throws ModeShapeConfigurationException if there is an error reading the file
198          */
199         RepositoryDefinition<ReturnType> addNodeTypes( File cndFile );
200 
201         /**
202          * Specify that the CND file is to be loaded into the repository.
203          * <p>
204          * This method may be called multiple times, once per CND file.
205          * </p>
206          * 
207          * @param urlOfCndFile the URL of the CND file
208          * @return this object for chained method invocation
209          * @throws IllegalArgumentException if the URL is null
210          * @throws ModeShapeConfigurationException if there is an error reading the content at the URL
211          */
212         RepositoryDefinition<ReturnType> addNodeTypes( URL urlOfCndFile );
213 
214         /**
215          * Specify that the CND file is to be loaded into the repository.
216          * <p>
217          * This method may be called multiple times, once per CND file.
218          * </p>
219          * 
220          * @param cndContent the stream containing the CND content
221          * @return this object for chained method invocation
222          * @throws IllegalArgumentException if the URL is null
223          * @throws ModeShapeConfigurationException if there is an error reading the stream at the URL
224          */
225         RepositoryDefinition<ReturnType> addNodeTypes( InputStream cndContent );
226 
227         /**
228          * Specify the namespace binding that should be made available in this repository.
229          * 
230          * @param prefix the namespace prefix; may not be null or empty, and must be a valid prefix
231          * @param uri the uri for the namespace; may not be null or empty
232          * @return the interface used to set the value for the property; never null
233          */
234         RepositoryDefinition<ReturnType> registerNamespace( String prefix,
235                                                             String uri );
236     }
237 
238     private final Map<String, RepositoryDefinition<? extends JcrConfiguration>> repositoryDefinitions = new HashMap<String, RepositoryDefinition<? extends JcrConfiguration>>();
239 
240     /**
241      * Create a new configuration, using a default-constructed {@link ExecutionContext}.
242      */
243     public JcrConfiguration() {
244         super();
245     }
246 
247     /**
248      * Create a new configuration using the supplied {@link ExecutionContext}.
249      * 
250      * @param context the execution context
251      * @throws IllegalArgumentException if the path is null or empty
252      */
253     public JcrConfiguration( ExecutionContext context ) {
254         super(context);
255     }
256 
257     /**
258      * {@inheritDoc}
259      * 
260      * @throws IOException
261      * @throws SAXException
262      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(java.lang.String)
263      */
264     @Override
265     public JcrConfiguration loadFrom( String pathToFile ) throws IOException, SAXException {
266         super.loadFrom(pathToFile);
267         return this;
268     }
269 
270     /**
271      * {@inheritDoc}
272      * 
273      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(java.lang.String, java.lang.String)
274      */
275     @Override
276     public JcrConfiguration loadFrom( String pathToConfigurationFile,
277                                       String path ) throws IOException, SAXException {
278         super.loadFrom(pathToConfigurationFile, path);
279         return this;
280     }
281 
282     /**
283      * {@inheritDoc}
284      * 
285      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(java.io.File)
286      */
287     @Override
288     public JcrConfiguration loadFrom( File configurationFile ) throws IOException, SAXException {
289         super.loadFrom(configurationFile);
290         return this;
291     }
292 
293     /**
294      * {@inheritDoc}
295      * 
296      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(java.io.File, java.lang.String)
297      */
298     @Override
299     public JcrConfiguration loadFrom( File configurationFile,
300                                       String path ) throws IOException, SAXException {
301         super.loadFrom(configurationFile, path);
302         return this;
303     }
304 
305     /**
306      * {@inheritDoc}
307      * 
308      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(java.net.URL)
309      */
310     @Override
311     public JcrConfiguration loadFrom( URL urlToConfigurationFile ) throws IOException, SAXException {
312         super.loadFrom(urlToConfigurationFile);
313         return this;
314     }
315 
316     /**
317      * {@inheritDoc}
318      * 
319      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(java.net.URL, java.lang.String)
320      */
321     @Override
322     public JcrConfiguration loadFrom( URL urlToConfigurationFile,
323                                       String path ) throws IOException, SAXException {
324         super.loadFrom(urlToConfigurationFile, path);
325         return this;
326     }
327 
328     /**
329      * {@inheritDoc}
330      * 
331      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(java.io.InputStream)
332      */
333     @Override
334     public JcrConfiguration loadFrom( InputStream configurationFileInputStream ) throws IOException, SAXException {
335         super.loadFrom(configurationFileInputStream);
336         return this;
337     }
338 
339     /**
340      * {@inheritDoc}
341      * 
342      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(java.io.InputStream, java.lang.String)
343      */
344     @Override
345     public JcrConfiguration loadFrom( InputStream configurationFileInputStream,
346                                       String path ) throws IOException, SAXException {
347         super.loadFrom(configurationFileInputStream, path);
348         return this;
349     }
350 
351     /**
352      * {@inheritDoc}
353      * 
354      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(org.modeshape.graph.connector.RepositorySource)
355      */
356     @Override
357     public JcrConfiguration loadFrom( RepositorySource source ) {
358         super.loadFrom(source);
359         return this;
360     }
361 
362     /**
363      * {@inheritDoc}
364      * 
365      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(org.modeshape.graph.connector.RepositorySource,
366      *      java.lang.String)
367      */
368     @Override
369     public JcrConfiguration loadFrom( RepositorySource source,
370                                       String workspaceName ) {
371         super.loadFrom(source, workspaceName);
372         return this;
373     }
374 
375     /**
376      * {@inheritDoc}
377      * 
378      * @see org.modeshape.repository.ModeShapeConfiguration#loadFrom(org.modeshape.graph.connector.RepositorySource,
379      *      java.lang.String, java.lang.String)
380      */
381     @Override
382     public JcrConfiguration loadFrom( RepositorySource source,
383                                       String workspaceName,
384                                       String pathInWorkspace ) {
385         super.loadFrom(source, workspaceName, pathInWorkspace);
386         return this;
387     }
388 
389     /**
390      * {@inheritDoc}
391      * 
392      * @see org.modeshape.repository.ModeShapeConfiguration#and()
393      */
394     @Override
395     public JcrConfiguration and() {
396         return this;
397     }
398 
399     /**
400      * {@inheritDoc}
401      * 
402      * @see org.modeshape.repository.ModeShapeConfiguration#withClassLoaderFactory(org.modeshape.common.component.ClassLoaderFactory)
403      */
404     @Override
405     public JcrConfiguration withClassLoaderFactory( ClassLoaderFactory classLoaderFactory ) {
406         super.withClassLoaderFactory(classLoaderFactory);
407         return this;
408     }
409 
410     /**
411      * {@inheritDoc}
412      * 
413      * @see org.modeshape.repository.ModeShapeConfiguration#mimeTypeDetector(java.lang.String)
414      */
415     @Override
416     public MimeTypeDetectorDefinition<JcrConfiguration> mimeTypeDetector( String name ) {
417         return mimeTypeDetectorDefinition(this, name);
418     }
419 
420     /**
421      * {@inheritDoc}
422      * 
423      * @see org.modeshape.repository.ModeShapeConfiguration#repositorySource(java.lang.String)
424      */
425     @Override
426     public RepositorySourceDefinition<JcrConfiguration> repositorySource( String name ) {
427         return repositorySourceDefinition(this, name);
428     }
429 
430     /**
431      * {@inheritDoc}
432      * 
433      * @see org.modeshape.repository.ModeShapeConfiguration#sequencer(java.lang.String)
434      */
435     @Override
436     public SequencerDefinition<JcrConfiguration> sequencer( String name ) {
437         return sequencerDefinition(this, name);
438     }
439 
440     /**
441      * Obtain or create a definition for the {@link javax.jcr.Repository JCR Repository} with the supplied name or identifier. A
442      * new definition will be created if there currently is no sequencer defined with the supplied name.
443      * 
444      * @param name the name or identifier of the sequencer
445      * @return the details of the sequencer definition; never null
446      */
447     public RepositoryDefinition<JcrConfiguration> repository( String name ) {
448         return repositoryDefinition(this, name);
449     }
450 
451     /**
452      * Get the list of sequencer definitions.
453      * 
454      * @return the unmodifiable set of definitions; never null but possibly empty if there are no definitions
455      */
456     public Set<RepositoryDefinition<JcrConfiguration>> repositories() {
457         // Get the children under the 'dna:mimeTypeDetectors' node ...
458         Set<String> names = getNamesOfComponentsUnder(ModeShapeLexicon.REPOSITORIES);
459         names.addAll(this.repositoryDefinitions.keySet());
460         Set<RepositoryDefinition<JcrConfiguration>> results = new HashSet<RepositoryDefinition<JcrConfiguration>>();
461         for (String name : names) {
462             results.add(repository(name));
463         }
464         return Collections.unmodifiableSet(results);
465     }
466 
467     /**
468      * {@inheritDoc}
469      * 
470      * @see org.modeshape.repository.ModeShapeConfiguration#save()
471      */
472     @Override
473     public JcrConfiguration save() {
474         super.save();
475         this.repositoryDefinitions.clear();
476         return this;
477     }
478 
479     /**
480      * {@inheritDoc}
481      * 
482      * @see org.modeshape.repository.ModeShapeConfiguration#build()
483      */
484     @Override
485     public JcrEngine build() {
486         save();
487         return new JcrEngine(getExecutionContextForEngine(), getConfigurationDefinition());
488     }
489 
490     /**
491      * Utility method to construct a definition object for the repository with the supplied name and return type.
492      * 
493      * @param <ReturnType> the type of the return object
494      * @param returnObject the return object
495      * @param name the name of the repository
496      * @return the definition for the repository
497      */
498     @SuppressWarnings( "unchecked" )
499     protected <ReturnType extends JcrConfiguration> RepositoryDefinition<ReturnType> repositoryDefinition( ReturnType returnObject,
500                                                                                                            String name ) {
501         RepositoryDefinition<ReturnType> definition = (RepositoryDefinition<ReturnType>)repositoryDefinitions.get(name);
502         if (definition == null) {
503             definition = new RepositoryBuilder<ReturnType>(returnObject, changes(), path(), ModeShapeLexicon.REPOSITORIES,
504                                                            name(name));
505             repositoryDefinitions.put(name, definition);
506         }
507         return definition;
508     }
509 
510     protected class RepositoryBuilder<ReturnType> extends GraphReturnable<ReturnType, RepositoryDefinition<ReturnType>>
511         implements RepositoryDefinition<ReturnType> {
512         private final EnumMap<JcrRepository.Option, String> optionValues = new EnumMap<Option, String>(Option.class);
513 
514         protected RepositoryBuilder( ReturnType returnObject,
515                                      Graph.Batch batch,
516                                      Path path,
517                                      Name... names ) {
518             super(returnObject, batch, path, names);
519             // Load the current options ...
520             try {
521                 Path optionsPath = context.getValueFactories().getPathFactory().create(path, ModeShapeLexicon.OPTIONS);
522                 Subgraph options = batch.getGraph().getSubgraphOfDepth(2).at(optionsPath);
523                 for (Location optionChild : options.getRoot().getChildren()) {
524                     Node option = options.getNode(optionChild);
525                     Property property = option.getProperty(ModeShapeLexicon.VALUE);
526                     if (property != null && property.isEmpty()) {
527                         try {
528                             Option key = Option.findOption(optionChild.getPath()
529                                                                       .getLastSegment()
530                                                                       .getString(context.getNamespaceRegistry()));
531                             String value = context.getValueFactories().getStringFactory().create(property.getFirstValue());
532                             optionValues.put(key, value);
533                         } catch (IllegalArgumentException e) {
534                             // the key is not valid, so skip it ...
535                         }
536                     }
537                 }
538             } catch (PathNotFoundException e) {
539                 // No current options
540             }
541         }
542 
543         @Override
544         protected RepositoryDefinition<ReturnType> thisType() {
545             return this;
546         }
547 
548         public RepositoryDefinition<ReturnType> setSource( String sourceName ) {
549             setProperty(ModeShapeLexicon.SOURCE_NAME, sourceName);
550             return this;
551         }
552 
553         public String getSource() {
554             Property property = getProperty(ModeShapeLexicon.SOURCE_NAME);
555             if (property != null && !property.isEmpty()) {
556                 return context.getValueFactories().getStringFactory().create(property.getFirstValue());
557             }
558             return null;
559         }
560 
561         public RepositoryDefinition<ReturnType> setOption( JcrRepository.Option option,
562                                                            String value ) {
563             CheckArg.isNotNull(option, "option");
564             CheckArg.isNotNull(value, "value");
565             createIfMissing(ModeShapeLexicon.OPTIONS, option.name()).with(ModeShapeLexicon.VALUE, value.trim()).and();
566             optionValues.put(option, value);
567             return this;
568         }
569 
570         public RepositoryDefinition<ReturnType> setOption( Option option,
571                                                            boolean value ) {
572             return setOption(option, Boolean.toString(value));
573         }
574 
575         public RepositoryDefinition<ReturnType> setOption( Option option,
576                                                            int value ) {
577             return setOption(option, Integer.toString(value));
578         }
579 
580         public RepositoryDefinition<ReturnType> setOption( Option option,
581                                                            long value ) {
582             return setOption(option, Long.toString(value));
583         }
584 
585         public RepositoryDefinition<ReturnType> setOption( Option option,
586                                                            float value ) {
587             return setOption(option, Float.toString(value));
588         }
589 
590         public RepositoryDefinition<ReturnType> setOption( Option option,
591                                                            double value ) {
592             return setOption(option, Double.toString(value));
593         }
594 
595         public String getOption( Option option ) {
596             CheckArg.isNotNull(option, "option");
597             return optionValues.get(option);
598         }
599 
600         public RepositoryDefinition<ReturnType> registerNamespace( String prefix,
601                                                                    String uri ) {
602             CheckArg.isNotEmpty(prefix, "prefix");
603             CheckArg.isNotEmpty(uri, "uri");
604             prefix = prefix.trim();
605             uri = uri.trim();
606             createIfMissing(ModeShapeLexicon.NAMESPACES, prefix).with(ModeShapeLexicon.URI, uri).and();
607             return this;
608         }
609 
610         public RepositoryDefinition<ReturnType> addNodeTypes( String pathToCndFile ) {
611             CheckArg.isNotEmpty(pathToCndFile, "pathToCndFile");
612             return addNodeTypes(new File(pathToCndFile));
613         }
614 
615         public RepositoryDefinition<ReturnType> addNodeTypes( File file ) {
616             CheckArg.isNotNull(file, "file");
617             if (file.exists() && file.canRead()) {
618                 CndImporter importer = createCndImporter();
619                 try {
620                     Set<Namespace> namespacesBefore = batch.getGraph().getContext().getNamespaceRegistry().getNamespaces();
621                     importer.importFrom(file, getProblems());
622 
623                     // Record any new namespaces added by this import ...
624                     registerNewNamespaces(namespacesBefore);
625                 } catch (IOException e) {
626                     throw new ModeShapeConfigurationException(e);
627                 }
628                 return this;
629             }
630             throw new ModeShapeConfigurationException(JcrI18n.fileDoesNotExist.text(file.getPath()));
631         }
632 
633         public RepositoryDefinition<ReturnType> addNodeTypes( URL url ) {
634             CheckArg.isNotNull(url, "url");
635             // Obtain the stream ...
636             InputStream stream = null;
637             boolean foundError = false;
638             try {
639                 Set<Namespace> namespacesBefore = batch.getGraph().getContext().getNamespaceRegistry().getNamespaces();
640                 stream = url.openStream();
641                 CndImporter importer = createCndImporter();
642                 importer.importFrom(stream, getProblems(), url.toString());
643 
644                 if (getProblems().hasErrors()) {
645                     for (Problem problem : getProblems()) {
646                         if (problem.getStatus() == Status.ERROR) {
647                             throw new ModeShapeConfigurationException(problem.getThrowable());
648                         }
649                     }
650                 }
651                 // Record any new namespaces added by this import ...
652                 registerNewNamespaces(namespacesBefore);
653             } catch (IOException e) {
654                 foundError = true;
655                 throw new ModeShapeConfigurationException(e);
656             } finally {
657                 if (stream != null) {
658                     try {
659                         stream.close();
660                     } catch (IOException e) {
661                         if (!foundError) {
662                             throw new ModeShapeConfigurationException(e);
663                         }
664                     }
665                 }
666             }
667             return this;
668         }
669 
670         public RepositoryDefinition<ReturnType> addNodeTypes( InputStream cndContent ) {
671             CndImporter importer = createCndImporter();
672             try {
673                 Set<Namespace> namespacesBefore = batch.getGraph().getContext().getNamespaceRegistry().getNamespaces();
674                 importer.importFrom(cndContent, getProblems(), "stream");
675 
676                 // Record any new namespaces added by this import ...
677                 registerNewNamespaces(namespacesBefore);
678             } catch (IOException e) {
679                 throw new ModeShapeConfigurationException(e);
680             }
681             return this;
682         }
683 
684         protected void registerNewNamespaces( Set<Namespace> namespacesBefore ) {
685             Set<Namespace> namespacesAfter = batch.getGraph().getContext().getNamespaceRegistry().getNamespaces();
686             Set<Namespace> newNamespaces = new HashSet<Namespace>(namespacesAfter);
687             newNamespaces.removeAll(namespacesBefore);
688             for (Namespace namespace : newNamespaces) {
689                 registerNamespace(namespace.getPrefix(), namespace.getNamespaceUri());
690             }
691         }
692 
693         protected CndImporter createCndImporter() {
694             // The node types will be loaded into 'dna:repositories/{repositoryName}/dna:nodeTypes/' ...
695             Path nodeTypesPath = subpath(JcrLexicon.NODE_TYPES);
696             createIfMissing(JcrLexicon.NODE_TYPES).and();
697 
698             // Now set up the destination, but make it so that ...
699             Destination destination = new GraphBatchDestination(batch, true); // will NOT be executed
700 
701             // And create the importer that will load the CND content into the repository ...
702             return new CndImporter(destination, nodeTypesPath);
703         }
704     }
705 
706 }