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