org.xwiki.extension.xar.internal.handler.packager.Packager.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.extension.xar.internal.handler.packager.Packager.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.extension.xar.internal.handler.packager;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.extension.ExtensionId;
import org.xwiki.extension.repository.InstalledExtensionRepository;
import org.xwiki.extension.xar.XarExtensionExtension;
import org.xwiki.extension.xar.internal.handler.XarExtensionHandler;
import org.xwiki.extension.xar.internal.handler.XarExtensionPlan;
import org.xwiki.extension.xar.internal.repository.XarInstalledExtension;
import org.xwiki.extension.xar.internal.repository.XarInstalledExtensionRepository;
import org.xwiki.extension.xar.job.diff.DocumentVersionReference;
import org.xwiki.filter.FilterException;
import org.xwiki.filter.input.DefaultInputStreamInputSource;
import org.xwiki.filter.instance.output.DocumentInstanceOutputProperties;
import org.xwiki.filter.xar.input.XARInputProperties;
import org.xwiki.logging.marker.BeginTranslationMarker;
import org.xwiki.logging.marker.EndTranslationMarker;
import org.xwiki.logging.marker.TranslationMarker;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.model.reference.LocalDocumentReference;
import org.xwiki.model.reference.WikiReference;
import org.xwiki.observation.ObservationManager;
import org.xwiki.wiki.descriptor.WikiDescriptorManager;
import org.xwiki.wiki.manager.WikiManagerException;
import org.xwiki.xar.XarEntry;
import org.xwiki.xar.XarException;
import org.xwiki.xar.XarFile;
import org.xwiki.xar.internal.model.XarModel;

import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.MandatoryDocumentInitializerManager;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.internal.event.XARImportedEvent;
import com.xpn.xwiki.internal.event.XARImportingEvent;
import com.xpn.xwiki.internal.filter.XWikiDocumentFilterUtils;

/**
 * Default implementation of {@link Packager}.
 * 
 * @version $Id: d39d4ac9b979683b8a6d0e4728bf3b2ee21f67fb $
 * @since 4.0M1
 */
@Component(roles = Packager.class)
@Singleton
public class Packager {
    private static final BeginTranslationMarker LOG_INSTALLDOCUMENT_BEGIN = new BeginTranslationMarker(
            "extension.xar.log.install.document.begin");

    private static final EndTranslationMarker LOG_INSTALLDOCUMENT_SUCCESS_END = new EndTranslationMarker(
            "extension.xar.log.install.document.success.end");

    private static final EndTranslationMarker LOG_INSTALLDOCUMENT_FAILURE_END = new EndTranslationMarker(
            "extension.xar.log.install.document.failure.end");

    private static final TranslationMarker LOG_DELETEDDOCUMENT = new TranslationMarker(
            "extension.xar.log.delete.document");

    private static final TranslationMarker LOG_DELETEDDOCUMENT_FAILURE = new TranslationMarker(
            "extension.xar.log.delete.document.failure");

    @Inject
    @Named("explicit")
    private DocumentReferenceResolver<EntityReference> resolver;

    /**
     * The logger to log.
     */
    @Inject
    private Logger logger;

    @Inject
    private ObservationManager observation;

    @Inject
    private Provider<XWikiContext> xcontextProvider;

    @Inject
    private DocumentMergeImporter importer;

    @Inject
    private MandatoryDocumentInitializerManager initializerManager;

    @Inject
    private XWikiDocumentFilterUtils documentImporter;

    @Inject
    private WikiDescriptorManager wikiDescriptors;

    @Inject
    @Named(XarExtensionHandler.TYPE)
    private InstalledExtensionRepository installedXARs;

    private XarInstalledExtensionRepository getXarInstalledExtensionRepository() {
        return (XarInstalledExtensionRepository) this.installedXARs;
    }

    public void importXAR(String comment, File xarFile, PackageConfiguration configuration)
            throws IOException, XWikiException, ComponentLookupException, FilterException, WikiManagerException {
        if (configuration.getWiki() == null) {
            Collection<String> wikis = this.wikiDescriptors.getAllIds();

            for (String subwiki : wikis) {
                importXARToWiki(comment, xarFile, new WikiReference(subwiki), configuration);
            }
        } else {
            importXARToWiki(comment, xarFile, new WikiReference(configuration.getWiki()), configuration);
        }
    }

