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