org.nuxeo.ecm.core.io.impl.AbstractDocumentModelWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.core.io.impl.AbstractDocumentModelWriter.java

Source

/*
 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * 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.
 *
 * Contributors:
 *     bstefanescu
 */
package org.nuxeo.ecm.core.io.impl;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.nuxeo.common.collections.PrimitiveArrays;
import org.nuxeo.common.utils.Path;
import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.Blobs;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentLocation;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.ecm.core.api.impl.DocumentModelImpl;
import org.nuxeo.ecm.core.api.security.ACE;
import org.nuxeo.ecm.core.api.security.ACL;
import org.nuxeo.ecm.core.api.security.ACP;
import org.nuxeo.ecm.core.api.security.impl.ACLImpl;
import org.nuxeo.ecm.core.api.security.impl.ACPImpl;
import org.nuxeo.ecm.core.io.ExportConstants;
import org.nuxeo.ecm.core.io.ExportedDocument;
import org.nuxeo.ecm.core.schema.SchemaManager;
import org.nuxeo.ecm.core.schema.TypeConstants;
import org.nuxeo.ecm.core.schema.types.ComplexType;
import org.nuxeo.ecm.core.schema.types.CompositeType;
import org.nuxeo.ecm.core.schema.types.Field;
import org.nuxeo.ecm.core.schema.types.JavaTypes;
import org.nuxeo.ecm.core.schema.types.ListType;
import org.nuxeo.ecm.core.schema.types.Schema;
import org.nuxeo.ecm.core.schema.types.Type;
import org.nuxeo.ecm.core.schema.utils.DateParser;
import org.nuxeo.ecm.core.versioning.VersioningService;
import org.nuxeo.runtime.api.Framework;

/**
 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
 */
// TODO: improve it ->
// modify core session to add a batch create method and use it
public abstract class AbstractDocumentModelWriter extends AbstractDocumentWriter {

    private static final Log log = LogFactory.getLog(AbstractDocumentModelWriter.class);

    protected CoreSession session;

    protected Path root;

    private int saveInterval;

    protected int unsavedDocuments = 0;

    private final Map<DocumentLocation, DocumentLocation> translationMap = new HashMap<DocumentLocation, DocumentLocation>();

    /**
     * @param session the session to the repository where to write
     * @param parentPath where to write the tree. this document will be used as the parent of all top level documents
     *            passed as input. Note that you may have
     */
    protected AbstractDocumentModelWriter(CoreSession session, String parentPath) {
        this(session, parentPath, 10);
    }

    protected AbstractDocumentModelWriter(CoreSession session, String parentPath, int saveInterval) {
        if (session == null) {
            throw new IllegalArgumentException("null session");
        }
        this.session = session;
        this.saveInterval = saveInterval;
        root = new Path(parentPath);
    }

    public Map<DocumentLocation, DocumentLocation> getTranslationMap() {
        return translationMap;
    }

    protected void saveIfNeeded() {
        if (unsavedDocuments >= saveInterval) {
            session.save();
            unsavedDocuments = 0;
        }
    }

    @Override
    public void close() {
        if (unsavedDocuments > 0) {
            session.save();
        }
        session = null;
        root = null;
    }

    /**
     * Creates a new document given its path.
     * <p>
     * The parent of this document is assumed to exist.
     *
     * @param xdoc the document containing
     * @param toPath the path of the doc to create
     */
    protected DocumentModel createDocument(ExportedDocument xdoc, Path toPath) {
        Path parentPath = toPath.removeLastSegments(1);
        String name = toPath.lastSegment();

        DocumentModel doc = new DocumentModelImpl(parentPath.toString(), name, xdoc.getType());

        // set lifecycle state at creation
        Element system = xdoc.getDocument().getRootElement().element(ExportConstants.SYSTEM_TAG);
        String lifeCycleState = system.element(ExportConstants.LIFECYCLE_STATE_TAG).getText();
        doc.putContextData("initialLifecycleState", lifeCycleState);

        // loadFacets before schemas so that additional schemas are not skipped
        loadFacetsInfo(doc, xdoc.getDocument());

        // then load schemas data
        loadSchemas(xdoc, doc, xdoc.getDocument());

        if (doc.hasSchema("uid")) {
            doc.putContextData(VersioningService.SKIP_VERSIONING, true);
        }

        beforeCreateDocument(doc);
        doc = session.createDocument(doc);

        // load into the document the system properties, document needs to exist
        loadSystemInfo(doc, xdoc.getDocument());

        unsavedDocuments += 1;
        saveIfNeeded();

        return doc;
    }

