001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.repository;
029
030import org.opencms.configuration.CmsConfigurationException;
031import org.opencms.configuration.CmsParameterConfiguration;
032import org.opencms.file.CmsObject;
033import org.opencms.file.wrapper.I_CmsResourceWrapper;
034import org.opencms.jlan.CmsJlanRepository;
035import org.opencms.jlan.CmsJlanThreadManager;
036import org.opencms.jlan.CmsJlanUsers;
037import org.opencms.main.CmsException;
038import org.opencms.main.CmsLog;
039import org.opencms.main.CmsShell;
040
041import java.util.ArrayList;
042import java.util.HashMap;
043import java.util.LinkedHashMap;
044import java.util.List;
045import java.util.Map;
046
047import org.apache.commons.logging.Log;
048
049import com.google.common.collect.Lists;
050
051/**
052 * The RepositoryManager keeps a list with all configured {@link I_CmsRepository}
053 * and can be used to get a repository by its name.<p>
054 *
055 * The configuration of the repositories is done in the configuration file
056 * <code>opencms-importexport.xml</code>.<p>
057 *
058 * @since 6.2.4
059 */
060public class CmsRepositoryManager {
061
062    /** The logger instance for this class. */
063    private static final Log LOG = CmsLog.getLog(CmsRepositoryManager.class);
064
065    /** Separator between a wrapper class and its parameters. */
066    private static final String WRAPPER_CONFIG_SEPARATOR = ":";
067
068    /** Determines if the repository manager was configured or not. */
069    private boolean m_configured;
070
071    /** Indicates if the configuration is finalized (frozen). */
072    private boolean m_frozen;
073
074    /** Flag indicating that a JLan repository is configured and enabled. */
075    private boolean m_hasJlan;
076
077    /** The JLAN thread manager. */
078    private CmsJlanThreadManager m_jlanThreadManager;
079
080    /** The list of repositories. */
081    private List<I_CmsRepository> m_repositoryList = new ArrayList<I_CmsRepository>();
082
083    /** All initialized repositories, mapped to their name. */
084    private Map<String, I_CmsRepository> m_repositoryMap = new LinkedHashMap<String, I_CmsRepository>();
085
086    /**
087     * Creates a new instance for the resource manager,
088     * will be called by the vfs configuration manager.<p>
089     */
090    public CmsRepositoryManager() {
091
092        if (CmsLog.INIT.isInfoEnabled()) {
093            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_STARTING_REPOSITORY_CONFIG_0));
094        }
095        m_repositoryMap = new HashMap<String, I_CmsRepository>();
096        m_frozen = false;
097        m_configured = true;
098    }
099
100    /**
101     * Creates a new unconfigured instance of the repository manager.<p>
102     *
103     * Is used if there are no repositories configured.<p>
104     *
105     * @param configured determines if the repository manager was configured
106     */
107    public CmsRepositoryManager(boolean configured) {
108
109        this();
110        m_configured = configured;
111        m_frozen = true;
112    }
113
114    /**
115     * Creates a list of resource wrappers from a collection of configuration parameters, for use in configuring repositories.<p>
116     *
117     * @param config the configuration
118     * @param paramName the parameter name
119     * @param log the logger to use for error messages
120     *
121     * @return the list of resource wrappers
122     *
123     * @throws CmsConfigurationException if something goes wrong with reading the configuration
124     */
125    public static List<I_CmsResourceWrapper> createResourceWrappersFromConfiguration(
126        CmsParameterConfiguration config,
127        String paramName,
128        Log log)
129    throws CmsConfigurationException {
130
131        List<I_CmsResourceWrapper> wrapperObjects = Lists.newArrayList();
132        if (config.containsKey(paramName)) {
133            List<String> wrappers = config.getList(paramName);
134            for (String wrapperString : wrappers) {
135                wrapperString = wrapperString.trim();
136                String className;
137                String configString = null;
138                int separatorPos = wrapperString.indexOf(WRAPPER_CONFIG_SEPARATOR);
139                if (separatorPos < 0) {
140                    className = wrapperString;
141                } else {
142                    className = wrapperString.substring(0, separatorPos);
143                    configString = wrapperString.substring(separatorPos + 1);
144                }
145
146                Class<?> nameClazz;
147
148                // init class for wrapper
149                try {
150                    nameClazz = Class.forName(className);
151                } catch (ClassNotFoundException e) {
152                    log.error(Messages.get().getBundle().key(Messages.LOG_WRAPPER_CLASS_NOT_FOUND_1, className), e);
153                    wrapperObjects.clear();
154                    break;
155                }
156
157                I_CmsResourceWrapper wrapper;
158                try {
159                    wrapper = (I_CmsResourceWrapper)nameClazz.newInstance();
160                    if (configString != null) {
161                        wrapper.configure(configString);
162                    }
163                } catch (InstantiationException e) {
164                    throw new CmsConfigurationException(
165                        Messages.get().container(Messages.ERR_INVALID_WRAPPER_NAME_1, wrapperString));
166                } catch (IllegalAccessException e) {
167                    throw new CmsConfigurationException(
168                        Messages.get().container(Messages.ERR_INVALID_WRAPPER_NAME_1, wrapperString));
169                } catch (ClassCastException e) {
170                    throw new CmsConfigurationException(
171                        Messages.get().container(Messages.ERR_INVALID_WRAPPER_NAME_1, wrapperString));
172                }
173
174                wrapperObjects.add(wrapper);
175
176                if (CmsLog.INIT.isInfoEnabled()) {
177                    CmsLog.INIT.info(
178                        Messages.get().getBundle().key(Messages.INIT_ADD_WRAPPER_1, wrapper.getClass().getName()));
179                }
180            }
181        }
182        return wrapperObjects;
183    }
184
185    /**
186     * Adds a new configured repository.<p>
187     *
188     * @param repository the repository to add
189     *
190     * @throws CmsConfigurationException in case the resource manager configuration is already initialized
191     */
192    public void addRepositoryClass(I_CmsRepository repository) throws CmsConfigurationException {
193
194        // check if new repositories can still be added
195        if (m_frozen) {
196            throw new CmsConfigurationException(Messages.get().container(Messages.ERR_NO_CONFIG_AFTER_STARTUP_0));
197        }
198        m_repositoryList.add(repository);
199    }
200
201    /**
202     * Gets the additional infos for the user who just logged in which is required for the repositories to work.<p>
203     *
204     * @param userName the name of the logged in user
205     * @param password the password of the logged in user
206     *
207     * @return the additional info entries which should be written to the user
208     */
209    public Map<String, Object> getAdditionalInfoForLogin(String userName, String password) {
210
211        Map<String, Object> additionalInfos = new HashMap<String, Object>();
212        if (m_hasJlan) {
213            try {
214                additionalInfos.put(CmsJlanUsers.JLAN_HASH, CmsJlanUsers.hashPassword(password));
215            } catch (Exception e) {
216                LOG.error(e.getLocalizedMessage(), e);
217            }
218        }
219        return additionalInfos;
220    }
221
222    /**
223     * Returns the repositories.<p>
224     *
225     * @return the repositories
226     */
227    public List<I_CmsRepository> getRepositories() {
228
229        return new ArrayList<I_CmsRepository>(m_repositoryMap.values());
230    }
231
232    /**
233     * Gets a list of the repositories for the given superclass.<p>
234     *
235     * @param cls the superclass
236     *
237     * @return the repositories for whose classes the given class is a superclass
238     */
239    @SuppressWarnings("unchecked")
240    public <REPO extends I_CmsRepository> List<REPO> getRepositories(Class<REPO> cls) {
241
242        List<REPO> result = new ArrayList<REPO>();
243        for (I_CmsRepository repo : m_repositoryMap.values()) {
244            if (cls.isInstance(repo)) {
245                result.add((REPO)repo);
246            }
247        }
248        return result;
249    }
250
251    /**
252     * Returns the repository with the given name.<p>
253     *
254     * @param name the name of the repository
255     *
256     * @return the repository configured for that name
257     */
258    public I_CmsRepository getRepository(String name) {
259
260        return m_repositoryMap.get(name);
261    }
262
263    /**
264     * Gets a repository by name, but only if its class is a subclass of the class passed as a parameter.<p>
265     * Otherwise, null will be returned.<p>
266     *
267     * @param name the repository name
268     * @param cls the class used to filter repositories
269     *
270     * @return the repository with the given name, or null
271     */
272    @SuppressWarnings("unchecked")
273    public <REPO extends I_CmsRepository> REPO getRepository(String name, Class<REPO> cls) {
274
275        I_CmsRepository repo = getRepository(name);
276        if (repo == null) {
277            return null;
278        }
279        if (cls.isInstance(repo)) {
280            return (REPO)repo;
281        } else {
282            return null;
283        }
284
285    }
286
287    /**
288     * Initializes a configuration after all parameters have been added.<p>
289     *
290     * @throws CmsConfigurationException if something goes wrong
291     */
292    public void initConfiguration() throws CmsConfigurationException {
293
294        for (I_CmsRepository rep : m_repositoryList) {
295            if (CmsLog.INIT.isInfoEnabled()) {
296                CmsLog.INIT.info(
297                    Messages.get().getBundle().key(
298                        Messages.INIT_ADD_REPOSITORY_2,
299                        rep.getClass().getName(),
300                        rep.getName()));
301            }
302            rep.initConfiguration();
303            m_repositoryMap.put(rep.getName(), rep);
304        }
305        m_frozen = true;
306
307        if (CmsLog.INIT.isInfoEnabled()) {
308            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_REPOSITORY_CONFIG_FINISHED_0));
309        }
310
311    }
312
313    /**
314     * Initializes repositories using an admin CMS object.<p>
315     *
316     * @param cms the CMS object with admin privileges
317     */
318    public void initializeCms(CmsObject cms) {
319
320        List<String> toRemove = new ArrayList<String>();
321        // Repositories which can't be fully initialized need to be removed.
322        for (I_CmsRepository repository : m_repositoryMap.values()) {
323            String repoName = repository.getName();
324            try {
325                repository.initializeCms(cms);
326            } catch (CmsException e) {
327                LOG.warn("Could not fully initialize repository " + repoName, e);
328                toRemove.add(repoName);
329            }
330        }
331        for (String removeRepo : toRemove) {
332            m_repositoryMap.remove(removeRepo);
333        }
334        m_hasJlan = !getRepositories(CmsJlanRepository.class).isEmpty();
335        if (!CmsShell.isJlanDisabled() && m_hasJlan) {
336            CmsJlanUsers.setAdminCms(cms);
337            m_jlanThreadManager = new CmsJlanThreadManager();
338            m_jlanThreadManager.start();
339        }
340
341    }
342
343    /**
344     * Returns the configured.<p>
345     *
346     * @return the configured
347     */
348    public boolean isConfigured() {
349
350        return m_configured;
351    }
352
353    /**
354     * Shuts down the repository manager.<p>
355     */
356    public void shutDown() {
357
358        if (m_jlanThreadManager != null) {
359            m_jlanThreadManager.stop();
360        }
361    }
362
363}