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.module;
029
030import org.opencms.configuration.CmsConfigurationException;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsProject;
033import org.opencms.file.types.I_CmsResourceType;
034import org.opencms.i18n.CmsMessageContainer;
035import org.opencms.importexport.CmsExport;
036import org.opencms.importexport.CmsExportParameters;
037import org.opencms.importexport.CmsImport;
038import org.opencms.importexport.CmsImportExportException;
039import org.opencms.importexport.CmsImportExportManager;
040import org.opencms.importexport.CmsImportHelper;
041import org.opencms.importexport.CmsImportParameters;
042import org.opencms.importexport.I_CmsImportExportHandler;
043import org.opencms.main.CmsException;
044import org.opencms.main.CmsLog;
045import org.opencms.main.CmsShell;
046import org.opencms.main.CmsSystemInfo;
047import org.opencms.main.OpenCms;
048import org.opencms.module.CmsModuleXmlHandler.XmlWriteMode;
049import org.opencms.report.CmsHtmlReport;
050import org.opencms.report.I_CmsReport;
051import org.opencms.security.CmsRole;
052import org.opencms.security.CmsRoleViolationException;
053import org.opencms.security.CmsSecurityException;
054import org.opencms.util.CmsFileUtil;
055import org.opencms.util.CmsMacroResolver;
056import org.opencms.util.CmsStringUtil;
057import org.opencms.xml.CmsXmlErrorHandler;
058import org.opencms.xml.CmsXmlException;
059
060import java.io.ByteArrayInputStream;
061import java.io.ByteArrayOutputStream;
062import java.io.File;
063import java.io.FileInputStream;
064import java.io.IOException;
065import java.io.InputStream;
066import java.io.PrintStream;
067import java.util.ArrayList;
068import java.util.Arrays;
069import java.util.Collections;
070import java.util.List;
071import java.util.zip.ZipEntry;
072import java.util.zip.ZipFile;
073
074import org.apache.commons.digester3.Digester;
075import org.apache.commons.digester3.Rule;
076import org.apache.commons.logging.Log;
077
078import org.dom4j.Document;
079import org.dom4j.Element;
080import org.xml.sax.SAXException;
081
082/**
083 * Import/export handler implementation for Cms modules.<p>
084 *
085 * @since 6.0.0
086 */
087public class CmsModuleImportExportHandler implements I_CmsImportExportHandler {
088
089    /** The log object for this class. */
090    private static final Log LOG = CmsLog.getLog(CmsModuleImportExportHandler.class);
091
092    /** The VFS resources to be exported additionally with the module.<p> */
093    private List<String> m_additionalResources;
094
095    /** The description of this import/export handler.<p> */
096    private String m_description;
097
098    /** The name of the export file in the real file system.<p> */
099    private String m_fileName;
100
101    /** The module imported with the digester. */
102    private CmsModule m_importedModule;
103
104    /** The import parameters. */
105    private CmsImportParameters m_importParams;
106
107    /** The (package) name of the module to be exported.<p> */
108    private String m_moduleName;
109
110    /**
111     * Creates a new Cms module import/export handler.<p>
112     */
113    public CmsModuleImportExportHandler() {
114
115        super();
116        m_description = org.opencms.importexport.Messages.get().getBundle().key(
117            org.opencms.importexport.Messages.GUI_CMSIMPORTHANDLER_DEFAULT_DESC_0);
118    }
119
120    /**
121     * Gets the module export handler containing all resources used in the module export.<p>
122     * @param cms the {@link CmsObject} used by to set up the handler. The object's site root might be adjusted to the import site of the module.
123     * @param module The module to export
124     * @param handlerDescription A description of the export handler, shown when the export thread using the handler runs.
125     * @return CmsModuleImportExportHandler with all module resources
126     */
127    public static CmsModuleImportExportHandler getExportHandler(
128        CmsObject cms,
129        final CmsModule module,
130        final String handlerDescription) {
131
132        // check if all resources are valid
133        List<String> resListCopy = new ArrayList<String>();
134
135        String moduleName = module.getName();
136
137        try {
138            cms = OpenCms.initCmsObject(cms);
139            String importSite = module.getSite();
140            if (!CmsStringUtil.isEmptyOrWhitespaceOnly(importSite)) {
141                cms.getRequestContext().setSiteRoot(importSite);
142            }
143        } catch (CmsException e) {
144            // should never happen
145            LOG.error(e.getLocalizedMessage(), e);
146        }
147        try {
148            resListCopy = CmsModule.calculateModuleResourceNames(cms, module);
149        } catch (CmsException e) {
150            // some resource did not exist / could not be read
151            if (LOG.isInfoEnabled()) {
152                LOG.warn(Messages.get().getBundle().key(Messages.ERR_READ_MODULE_RESOURCES_1, module.getName()), e);
153            }
154        }
155        resListCopy = CmsFileUtil.removeRedundancies(resListCopy);
156        String[] resources = new String[resListCopy.size()];
157
158        for (int i = 0; i < resListCopy.size(); i++) {
159            resources[i] = resListCopy.get(i);
160        }
161
162        String filename = OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebInf(
163            OpenCms.getSystemInfo().getPackagesRfsPath()
164                + CmsSystemInfo.FOLDER_MODULES
165                + moduleName
166                + "_"
167                + "%(version)");
168
169        CmsModuleImportExportHandler moduleExportHandler = new CmsModuleImportExportHandler();
170        moduleExportHandler.setFileName(filename);
171        moduleExportHandler.setModuleName(moduleName.replace('\\', '/'));
172        moduleExportHandler.setAdditionalResources(resources);
173        moduleExportHandler.setDescription(handlerDescription);
174
175        return moduleExportHandler;
176    }
177
178    /**
179     * Reads a module object from an external file source.<p>
180     *
181     * @param importResource the name of the input source
182     *
183     * @return the imported module
184     *
185     * @throws CmsConfigurationException if the module could not be imported
186     */
187    public static CmsModule readModuleFromImport(String importResource) throws CmsConfigurationException {
188
189        // instantiate Digester and enable XML validation
190        Digester digester = new Digester();
191        digester.setUseContextClassLoader(true);
192        digester.setValidating(false);
193        digester.setRuleNamespaceURI(null);
194        digester.setErrorHandler(new CmsXmlErrorHandler(importResource));
195
196        // add this class to the Digester
197        CmsModuleImportExportHandler handler = new CmsModuleImportExportHandler();
198        final String[] version = new String[] {null};
199        digester.push(handler);
200
201        digester.addRule("*/export_version", new Rule() {
202
203            @Override
204            public void body(String namespace, String name, String text) throws Exception {
205
206                version[0] = text.trim();
207            }
208
209        });
210        CmsModuleXmlHandler.addXmlDigesterRules(digester);
211
212        InputStream stream = null;
213        ZipFile importZip = null;
214
215        try {
216            File file = new File(importResource);
217            if (!file.exists()) {
218                throw new IOException("readModuleFromImport: Path '" + importResource + "' does not exist.");
219            }
220            if (file.isFile()) {
221                importZip = new ZipFile(importResource);
222                ZipEntry entry = importZip.getEntry(CmsImportExportManager.EXPORT_MANIFEST);
223                if (entry != null) {
224                    stream = importZip.getInputStream(entry);
225                } else {
226                    CmsMessageContainer message = Messages.get().container(
227                        Messages.ERR_NO_MANIFEST_MODULE_IMPORT_1,
228                        importResource);
229                    LOG.error(message.key());
230                    throw new CmsConfigurationException(message);
231                }
232            } else if (file.isDirectory()) {
233                file = new File(file, CmsImportExportManager.EXPORT_MANIFEST);
234                stream = new FileInputStream(file);
235            }
236
237            // start the parsing process
238            digester.parse(stream);
239        } catch (IOException e) {
240            CmsMessageContainer message = Messages.get().container(Messages.ERR_IO_MODULE_IMPORT_1, importResource);
241            LOG.error(message.key(), e);
242            throw new CmsConfigurationException(message, e);
243        } catch (SAXException e) {
244            CmsMessageContainer message = Messages.get().container(Messages.ERR_SAX_MODULE_IMPORT_1, importResource);
245            LOG.error(message.key(), e);
246            throw new CmsConfigurationException(message, e);
247        } finally {
248            try {
249                if (importZip != null) {
250                    importZip.close();
251                }
252                if (stream != null) {
253                    stream.close();
254                }
255            } catch (Exception e) {
256                // noop
257            }
258        }
259
260        CmsModule importedModule = handler.getModule();
261        // the digester must have set the module now
262        if (importedModule == null) {
263            throw new CmsConfigurationException(
264                Messages.get().container(Messages.ERR_IMPORT_MOD_ALREADY_INSTALLED_1, importResource));
265        } else {
266            importedModule.setExportVersion(version[0]);
267        }
268
269        return importedModule;
270    }
271
272    /**
273     * Reads a module object from an external file source.<p>
274     *
275     * @param manifest the manifest data
276     *
277     * @return the imported module
278     *
279     * @throws CmsConfigurationException if the module could not be imported
280     */
281    public static CmsModule readModuleFromManifest(byte[] manifest) throws CmsConfigurationException {
282
283        // instantiate Digester and enable XML validation
284        Digester digester = new Digester();
285        digester.setUseContextClassLoader(true);
286        digester.setValidating(false);
287        digester.setRuleNamespaceURI(null);
288        digester.setErrorHandler(new CmsXmlErrorHandler("manifest data"));
289
290        // add this class to the Digester
291        CmsModuleImportExportHandler handler = new CmsModuleImportExportHandler();
292        final String[] version = new String[] {null};
293        digester.push(handler);
294
295        digester.addRule("*/export_version", new Rule() {
296
297            @Override
298            public void body(String namespace, String name, String text) throws Exception {
299
300                version[0] = text.trim();
301            }
302
303        });
304        CmsModuleXmlHandler.addXmlDigesterRules(digester);
305
306        InputStream stream = new ByteArrayInputStream(manifest);
307
308        try {
309            digester.parse(stream);
310        } catch (IOException e) {
311            CmsMessageContainer message = Messages.get().container(Messages.ERR_IO_MODULE_IMPORT_1, "manifest data");
312            LOG.error(message.key(), e);
313            throw new CmsConfigurationException(message, e);
314        } catch (SAXException e) {
315            CmsMessageContainer message = Messages.get().container(Messages.ERR_SAX_MODULE_IMPORT_1, "manifest data");
316            LOG.error(message.key(), e);
317            throw new CmsConfigurationException(message, e);
318        }
319        CmsModule importedModule = handler.getModule();
320        // the digester must have set the module now
321        if (importedModule == null) {
322            throw new CmsConfigurationException(
323                Messages.get().container(Messages.ERR_IMPORT_MOD_ALREADY_INSTALLED_1, "manifest data"));
324        } else {
325            importedModule.setExportVersion(version[0]);
326        }
327
328        return importedModule;
329    }
330
331    /**
332     * Writes the messages for starting an import to the given report.<p>
333     *
334     * @param report the report to write to
335     * @param modulePackageName the module name
336     */
337    public static void reportBeginImport(I_CmsReport report, String modulePackageName) {
338
339        report.print(Messages.get().container(Messages.RPT_IMPORT_MODULE_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
340        if (report instanceof CmsHtmlReport) {
341            report.print(
342                org.opencms.report.Messages.get().container(
343                    org.opencms.report.Messages.RPT_ARGUMENT_1,
344                    "<i>" + modulePackageName + "</i>"));
345        } else {
346            report.print(
347                org.opencms.report.Messages.get().container(
348                    org.opencms.report.Messages.RPT_ARGUMENT_1,
349                    modulePackageName));
350        }
351        report.println(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
352    }
353
354    /**
355     * Writes the messages for finishing an import to the given report.<p>
356     *
357     * @param report the report to write to
358     */
359    public static void reportEndImport(I_CmsReport report) {
360
361        report.println(Messages.get().container(Messages.RPT_IMPORT_MODULE_END_0), I_CmsReport.FORMAT_HEADLINE);
362    }
363
364    /**
365     * @see org.opencms.importexport.I_CmsImportExportHandler#exportData(org.opencms.file.CmsObject, org.opencms.report.I_CmsReport)
366     */
367    public void exportData(CmsObject cms, I_CmsReport report)
368    throws CmsConfigurationException, CmsImportExportException, CmsRoleViolationException {
369
370        // check if the user has the required permissions
371        OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER);
372
373        report.print(Messages.get().container(Messages.RPT_EXPORT_MODULE_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
374        if (report instanceof CmsHtmlReport) {
375            report.print(
376                org.opencms.report.Messages.get().container(
377                    org.opencms.report.Messages.RPT_ARGUMENT_1,
378                    "<i>" + getModuleName() + "</i>"));
379
380        } else {
381            report.print(
382                org.opencms.report.Messages.get().container(
383                    org.opencms.report.Messages.RPT_ARGUMENT_1,
384                    getModuleName()));
385        }
386        report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
387
388        if (!OpenCms.getModuleManager().hasModule(getModuleName())) {
389            // module not available
390            throw new CmsConfigurationException(
391                Messages.get().container(Messages.ERR_NO_MOD_FOR_EXPORT_1, getModuleName()));
392        }
393
394        // generate module XML
395        CmsModule module = OpenCms.getModuleManager().getModule(getModuleName());
396        boolean shouldIncrementVersion;
397        try {
398            shouldIncrementVersion = module.isAutoIncrement()
399                && (module.getVersion().isUpdated() || module.shouldIncrementVersionBasedOnResources(cms));
400        } catch (CmsException e) {
401            shouldIncrementVersion = false;
402            LOG.error(e.getLocalizedMessage(), e);
403        }
404        module.getVersion().setUpdated(false);
405        if (shouldIncrementVersion) {
406            module.getVersion().increment();
407            module.setCheckpointTime(System.currentTimeMillis());
408            OpenCms.getModuleManager().updateModuleConfiguration();
409        }
410
411        Element moduleElement = CmsModuleXmlHandler.generateXml(module, XmlWriteMode.manifest);
412
413        CmsExportParameters params = new CmsExportParameters(
414            getFileName(),
415            moduleElement,
416            true,
417            false,
418            false,
419            getAdditionalResources(),
420            true,
421            true,
422            0,
423            true,
424            false,
425            module.getExportMode(),
426            // provide the extra resources only in case of excluded resources, otherwise not needed
427            ((null == module.getExcludeResources()) || module.getExcludeResources().isEmpty())
428            ? null
429            : module.getResources());
430
431        // export the module using the standard export
432        CmsObject exportCms = cms;
433        String importSite = module.getSite();
434        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(importSite)) {
435            try {
436                exportCms = OpenCms.initCmsObject(exportCms);
437                exportCms.getRequestContext().setSiteRoot(importSite);
438            } catch (Exception e) {
439                LOG.error(e.getLocalizedMessage(), e);
440            }
441        }
442        new CmsExport(exportCms, report).exportData(params);
443        report.println(Messages.get().container(Messages.RPT_EXPORT_MODULE_END_0), I_CmsReport.FORMAT_HEADLINE);
444    }
445
446    /**
447     * Returns the VFS resources to be exported additionally with the module.<p>
448     *
449     * @return the VFS resources to be exported additionally with the module
450     */
451    public List<String> getAdditionalResources() {
452
453        return m_additionalResources;
454    }
455
456    /**
457     * @see org.opencms.importexport.I_CmsImportExportHandler#getDescription()
458     */
459    public String getDescription() {
460
461        return m_description;
462    }
463
464    /**
465     * Returns the name of the export file in the real file system.<p>
466     *
467     * @return the name of the export file in the real file system
468     */
469    public String getFileName() {
470
471        CmsMacroResolver resolver = new CmsMacroResolver();
472        resolver.addMacro("version", OpenCms.getModuleManager().getModule(m_moduleName).getVersionStr());
473        return resolver.resolveMacros(m_fileName);
474    }
475
476    /**
477     * Returns the import parameters.<p>
478     *
479     * @return the import parameters
480     */
481    public CmsImportParameters getImportParameters() {
482
483        return m_importParams;
484    }
485
486    /**
487     * Returns the (package) name of the module to be exported.<p>
488     *
489     * @return the (package) name of the module to be exported
490     */
491    public String getModuleName() {
492
493        return m_moduleName;
494    }
495
496    /**
497     * Returns the VFS resources to be exported additionally with the module as a list.<p>
498     *
499     * @return the VFS resources to be exported additionally with the module as a list
500     */
501    public List<String> getResourcesAsList() {
502
503        return m_additionalResources;
504    }
505
506    /**
507     * @see org.opencms.importexport.I_CmsImportExportHandler#importData(CmsObject, I_CmsReport)
508     */
509    public synchronized void importData(CmsObject cms, I_CmsReport report)
510    throws CmsXmlException, CmsImportExportException, CmsRoleViolationException, CmsException {
511
512        CmsImportParameters parameters = getImportParameters();
513        CmsProject previousProject = cms.getRequestContext().getCurrentProject();
514        try {
515            CmsProject importProject = null;
516            String modulePackageName = null;
517            String storedSiteRoot = cms.getRequestContext().getSiteRoot();
518            CmsImportHelper helper = new CmsImportHelper(parameters);
519            try {
520                cms.getRequestContext().setSiteRoot("/");
521                helper.openFile();
522                modulePackageName = helper.getFileName();
523
524                try {
525                    // try to read a (leftover) module import project
526                    importProject = cms.readProject(
527                        Messages.get().getBundle(cms.getRequestContext().getLocale()).key(
528                            Messages.GUI_IMPORT_MODULE_PROJECT_NAME_1,
529                            new Object[] {modulePackageName}));
530                } catch (CmsException e) {
531                    // create a Project to import the module
532                    importProject = cms.createProject(
533                        Messages.get().getBundle(cms.getRequestContext().getLocale()).key(
534                            Messages.GUI_IMPORT_MODULE_PROJECT_NAME_1,
535                            new Object[] {modulePackageName}),
536                        Messages.get().getBundle(cms.getRequestContext().getLocale()).key(
537                            Messages.GUI_IMPORT_MODULE_PROJECT_DESC_1,
538                            new Object[] {modulePackageName}),
539                        OpenCms.getDefaultUsers().getGroupAdministrators(),
540                        OpenCms.getDefaultUsers().getGroupAdministrators(),
541                        CmsProject.PROJECT_TYPE_TEMPORARY);
542                }
543
544                cms.getRequestContext().setCurrentProject(importProject);
545
546                // copy the root folder to the project
547                cms.copyResourceToProject("/");
548            } catch (Exception e) {
549                throw new CmsImportExportException(
550                    Messages.get().container(Messages.ERR_IO_MODULE_IMPORT_1, parameters.getPath()),
551                    e);
552            } finally {
553                helper.closeFile();
554                cms.getRequestContext().setSiteRoot(storedSiteRoot);
555            }
556
557            reportBeginImport(report, modulePackageName);
558            importModule(cms, report, parameters);
559            report.println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
560            // now unlock and publish the project
561            cms.unlockProject(importProject.getUuid());
562            OpenCms.getPublishManager().publishProject(cms, report);
563            OpenCms.getPublishManager().waitWhileRunning();
564
565            report.println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_END_0), I_CmsReport.FORMAT_HEADLINE);
566            reportEndImport(report);
567        } finally {
568            cms.getRequestContext().setCurrentProject(previousProject);
569        }
570    }
571
572    /**
573     * @see org.opencms.importexport.I_CmsImportExportHandler#importData(org.opencms.file.CmsObject, java.lang.String, java.lang.String, org.opencms.report.I_CmsReport)
574     *
575     * @deprecated use {@link #importData(CmsObject, I_CmsReport)} instead
576     */
577    @Deprecated
578    public void importData(CmsObject cms, String importFile, String importPath, I_CmsReport report)
579    throws CmsXmlException, CmsImportExportException, CmsRoleViolationException, CmsException {
580
581        CmsImportParameters parameters = new CmsImportParameters(importFile, importPath, true);
582        setImportParameters(parameters);
583
584        importData(cms, report);
585    }
586
587    /**
588     * @see org.opencms.importexport.I_CmsImportExportHandler#matches(org.dom4j.Document)
589     */
590    public boolean matches(Document manifest) {
591
592        Element rootElement = manifest.getRootElement();
593
594        return (rootElement.selectNodes("./module/name").size() > 0);
595    }
596
597    /**
598     * Sets the VFS resources to be exported additionally with the module.<p>
599     *
600     * @param resources the VFS resources to be exported additionally with the module
601     */
602    public void setAdditionalResources(String[] resources) {
603
604        m_additionalResources = Arrays.asList(resources);
605    }
606
607    /**
608     * @see org.opencms.importexport.I_CmsImportExportHandler#setDescription(java.lang.String)
609     */
610    public void setDescription(String description) {
611
612        m_description = description;
613    }
614
615    /**
616     * Sets the name of the export file in the real file system.<p>
617     *
618     * @param fileName the name of the export file in the real file system
619     */
620    public void setFileName(String fileName) {
621
622        m_fileName = fileName;
623    }
624
625    /**
626     * Sets the import parameters.<p>
627     *
628     * @param importParams the parameters to set
629     */
630    public void setImportParameters(CmsImportParameters importParams) {
631
632        m_importParams = importParams;
633    }
634
635    /**
636     * Will be called by the digester if a module was imported.<p>
637     *
638     * @param moduleHandler contains the imported module
639     */
640    public void setModule(CmsModuleXmlHandler moduleHandler) {
641
642        m_importedModule = moduleHandler.getModule();
643    }
644
645    /**
646     * Sets the (package) name of the module to be exported.<p>
647     *
648     * @param moduleName the (package) name of the module to be exported
649     */
650    public void setModuleName(String moduleName) {
651
652        m_moduleName = moduleName;
653    }
654
655    /**
656     * Returns the module imported with the digester.<p>
657     *
658     * @return the module imported with the digester
659     */
660    private CmsModule getModule() {
661
662        return m_importedModule;
663    }
664
665    /**
666     * Imports a module from an external file source.<p>
667     *
668     * @param cms must have been initialized with {@link CmsRole#DATABASE_MANAGER} permissions
669     * @param report the report to print the progress information to
670     * @param parameters the import parameters
671     *
672     * @return the imported module
673     *
674     * @throws CmsSecurityException if no {@link CmsRole#DATABASE_MANAGER} permissions are available
675     * @throws CmsConfigurationException if the module is already installed or the
676     *      dependencies are not fulfilled
677     * @throws CmsException if errors occur reading the module data
678     */
679    private synchronized CmsModule importModule(CmsObject cms, I_CmsReport report, CmsImportParameters parameters)
680    throws CmsSecurityException, CmsConfigurationException, CmsException {
681
682        // check if the user has the required permissions
683        OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER);
684
685        // read the module from the import file
686        CmsModule importedModule = readModuleFromImport(parameters.getPath());
687
688        // check if the module is already installed
689        if (OpenCms.getModuleManager().hasModule(importedModule.getName())) {
690            throw new CmsConfigurationException(
691                Messages.get().container(Messages.ERR_MOD_ALREADY_INSTALLED_1, importedModule.getName()));
692        }
693
694        // check the module dependencies
695        List<CmsModuleDependency> dependencies = OpenCms.getModuleManager().checkDependencies(
696            importedModule,
697            CmsModuleManager.DEPENDENCY_MODE_IMPORT);
698        if (dependencies.size() > 0) {
699            // some dependencies not fulfilled
700            StringBuffer missingModules = new StringBuffer();
701            for (CmsModuleDependency dependency : dependencies) {
702                missingModules.append("  ").append(dependency.getName()).append(", Version ").append(
703                    dependency.getVersion()).append("\r\n");
704            }
705            throw new CmsConfigurationException(
706                Messages.get().container(
707                    Messages.ERR_MOD_DEPENDENCY_INFO_2,
708                    importedModule.getName() + ", Version " + importedModule.getVersion(),
709                    missingModules));
710        }
711
712        // check the imported resource types for name / id conflicts
713        List<I_CmsResourceType> checkedTypes = new ArrayList<I_CmsResourceType>();
714        for (I_CmsResourceType type : importedModule.getResourceTypes()) {
715            // first check against the already configured resource types
716            int externalConflictIndex = OpenCms.getResourceManager().getResourceTypes().indexOf(type);
717            if (externalConflictIndex >= 0) {
718                I_CmsResourceType conflictingType = OpenCms.getResourceManager().getResourceTypes().get(
719                    externalConflictIndex);
720                if (!type.isIdentical(conflictingType)) {
721                    // if name and id are identical, we assume this is a module replace operation
722                    throw new CmsConfigurationException(
723                        org.opencms.loader.Messages.get().container(
724                            org.opencms.loader.Messages.ERR_CONFLICTING_MODULE_RESOURCE_TYPES_5,
725                            new Object[] {
726                                type.getTypeName(),
727                                new Integer(type.getTypeId()),
728                                importedModule.getName(),
729                                conflictingType.getTypeName(),
730                                new Integer(conflictingType.getTypeId())}));
731                }
732            }
733            // now check against the other resource types of the imported module
734            int internalConflictIndex = checkedTypes.indexOf(type);
735            if (internalConflictIndex >= 0) {
736                I_CmsResourceType conflictingType = checkedTypes.get(internalConflictIndex);
737                throw new CmsConfigurationException(
738                    org.opencms.loader.Messages.get().container(
739                        org.opencms.loader.Messages.ERR_CONFLICTING_RESTYPES_IN_MODULE_5,
740                        new Object[] {
741                            importedModule.getName(),
742                            type.getTypeName(),
743                            new Integer(type.getTypeId()),
744                            conflictingType.getTypeName(),
745                            new Integer(conflictingType.getTypeId())}));
746            }
747            // add the resource type for the next check
748            checkedTypes.add(type);
749        }
750
751        // import the module resources
752        CmsObject importCms = OpenCms.initCmsObject(cms);
753        String importSite = importedModule.getImportSite();
754        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(importSite)) {
755            importCms.getRequestContext().setSiteRoot(importSite);
756        } else {
757            String siteToSet = importCms.getRequestContext().getSiteRoot();
758            if ("".equals(siteToSet)) {
759                siteToSet = "/";
760            }
761            importedModule.setSite(siteToSet);
762        }
763
764        //  add the imported module to the module manager
765        OpenCms.getModuleManager().addModule(cms, importedModule);
766
767        // reinitialize the resource manager with additional module resource types if necessary
768        if (importedModule.getResourceTypes() != Collections.EMPTY_LIST) {
769            OpenCms.getResourceManager().initialize(cms);
770        }
771        // reinitialize the workplace manager with additional module explorer types if necessary
772        if (importedModule.getExplorerTypes() != Collections.EMPTY_LIST) {
773            OpenCms.getWorkplaceManager().addExplorerTypeSettings(importedModule);
774        }
775
776        CmsImport cmsImport = new CmsImport(importCms, report);
777        cmsImport.importData(parameters);
778        String importScript = importedModule.getImportScript();
779        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(importScript)) {
780            LOG.info("Executing import script for module " + importedModule.getName());
781            report.println(Messages.get().container(Messages.RPT_IMPORT_SCRIPT_HEADER_0), I_CmsReport.FORMAT_HEADLINE);
782            importScript = "echo on\n" + importScript;
783            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
784            PrintStream out = new PrintStream(buffer);
785            CmsShell shell = new CmsShell(cms, "${user}@${project}:${siteroot}|${uri}>", null, out, out);
786            shell.execute(importScript);
787            String outputString = buffer.toString();
788            LOG.info("Shell output for import script was: \n" + outputString);
789            report.println(Messages.get().container(Messages.RPT_IMPORT_SCRIPT_OUTPUT_1, outputString));
790        }
791        importedModule.setCheckpointTime(System.currentTimeMillis());
792        OpenCms.getModuleManager().updateModuleConfiguration();
793        return importedModule;
794    }
795}