001/*
002 * Copyright (c) 2006-2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Bogdan Stefanescu
011 *     Florent Guillaume
012 */
013package org.nuxeo.ecm.core.repository;
014
015import java.util.HashMap;
016import java.util.List;
017import java.util.Map;
018
019import javax.transaction.Transaction;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023
024import org.nuxeo.ecm.core.api.CoreSession;
025import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner;
026import org.nuxeo.ecm.core.api.local.LocalException;
027import org.nuxeo.ecm.core.api.local.LocalSession;
028import org.nuxeo.ecm.core.api.repository.RepositoryManager;
029import org.nuxeo.ecm.core.model.Repository;
030import org.nuxeo.ecm.core.model.Session;
031import org.nuxeo.runtime.RuntimeServiceEvent;
032import org.nuxeo.runtime.RuntimeServiceListener;
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 applicationStarted(ComponentContext context) {
070        Framework.addListener(new RuntimeServiceListener() {
071
072            @Override
073            public void handleEvent(RuntimeServiceEvent event) {
074                if (event.id != RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP) {
075                    return;
076                }
077                Framework.removeListener(this);
078                shutdown();
079            }
080        });
081        RepositoryInitializationHandler handler = RepositoryInitializationHandler.getInstance();
082        if (handler == null) {
083            return;
084        }
085        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
086        boolean started = false;
087        boolean ok = false;
088        { // open repositories without a tx active
089            Transaction tx = TransactionHelper.suspendTransaction();
090            try {
091                for (String name : repositoryManager.getRepositoryNames()) {
092                    openRepository(name);
093                }
094            } finally {
095                TransactionHelper.resumeTransaction(tx);
096            }
097        }
098        // initialize repositories with a tx active
099        try {
100            started = !TransactionHelper.isTransactionActive() && TransactionHelper.startTransaction();
101            for (String name : repositoryManager.getRepositoryNames()) {
102                initializeRepository(handler, name);
103            }
104            ok = true;
105        } finally {
106            if (started) {
107                try {
108                    if (!ok) {
109                        TransactionHelper.setTransactionRollbackOnly();
110                    }
111                } finally {
112                    TransactionHelper.commitOrRollbackTransaction();
113                }
114            }
115        }
116    }
117
118    @SuppressWarnings("unchecked")
119    @Override
120    public <T> T getAdapter(Class<T> adapter) {
121        if (adapter.isAssignableFrom(getClass())) {
122            return (T) this;
123        }
124        if (adapter.isAssignableFrom(CoreSession.class)) {
125            return (T) LocalSession.createInstance();
126        }
127        return null;
128    }
129
130    protected void openRepository(String name) {
131        new UnrestrictedSessionRunner(name) {
132
133            @Override
134            public void run() {;
135            }
136
137        }.runUnrestricted();
138    }
139
140    protected void initializeRepository(final RepositoryInitializationHandler handler, String name) {
141        new UnrestrictedSessionRunner(name) {
142            @Override
143            public void run() {
144                handler.initializeRepository(session);
145            }
146        }.runUnrestricted();
147    }
148
149    /**
150     * Gets a repository given its name.
151     * <p>
152     * Null is returned if no repository with that name was registered.
153     *
154     * @param repositoryName the repository name
155     * @return the repository instance or null if no repository with that name was registered
156     */
157    public Repository getRepository(String repositoryName) {
158        synchronized (repositories) {
159            return doGetRepository(repositoryName);
160        }
161    }
162
163    /**
164     * Calls to that method should be synchronized on repositories
165     *
166     * @since 7.2
167     * @see #getRepository(String)
168     * @see #getSession(String, String)
169     */
170    protected Repository doGetRepository(String repositoryName) {
171        Repository repository = repositories.get(repositoryName);
172        if (repository == null) {
173            RepositoryFactory factory = getFactory(repositoryName);
174            if (factory == null) {
175                return null;
176            }
177            repository = (Repository) factory.call();
178            repositories.put(repositoryName, repository);
179        }
180        return repository;
181    }
182
183    protected RepositoryFactory getFactory(String repositoryName) {
184        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
185        if (repositoryManager == null) {
186            // tests with no high-level repository manager
187            return null;
188        }
189        org.nuxeo.ecm.core.api.repository.Repository repo = repositoryManager.getRepository(repositoryName);
190        if (repo == null) {
191            return null;
192        }
193        RepositoryFactory repositoryFactory = (RepositoryFactory) repo.getRepositoryFactory();
194        if (repositoryFactory == null) {
195            throw new NullPointerException("Missing repositoryFactory for repository: " + repositoryName);
196        }
197        return repositoryFactory;
198    }
199
200    public List<String> getRepositoryNames() {
201        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
202        return repositoryManager.getRepositoryNames();
203    }
204
205    /**
206     * Creates a new session with the given session id from the given repository.
207     * <p/>
208     * Locks repositories before entering the pool. That allows concurrency with shutdown.
209     *
210     * @since 7.2
211     */
212    public Session getSession(String repositoryName) {
213        synchronized (repositories) {
214            Repository repository = doGetRepository(repositoryName);
215            if (repository == null) {
216                throw new LocalException("No such repository: " + repositoryName);
217            }
218            return repository.getSession();
219        }
220    }
221
222}