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