001    /*
002     * JBoss DNA (http://www.jboss.org/dna)
003     * See the COPYRIGHT.txt file distributed with this work for information
004     * regarding copyright ownership.  Some portions may be licensed
005     * to Red Hat, Inc. under one or more contributor license agreements.
006     * See the AUTHORS.txt file in the distribution for a full listing of 
007     * individual contributors.
008     *
009     * Unless otherwise indicated, all code in JBoss DNA is licensed
010     * to you under the terms of the GNU Lesser General Public License as
011     * published by the Free Software Foundation; either version 2.1 of
012     * the License, or (at your option) any later version.
013     * 
014     * JBoss DNA is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     * Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this software; if not, write to the Free
021     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023     */
024    package org.jboss.dna.jcr;
025    
026    import java.util.Collections;
027    import java.util.HashMap;
028    import java.util.HashSet;
029    import java.util.Map;
030    import java.util.Set;
031    import java.util.concurrent.locks.Lock;
032    import java.util.concurrent.locks.ReentrantLock;
033    import javax.jcr.Repository;
034    import javax.jcr.RepositoryException;
035    import org.jboss.dna.common.util.CheckArg;
036    import org.jboss.dna.graph.ExecutionContext;
037    import org.jboss.dna.graph.Graph;
038    import org.jboss.dna.graph.Location;
039    import org.jboss.dna.graph.Node;
040    import org.jboss.dna.graph.Subgraph;
041    import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
042    import org.jboss.dna.graph.connector.RepositorySource;
043    import org.jboss.dna.graph.property.Name;
044    import org.jboss.dna.graph.property.Path;
045    import org.jboss.dna.graph.property.PathFactory;
046    import org.jboss.dna.graph.property.PathNotFoundException;
047    import org.jboss.dna.graph.property.Property;
048    import org.jboss.dna.graph.property.basic.GraphNamespaceRegistry;
049    import org.jboss.dna.jcr.JcrRepository.Option;
050    import org.jboss.dna.repository.DnaConfiguration;
051    import org.jboss.dna.repository.DnaEngine;
052    
053    /**
054     * The basic component that encapsulates the JBoss DNA services, including the {@link Repository} instances.
055     */
056    public class JcrEngine extends DnaEngine {
057    
058        private final Map<String, JcrRepository> repositories;
059        private final Lock repositoriesLock;
060    
061        JcrEngine( ExecutionContext context,
062                   DnaConfiguration.ConfigurationDefinition configuration ) {
063            super(context, configuration);
064            this.repositories = new HashMap<String, JcrRepository>();
065            this.repositoriesLock = new ReentrantLock();
066        }
067    
068        /**
069         * Get the {@link Repository} implementation for the named repository.
070         * 
071         * @param repositoryName the name of the repository, which corresponds to the name of a configured {@link RepositorySource}
072         * @return the named repository instance
073         * @throws IllegalArgumentException if the repository name is null, blank or invalid
074         * @throws RepositoryException if there is no repository with the specified name
075         * @throws IllegalStateException if this engine was not {@link #start() started}
076         */
077        public final JcrRepository getRepository( String repositoryName ) throws RepositoryException {
078            CheckArg.isNotEmpty(repositoryName, "repositoryName");
079            checkRunning();
080            try {
081                repositoriesLock.lock();
082                JcrRepository repository = repositories.get(repositoryName);
083                if (repository == null) {
084                    try {
085                        repository = doCreateJcrRepository(repositoryName);
086                    } catch (PathNotFoundException e) {
087                        // The repository name is not a valid repository ...
088                        String msg = JcrI18n.repositoryDoesNotExist.text(repositoryName);
089                        throw new RepositoryException(msg);
090                    }
091                    repositories.put(repositoryName, repository);
092                }
093                return repository;
094            } finally {
095                repositoriesLock.unlock();
096            }
097        }
098    
099        /**
100         * Get the names of each of the JCR repositories.
101         * 
102         * @return the immutable names of the repositories that exist at the time this method is called
103         */
104        public Set<String> getRepositoryNames() {
105            checkRunning();
106            Set<String> results = new HashSet<String>();
107            // Read the names of the JCR repositories from the configuration (not from the Repository objects used so far) ...
108            PathFactory pathFactory = getExecutionContext().getValueFactories().getPathFactory();
109            Path repositoriesPath = pathFactory.create(configuration.getPath(), DnaLexicon.REPOSITORIES);
110            Graph configuration = getConfigurationGraph();
111            for (Location child : configuration.getChildren().of(repositoriesPath)) {
112                Name repositoryName = child.getPath().getLastSegment().getName();
113                results.add(readable(repositoryName));
114            }
115            return Collections.unmodifiableSet(results);
116        }
117    
118        protected JcrRepository doCreateJcrRepository( String repositoryName ) throws RepositoryException, PathNotFoundException {
119            RepositoryConnectionFactory connectionFactory = getRepositoryConnectionFactory();
120            Map<String, String> descriptors = null;
121            Map<Option, String> options = new HashMap<Option, String>();
122    
123            // Read the subgraph that represents the repository ...
124            PathFactory pathFactory = getExecutionContext().getValueFactories().getPathFactory();
125            Path repositoriesPath = pathFactory.create(configuration.getPath(), DnaLexicon.REPOSITORIES);
126            Path repositoryPath = pathFactory.create(repositoriesPath, repositoryName);
127            Graph configuration = getConfigurationGraph();
128            Subgraph subgraph = configuration.getSubgraphOfDepth(6).at(repositoryPath);
129    
130            // Read the options ...
131            Node optionsNode = subgraph.getNode(DnaLexicon.OPTIONS);
132            if (optionsNode != null) {
133                for (Location optionLocation : optionsNode.getChildren()) {
134                    Node optionNode = configuration.getNodeAt(optionLocation);
135                    Path.Segment segment = optionLocation.getPath().getLastSegment();
136                    Property valueProperty = optionNode.getProperty(DnaLexicon.VALUE);
137                    if (valueProperty == null) continue;
138                    Option option = Option.findOption(segment.getName().getLocalName());
139                    if (option == null) continue;
140                    options.put(option, valueProperty.getFirstValue().toString());
141                }
142            }
143    
144            // Read the namespaces ...
145            ExecutionContext context = getExecutionContext();
146            Node namespacesNode = subgraph.getNode(DnaLexicon.NAMESPACES);
147            if (namespacesNode != null) {
148                GraphNamespaceRegistry registry = new GraphNamespaceRegistry(configuration, namespacesNode.getLocation().getPath(),
149                                                                             DnaLexicon.NAMESPACE_URI);
150                context = context.with(registry);
151            }
152    
153            // Get the name of the source ...
154            Property property = subgraph.getRoot().getProperty(DnaLexicon.SOURCE_NAME);
155            if (property == null || property.isEmpty()) {
156                String readableName = readable(DnaLexicon.SOURCE_NAME);
157                String readablePath = readable(subgraph.getLocation());
158                String msg = JcrI18n.propertyNotFoundOnNode.text(readableName, readablePath, configuration.getCurrentWorkspaceName());
159                throw new RepositoryException(msg);
160            }
161            String sourceName = context.getValueFactories().getStringFactory().create(property.getFirstValue());
162    
163            // Create the repository ...
164            JcrRepository repository = new JcrRepository(context, connectionFactory, sourceName, descriptors, options);
165    
166            // Register all the the node types ...
167            Node nodeTypesNode = subgraph.getNode(DnaLexicon.NODE_TYPES);
168            if (nodeTypesNode != null) {
169                repository.getRepositoryTypeManager().registerNodeTypes(subgraph, nodeTypesNode.getLocation());// throws exception
170            }
171    
172            return repository;
173        }
174    
175        protected final String readable( Name name ) {
176            return name.getString(context.getNamespaceRegistry());
177        }
178    
179        protected final String readable( Path path ) {
180            return path.getString(context.getNamespaceRegistry());
181        }
182    
183        protected final String readable( Location location ) {
184            return location.getString(context.getNamespaceRegistry());
185        }
186    }