    private XarMergeResult importXARToWiki(String comment, File xarFile, WikiReference wikiReference,
            PackageConfiguration configuration)
            throws IOException, ComponentLookupException, XWikiException, FilterException {
        FileInputStream fis = new FileInputStream(xarFile);
        try {
            return importXARToWiki(comment, fis, wikiReference, configuration);
        } finally {
            fis.close();
        }
    }

    private XarMergeResult importXARToWiki(String comment, InputStream xarInputStream, WikiReference wikiReference,
            PackageConfiguration configuration)
            throws IOException, ComponentLookupException, XWikiException, FilterException {
        XarMergeResult mergeResult = new XarMergeResult();

        ZipArchiveInputStream zis = new ZipArchiveInputStream(xarInputStream);

        XWikiContext xcontext = this.xcontextProvider.get();

        String currentWiki = xcontext.getWikiId();
        try {
            xcontext.setWikiId(wikiReference.getName());

            this.observation.notify(new XARImportingEvent(), null, xcontext);

            for (ArchiveEntry entry = zis.getNextEntry(); entry != null; entry = zis.getNextEntry()) {
                if (!entry.isDirectory()) {
                    // Only import what should be imported
                    if (!entry.getName().equals(XarModel.PATH_PACKAGE)
                            && (configuration.getEntriesToImport() == null
                                    || configuration.getEntriesToImport().contains(entry.getName()))) {
                        XarEntryMergeResult entityMergeResult = importDocumentToWiki(comment, wikiReference, zis,
                                configuration);
                        if (entityMergeResult != null) {
                            mergeResult.addMergeResult(entityMergeResult);
                        }
                    }
                }
            }
        } finally {
            this.observation.notify(new XARImportedEvent(), null, xcontext);

            xcontext.setWikiId(currentWiki);
        }

        return mergeResult;
    }

    private XarEntryMergeResult importDocumentToWiki(String comment, WikiReference wikiReference,
            InputStream inputStream, PackageConfiguration configuration)
            throws XWikiException, FilterException, ComponentLookupException, IOException {
        XWikiContext xcontext = this.xcontextProvider.get();

        XWikiDocument nextDocument;
        try {
            nextDocument = getXWikiDocument(inputStream, wikiReference);
        } catch (Exception e) {
            this.logger.error("Failed to parse document", e);

            return null;
        }

        DocumentReference reference = nextDocument.getDocumentReferenceWithLocale();
        XWikiDocument currentDocument = xcontext.getWiki().getDocument(reference, xcontext);
        currentDocument.loadAttachmentsContent(xcontext);
        XWikiDocument previousDocument;
        XarExtensionPlan xarExtensionPlan = configuration.getXarExtensionPlan();
        if (xarExtensionPlan != null) {
            previousDocument = xarExtensionPlan.getPreviousXWikiDocument(reference, this);
        } else {
            previousDocument = null;
        }

        if (configuration.isVerbose()) {
            this.logger.info(LOG_INSTALLDOCUMENT_BEGIN, "Installing document [{}]",
                    nextDocument.getDocumentReferenceWithLocale());
        }

        try {
            XarEntryMergeResult entityMergeResult = this.importer.saveDocument(comment, previousDocument,
                    currentDocument, nextDocument, configuration);

            if (configuration.isVerbose()) {
                this.logger.info(LOG_INSTALLDOCUMENT_SUCCESS_END, "Done installing document [{}]",
                        nextDocument.getDocumentReferenceWithLocale());
            }

            return entityMergeResult;
        } catch (Exception e) {
            if (configuration.isVerbose()) {
                this.logger.error(LOG_INSTALLDOCUMENT_FAILURE_END, "Failed to install document [{}]",
                        nextDocument.getDocumentReferenceWithLocale(), e);
            }
        }

        return null;
    }

