org.hippoecm.frontend.plugins.console.menu.content.ContentImportDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.hippoecm.frontend.plugins.console.menu.content.ContentImportDialog.java

Source

/*
 *  Copyright 2008-2014 Hippo B.V. (http://www.onehippo.com)
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.hippoecm.frontend.plugins.console.menu.content;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipFile;

import javax.jcr.ImportUUIDBehavior;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.util.value.IValueMap;
import org.apache.wicket.util.value.ValueMap;
import org.hippoecm.frontend.dialog.AbstractDialog;
import org.hippoecm.frontend.model.IModelReference;
import org.hippoecm.frontend.model.JcrNodeModel;
import org.hippoecm.frontend.plugins.console.menu.t9ids.GenerateNewTranslationIdsVisitor;
import org.hippoecm.frontend.session.UserSession;
import org.hippoecm.frontend.widgets.LabelledBooleanFieldWidget;
import org.hippoecm.repository.api.HippoSession;
import org.hippoecm.repository.api.ImportReferenceBehavior;
import org.onehippo.repository.util.ZipFileContentResourceLoader;
import org.onehippo.repository.xml.ContentResourceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ContentImportDialog extends AbstractDialog<Node> {
    private static final long serialVersionUID = 1L;
    private static final Logger log = LoggerFactory.getLogger(ContentImportDialog.class);

    public class LookupHashMap<K, V> extends HashMap<K, V> {
        private static final long serialVersionUID = 9065806784464553409L;

        public K getFirstKey(Object value) {
            if (value == null) {
                return null;
            }
            for (Map.Entry<K, V> e : entrySet()) {
                if (value.equals(e.getValue())) {
                    return e.getKey();
                }
            }
            return null;
        }
    }

    private final LookupHashMap<Integer, String> uuidOpts = new LookupHashMap<Integer, String>();
    private final LookupHashMap<Integer, String> derefOpts = new LookupHashMap<Integer, String>();

    private final JcrNodeModel nodeModel;
    private FileUploadField fileUploadField;
    private String xmlInput;
    private Boolean saveBehavior = false;
    private Boolean generate = false;

    // hard coded defaults
    private String uuidBehavior = "Create new uuids on import";
    private String derefBehavior = "Throw error when not found";

    private void InitMaps() {
        uuidOpts.put(ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING,
                "Remove existing node with same uuid");
        uuidOpts.put(ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING,
                "Replace existing node with same uuid");
        uuidOpts.put(ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW, "Throw error on uuid collision");
        uuidOpts.put(ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW, "Create new uuids on import");

        derefOpts.put(ImportReferenceBehavior.IMPORT_REFERENCE_NOT_FOUND_REMOVE, "Remove reference when not found");
        derefOpts.put(ImportReferenceBehavior.IMPORT_REFERENCE_NOT_FOUND_THROW, "Throw error when not found");
        derefOpts.put(ImportReferenceBehavior.IMPORT_REFERENCE_NOT_FOUND_TO_ROOT,
                "Add reference to root node when not found");

    }

    public ContentImportDialog(IModelReference<Node> modelReference) {
        InitMaps();
        this.nodeModel = (JcrNodeModel) modelReference.getModel();

        DropDownChoice<String> uuid = new DropDownChoice<>("uuidBehaviors",
                new PropertyModel<String>(this, "uuidBehavior"), new ArrayList<>(uuidOpts.values()));
        DropDownChoice<String> reference = new DropDownChoice<>("derefBehaviors",
                new PropertyModel<String>(this, "derefBehavior"), new ArrayList<>(derefOpts.values()));
        LabelledBooleanFieldWidget save = new LabelledBooleanFieldWidget("saveBehavior",
                new PropertyModel<Boolean>(this, "saveBehavior"), Model.of("Immediate save after import"));

        add(uuid.setNullValid(false).setRequired(true));
        add(reference.setNullValid(false).setRequired(true));
        add(save);

        LabelledBooleanFieldWidget generate = new LabelledBooleanFieldWidget("generate",
                new PropertyModel<Boolean>(this, "generate"),
                Model.of("Generate new translation ids (only when adding a node)"));
        add(generate);

        // file upload
        setMultiPart(true);
        setNonAjaxSubmit();
        add(fileUploadField = new FileUploadField("fileInput"));

        //xml import
        add(new TextArea<String>("xmlInput", new PropertyModel<String>(this, "xmlInput")));

        setOkLabel("Import");
        setFocus(uuid);

        try {
            String path = this.nodeModel.getNode().getPath();
            add(new Label("message", new StringResourceModel("dialog.message", this, null, null, path)));

            //info("Import content from a file to node: " + nodeModel.getNode().getPath());
        } catch (RepositoryException e) {
            log.error("Error getting node from model for contant import", e);
            throw new RuntimeException("Error getting node from model for contant import: " + e.getMessage());
        }
    }

    public IModel<String> getTitle() {
        return new Model<>("XML Import");
    }

    @Override
    protected void onOk() {
        final FileUpload upload = fileUploadField.getFileUpload();

        int uuidOpt = uuidOpts.getFirstKey(uuidBehavior);
        int derefOpt = derefOpts.getFirstKey(derefBehavior);

        try {

            if (upload == null && StringUtils.isEmpty(xmlInput)) {
                warn("No file was uploaded and no xml input provided. Nothing to import");
                return;
            }

            String absPath = nodeModel.getNode().getPath();

            // If save-after-import is enabled and the import fails, we do a Session.refresh(false) to revert any
            // changes done by the import. However, any changes done *before* the import will then also be lost.
            // We therefore have to save before importing, so all changes before the import are persisted regardless
            // of what happens during the import.
            if (saveBehavior) {
                nodeModel.getNode().getSession().save();
            }

            File tempFile = null;
            ZipFile zipFile = null;
            InputStream in = null;
            InputStream esvIn = null;
            OutputStream out = null;
            try {
                final HippoSession session = (HippoSession) UserSession.get().getJcrSession();
                List<String> nodesBefore = new ArrayList<>();

                if (generate) {
                    for (NodeIterator nodeIterator = nodeModel.getNode().getNodes(); nodeIterator.hasNext();) {
                        final Node node = nodeIterator.nextNode();
                        nodesBefore.add(node.getPath());
                    }
                }

                if (upload != null) {
                    final String fileName = upload.getClientFileName();
                    if (fileName.endsWith(".zip")) {
                        tempFile = File.createTempFile("package", "zip");
                        out = new FileOutputStream(tempFile);
                        in = upload.getInputStream();
                        IOUtils.copy(in, out);
                        out.close();
                        out = null;
                        zipFile = new ZipFile(tempFile);
                        ContentResourceLoader contentResourceLoader = new ZipFileContentResourceLoader(zipFile);
                        esvIn = contentResourceLoader.getResourceAsStream("esv.xml");
                        session.importEnhancedSystemViewXML(absPath, esvIn, uuidOpt, derefOpt,
                                contentResourceLoader);
                    } else if (fileName.endsWith(".xml")) {
                        in = new BufferedInputStream(upload.getInputStream());
                        session.importEnhancedSystemViewXML(absPath, in, uuidOpt, derefOpt, null);
                    } else {
                        warn("Unrecognized file: only .xml and .zip can be processed");
                        return;
                    }
                } else {
                    in = new ByteArrayInputStream(xmlInput.getBytes("UTF-8"));
                    session.importEnhancedSystemViewXML(absPath, in, uuidOpt, derefOpt, null);
                }

                if (generate) {
                    final Node newNode = findNewNode(nodesBefore, nodeModel.getNode());
                    if (newNode != null) {
                        log.debug("Applying new translation ids on node: " + newNode.getPath());
                        newNode.accept(new GenerateNewTranslationIdsVisitor());
                    }
                }

                if (saveBehavior) {
                    nodeModel.getNode().getSession().save();
                }
            } finally {
                if (saveBehavior) {
                    nodeModel.getNode().getSession().refresh(false);
                }
                IOUtils.closeQuietly(out);
                IOUtils.closeQuietly(esvIn);
                IOUtils.closeQuietly(in);
                if (zipFile != null) {
                    try {
                        zipFile.close();
                    } catch (Exception ignore) {
                    }
                }
                FileUtils.deleteQuietly(tempFile);
            }

        } catch (RepositoryException ex) {
            log.error("Error initializing content in '" + nodeModel.getItemModel().getPath() + "' : "
                    + ex.getMessage(), ex);
            error("Import failed: " + ex.getMessage());
        } catch (IOException ex) {
            log.error("IOException initializing content in '" + nodeModel.getItemModel().getPath() + "' : "
                    + ex.getMessage(), ex);
            error("Import failed: " + ex.getMessage());
        }
    }

    /**
     * Check all childnodes after the import and find the new node
     * @param nodesBefore list of nodepaths of the childnodes before the import
     * @param node the node on which the import has been done
     * @return the new child node or null (e.g. in case a merge was done and no new node was created)
     * @throws RepositoryException if iterating child nodes goes wrong
     */
    private Node findNewNode(final List<String> nodesBefore, final Node node) throws RepositoryException {
        // iterate all childnodes after the import
        for (final NodeIterator nodesAfter = node.getNodes(); nodesAfter.hasNext();) {
            final Node afterNode = nodesAfter.nextNode();
            // if its path is new, it is the new node
            if (!nodesBefore.contains(afterNode.getPath())) {
                return afterNode;
            }
        }
        return null;
    }

    public void setDerefBehavior(String derefBehavior) {
        this.derefBehavior = derefBehavior;
    }

    public String getDerefBehavior() {
        return derefBehavior;
    }

    public void setUuidBehavior(String uuidBehavior) {
        this.uuidBehavior = uuidBehavior;
    }

    public String getUuidBehavior() {
        return uuidBehavior;
    }

    public void setSaveBehavior(boolean saveBehavior) {
        this.saveBehavior = saveBehavior;
    }

    public boolean getSaveBehavior() {
        return saveBehavior;
    }

    public String getXmlInput() {
        return xmlInput;
    }

    public void setXmlInput(String xmlInput) {
        this.xmlInput = xmlInput;
    }

    @Override
    public IValueMap getProperties() {
        return new ValueMap("width=855,height=460").makeImmutable();
    }

}