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.configuration;
029
030import org.opencms.file.CmsObject;
031import org.opencms.i18n.CmsEncoder;
032import org.opencms.main.CmsLog;
033import org.opencms.util.CmsFileUtil;
034import org.opencms.util.CmsStringUtil;
035import org.opencms.xml.CmsXmlEntityResolver;
036import org.opencms.xml.CmsXmlErrorHandler;
037
038import java.io.ByteArrayInputStream;
039import java.io.ByteArrayOutputStream;
040import java.io.File;
041import java.io.FileOutputStream;
042import java.io.IOException;
043import java.io.OutputStream;
044import java.io.PrintStream;
045import java.net.InetAddress;
046import java.net.URL;
047import java.text.SimpleDateFormat;
048import java.util.ArrayList;
049import java.util.Date;
050import java.util.Iterator;
051import java.util.List;
052
053import javax.xml.parsers.ParserConfigurationException;
054import javax.xml.parsers.SAXParserFactory;
055import javax.xml.transform.OutputKeys;
056import javax.xml.transform.Result;
057import javax.xml.transform.Source;
058import javax.xml.transform.Transformer;
059import javax.xml.transform.TransformerException;
060import javax.xml.transform.TransformerFactory;
061import javax.xml.transform.sax.SAXSource;
062import javax.xml.transform.stream.StreamResult;
063import javax.xml.transform.stream.StreamSource;
064
065import org.apache.commons.digester3.Digester;
066import org.apache.commons.logging.Log;
067
068import org.dom4j.Document;
069import org.dom4j.DocumentHelper;
070import org.dom4j.Element;
071import org.dom4j.dom.DOMDocumentType;
072import org.dom4j.io.OutputFormat;
073import org.dom4j.io.XMLWriter;
074import org.xml.sax.InputSource;
075import org.xml.sax.SAXException;
076import org.xml.sax.XMLReader;
077
078/**
079 * Configuration manager for digesting the OpenCms XML configuration.<p>
080 *
081 * Reads the individual configuration class nodes first and creaes new
082 * instances of the "base" configuration classes.<p>
083 *
084 * @since 6.0.0
085 */
086public class CmsConfigurationManager implements I_CmsXmlConfiguration {
087
088    /** The location of the OpenCms configuration DTD if the default prefix is the system ID. */
089    public static final String DEFAULT_DTD_LOCATION = "org/opencms/configuration/";
090
091    /** Location of the optional XSLT file used to transform the configuration. */
092    public static final String DEFAULT_XSLT_FILENAME = "opencms-configuration.xslt";
093
094    /** The default prefix for the OpenCms configuration DTD. */
095    public static final String DEFAULT_DTD_PREFIX = "http://www.opencms.org/dtd/6.0/";
096
097    /** The name of the default XML file for this configuration. */
098    public static final String DEFAULT_XML_FILE_NAME = "opencms.xml";
099
100    /** The name of the DTD file for this configuration. */
101    public static final String DTD_FILE_NAME = "opencms-configuration.dtd";
102
103    /** The "opencms" root node of the XML configuration. */
104    public static final String N_ROOT = "opencms";
105
106    /** Postfix for original configuration files. */
107    public static final String POSTFIX_ORI = ".ori";
108
109    /** The config node. */
110    protected static final String N_CONFIG = "config";
111
112    /** The configurations node. */
113    protected static final String N_CONFIGURATION = "configuration";
114
115    /** Date format for the backup file time prefix. */
116    private static final SimpleDateFormat BACKUP_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_");
117
118    /** The log object for this class. */
119    private static final Log LOG = CmsLog.getLog(CmsConfigurationManager.class);
120
121    /** The number of days to keep old backups for. */
122    private static final long MAX_BACKUP_DAYS = 15;
123
124    /** The folder where to store the backup files of the configuration. */
125    private File m_backupFolder;
126
127    /** The base folder where the configuration files are located. */
128    private File m_baseFolder;
129
130    /** The initialized configuration classes. */
131    private List<I_CmsXmlConfiguration> m_configurations;
132
133    /** The digester for reading the XML configuration. */
134    private Digester m_digester;
135
136    /** The configuration based on <code>opencms.properties</code>. */
137    private CmsParameterConfiguration m_propertyConfiguration;
138
139    /** The admin CmsObject. */
140    private CmsObject m_adminCms;
141
142    /**
143     * Creates a new OpenCms configuration manager.<p>
144     *
145     * @param baseFolder base folder where XML configurations to load are located
146     */
147    public CmsConfigurationManager(String baseFolder) {
148
149        m_baseFolder = new File(baseFolder);
150        if (!m_baseFolder.exists()) {
151            if (LOG.isErrorEnabled()) {
152                LOG.error(
153                    Messages.get().getBundle().key(
154                        Messages.LOG_INVALID_CONFIG_BASE_FOLDER_1,
155                        m_baseFolder.getAbsolutePath()));
156            }
157        }
158        m_backupFolder = new File(m_baseFolder.getAbsolutePath() + File.separatorChar + "backup");
159        if (!m_backupFolder.exists()) {
160            if (LOG.isDebugEnabled()) {
161                LOG.debug(
162                    Messages.get().getBundle().key(
163                        Messages.LOG_CREATE_CONFIG_BKP_FOLDER_1,
164                        m_backupFolder.getAbsolutePath()));
165            }
166            m_backupFolder.mkdirs();
167        }
168        if (LOG.isDebugEnabled()) {
169            LOG.debug(
170                Messages.get().getBundle().key(Messages.LOG_CONFIG_BASE_FOLDER_1, m_baseFolder.getAbsolutePath()));
171            LOG.debug(
172                Messages.get().getBundle().key(Messages.LOG_CONFIG_BKP_FOLDER_1, m_backupFolder.getAbsolutePath()));
173        }
174        cacheDtdSystemId(this);
175        m_configurations = new ArrayList<I_CmsXmlConfiguration>();
176    }
177
178    /**
179     * Adds a configuration object to the configuration manager.<p>
180     *
181     * @param configuration the configuration to add
182     */
183    public void addConfiguration(I_CmsXmlConfiguration configuration) {
184
185        if (LOG.isDebugEnabled()) {
186            LOG.debug(Messages.get().getBundle().key(Messages.LOG_ADD_CONFIG_1, configuration));
187        }
188        m_configurations.add(configuration);
189        cacheDtdSystemId(configuration);
190    }
191
192    /**
193     * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String)
194     */
195    public void addConfigurationParameter(String paramName, String paramValue) {
196
197        // noop, this configuration has no additional parameters
198    }
199
200    /**
201     * @see org.opencms.configuration.I_CmsXmlConfiguration#addXmlDigesterRules(org.apache.commons.digester3.Digester)
202     */
203    public void addXmlDigesterRules(Digester digester) {
204
205        // add rule for <configuration> node
206        digester.addObjectCreate(
207            "*/" + N_CONFIGURATION + "/" + N_CONFIG,
208            CmsConfigurationException.class.getName(),
209            I_CmsXmlConfiguration.A_CLASS);
210        digester.addSetNext("*/" + N_CONFIGURATION + "/" + N_CONFIG, "addConfiguration");
211    }
212
213    /**
214     * @see org.opencms.configuration.I_CmsXmlConfiguration#generateXml(org.dom4j.Element)
215     */
216    public Element generateXml(Element parent) {
217
218        // add the <configuration> node
219        Element configurationElement = parent.addElement(N_CONFIGURATION);
220        for (int i = 0; i < m_configurations.size(); i++) {
221            // append the individual configuration
222            I_CmsXmlConfiguration configuration = m_configurations.get(i);
223            configurationElement.addElement(N_CONFIG).addAttribute(
224                I_CmsXmlConfiguration.A_CLASS,
225                configuration.getClass().getName());
226        }
227        return parent;
228    }
229
230    /**
231     * Creates the XML document build from the provided configuration.<p>
232     *
233     * @param configuration the configuration to build the XML for
234     * @return the XML document build from the provided configuration
235     */
236    public Document generateXml(I_CmsXmlConfiguration configuration) {
237
238        // create a new document
239        Document result = DocumentHelper.createDocument();
240
241        // set the document type
242        DOMDocumentType docType = new DOMDocumentType();
243        docType.setElementName(N_ROOT);
244        docType.setSystemID(configuration.getDtdUrlPrefix() + configuration.getDtdFilename());
245        result.setDocType(docType);
246
247        Element root = result.addElement(N_ROOT);
248        // start the XML generation
249        configuration.generateXml(root);
250
251        // return the resulting document
252        return result;
253    }
254
255    /**
256     * Returns the backup folder.<p>
257     *
258     * @return the backup folder
259     */
260    public File getBackupFolder() {
261
262        return m_backupFolder;
263    }
264
265    /**
266     * Returns the properties read from <code>opencms.properties</code>.<p>
267     *
268     * @see #setConfiguration(CmsParameterConfiguration)
269     * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration()
270     */
271    public CmsParameterConfiguration getConfiguration() {
272
273        return m_propertyConfiguration;
274    }
275
276    /**
277     * Returns a specific configuration from the list of initialized configurations.<p>
278     *
279     * @param clazz the configuration class that should be returned
280     * @return the initialized configuration class instance, or <code>null</code> if this is not found
281     */
282    public I_CmsXmlConfiguration getConfiguration(Class<?> clazz) {
283
284        for (int i = 0; i < m_configurations.size(); i++) {
285            I_CmsXmlConfiguration configuration = m_configurations.get(i);
286            if (clazz.equals(configuration.getClass())) {
287                return configuration;
288            }
289        }
290        return null;
291    }
292
293    /**
294     * Returns the list of all initialized configurations.<p>
295     *
296     * @return the list of all initialized configurations
297     */
298    public List<I_CmsXmlConfiguration> getConfigurations() {
299
300        return m_configurations;
301    }
302
303    /**
304     * @see org.opencms.configuration.I_CmsXmlConfiguration#getDtdFilename()
305     */
306    public String getDtdFilename() {
307
308        return DTD_FILE_NAME;
309    }
310
311    /**
312     * @see org.opencms.configuration.I_CmsXmlConfiguration#getDtdSystemLocation()
313     */
314    public String getDtdSystemLocation() {
315
316        return DEFAULT_DTD_LOCATION;
317    }
318
319    /**
320     * @see org.opencms.configuration.I_CmsXmlConfiguration#getDtdUrlPrefix()
321     */
322    public String getDtdUrlPrefix() {
323
324        return DEFAULT_DTD_PREFIX;
325    }
326
327    /**
328     * @see org.opencms.configuration.I_CmsXmlConfiguration#getXmlFileName()
329     */
330    public String getXmlFileName() {
331
332        return DEFAULT_XML_FILE_NAME;
333    }
334
335    /**
336     * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration()
337     */
338    public void initConfiguration() {
339
340        // does not need to be initialized
341        if (LOG.isDebugEnabled()) {
342            LOG.debug(Messages.get().getBundle().key(Messages.LOG_INIT_CONFIGURATION_1, this));
343        }
344    }
345
346    /**
347     * Loads the OpenCms configuration from the given XML file.<p>
348     *
349     * @throws SAXException in case of XML parse errors
350     * @throws IOException in case of file IO errors
351     */
352    public void loadXmlConfiguration() throws SAXException, IOException {
353
354        URL baseUrl = m_baseFolder.toURI().toURL();
355        if (LOG.isDebugEnabled()) {
356            LOG.debug(Messages.get().getBundle().key(Messages.LOG_BASE_URL_1, baseUrl));
357        }
358
359        // first load the base configuration
360        loadXmlConfiguration(baseUrl, this);
361
362        // now iterate all sub-configurations
363        Iterator<I_CmsXmlConfiguration> i = m_configurations.iterator();
364        while (i.hasNext()) {
365            loadXmlConfiguration(baseUrl, i.next());
366        }
367
368        // remove the old backups
369        removeOldBackups(MAX_BACKUP_DAYS);
370    }
371
372    /**
373     * Sets the admin CmsObject.<p>
374     * 
375     * @param cms the admin CmsObject
376     */
377    public void setAdminCms(CmsObject cms) {
378
379        if (m_adminCms != null) {
380            LOG.error("Can not set admin CmsObject of configuration manager because it is already set.");
381            return;
382        }
383        for (I_CmsXmlConfiguration config : m_configurations) {
384            if (config instanceof I_CmsXmlConfigurationWithUpdateHandler) {
385                ((I_CmsXmlConfigurationWithUpdateHandler)config).setCmsObject(cms);
386            }
387        }
388        m_adminCms = cms;
389    }
390
391    /**
392     * Sets the configuration read from the <code>opencms.properties</code>.<p>
393     *
394     * @param propertyConfiguration the configuration read from the <code>opencms.properties</code>
395     *
396     * @see #getConfiguration()
397     */
398    public void setConfiguration(CmsParameterConfiguration propertyConfiguration) {
399
400        m_propertyConfiguration = propertyConfiguration;
401    }
402
403    /**
404     * Writes the XML configuration for the provided configuration instance.<p>
405     *
406     * @param clazz the configuration class to write the XML for
407     * @throws IOException in case of I/O errors while writing
408     * @throws CmsConfigurationException if the given class is not a valid configuration class
409     */
410    public void writeConfiguration(Class<?> clazz) throws IOException, CmsConfigurationException {
411
412        I_CmsXmlConfiguration configuration = getConfiguration(clazz);
413        if (configuration == null) {
414            throw new CmsConfigurationException(
415                Messages.get().container(Messages.ERR_CONFIG_WITH_UNKNOWN_CLASS_1, clazz.getName()));
416        }
417
418        // generate the file URL for the XML input
419        File file = new File(m_baseFolder, configuration.getXmlFileName());
420        if (LOG.isDebugEnabled()) {
421            LOG.debug(Messages.get().getBundle().key(Messages.LOG_WRITE_CONFIG_XMLFILE_1, file.getAbsolutePath()));
422        }
423
424        // generate the XML document
425        Document config = generateXml(configuration);
426
427        // output the document
428        XMLWriter writer = null;
429        OutputFormat format = OutputFormat.createPrettyPrint();
430        format.setIndentSize(4);
431        format.setTrimText(false);
432        format.setEncoding(CmsEncoder.ENCODING_UTF_8);
433
434        OutputStream out = null;
435        try {
436            out = new FileOutputStream(file);
437            writer = new XMLWriter(out, format);
438            writer.write(config);
439            writer.flush();
440        } finally {
441            if (writer != null) {
442                writer.close();
443            }
444            if (out != null) {
445                out.close();
446            }
447        }
448
449        if (configuration instanceof I_CmsXmlConfigurationWithUpdateHandler) {
450            try {
451                LOG.info("Running configuration update handler for " + configuration.getClass().getName());
452                ((I_CmsXmlConfigurationWithUpdateHandler)configuration).handleUpdate();
453                LOG.info("Finished configuration update handler for " + configuration.getClass().getName());
454            } catch (Exception e) {
455                LOG.error(e.getLocalizedMessage(), e);
456            }
457        }
458
459        if (LOG.isInfoEnabled()) {
460            LOG.info(
461                Messages.get().getBundle().key(
462                    Messages.LOG_WRITE_CONFIG_SUCCESS_2,
463                    file.getAbsolutePath(),
464                    configuration.getClass().getName()));
465        }
466
467    }
468
469    /**
470     * Gets the path to the XSLT transformation file that should be used for the configuration.<p>
471     *
472     * @return the path to the XSLT transformation
473     */
474    String getTransformationPath() {
475
476        String path = System.getProperty("opencms.config.transform");
477        if (path == null) {
478            path = CmsStringUtil.joinPaths(m_baseFolder.getAbsolutePath(), DEFAULT_XSLT_FILENAME);
479        }
480        return path;
481    }
482
483    /**
484     * Checks if an XSLT transformation file is available.<p>
485     *
486     * @return true if an XSLT transformation file is available
487     */
488    boolean hasTransformation() {
489
490        String transformationPath = getTransformationPath();
491        boolean result = (transformationPath != null) && new File(transformationPath).exists();
492        if (result) {
493            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_XSLT_CONFIG_ENABLED_1, transformationPath));
494        } else {
495            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_XSLT_CONFIG_DISABLED_0));
496        }
497        return result;
498    }
499
500    /**
501     * Transforms the given configuration using an XSLT transformation.<p>
502     *
503     * @param url the URL of the base folder
504     * @param config the configuration object
505     *
506     * @return the InputSource to feed the configuration digester
507     *
508     * @throws TransformerException if the transformation fails
509     * @throws IOException if an error occurs while reading the configuration or transformation
510     * @throws SAXException if parsing the configuration file fails
511     * @throws ParserConfigurationException if something goes wrong with configuring the parser
512     */
513    InputSource transformConfiguration(URL url, I_CmsXmlConfiguration config)
514    throws TransformerException, IOException, SAXException, ParserConfigurationException {
515
516        String configPath = CmsStringUtil.joinPaths(url.getFile(), config.getXmlFileName());
517        String transformPath = getTransformationPath();
518        TransformerFactory factory = TransformerFactory.newInstance();
519
520        ByteArrayOutputStream errBaos = new ByteArrayOutputStream();
521        PrintStream oldErr = System.err;
522        System.setErr(new PrintStream(errBaos));
523        try {
524            LOG.info("Transforming '" + configPath + "' with transformation '" + transformPath + "'");
525            Transformer transformer = factory.newTransformer(new StreamSource(new File(transformPath)));
526            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
527            transformer.setParameter("file", config.getXmlFileName());
528            InetAddress localhost = InetAddress.getLocalHost();
529            transformer.setParameter("hostName", localhost.getHostName());
530            transformer.setParameter("canonicalHostName", localhost.getCanonicalHostName());
531            transformer.setParameter("hostAddress", localhost.getHostAddress());
532            // use a SAXSource here because we need to set the correct entity resolver to prevent errors
533            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
534            parserFactory.setNamespaceAware(true);
535            parserFactory.setValidating(false); // Turn off validation
536            XMLReader reader = parserFactory.newSAXParser().getXMLReader();
537            reader.setEntityResolver(new CmsXmlEntityResolver(null));
538            Source source = new SAXSource(reader, new InputSource(configPath));
539
540            ByteArrayOutputStream baos = new ByteArrayOutputStream();
541            Result target = new StreamResult(baos);
542
543            transformer.transform(source, target);
544
545            byte[] transformedConfig = baos.toByteArray();
546            // We can't set the doctype dynamically from inside the XSLT transform using XSLT 1.0, and XSLT 2.0
547            // isn't supported by the standard implementation in the JDK. So we do some macro replacement after the
548            // transformation.
549            String transformedConfigStr = new String(transformedConfig, "UTF-8").replaceFirst(
550                "@dtd@",
551                config.getDtdUrlPrefix() + config.getDtdFilename());
552            if (LOG.isDebugEnabled()) {
553                LOG.debug("");
554                LOG.debug(
555                    "=================== Transformation result for config file '" + config.getXmlFileName() + "':");
556                LOG.debug(transformedConfigStr);
557            }
558            return new InputSource(new ByteArrayInputStream(transformedConfigStr.getBytes("UTF-8")));
559        } finally {
560            System.setErr(oldErr);
561            byte[] errorBytes = errBaos.toByteArray();
562            if (errorBytes.length > 0) {
563                LOG.warn(new String(errorBytes, "UTF-8"));
564            }
565        }
566    }
567
568    /**
569     * Creates a backup of the given XML configurations input file.<p>
570     *
571     * @param configuration the configuration for which the input file should be backed up
572     */
573    private void backupXmlConfiguration(I_CmsXmlConfiguration configuration) {
574
575        String fromName = m_baseFolder.getAbsolutePath() + File.separatorChar + configuration.getXmlFileName();
576        String toDatePrefix = BACKUP_DATE_FORMAT.format(new Date());
577        String toName = m_backupFolder.getAbsolutePath()
578            + File.separatorChar
579            + toDatePrefix
580            + configuration.getXmlFileName();
581
582        if (LOG.isDebugEnabled()) {
583            LOG.debug(Messages.get().getBundle().key(Messages.LOG_CREATE_CONFIG_BKP_2, fromName, toName));
584        }
585
586        try {
587            CmsFileUtil.copy(fromName, toName);
588        } catch (IOException e) {
589            LOG.error(Messages.get().getBundle().key(Messages.LOG_CREATE_CONFIG_BKP_FAILURE_1, toName), e);
590        }
591    }
592
593    /**
594     * Adds a new DTD system id prefix mapping for internal resolution of external URLs.<p>
595     *
596     * @param configuration the configuration to add the mapping from
597     */
598    private void cacheDtdSystemId(I_CmsXmlConfiguration configuration) {
599
600        if (configuration.getDtdSystemLocation() != null) {
601            try {
602                String file = CmsFileUtil.readFile(
603                    configuration.getDtdSystemLocation() + configuration.getDtdFilename(),
604                    CmsEncoder.ENCODING_UTF_8);
605                CmsXmlEntityResolver.cacheSystemId(
606                    configuration.getDtdUrlPrefix() + configuration.getDtdFilename(),
607                    file.getBytes(CmsEncoder.ENCODING_UTF_8));
608                if (LOG.isDebugEnabled()) {
609                    LOG.debug(
610                        Messages.get().getBundle().key(
611                            Messages.LOG_CACHE_DTD_SYSTEM_ID_1,
612                            configuration.getDtdUrlPrefix()
613                                + configuration.getDtdFilename()
614                                + " --> "
615                                + configuration.getDtdSystemLocation()
616                                + configuration.getDtdFilename()));
617                }
618            } catch (IOException e) {
619                LOG.error(
620                    Messages.get().getBundle().key(
621                        Messages.LOG_CACHE_DTD_SYSTEM_ID_FAILURE_1,
622                        configuration.getDtdSystemLocation() + configuration.getDtdFilename()),
623                    e);
624            }
625        }
626    }
627
628    /**
629     * Loads the OpenCms configuration from the given XML URL.<p>
630     *
631     * @param url the base URL of the XML configuration to load
632     * @param configuration the configuration to load
633     * @throws SAXException in case of XML parse errors
634     * @throws IOException in case of file IO errors
635     */
636    private void loadXmlConfiguration(URL url, I_CmsXmlConfiguration configuration) throws SAXException, IOException {
637
638        // generate the file URL for the XML input
639        URL fileUrl = new URL(url, configuration.getXmlFileName());
640        CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_LOAD_CONFIG_XMLFILE_1, fileUrl));
641        // Check transformation rule here so we have the XML file / XSLT file log output together
642        boolean hasTransformation = hasTransformation();
643
644        // create a backup of the configuration
645        backupXmlConfiguration(configuration);
646
647        // instantiate Digester and enable XML validation
648        m_digester = new Digester();
649        m_digester.setUseContextClassLoader(true);
650        //TODO: For this to work with transformed configurations, we need to add the correct DOCTYPE declarations to the transformed files
651        m_digester.setValidating(true);
652        m_digester.setEntityResolver(new CmsXmlEntityResolver(null));
653        m_digester.setRuleNamespaceURI(null);
654        m_digester.setErrorHandler(new CmsXmlErrorHandler(fileUrl.getFile()));
655
656        // add this class to the Digester
657        m_digester.push(configuration);
658
659        configuration.addXmlDigesterRules(m_digester);
660
661        InputSource inputSource = null;
662        if (hasTransformation) {
663            try {
664                inputSource = transformConfiguration(url, configuration);
665            } catch (Exception e) {
666                LOG.error("Error transforming " + configuration.getXmlFileName() + ": " + e.getLocalizedMessage(), e);
667            }
668        }
669        if (inputSource == null) {
670            inputSource = new InputSource(fileUrl.openStream());
671        }
672        // start the parsing process
673        m_digester.parse(inputSource);
674    }
675
676    /**
677     * Removes all backups that are older then the given number of days.<p>
678     *
679     * @param daysToKeep the days to keep the backups for
680     */
681    private void removeOldBackups(long daysToKeep) {
682
683        long maxAge = (System.currentTimeMillis() - (daysToKeep * 24 * 60 * 60 * 1000));
684        File[] files = m_backupFolder.listFiles();
685        for (int i = 0; i < files.length; i++) {
686            File file = files[i];
687            long lastMod = file.lastModified();
688            if ((lastMod < maxAge) & (!file.getAbsolutePath().endsWith(CmsConfigurationManager.POSTFIX_ORI))) {
689                file.delete();
690                if (LOG.isDebugEnabled()) {
691                    LOG.debug(
692                        Messages.get().getBundle().key(Messages.LOG_REMOVE_CONFIG_FILE_1, file.getAbsolutePath()));
693                }
694            }
695        }
696    }
697}