001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 * $Id$
017 */
018
019package org.nuxeo.ecm.directory;
020
021import static org.nuxeo.ecm.directory.localconfiguration.DirectoryConfigurationConstants.DIRECTORY_CONFIGURATION_FACET;
022
023import java.util.ArrayList;
024import java.util.List;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028import org.nuxeo.ecm.core.api.DocumentModel;
029import org.nuxeo.ecm.core.api.localconfiguration.LocalConfigurationService;
030import org.nuxeo.ecm.directory.api.DirectoryService;
031import org.nuxeo.ecm.directory.localconfiguration.DirectoryConfiguration;
032import org.nuxeo.ecm.directory.memory.MemoryDirectoryFactory;
033import org.nuxeo.ecm.directory.registry.DirectoryFactoryMapper;
034import org.nuxeo.ecm.directory.registry.DirectoryFactoryMapperRegistry;
035import org.nuxeo.ecm.directory.registry.DirectoryFactoryRegistry;
036import org.nuxeo.runtime.api.Framework;
037import org.nuxeo.runtime.model.ComponentContext;
038import org.nuxeo.runtime.model.DefaultComponent;
039import org.nuxeo.runtime.model.Extension;
040import org.nuxeo.runtime.transaction.TransactionHelper;
041
042public class DirectoryServiceImpl extends DefaultComponent implements DirectoryService {
043
044    protected static final String DELIMITER_BETWEEN_DIRECTORY_NAME_AND_SUFFIX = "_";
045
046    private static final Log log = LogFactory.getLog(DirectoryServiceImpl.class);
047
048    protected DirectoryFactoryRegistry factories;
049
050    protected DirectoryFactoryMapperRegistry factoriesByDirectoryName;
051
052    @Override
053    public void applicationStarted(ComponentContext context) {
054        if (Framework.isTestModeSet()) {
055            // when testing, DatabaseHelper init hasn't occurred yet,
056            // so keep to lazy initialization
057            return;
058        }
059        // open all directories at application startup, so that
060        // their tables are created (outside a transaction) if needed
061        for (Directory dir : getDirectories()) {
062            // open directory to init its resources (tables for SQLDirectory)
063            dir.getSession().close();
064        }
065        // commit the transaction so that tables are committed
066        if (TransactionHelper.isTransactionActiveOrMarkedRollback()) {
067            TransactionHelper.commitOrRollbackTransaction();
068            TransactionHelper.startTransaction();
069        }
070    }
071
072    protected DirectoryConfiguration getDirectoryConfiguration(DocumentModel documentContext) {
073        LocalConfigurationService localConfigurationService = Framework.getService(LocalConfigurationService.class);
074
075        if (localConfigurationService == null) {
076            log.info("Local configuration not deployed, will use default configuration");
077            return null;
078        }
079
080        return localConfigurationService.getConfiguration(DirectoryConfiguration.class, DIRECTORY_CONFIGURATION_FACET,
081                documentContext);
082    }
083
084    /**
085     * This will return the local directory name according the local configuration. If the local configuration is null
086     * or the suffix value is null or the suffix value trimmed is an empty string the returned value is the
087     * directoryName given in parameter. If not this is directoryName + DELIMITER_BETWEEN_DIRECTORY_NAME_AND_SUFFIX +
088     * suffix. if directoryName is null, return null.
089     */
090    protected String getWaitingLocalDirectoryName(String directoryName, DirectoryConfiguration configuration) {
091        if (directoryName == null) {
092            return null;
093        }
094
095        if (configuration != null && configuration.getDirectorySuffix() != null) {
096            String suffix = configuration.getDirectorySuffix().trim();
097            if (!"".equals(suffix)) {
098                return directoryName + DELIMITER_BETWEEN_DIRECTORY_NAME_AND_SUFFIX + suffix;
099            }
100            log.warn("The local configuration detected is an empty value, we consider it as no configuration set.");
101            log.debug("Directory Local Configuration is on : " + configuration.getDocumentRef());
102        }
103
104        return directoryName;
105    }
106
107    public Directory getDirectory(String directoryName) throws DirectoryException {
108        if (directoryName == null) {
109            return null;
110        }
111        List<String> factoryNames = factoriesByDirectoryName.getFactoriesForDirectory(directoryName);
112        if (factoryNames == null || factoryNames.isEmpty()) {
113            return null;
114        }
115        for (String factoryName : factoryNames) {
116            DirectoryFactory targetFactory = factories.getFactory(factoryName);
117            if (targetFactory != null) {
118                return targetFactory.getDirectory(directoryName);
119            }
120        }
121        return null;
122    }
123
124    public Directory getDirectory(String name, DocumentModel documentContext) throws DirectoryException {
125        if (name == null) {
126            return null;
127        }
128
129        String localDirectoryName = getWaitingLocalDirectoryName(name, getDirectoryConfiguration(documentContext));
130
131        Directory directory = getDirectory(localDirectoryName);
132
133        if (directory == null && !name.equals(localDirectoryName)) {
134            log.debug(String.format("The local directory named '%s' was"
135                    + " not found. Look for the default one named: %s", localDirectoryName, name));
136            directory = getDirectory(name);
137        }
138
139        return directory;
140    }
141
142    private Directory getDirectoryOrFail(String name) throws DirectoryException {
143        return getDirectoryOrFail(name, null);
144    }
145
146    private Directory getDirectoryOrFail(String name, DocumentModel documentContext) throws DirectoryException {
147
148        Directory dir = getDirectory(name, documentContext);
149        if (null == dir) {
150            throw new DirectoryException(String.format("no directory registered with name '%s'", name));
151        }
152        return dir;
153    }
154
155    public List<Directory> getDirectories() throws DirectoryException {
156        List<Directory> directoryList = new ArrayList<Directory>();
157        for (DirectoryFactory factory : factories.getFactories()) {
158            List<Directory> list = factory.getDirectories();
159            directoryList.addAll(list);
160        }
161        return directoryList;
162    }
163
164    @Override
165    public void activate(ComponentContext context) {
166        factories = new DirectoryFactoryRegistry();
167        factoriesByDirectoryName = new DirectoryFactoryMapperRegistry();
168    }
169
170    @Override
171    public void deactivate(ComponentContext context) {
172        for (DirectoryFactory factory : factories.getFactories()) {
173            factory.shutdown();
174        }
175        factories = null;
176        factoriesByDirectoryName = null;
177    }
178
179    @Override
180    public void registerExtension(Extension extension) {
181        Object[] contribs = extension.getContributions();
182        for (Object contrib : contribs) {
183            DirectoryFactoryDescriptor factoryDescriptor = (DirectoryFactoryDescriptor) contrib;
184            String factoryName = factoryDescriptor.getFactoryName();
185            factories.addContribution(new DirectoryFactoryProxy(factoryName));
186            log.debug("registered factory: " + factoryName);
187        }
188    }
189
190    @Override
191    public void unregisterExtension(Extension extension) {
192        Object[] contribs = extension.getContributions();
193        for (Object contrib : contribs) {
194            DirectoryFactoryDescriptor factoryDescriptor = (DirectoryFactoryDescriptor) contrib;
195            String factoryName = factoryDescriptor.getFactoryName();
196            DirectoryFactory factoryToRemove = factories.getFactory(factoryName);
197            if (factoryToRemove == null) {
198                log.warn(String.format("Factory '%s' was not registered", factoryName));
199                return;
200            }
201            factoryToRemove.shutdown();
202            // XXX: do not cleanup mappers, lookup will ignore non-registered
203            // factories anyway
204            factories.removeContribution(factoryToRemove);
205            log.debug("unregistered factory: " + factoryName);
206        }
207    }
208
209    public void registerDirectory(String directoryName, DirectoryFactory factory) {
210        // compatibility code to add otherwise missing memory factory (as it's
211        // not registered via extension points)
212        if (factory instanceof MemoryDirectoryFactory) {
213            factories.addContribution(factory);
214        }
215        DirectoryFactoryMapper contrib = new DirectoryFactoryMapper(directoryName, factory.getName());
216        factoriesByDirectoryName.addContribution(contrib);
217    }
218
219    public void unregisterDirectory(String directoryName, DirectoryFactory factory) {
220        DirectoryFactoryMapper contrib = new DirectoryFactoryMapper(directoryName, factory.getName());
221        factoriesByDirectoryName.removeContribution(contrib);
222    }
223
224    public List<String> getDirectoryNames() throws DirectoryException {
225        List<Directory> directories = getDirectories();
226        List<String> directoryNames = new ArrayList<String>();
227        for (Directory directory : directories) {
228            directoryNames.add(directory.getName());
229        }
230        return directoryNames;
231    }
232
233    public String getDirectorySchema(String directoryName) throws DirectoryException {
234        return getDirectoryOrFail(directoryName).getSchema();
235    }
236
237    public String getDirectoryIdField(String directoryName) throws DirectoryException {
238        return getDirectoryOrFail(directoryName).getIdField();
239    }
240
241    public String getDirectoryPasswordField(String directoryName) throws DirectoryException {
242        return getDirectoryOrFail(directoryName).getPasswordField();
243    }
244
245    public Session open(String directoryName) throws DirectoryException {
246        return getDirectoryOrFail(directoryName).getSession();
247    }
248
249    public Session open(String directoryName, DocumentModel documentContext) throws DirectoryException {
250        return getDirectoryOrFail(directoryName, documentContext).getSession();
251    }
252
253    public String getParentDirectoryName(String directoryName) throws DirectoryException {
254        return getDirectoryOrFail(directoryName).getParentDirectory();
255    }
256
257}