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 activate(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    }
088
089    @Override
090    public void applicationStarted(ComponentContext context) {
091        RepositoryInitializationHandler handler = RepositoryInitializationHandler.getInstance();
092        if (handler == null) {
093            return;
094        }
095        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
096        boolean started = false;
097        boolean ok = false;
098        { // open repositories without a tx active
099            Transaction tx = TransactionHelper.suspendTransaction();
100            try {
101                for (String name : repositoryManager.getRepositoryNames()) {
102                    openRepository(name);
103                }
104            } finally {
105                TransactionHelper.resumeTransaction(tx);
106            }
107        }
108        // initialize repositories with a tx active
109        try {
110            started = !TransactionHelper.isTransactionActive() && TransactionHelper.startTransaction();
111            for (String name : repositoryManager.getRepositoryNames()) {
112                initializeRepository(handler, name);
113            }
114            ok = true;
115        } finally {
116            if (started) {
117                try {
118                    if (!ok) {
119                        TransactionHelper.setTransactionRollbackOnly();
120                    }
121                } finally {
122                    TransactionHelper.commitOrRollbackTransaction();
123                }
124            }
125        }
126    }
127
128    @Override
129    public <T> T getAdapter(Class<T> adapter) {
130        if (adapter.isAssignableFrom(getClass())) {
131            return adapter.cast(this);
132        }
133        if (adapter.isAssignableFrom(CoreSession.class)) {
134            return adapter.cast(LocalSession.createInstance());
135        }
136        return null;
137    }
138
139    protected void openRepository(String name) {
140        new UnrestrictedSessionRunner(name) {
141
142            @Override
143            public void run() {;
144            }
145
146        }.runUnrestricted();
147    }
148
149    protected void initializeRepository(final RepositoryInitializationHandler handler, String name) {
150        new UnrestrictedSessionRunner(name) {
151            @Override
152            public void run() {
153                handler.initializeRepository(session);
154            }
155        }.runUnrestricted();
156    }
157
158    /**
159     * Gets a repository given its name.
160     * <p>
161     * Null is returned if no repository with that name was registered.
162     *
163     * @param repositoryName the repository name
164     * @return the repository instance or null if no repository with that name was registered
165     */
166    public Repository getRepository(String repositoryName) {
167        synchronized (repositories) {
168            return doGetRepository(repositoryName);
169        }
170    }
171
172    /**
173     * Calls to that method should be synchronized on repositories
174     *
175     * @since 7.2
176     * @see #getRepository(String)
177     * @see #getSession(String, String)
178     */
179    protected Repository doGetRepository(String repositoryName) {
180        Repository repository = repositories.get(repositoryName);
181        if (repository == null) {
182            RepositoryFactory factory = getFactory(repositoryName);
183            if (factory == null) {
184                return null;
185            }
186            repository = (Repository) factory.call();
187            repositories.put(repositoryName, repository);
188        }
189        return repository;
190    }
191
192    protected RepositoryFactory getFactory(String repositoryName) {
193        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
194        if (repositoryManager == null) {
195            // tests with no high-level repository manager
196            return null;
197        }
198        org.nuxeo.ecm.core.api.repository.Repository repo = repositoryManager.getRepository(repositoryName);
199        if (repo == null) {
200            return null;
201        }
202        RepositoryFactory repositoryFactory = (RepositoryFactory) repo.getRepositoryFactory();
203        if (repositoryFactory == null) {
204            throw new NullPointerException("Missing repositoryFactory for repository: " + repositoryName);
205        }
206        return repositoryFactory;
207    }
208
209    public List<String> getRepositoryNames() {
210        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
211        return repositoryManager.getRepositoryNames();
212    }
213
214    /**
215     * Creates a new session with the given session id from the given repository.
216     * <p/>
217     * Locks repositories before entering the pool. That allows concurrency with shutdown.
218     *
219     * @since 7.2
220     */
221    public Session getSession(String repositoryName) {
222        synchronized (repositories) {
223            Repository repository = doGetRepository(repositoryName);
224            if (repository == null) {
225                throw new LocalException("No such repository: " + repositoryName);
226            }
227            return repository.getSession();
228        }
229    }
230
231}