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;
040
041public class DirectoryServiceImpl extends DefaultComponent implements DirectoryService {
042
043    protected static final String DELIMITER_BETWEEN_DIRECTORY_NAME_AND_SUFFIX = "_";
044
045    private static final Log log = LogFactory.getLog(DirectoryServiceImpl.class);
046
047    protected DirectoryFactoryRegistry factories;
048
049    protected DirectoryFactoryMapperRegistry factoriesByDirectoryName;
050
051    @Override
052    public void applicationStarted(ComponentContext context) {
053        if (Framework.isTestModeSet()) {
054            // when testing, DatabaseHelper init hasn't occurred yet,
055            // so keep to lazy initialization
056            return;
057        }
058        // open all directories at application startup, so that
059        // their tables are created (outside a transaction) if needed
060        for (Directory dir : getDirectories()) {
061            dir.getName(); // enough to create tables for SQL directories
062        }
063    }
064
065    protected DirectoryConfiguration getDirectoryConfiguration(DocumentModel documentContext) {
066        LocalConfigurationService localConfigurationService = Framework.getService(LocalConfigurationService.class);
067
068        if (localConfigurationService == null) {
069            log.info("Local configuration not deployed, will use default configuration");
070            return null;
071        }
072
073        return localConfigurationService.getConfiguration(DirectoryConfiguration.class, DIRECTORY_CONFIGURATION_FACET,
074                documentContext);
075    }
076
077    /**
078     * This will return the local directory name according the local configuration. If the local configuration is null
079     * or the suffix value is null or the suffix value trimmed is an empty string the returned value is the
080     * directoryName given in parameter. If not this is directoryName + DELIMITER_BETWEEN_DIRECTORY_NAME_AND_SUFFIX +
081     * suffix. if directoryName is null, return null.
082     */
083    protected String getWaitingLocalDirectoryName(String directoryName, DirectoryConfiguration configuration) {
084        if (directoryName == null) {
085            return null;
086        }
087
088        if (configuration != null && configuration.getDirectorySuffix() != null) {
089            String suffix = configuration.getDirectorySuffix().trim();
090            if (!"".equals(suffix)) {
091                return directoryName + DELIMITER_BETWEEN_DIRECTORY_NAME_AND_SUFFIX + suffix;
092            }
093            log.warn("The local configuration detected is an empty value, we consider it as no configuration set.");
094            log.debug("Directory Local Configuration is on : " + configuration.getDocumentRef());
095        }
096
097        return directoryName;
098    }
099
100    public Directory getDirectory(String directoryName) throws DirectoryException {
101        if (directoryName == null) {
102            return null;
103        }
104        List<String> factoryNames = factoriesByDirectoryName.getFactoriesForDirectory(directoryName);
105        if (factoryNames == null || factoryNames.isEmpty()) {
106            return null;
107        }
108        for (String factoryName : factoryNames) {
109            DirectoryFactory targetFactory = factories.getFactory(factoryName);
110            if (targetFactory != null) {
111                return targetFactory.getDirectory(directoryName);
112            }
113        }
114        return null;
115    }
116
117    public Directory getDirectory(String name, DocumentModel documentContext) throws DirectoryException {
118        if (name == null) {
119            return null;
120        }
121
122        String localDirectoryName = getWaitingLocalDirectoryName(name, getDirectoryConfiguration(documentContext));
123
124        Directory directory = getDirectory(localDirectoryName);
125
126        if (directory == null && !name.equals(localDirectoryName)) {
127            log.debug(String.format("The local directory named '%s' was"
128                    + " not found. Look for the default one named: %s", localDirectoryName, name));
129            directory = getDirectory(name);
130        }
131
132        return directory;
133    }
134
135    private Directory getDirectoryOrFail(String name) throws DirectoryException {
136        return getDirectoryOrFail(name, null);
137    }
138
139    private Directory getDirectoryOrFail(String name, DocumentModel documentContext) throws DirectoryException {
140
141        Directory dir = getDirectory(name, documentContext);
142        if (null == dir) {
143            throw new DirectoryException(String.format("no directory registered with name '%s'", name));
144        }
145        return dir;
146    }
147
148    public List<Directory> getDirectories() throws DirectoryException {
149        List<Directory> directoryList = new ArrayList<Directory>();
150        for (DirectoryFactory factory : factories.getFactories()) {
151            List<Directory> list = factory.getDirectories();
152            directoryList.addAll(list);
153        }
154        return directoryList;
155    }
156
157    @Override
158    public void activate(ComponentContext context) {
159        factories = new DirectoryFactoryRegistry();
160        factoriesByDirectoryName = new DirectoryFactoryMapperRegistry();
161    }
162
163    @Override
164    public void deactivate(ComponentContext context) {
165        for (DirectoryFactory factory : factories.getFactories()) {
166            factory.shutdown();
167        }
168        factories = null;
169        factoriesByDirectoryName = null;
170    }
171
172    @Override
173    public void registerExtension(Extension extension) {
174        Object[] contribs = extension.getContributions();
175        for (Object contrib : contribs) {
176            DirectoryFactoryDescriptor factoryDescriptor = (DirectoryFactoryDescriptor) contrib;
177            String factoryName = factoryDescriptor.getFactoryName();
178            factories.addContribution(new DirectoryFactoryProxy(factoryName));
179            log.debug("registered factory: " + factoryName);
180        }
181    }
182
183    @Override
184    public void unregisterExtension(Extension extension) {
185        Object[] contribs = extension.getContributions();
186        for (Object contrib : contribs) {
187            DirectoryFactoryDescriptor factoryDescriptor = (DirectoryFactoryDescriptor) contrib;
188            String factoryName = factoryDescriptor.getFactoryName();
189            DirectoryFactory factoryToRemove = factories.getFactory(factoryName);
190            if (factoryToRemove == null) {
191                log.warn(String.format("Factory '%s' was not registered", factoryName));
192                return;
193            }
194            factoryToRemove.shutdown();
195            // XXX: do not cleanup mappers, lookup will ignore non-registered
196            // factories anyway
197            factories.removeContribution(factoryToRemove);
198            log.debug("unregistered factory: " + factoryName);
199        }
200    }
201
202    public void registerDirectory(String directoryName, DirectoryFactory factory) {
203        // compatibility code to add otherwise missing memory factory (as it's
204        // not registered via extension points)
205        if (factory instanceof MemoryDirectoryFactory) {
206            factories.addContribution(factory);
207        }
208        DirectoryFactoryMapper contrib = new DirectoryFactoryMapper(directoryName, factory.getName());
209        factoriesByDirectoryName.addContribution(contrib);
210    }
211
212    public void unregisterDirectory(String directoryName, DirectoryFactory factory) {
213        DirectoryFactoryMapper contrib = new DirectoryFactoryMapper(directoryName, factory.getName());
214        factoriesByDirectoryName.removeContribution(contrib);
215    }
216
217    public List<String> getDirectoryNames() throws DirectoryException {
218        List<Directory> directories = getDirectories();
219        List<String> directoryNames = new ArrayList<String>();
220        for (Directory directory : directories) {
221            directoryNames.add(directory.getName());
222        }
223        return directoryNames;
224    }
225
226    public String getDirectorySchema(String directoryName) throws DirectoryException {
227        return getDirectoryOrFail(directoryName).getSchema();
228    }
229
230    public String getDirectoryIdField(String directoryName) throws DirectoryException {
231        return getDirectoryOrFail(directoryName).getIdField();
232    }
233
234    public String getDirectoryPasswordField(String directoryName) throws DirectoryException {
235        return getDirectoryOrFail(directoryName).getPasswordField();
236    }
237
238    public Session open(String directoryName) throws DirectoryException {
239        return getDirectoryOrFail(directoryName).getSession();
240    }
241
242    public Session open(String directoryName, DocumentModel documentContext) throws DirectoryException {
243        return getDirectoryOrFail(directoryName, documentContext).getSession();
244    }
245
246    public String getParentDirectoryName(String directoryName) throws DirectoryException {
247        return getDirectoryOrFail(directoryName).getParentDirectory();
248    }
249
250}