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.isFile()) {
218                importZip = new ZipFile(importResource);
219                ZipEntry entry = importZip.getEntry(CmsImportExportManager.EXPORT_MANIFEST);
220                if (entry != null) {
221                    stream = importZip.getInputStream(entry);
222                } else {
223                    CmsMessageContainer message = Messages.get().container(
224                        Messages.ERR_NO_MANIFEST_MODULE_IMPORT_1,
225                        importResource);
226                    LOG.error(message.key());
227                    throw new CmsConfigurationException(message);
228                }
229            } else if (file.isDirectory()) {
230                file = new File(file, CmsImportExportManager.EXPORT_MANIFEST);
231                stream = new FileInputStream(file);
232            }
233
234            // start the parsing process
235            digester.parse(stream);
236        } catch (IOException e) {
237            CmsMessageContainer message = Messages.get().container(Messages.ERR_IO_MODULE_IMPORT_1, importResource);
238            LOG.error(message.key(), e);
239            throw new CmsConfigurationException(message, e);
240        } catch (SAXException e) {
241            CmsMessageContainer message = Messages.get().container(Messages.ERR_SAX_MODULE_IMPORT_1, importResource);
242            LOG.error(message.key(), e);
243            throw new CmsConfigurationException(message, e);
244        } finally {
245            try {
246                if (importZip != null) {
247                    importZip.close();
248                }
249                if (stream != null) {
250                    stream.close();
251                }
252            } catch (Exception e) {
253                // noop
254            }
255        }
256
257        CmsModule importedModule = handler.getModule();
258        // the digester must have set the module now
259        if (importedModule == null) {
260            throw new CmsConfigurationException(
261                Messages.get().container(Messages.ERR_IMPORT_MOD_ALREADY_INSTALLED_1, importResource));
262        } else {
263            importedModule.setExportVersion(version[0]);
264        }
265
266        return importedModule;
267    }
268
269    /**
270     * Reads a module object from an external file source.<p>
271     *
272     * @param manifest the manifest data
273     *
274     * @return the imported module
275     *
276     * @throws CmsConfigurationException if the module could not be imported
277     */
278    public static CmsModule readModuleFromManifest(byte[] manifest) throws CmsConfigurationException {
279
280        // instantiate Digester and enable XML validation
281        Digester digester = new Digester();
282        digester.setUseContextClassLoader(true);
283        digester.setValidating(false);
284        digester.setRuleNamespaceURI(null);
285        digester.setErrorHandler(new CmsXmlErrorHandler("manifest data"));
286
287        // add this class to the Digester
288        CmsModuleImportExportHandler handler = new CmsModuleImportExportHandler();
289        final String[] version = new String[] {null};
290        digester.push(handler);
291
292        digester.addRule("*/export_version", new Rule() {
293
294            @Override
295            public void body(String namespace, String name, String text) throws Exception {
296
297                version[0] = text.trim();
298            }
299
300        });
301        CmsModuleXmlHandler.addXmlDigesterRules(digester);
302
303        InputStream stream = new ByteArrayInputStream(manifest);
304
305        try {
306            digester.parse(stream);
307        } catch (IOException e) {
308            CmsMessageContainer message = Messages.get().container(Messages.ERR_IO_MODULE_IMPORT_1, "manifest data");
309            LOG.error(message.key(), e);
310            throw new CmsConfigurationException(message, e);
311        } catch (SAXException e) {
312            CmsMessageContainer message = Messages.get().container(Messages.ERR_SAX_MODULE_IMPORT_1, "manifest data");
313            LOG.error(message.key(), e);
314            throw new CmsConfigurationException(message, e);
315        }
316        CmsModule importedModule = handler.getModule();
317        // the digester must have set the module now
318        if (importedModule == null) {
319            throw new CmsConfigurationException(
320                Messages.get().container(Messages.ERR_IMPORT_MOD_ALREADY_INSTALLED_1, "manifest data"));
321        } else {
322            importedModule.setExportVersion(version[0]);
323        }
324
325        return importedModule;
326    }
327
328    /**
329     * Writes the messages for starting an import to the given report.<p>
330     *
331     * @param report the report to write to
332     * @param modulePackageName the module name
333     */
334    public static void reportBeginImport(I_CmsReport report, String modulePackageName) {
335
336        report.print(Messages.get().container(Messages.RPT_IMPORT_MODULE_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
337        if (report instanceof CmsHtmlReport) {
338            report.print(
339                org.opencms.report.Messages.get().container(
340                    org.opencms.report.Messages.RPT_ARGUMENT_1,
341                    "<i>" + modulePackageName + "</i>"));
342        } else {
343            report.print(
344                org.opencms.report.Messages.get().container(
345                    org.opencms.report.Messages.RPT_ARGUMENT_1,
346                    modulePackageName));
347        }
348        report.println(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
349    }
350
351    /**
352     * Writes the messages for finishing an import to the given report.<p>
353     *
354     * @param report the report to write to
355     */
356    public static void reportEndImport(I_CmsReport report) {
357
358        report.println(Messages.get().container(Messages.RPT_IMPORT_MODULE_END_0), I_CmsReport.FORMAT_HEADLINE);
359    }
360
361    /**
362     * @see org.opencms.importexport.I_CmsImportExportHandler#exportData(org.opencms.file.CmsObject, org.opencms.report.I_CmsReport)
363     */
364    public void exportData(CmsObject cms, I_CmsReport report)
365    throws CmsConfigurationException, CmsImportExportException, CmsRoleViolationException {
366
367        // check if the user has the required permissions
368        OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER);
369
370        report.print(Messages.get().container(Messages.RPT_EXPORT_MODULE_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
371        if (report instanceof CmsHtmlReport) {
372            report.print(
373                org.opencms.report.Messages.get().container(
374                    org.opencms.report.Messages.RPT_ARGUMENT_1,
375                    "<i>" + getModuleName() + "</i>"));
376
377        } else {
378            report.print(
379                org.opencms.report.Messages.get().container(
380                    org.opencms.report.Messages.RPT_ARGUMENT_1,
381                    getModuleName()));
382        }
383        report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
384
385        if (!OpenCms.getModuleManager().hasModule(getModuleName())) {
386            // module not available
387            throw new CmsConfigurationException(
388                Messages.get().container(Messages.ERR_NO_MOD_FOR_EXPORT_1, getModuleName()));
389        }
390
391        // generate module XML
392        CmsModule module = OpenCms.getModuleManager().getModule(getModuleName());
393        boolean shouldIncrementVersion;
394        try {
395            shouldIncrementVersion = module.isAutoIncrement()
396                && (module.getVersion().isUpdated() || module.shouldIncrementVersionBasedOnResources(cms));
397        } catch (CmsException e) {
398            shouldIncrementVersion = false;
399            LOG.error(e.getLocalizedMessage(), e);
400        }
401        module.getVersion().setUpdated(false);
402        if (shouldIncrementVersion) {
403            module.getVersion().increment();
404            module.setCheckpointTime(System.currentTimeMillis());
405            OpenCms.getModuleManager().updateModuleConfiguration();
406        }
407
408        Element moduleElement = CmsModuleXmlHandler.generateXml(module, XmlWriteMode.manifest);
409
410        CmsExportParameters params = new CmsExportParameters(
411            getFileName(),
412            moduleElement,
413            true,
414            false,
415            false,
416            getAdditionalResources(),
417            true,
418            true,
419            0,
420            true,
421            false,
422            module.getExportMode(),
423            // provide the extra resources only in case of excluded resources, otherwise not needed
424            ((null == module.getExcludeResources()) || module.getExcludeResources().isEmpty())
425            ? null
426            : module.getResources());
427
428        // export the module using the standard export
429        CmsObject exportCms = cms;
430        String importSite = module.getSite();
431        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(importSite)) {
432            try {
433                exportCms = OpenCms.initCmsObject(exportCms);
434                exportCms.getRequestContext().setSiteRoot(importSite);
435            } catch (Exception e) {
436                LOG.error(e.getLocalizedMessage(), e);
437            }
438        }
439        new CmsExport(exportCms, report).exportData(params);
440        report.println(Messages.get().container(Messages.RPT_EXPORT_MODULE_END_0), I_CmsReport.FORMAT_HEADLINE);
441    }
442
443    /**
444     * Returns the VFS resources to be exported additionally with the module.<p>
445     *
446     * @return the VFS resources to be exported additionally with the module
447     */
448    public List<String> getAdditionalResources() {
449
450        return m_additionalResources;
451    }
452
453    /**
454     * @see org.opencms.importexport.I_CmsImportExportHandler#getDescription()
455     */
456    public String getDescription() {
457
458        return m_description;
459    }
460
461    /**
462     * Returns the name of the export file in the real file system.<p>
463     *
464     * @return the name of the export file in the real file system
465     */
466    public String getFileName() {
467
468        CmsMacroResolver resolver = new CmsMacroResolver();
469        resolver.addMacro("version", OpenCms.getModuleManager().getModule(m_moduleName).getVersionStr());
470        return resolver.resolveMacros(m_fileName);
471    }
472
473    /**
474     * Returns the import parameters.<p>
475     *
476     * @return the import parameters
477     */
478    public CmsImportParameters getImportParameters() {
479
480        return m_importParams;
481    }
482
483    /**
484     * Returns the (package) name of the module to be exported.<p>
485     *
486     * @return the (package) name of the module to be exported
487     */
488    public String getModuleName() {
489
490        return m_moduleName;
491    }
492
493    /**
494     * Returns the VFS resources to be exported additionally with the module as a list.<p>
495     *
496     * @return the VFS resources to be exported additionally with the module as a list
497     */
498    public List<String> getResourcesAsList() {
499
500        return m_additionalResources;
501    }
502
503    /**
504     * @see org.opencms.importexport.I_CmsImportExportHandler#importData(CmsObject, I_CmsReport)
505     */
506    public synchronized void importData(CmsObject cms, I_CmsReport report)
507    throws CmsXmlException, CmsImportExportException, CmsRoleViolationException, CmsException {
508
509        CmsImportParameters parameters = getImportParameters();
510        CmsProject previousProject = cms.getRequestContext().getCurrentProject();
511        try {
512            CmsProject importProject = null;
513            String modulePackageName = null;
514            String storedSiteRoot = cms.getRequestContext().getSiteRoot();
515            CmsImportHelper helper = new CmsImportHelper(parameters);
516            try {
517                cms.getRequestContext().setSiteRoot("/");
518                helper.openFile();
519                modulePackageName = helper.getFileName();
520
521                try {
522                    // try to read a (leftover) module import project
523                    importProject = cms.readProject(
524                        Messages.get().getBundle(cms.getRequestContext().getLocale()).key(
525                            Messages.GUI_IMPORT_MODULE_PROJECT_NAME_1,
526                            new Object[] {modulePackageName}));
527                } catch (CmsException e) {
528                    // create a Project to import the module
529                    importProject = cms.createProject(
530                        Messages.get().getBundle(cms.getRequestContext().getLocale()).key(
531                            Messages.GUI_IMPORT_MODULE_PROJECT_NAME_1,
532                            new Object[] {modulePackageName}),
533                        Messages.get().getBundle(cms.getRequestContext().getLocale()).key(
534                            Messages.GUI_IMPORT_MODULE_PROJECT_DESC_1,
535                            new Object[] {modulePackageName}),
536                        OpenCms.getDefaultUsers().getGroupAdministrators(),
537                        OpenCms.getDefaultUsers().getGroupAdministrators(),
538                        CmsProject.PROJECT_TYPE_TEMPORARY);
539                }
540
541                cms.getRequestContext().setCurrentProject(importProject);
542
543                // copy the root folder to the project
544                cms.copyResourceToProject("/");
545            } catch (Exception e) {
546                throw new CmsImportExportException(
547                    Messages.get().container(Messages.ERR_IO_MODULE_IMPORT_1, parameters.getPath()),
548                    e);
549            } finally {
550                helper.closeFile();
551                cms.getRequestContext().setSiteRoot(storedSiteRoot);
552            }
553
554            reportBeginImport(report, modulePackageName);
555            importModule(cms, report, parameters);
556            report.println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
557            // now unlock and publish the project
558            cms.unlockProject(importProject.getUuid());
559            OpenCms.getPublishManager().publishProject(cms, report);
560            OpenCms.getPublishManager().waitWhileRunning();
561
562            report.println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_END_0), I_CmsReport.FORMAT_HEADLINE);
563            reportEndImport(report);
564        } finally {
565            cms.getRequestContext().setCurrentProject(previousProject);
566        }
567    }
568
569    /**
570     * @see org.opencms.importexport.I_CmsImportExportHandler#importData(org.opencms.file.CmsObject, java.lang.String, java.lang.String, org.opencms.report.I_CmsReport)
571     *
572     * @deprecated use {@link #importData(CmsObject, I_CmsReport)} instead
573     */
574    @Deprecated
575    public void importData(CmsObject cms, String importFile, String importPath, I_CmsReport report)
576    throws CmsXmlException, CmsImportExportException, CmsRoleViolationException, CmsException {
577
578        CmsImportParameters parameters = new CmsImportParameters(importFile, importPath, true);
579        setImportParameters(parameters);
580
581        importData(cms, report);
582    }
583
584    /**
585     * @see org.opencms.importexport.I_CmsImportExportHandler#matches(org.dom4j.Document)
586     */
587    public boolean matches(Document manifest) {
588
589        Element rootElement = manifest.getRootElement();
590
591        return (rootElement.selectNodes("./module/name").size() > 0);
592    }
593
594    /**
595     * Sets the VFS resources to be exported additionally with the module.<p>
596     *
597     * @param resources the VFS resources to be exported additionally with the module
598     */
599    public void setAdditionalResources(String[] resources) {
600
601        m_additionalResources = Arrays.asList(resources);
602    }
603
604    /**
605     * @see org.opencms.importexport.I_CmsImportExportHandler#setDescription(java.lang.String)
606     */
607    public void setDescription(String description) {
608
609        m_description = description;
610    }
611
612    /**
613     * Sets the name of the export file in the real file system.<p>
614     *
615     * @param fileName the name of the export file in the real file system
616     */
617    public void setFileName(String fileName) {
618
619        m_fileName = fileName;
620    }
621
622    /**
623     * Sets the import parameters.<p>
624     *
625     * @param importParams the parameters to set
626     */
627    public void setImportParameters(CmsImportParameters importParams) {
628
629        m_importParams = importParams;
630    }
631
632    /**
633     * Will be called by the digester if a module was imported.<p>
634     *
635     * @param moduleHandler contains the imported module
636     */
637    public void setModule(CmsModuleXmlHandler moduleHandler) {
638
639        m_importedModule = moduleHandler.getModule();
640    }
641
642    /**
643     * Sets the (package) name of the module to be exported.<p>
644     *
645     * @param moduleName the (package) name of the module to be exported
646     */
647    public void setModuleName(String moduleName) {
648
649        m_moduleName = moduleName;
650    }
651
652    /**
653     * Returns the module imported with the digester.<p>
654     *
655     * @return the module imported with the digester
656     */
657    private CmsModule getModule() {
658
659        return m_importedModule;
660    }
661
662    /**
663     * Imports a module from an external file source.<p>
664     *
665     * @param cms must have been initialized with {@link CmsRole#DATABASE_MANAGER} permissions
666     * @param report the report to print the progress information to
667     * @param parameters the import parameters
668     *
669     * @return the imported module
670     *
671     * @throws CmsSecurityException if no {@link CmsRole#DATABASE_MANAGER} permissions are available
672     * @throws CmsConfigurationException if the module is already installed or the
673     *      dependencies are not fulfilled
674     * @throws CmsException if errors occur reading the module data
675     */
676    private synchronized CmsModule importModule(CmsObject cms, I_CmsReport report, CmsImportParameters parameters)
677    throws CmsSecurityException, CmsConfigurationException, CmsException {
678
679        // check if the user has the required permissions
680        OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER);
681
682        // read the module from the import file
683        CmsModule importedModule = readModuleFromImport(parameters.getPath());
684
685        // check if the module is already installed
686        if (OpenCms.getModuleManager().hasModule(importedModule.getName())) {
687            throw new CmsConfigurationException(
688                Messages.get().container(Messages.ERR_MOD_ALREADY_INSTALLED_1, importedModule.getName()));
689        }
690
691        // check the module dependencies
692        List<CmsModuleDependency> dependencies = OpenCms.getModuleManager().checkDependencies(
693            importedModule,
694            CmsModuleManager.DEPENDENCY_MODE_IMPORT);
695        if (dependencies.size() > 0) {
696            // some dependencies not fulfilled
697            StringBuffer missingModules = new StringBuffer();
698            for (CmsModuleDependency dependency : dependencies) {
699                missingModules.append("  ").append(dependency.getName()).append(", Version ").append(
700                    dependency.getVersion()).append("\r\n");
701            }
702            throw new CmsConfigurationException(
703                Messages.get().container(
704                    Messages.ERR_MOD_DEPENDENCY_INFO_2,
705                    importedModule.getName() + ", Version " + importedModule.getVersion(),
706                    missingModules));
707        }
708
709        // check the imported resource types for name / id conflicts
710        List<I_CmsResourceType> checkedTypes = new ArrayList<I_CmsResourceType>();
711        for (I_CmsResourceType type : importedModule.getResourceTypes()) {
712            // first check against the already configured resource types
713            int externalConflictIndex = OpenCms.getResourceManager().getResourceTypes().indexOf(type);
714            if (externalConflictIndex >= 0) {
715                I_CmsResourceType conflictingType = OpenCms.getResourceManager().getResourceTypes().get(
716                    externalConflictIndex);
717                if (!type.isIdentical(conflictingType)) {
718                    // if name and id are identical, we assume this is a module replace operation
719                    throw new CmsConfigurationException(
720                        org.opencms.loader.Messages.get().container(
721                            org.opencms.loader.Messages.ERR_CONFLICTING_MODULE_RESOURCE_TYPES_5,
722                            new Object[] {
723                                type.getTypeName(),
724                                new Integer(type.getTypeId()),
725                                importedModule.getName(),
726                                conflictingType.getTypeName(),
727                                new Integer(conflictingType.getTypeId())}));
728                }
729            }
730            // now check against the other resource types of the imported module
731            int internalConflictIndex = checkedTypes.indexOf(type);
732            if (internalConflictIndex >= 0) {
733                I_CmsResourceType conflictingType = checkedTypes.get(internalConflictIndex);
734                throw new CmsConfigurationException(
735                    org.opencms.loader.Messages.get().container(
736                        org.opencms.loader.Messages.ERR_CONFLICTING_RESTYPES_IN_MODULE_5,
737                        new Object[] {
738                            importedModule.getName(),
739                            type.getTypeName(),
740                            new Integer(type.getTypeId()),
741                            conflictingType.getTypeName(),
742                            new Integer(conflictingType.getTypeId())}));
743            }
744            // add the resource type for the next check
745            checkedTypes.add(type);
746        }
747
748        // import the module resources
749        CmsObject importCms = OpenCms.initCmsObject(cms);
750        String importSite = importedModule.getImportSite();
751        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(importSite)) {
752            importCms.getRequestContext().setSiteRoot(importSite);
753        } else {
754            String siteToSet = importCms.getRequestContext().getSiteRoot();
755            if ("".equals(siteToSet)) {
756                siteToSet = "/";
757            }
758            importedModule.setSite(siteToSet);
759        }
760
761        //  add the imported module to the module manager
762        OpenCms.getModuleManager().addModule(cms, importedModule);
763
764        // reinitialize the resource manager with additional module resource types if necessary
765        if (importedModule.getResourceTypes() != Collections.EMPTY_LIST) {
766            OpenCms.getResourceManager().initialize(cms);
767        }
768        // reinitialize the workplace manager with additional module explorer types if necessary
769        if (importedModule.getExplorerTypes() != Collections.EMPTY_LIST) {
770            OpenCms.getWorkplaceManager().addExplorerTypeSettings(importedModule);
771        }
772
773        CmsImport cmsImport = new CmsImport(importCms, report);
774        cmsImport.importData(parameters);
775        String importScript = importedModule.getImportScript();
776        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(importScript)) {
777            LOG.info("Executing import script for module " + importedModule.getName());
778            report.println(Messages.get().container(Messages.RPT_IMPORT_SCRIPT_HEADER_0), I_CmsReport.FORMAT_HEADLINE);
779            importScript = "echo on\n" + importScript;
780            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
781            PrintStream out = new PrintStream(buffer);
782            CmsShell shell = new CmsShell(cms, "${user}@${project}:${siteroot}|${uri}>", null, out, out);
783            shell.execute(importScript);
784            String outputString = buffer.toString();
785            LOG.info("Shell output for import script was: \n" + outputString);
786            report.println(Messages.get().container(Messages.RPT_IMPORT_SCRIPT_OUTPUT_1, outputString));
787        }
788        importedModule.setCheckpointTime(System.currentTimeMillis());
789        OpenCms.getModuleManager().updateModuleConfiguration();
790        return importedModule;
791    }
792}