001/*
002 * ModeShape (http://www.modeshape.org)
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 */
016package org.modeshape.web.jcr;
017
018import java.util.Collections;
019import java.util.Enumeration;
020import java.util.HashMap;
021import java.util.Iterator;
022import java.util.Map;
023import java.util.ServiceLoader;
024import java.util.Set;
025import javax.jcr.Repository;
026import javax.jcr.RepositoryException;
027import javax.jcr.Session;
028import javax.servlet.ServletContext;
029import javax.servlet.http.HttpServletRequest;
030import org.modeshape.common.annotation.ThreadSafe;
031import org.modeshape.common.util.CheckArg;
032import org.modeshape.jcr.api.Logger;
033import org.modeshape.jcr.api.RepositoriesContainer;
034import org.modeshape.jcr.api.ServletCredentials;
035
036/**
037 * Manager for accessing JCR Repository instances. This manager uses the idiomatic way to find JCR Repository (and ModeShape
038 * Repositories) instances via the {@link ServiceLoader} and {@link org.modeshape.jcr.api.RepositoriesContainer} mechanism.
039 */
040@ThreadSafe
041public class RepositoryManager {
042
043    private static final Logger LOGGER = WebLogger.getLogger(RepositoryManager.class);
044    private static final Map<String, Object> factoryParams = new HashMap<String, Object>();
045
046    private static RepositoriesContainer repositoriesContainer;
047
048    private RepositoryManager() {
049    }
050
051    /**
052     * Initializes the repository factory. For more details, please see the {@link RepositoryManager class-level documentation}.
053     * 
054     * @param context the servlet context; may not be null
055     * @see RepositoryManager
056     */
057    static synchronized void initialize( ServletContext context ) {
058        CheckArg.isNotNull(context, "context");
059        loadFactoryParameters(context);
060        loadRepositoriesContainer();
061    }
062
063    private static void loadRepositoriesContainer() {
064        Iterator<RepositoriesContainer> containersIterator = ServiceLoader.load(RepositoriesContainer.class).iterator();
065        if (!containersIterator.hasNext()) {
066            throw new IllegalStateException(
067                    WebJcrI18n.repositoriesContainerNotFoundInClasspath.text(RepositoriesContainer.class.getName()));
068        }
069        //there shouldn't be more than 1 container
070        repositoriesContainer = containersIterator.next();
071    }
072
073    private static void loadFactoryParameters( ServletContext context ) {
074        factoryParams.clear();
075        Enumeration<?> names = context.getInitParameterNames();
076        if (names == null) {
077            addParameter(RepositoriesContainer.URL, context);
078            addParameter(RepositoriesContainer.REPOSITORY_NAME, context);
079        } else {
080            while (names.hasMoreElements()) {
081                Object next = names.nextElement();
082                if (next == null) continue;
083                String name = next.toString();
084                addParameter(name, context);
085            }
086        }
087    }
088
089    private static void addParameter( String name,
090                                      ServletContext context ) {
091        String value = context.getInitParameter(name);
092        if (value != null) factoryParams.put(name, value);
093    }
094
095    /**
096     * Get a JCR Session for the named workspace in the named repository, using the supplied HTTP servlet request for
097     * authentication information.
098     * 
099     * @param request the servlet request; may not be null or unauthenticated
100     * @param repositoryName the name of the repository in which the session is created
101     * @param workspaceName the name of the workspace to which the session should be connected
102     * @return an active session with the given workspace in the named repository
103     * @throws RepositoryException if the named repository does not exist or there was a problem obtaining the named repository
104     */
105    public static Session getSession( HttpServletRequest request,
106                                      String repositoryName,
107                                      String workspaceName ) throws RepositoryException {
108        // Go through all the RepositoryFactory instances and try to create one ...
109        Repository repository = getRepository(repositoryName);
110
111        // If there's no authenticated user, try an anonymous login
112        if (request == null || request.getUserPrincipal() == null) {
113            return repository.login(workspaceName);
114        }
115
116        return repository.login(new ServletCredentials(request), workspaceName);
117    }
118
119    /**
120     * Returns the {@link Repository} instance with the given name.
121     * @param repositoryName a {@code non-null} string
122     * @return a {@link Repository} instance, never {@code null}
123     * @throws NoSuchRepositoryException if no repository with the given name exists.
124     */
125    public static Repository getRepository( String repositoryName ) throws NoSuchRepositoryException {
126        Repository repository = null;
127        try {
128            repository = repositoriesContainer.getRepository(repositoryName, Collections.unmodifiableMap(factoryParams));
129        } catch (RepositoryException e) {
130            throw new NoSuchRepositoryException(WebJcrI18n.cannotInitializeRepository.text(repositoryName), e);
131        }
132
133        if (repository == null) {
134            throw new NoSuchRepositoryException(WebJcrI18n.repositoryNotFound.text(repositoryName));
135        }
136        return repository;
137    }
138
139    /**
140     * Returns a set with all the names of the available repositories.
141     * @return a set with the names, never {@code null}
142     */
143    public static Set<String> getJcrRepositoryNames() {
144        try {
145            return repositoriesContainer.getRepositoryNames(Collections.unmodifiableMap(factoryParams));
146        } catch (RepositoryException e) {
147            LOGGER.error(e, WebJcrI18n.cannotLoadRepositoryNames.text());
148            return Collections.emptySet();
149        }
150    }
151
152    static void shutdown() {
153        repositoriesContainer.shutdown();
154    }
155}