    /**
     * @since 8.4
     */
    protected void beforeCreateDocument(DocumentModel doc) {
        // Empty default implementation
    }

    /**
     * Updates an existing document.
     */
    protected DocumentModel updateDocument(ExportedDocument xdoc, DocumentModel doc) {
        // load schemas data
        loadSchemas(xdoc, doc, xdoc.getDocument());

        loadFacetsInfo(doc, xdoc.getDocument());

        beforeSaveDocument(doc);
        doc = session.saveDocument(doc);

        unsavedDocuments += 1;
        saveIfNeeded();

        return doc;
    }

    /**
     * @since 8.4
     */
    protected void beforeSaveDocument(DocumentModel doc) {
        // Empty default implementation
    }

    public int getSaveInterval() {
        return saveInterval;
    }

    public void setSaveInterval(int saveInterval) {
        this.saveInterval = saveInterval;
    }

    @SuppressWarnings("unchecked")
    protected boolean loadFacetsInfo(DocumentModel docModel, Document doc) {
        boolean added = false;
        Element system = doc.getRootElement().element(ExportConstants.SYSTEM_TAG);
        if (system == null) {
            return false;
        }

        Iterator<Element> facets = system.elementIterator(ExportConstants.FACET_TAG);
        while (facets.hasNext()) {
            Element element = facets.next();
            String facet = element.getTextTrim();

            SchemaManager schemaManager = Framework.getService(SchemaManager.class);
            CompositeType facetType = schemaManager.getFacet(facet);

            if (facetType == null) {
                log.warn("The document " + docModel.getName() + " with id=" + docModel.getId() + " and type="
                        + docModel.getDocumentType().getName() + " contains the facet '" + facet
                        + "', which is not registered as available in the schemaManager. This facet will be ignored.");
                if (log.isDebugEnabled()) {
                    log.debug("Available facets: " + Arrays.toString(schemaManager.getFacets()));
                }
                continue;
            }

            if (!docModel.hasFacet(facet)) {
                docModel.addFacet(facet);
                added = true;
            }
        }

        return added;
    }