    public void unimportPages(Collection<XarEntry> pages, PackageConfiguration configuration)
            throws WikiManagerException {
        if (configuration.getWiki() == null) {
            Collection<String> wikis = this.wikiDescriptors.getAllIds();

            for (String subwiki : wikis) {
                unimportPagesFromWiki(pages, subwiki, configuration);
            }
        } else {
            unimportPagesFromWiki(pages, configuration.getWiki(), configuration);
        }
    }

    private void unimportPagesFromWiki(Collection<XarEntry> entries, String wiki,
            PackageConfiguration configuration) {
        WikiReference wikiReference = new WikiReference(wiki);

        for (XarEntry xarEntry : entries) {
            // Only delete what should be deleted.
            if (configuration.getEntriesToImport() == null
                    || configuration.getEntriesToImport().contains(xarEntry.getEntryName())) {
                DocumentReference documentReference = new DocumentReference(
                        this.resolver.resolve(xarEntry, wikiReference), xarEntry.getLocale());

                if (!configuration.isSkipMandatorytDocuments() || !isMandatoryDocument(documentReference)) {
                    deleteDocument(documentReference, configuration);
                }
            }
        }
    }

    public void deleteDocument(DocumentReference documentReference, PackageConfiguration configuration) {
        XWikiContext xcontext = this.xcontextProvider.get();

        try {
            // Make sure to have an expected context as much as possible
            if (configuration.getUserReference() != null) {
                xcontext.setUserReference(configuration.getUserReference());
            }

            XWikiDocument document = xcontext.getWiki().getDocument(documentReference, xcontext);

            if (!document.isNew()) {
                xcontext.getWiki().deleteDocument(document, xcontext);

                if (configuration.isVerbose()) {
                    this.logger.info(LOG_DELETEDDOCUMENT, "Deleted document [{}]",
                            document.getDocumentReferenceWithLocale());
                }
            }
        } catch (XWikiException e) {
            this.logger.error(LOG_DELETEDDOCUMENT_FAILURE, "Failed to delete document [{}]", documentReference, e);
        }
    }

    private boolean isMandatoryDocument(DocumentReference documentReference) {
        return this.initializerManager.getMandatoryDocumentInitializer(documentReference) != null;
    }

    public XWikiDocument getXWikiDocument(WikiReference wikiReference, LocalDocumentReference documentReference,
            XarFile xarFile) throws FilterException, IOException, ComponentLookupException {
        XarEntry realEntry = xarFile.getEntry(documentReference);
        if (realEntry != null) {
            InputStream stream = xarFile.getInputStream(realEntry);

            try {
                return getXWikiDocument(stream, wikiReference);
            } finally {
                stream.close();
            }
        }

        return null;
    }

    private DocumentReference cleanDocumentReference(DocumentReference reference) {
        // Remove the version if any since it does not make sense in a XAR
        DocumentReference documentReference = reference;
        if (reference instanceof DocumentVersionReference) {
            documentReference = ((DocumentVersionReference) reference).removeVersion();
        }

        return documentReference;
    }

    public XWikiDocument getXWikiDocument(DocumentReference reference)
            throws FilterException, IOException, ComponentLookupException, XarException {
        if (reference != null) {
            Collection<XarInstalledExtension> extensions = getXarInstalledExtensionRepository()
                    .getXarInstalledExtensions(reference);
            if (!extensions.isEmpty()) {
                return getXWikiDocument(reference, extensions.iterator().next());
            }
        }

        return null;
    }

    public XWikiDocument getXWikiDocument(DocumentReference reference, ExtensionId extensionId)
            throws FilterException, IOException, ComponentLookupException, XarException {
        if (reference != null) {
            if (extensionId != null) {
                return getXWikiDocument(reference,
                        (XarInstalledExtension) this.installedXARs.getInstalledExtension(extensionId));
            }

            return getXWikiDocument(reference);
        }

        return null;
    }

    public XWikiDocument getXWikiDocument(DocumentReference reference, XarInstalledExtension extension)
            throws FilterException, IOException, ComponentLookupException, XarException {
        if (reference != null) {
            // Remove the version if any since it does not make sense in a XAR
            DocumentReference documentReference = cleanDocumentReference(reference);

            return getXWikiDocument(documentReference.getWikiReference(),
                    documentReference.getLocalDocumentReference(), extension);
        }

        return null;
    }

