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