    @SuppressWarnings("unchecked")
    protected void loadSystemInfo(DocumentModel docModel, Document doc) {
        Element system = doc.getRootElement().element(ExportConstants.SYSTEM_TAG);

        Element accessControl = system.element(ExportConstants.ACCESS_CONTROL_TAG);
        if (accessControl == null) {
            return;
        }
        Iterator<Element> it = accessControl.elementIterator(ExportConstants.ACL_TAG);
        while (it.hasNext()) {
            Element element = it.next();
            // import only the local acl
            if (ACL.LOCAL_ACL.equals(element.attributeValue(ExportConstants.NAME_ATTR))) {
                // this is the local ACL - import it
                List<Element> entries = element.elements();
                int size = entries.size();
                if (size > 0) {
                    ACP acp = new ACPImpl();
                    ACL acl = new ACLImpl(ACL.LOCAL_ACL);
                    acp.addACL(acl);
                    for (Element el : entries) {
                        String username = el.attributeValue(ExportConstants.PRINCIPAL_ATTR);
                        String permission = el.attributeValue(ExportConstants.PERMISSION_ATTR);
                        String grant = el.attributeValue(ExportConstants.GRANT_ATTR);
                        String creator = el.attributeValue(ExportConstants.CREATOR_ATTR);
                        String beginStr = el.attributeValue(ExportConstants.BEGIN_ATTR);
                        Calendar begin = null;
                        if (beginStr != null) {
                            Date date = DateParser.parseW3CDateTime(beginStr);
                            begin = new GregorianCalendar();
                            begin.setTimeInMillis(date.getTime());
                        }
                        String endStr = el.attributeValue(ExportConstants.END_ATTR);
                        Calendar end = null;
                        if (endStr != null) {
                            Date date = DateParser.parseW3CDateTime(endStr);
                            end = new GregorianCalendar();
                            end.setTimeInMillis(date.getTime());
                        }
                        ACE ace = ACE.builder(username, permission).isGranted(Boolean.parseBoolean(grant))
                                .creator(creator).begin(begin).end(end).build();
                        acl.add(ace);
                    }
                    acp.addACL(acl);
                    session.setACP(docModel.getRef(), acp, false);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    protected void loadSchemas(ExportedDocument xdoc, DocumentModel docModel, Document doc) {
        SchemaManager schemaMgr = Framework.getLocalService(SchemaManager.class);
        Iterator<Element> it = doc.getRootElement().elementIterator(ExportConstants.SCHEMA_TAG);
        while (it.hasNext()) {
            Element element = it.next();
            String schemaName = element.attributeValue(ExportConstants.NAME_ATTR);
            Schema schema = schemaMgr.getSchema(schemaName);
            if (schema == null) {
                log.warn("The document " + docModel.getName() + " with id=" + docModel.getId() + " and type="
                        + docModel.getDocumentType() + " contains the schema '" + schemaName
                        + "', which is not registered as available in the schemaManager. This schema will be ignored.");
                if (log.isDebugEnabled()) {
                    log.debug("Available schemas: " + Arrays.toString(schemaMgr.getSchemas()));
                }
                continue;
            }
            loadSchema(xdoc, schema, docModel, element);
        }
    }

    @SuppressWarnings("unchecked")
    protected static void loadSchema(ExportedDocument xdoc, Schema schema, DocumentModel doc,
            Element schemaElement) {
        String schemaName = schemaElement.attributeValue(ExportConstants.NAME_ATTR);
        Map<String, Object> data = new HashMap<>();
        Iterator<Element> it = schemaElement.elementIterator();
        while (it.hasNext()) {
            Element element = it.next();
            String name = element.getName();
            Field field = schema.getField(name);
            if (field == null) {
                throw new NuxeoException(
                        "Invalid input document. No such property was found " + name + " in schema " + schemaName);
            }
            Object value = getElementData(xdoc, element, field.getType());
            data.put(name, value);
        }
        doc.setProperties(schemaName, data);
    }

    protected static Class getFieldClass(Type fieldType) {
        Class klass = JavaTypes.getClass(fieldType);
        // for enumerated SimpleTypes we may need to lookup on the supertype
        // we do the recursion here and not in JavaTypes to avoid potential impacts
        if (klass == null && fieldType.getSuperType() != null) {
            return getFieldClass(fieldType.getSuperType());
        }
        return klass;
    }

    @SuppressWarnings("unchecked")
    private static Object getElementData(ExportedDocument xdoc, Element element, Type type) {
        // empty xml tag must be null value (not empty string)
        if (!element.hasContent()) {
            return null;
        }
        if (type.isSimpleType()) {
            return type.decode(element.getText());
        } else if (type.isListType()) {
            ListType ltype = (ListType) type;
            List<Object> list = new ArrayList<>();
            Iterator<Element> it = element.elementIterator();
            while (it.hasNext()) {
                Element el = it.next();
                list.add(getElementData(xdoc, el, ltype.getFieldType()));
            }
            Type ftype = ltype.getFieldType();
            if (ftype.isSimpleType()) { // these are stored as arrays
                Class klass = getFieldClass(ftype);
                if (klass.isPrimitive()) {
                    return PrimitiveArrays.toPrimitiveArray(list, klass);
                } else {
                    return list.toArray((Object[]) Array.newInstance(klass, list.size()));
                }
            }
            return list;
        } else {
            ComplexType ctype = (ComplexType) type;
            if (TypeConstants.isContentType(ctype)) {
                String mimeType = element.elementText(ExportConstants.BLOB_MIME_TYPE);
                String encoding = element.elementText(ExportConstants.BLOB_ENCODING);
                String content = element.elementTextTrim(ExportConstants.BLOB_DATA);
                String filename = element.elementTextTrim(ExportConstants.BLOB_FILENAME);
                if ((content == null || content.length() == 0) && (mimeType == null || mimeType.length() == 0)) {
                    return null; // remove blob
                }
                Blob blob = null;
                if (xdoc.hasExternalBlobs()) {
                    blob = xdoc.getBlob(content);
                }
                if (blob == null) { // maybe the blob is embedded in Base64
                    // encoded data
                    byte[] bytes = Base64.decodeBase64(content);
                    blob = Blobs.createBlob(bytes);
                }
                blob.setMimeType(mimeType);
                blob.setEncoding(encoding);
                blob.setFilename(filename);
                return blob;
            } else { // a complex type
                Map<String, Object> map = new HashMap<>();
                Iterator<Element> it = element.elementIterator();
                while (it.hasNext()) {
                    Element el = it.next();
                    String name = el.getName();
                    Object value = getElementData(xdoc, el, ctype.getField(el.getName()).getType());
                    map.put(name, value);
                }
                return map;
            }
        }
    }

}