    public XWikiDocument getXWikiDocument(WikiReference wikiReference, LocalDocumentReference documentReference,
            XarInstalledExtension extension)
            throws FilterException, IOException, ComponentLookupException, XarException {
        try (XarFile xarFile = new XarFile(new File(extension.getFile().getAbsolutePath()),
                extension.getXarPackage())) {
            return getXWikiDocument(wikiReference, documentReference, xarFile);
        }
    }

    public XWikiDocument getXWikiDocument(InputStream source, WikiReference wikiReference)
            throws FilterException, IOException, ComponentLookupException {
        // Output
        DocumentInstanceOutputProperties documentProperties = new DocumentInstanceOutputProperties();
        documentProperties.setDefaultReference(wikiReference);
        documentProperties.setVersionPreserved(false);

        // Input
        XARInputProperties xarProperties = new XARInputProperties();
        xarProperties.setWithHistory(false);

        return this.documentImporter.importDocument(new DefaultInputStreamInputSource(source), xarProperties,
                documentProperties);
    }

    /**
     * @since 9.3RC1
     */
    public void reset(DocumentReference reference, DocumentReference authorReference) throws FilterException,
            IOException, ComponentLookupException, XarException, XWikiException, XarExtensionExtension {
        Collection<XarInstalledExtension> installedExtensions = getXarInstalledExtensionRepository()
                .getXarInstalledExtensions(reference);
        if (!installedExtensions.isEmpty()) {
            XarInstalledExtension extension = installedExtensions.iterator().next();

            // Remove the version if any since it does not make sense in a XAR
            DocumentReference documentReference = cleanDocumentReference(reference);

            XWikiDocument document = getXWikiDocument(documentReference, extension);

            if (document != null) {
                XWikiContext xcontext = this.xcontextProvider.get();

                // Get database document
                XWikiDocument databaseDocument = xcontext.getWiki().getDocument(documentReference, xcontext);

                // Override data of database document with extension document
                databaseDocument.apply(document, true);
                // Make sure new version will have the right author
                databaseDocument.setAuthorReference(authorReference);
                databaseDocument.setContentAuthorReference(authorReference);
                // Force generating new version
                databaseDocument.setMetaDataDirty(true);
                databaseDocument.setContentDirty(true);

                // Save
                xcontext.getWiki().saveDocument(databaseDocument,
                        "Reset document from extension [" + extension + "]", xcontext);
            } else {
                throw new XarExtensionExtension("Can't find any document with reference [" + documentReference
                        + "] in extension [" + extension.getId() + "]");
            }
        } else {
            throw new XarExtensionExtension(
                    "Can't find any installed extension associated with the document reference [" + reference
                            + "]");
        }
    }

    public List<DocumentReference> getDocumentReferences(Collection<XarEntry> pages,
            PackageConfiguration configuration) throws WikiManagerException {
        List<DocumentReference> documents = new ArrayList<>(pages.size());

        if (configuration.getWiki() == null) {
            Collection<String> wikis = this.wikiDescriptors.getAllIds();

            for (String subwiki : wikis) {
                getDocumentReferencesFromWiki(documents, pages, subwiki, configuration);
            }
        } else {
            getDocumentReferencesFromWiki(documents, pages, configuration.getWiki(), configuration);
        }

        return documents;
    }

    private void getDocumentReferencesFromWiki(List<DocumentReference> documents, Collection<XarEntry> pages,
            String wiki, PackageConfiguration configuration) {
        WikiReference wikiReference = new WikiReference(wiki);

        for (XarEntry xarEntry : pages) {
            // Only delete what should be deleted.
            if (configuration.getEntriesToImport() == null
                    || configuration.getEntriesToImport().contains(xarEntry.getEntryName())) {
                DocumentReference documentReference = new DocumentReference(xarEntry, wikiReference);

                if (!configuration.isSkipMandatorytDocuments() || !isMandatoryDocument(documentReference)) {
                    documents.add(documentReference);
                }
            }
        }
    }
}