001/*
002 * (C) Copyright 2006-2017 Nuxeo (http://nuxeo.com/) and others.
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 *
016 * Contributors:
017 *     Bogdan Stefanescu
018 *     Florent Guillaume
019 */
020package org.nuxeo.ecm.core.repository;
021
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner;
029import org.nuxeo.ecm.core.api.local.LocalException;
030import org.nuxeo.ecm.core.api.repository.RepositoryManager;
031import org.nuxeo.ecm.core.model.Repository;
032import org.nuxeo.ecm.core.model.Session;
033import org.nuxeo.runtime.api.Framework;
034import org.nuxeo.runtime.model.ComponentContext;
035import org.nuxeo.runtime.model.ComponentName;
036import org.nuxeo.runtime.model.DefaultComponent;
037import org.nuxeo.runtime.transaction.TransactionHelper;
038
039/**
040 * Component and service managing low-level repository instances.
041 */
042public class RepositoryService extends DefaultComponent {
043
044    public static final ComponentName NAME = new ComponentName("org.nuxeo.ecm.core.repository.RepositoryService");
045
046    private static final Log log = LogFactory.getLog(RepositoryService.class);
047
048    public static final String XP_REPOSITORY = "repository";
049
050    // @GuardedBy("itself")
051    private final Map<String, Repository> repositories = new HashMap<>();
052
053    public void shutdown() {
054        log.info("Shutting down repository manager");
055        synchronized (repositories) {
056            for (Repository repository : repositories.values()) {
057                repository.shutdown();
058            }
059            repositories.clear();
060        }
061    }
062
063    @Override
064    public int getApplicationStartedOrder() {
065        return 100;
066    }
067
068    @Override
069    public void start(ComponentContext context) {
070        initRepositories();
071    }
072
073    @Override
074    public void stop(ComponentContext context) {
075        TransactionHelper.runInTransaction(this::shutdown);
076    }
077
078    /**
079     * Start a tx and initialize repositories content. This method is publicly exposed since it is needed by tests to
080     * initialize repositories after cleanups (see CoreFeature).
081     *
082     * @since 8.4
083     */
084    public void initRepositories() {
085        TransactionHelper.runInTransaction(this::doInitRepositories);
086    }
087
088    /**
089     * Initializes all repositories. Requires an active transaction.
090     *
091     * @since 9.2
092     */
093    protected void doInitRepositories() {
094        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
095        for (String name : repositoryManager.getRepositoryNames()) {
096            openRepository(name);
097        }
098        // give up if no handler configured
099        RepositoryInitializationHandler handler = RepositoryInitializationHandler.getInstance();
100        if (handler == null) {
101            return;
102        }
103        // invoke handlers
104        for (String name : repositoryManager.getRepositoryNames()) {
105            initializeRepository(handler, name);
106        }
107    }
108
109    @Override
110    public <T> T getAdapter(Class<T> adapter) {
111        if (adapter.isAssignableFrom(getClass())) {
112            return adapter.cast(this);
113        }
114        return null;
115    }
116
117    protected void openRepository(String name) {
118        new UnrestrictedSessionRunner(name) {
119
120            @Override
121            public void run() {
122            }
123
124        }.runUnrestricted();
125    }
126
127    protected void initializeRepository(final RepositoryInitializationHandler handler, String name) {
128        new UnrestrictedSessionRunner(name) {
129            @Override
130            public void run() {
131                handler.initializeRepository(session);
132            }
133        }.runUnrestricted();
134    }
135
136    /**
137     * Gets a repository given its name.
138     * <p>
139     * Null is returned if no repository with that name was registered.
140     *
141     * @param repositoryName the repository name
142     * @return the repository instance or null if no repository with that name was registered
143     */
144    public Repository getRepository(String repositoryName) {
145        synchronized (repositories) {
146            return doGetRepository(repositoryName);
147        }
148    }
149
150    /**
151     * Calls to that method should be synchronized on repositories
152     *
153     * @since 7.2
154     * @see #getRepository(String)
155     * @see #getSession(String, String)
156     */
157    protected Repository doGetRepository(String repositoryName) {
158        Repository repository = repositories.get(repositoryName);
159        if (repository == null) {
160            RepositoryFactory factory = getFactory(repositoryName);
161            if (factory == null) {
162                return null;
163            }
164            repository = (Repository) factory.call();
165            repositories.put(repositoryName, repository);
166        }
167        return repository;
168    }
169
170    protected RepositoryFactory getFactory(String repositoryName) {
171        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
172        if (repositoryManager == null) {
173            // tests with no high-level repository manager
174            return null;
175        }
176        org.nuxeo.ecm.core.api.repository.Repository repo = repositoryManager.getRepository(repositoryName);
177        if (repo == null) {
178            return null;
179        }
180        RepositoryFactory repositoryFactory = (RepositoryFactory) repo.getRepositoryFactory();
181        if (repositoryFactory == null) {
182            throw new NullPointerException("Missing repositoryFactory for repository: " + repositoryName);
183        }
184        return repositoryFactory;
185    }
186
187    public List<String> getRepositoryNames() {
188        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
189        return repositoryManager.getRepositoryNames();
190    }
191
192    /**
193     * Creates a new session with the given session id from the given repository.
194     * <p/>
195     * Locks repositories before entering the pool. That allows concurrency with shutdown.
196     *
197     * @since 7.2
198     */
199    public Session getSession(String repositoryName) {
200        synchronized (repositories) {
201            Repository repository = doGetRepository(repositoryName);
202            if (repository == null) {
203                throw new LocalException("No such repository: " + repositoryName);
204            }
205            return repository.getSession();
206        }
207    }
208
209}