info.magnolia.cms.core.DefaultContent.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.cms.core.DefaultContent.java

Source

/**
 * This file Copyright (c) 2003-2012 Magnolia International
 * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the Magnolia
 * Network Agreement and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or MNA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Magnolia Network Agreement (MNA), this file
 * and the accompanying materials are made available under the
 * terms of the MNA which accompanies this distribution, and
 * is available at http://www.magnolia-cms.com/mna.html
 *
 * Any modifications to this file must keep this entire header
 * intact.
 *
 */
package info.magnolia.cms.core;

import info.magnolia.cms.core.version.ContentVersion;
import info.magnolia.cms.core.version.VersionManager;
import info.magnolia.cms.security.AccessDeniedException;
import info.magnolia.cms.util.Rule;
import info.magnolia.context.MgnlContext;
import info.magnolia.context.MgnlContext.Op;
import info.magnolia.jcr.RuntimeRepositoryException;
import info.magnolia.jcr.util.NodeUtil;
import info.magnolia.jcr.wrapper.JCRPropertiesFilteringNodeWrapper;
import info.magnolia.logging.AuditLoggingUtil;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Workspace;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Default, JCR-based, implementation of {@link Content}.
 *
 * @version $Id$
 *
 * @deprecated since 4.5 use jcr.Node instead.
 */
@Deprecated
public class DefaultContent extends AbstractContent {
    private static final Logger log = LoggerFactory.getLogger(DefaultContent.class);

    /**
     * Wrapped jcr node.
     */
    protected Node node;

    /**
     * node metadata.
     */
    private MetaData metaData;

    /**
     * Empty constructor. Should NEVER be used for standard use, test only.
     */
    protected DefaultContent() {
    }

    /**
     * Constructor to get existing node.
     * @param rootNode node to start with
     * @param path absolute (primary) path to this <code>Node</code>
     * @throws PathNotFoundException if the node at <code>path</code> does not exist
     * @throws AccessDeniedException if the current session does not have sufficient access rights to complete the
     * operation
     * @throws RepositoryException if an error occurs
     */
    protected DefaultContent(Node rootNode, String path)
            throws PathNotFoundException, RepositoryException, AccessDeniedException {
        this.setNode(rootNode.getNode(path));
    }

    /**
     * Constructor to get existing node.
     * @param elem initialized node object
     * @throws AccessDeniedException if the current session does not have sufficient access rights to complete the
     * operation
     * @throws RepositoryException if an error occurs
     */
    public DefaultContent(Node node) {
        try {
            this.setNode(node);
        } catch (RepositoryException e) {
            throw new RuntimeRepositoryException(e);
        }
    }

    /**
     * Creates contentNode of type <b>contentType </b> contentType must be defined in item type definition of Magnolia
     * as well as JCR implementation.
     * @param rootNode node to start with
     * @param path absolute (primary) path to this <code>Node</code>
     * @param contentType JCR node type as configured
     * @throws PathNotFoundException if the node at <code>path</code> does not exist
     * @throws RepositoryException if an error occurs
     * @throws AccessDeniedException if the current session does not have sufficient access rights to complete the
     * operation
     */
    protected DefaultContent(Node rootNode, String path, String contentType)
            throws PathNotFoundException, RepositoryException, AccessDeniedException {
        this.setNode(rootNode.addNode(path, contentType));
        // add mix:lockable as default for all nodes created using this manager
        // for version 3.5 we cannot change node type definitions because of compatibility reasons
        // MAGNOLIA-1518
        this.addMixin(ItemType.MIX_LOCKABLE);
        AuditLoggingUtil.log(AuditLoggingUtil.ACTION_CREATE, rootNode.getSession().getWorkspace().getName(),
                this.getItemType(), Path.getAbsolutePath(node.getPath()));
    }

    /**
     * @param node
     */
    protected void setNode(Node node) throws RepositoryException {
        // Default content takes care of filtering jcr properties on its own
        this.node = NodeUtil.deepUnwrap(node, JCRPropertiesFilteringNodeWrapper.class);
    }

    @Override
    public Content getContent(String name)
            throws PathNotFoundException, RepositoryException, AccessDeniedException {
        return wrapAsContent(this.node, name);
    }

    @Override
    public Content createContent(String name, String contentType)
            throws PathNotFoundException, RepositoryException, AccessDeniedException {
        Content content = wrapAsContent(this.node, name, contentType);
        MetaData metaData = content.getMetaData();
        metaData.setCreationDate();
        return content;
    }

    @Override
    public boolean hasNodeData(String name) throws RepositoryException {
        if (this.node.hasProperty(name)) {
            return true;
        }
        if (hasBinaryNode(name)) {
            return true;
        }
        return false;
    }

    @Override
    public NodeData newNodeDataInstance(String name, int type, boolean createIfNotExisting)
            throws AccessDeniedException, RepositoryException {
        // create an empty dummy
        if (!hasNodeData(name) && !createIfNotExisting) {
            // TODO: This could also mean that user just doesn't have permission
            return new NonExistingNodeData(this, name);
        }

        if (type == PropertyType.UNDEFINED) {
            type = determineNodeDataType(name);
        }

        if (type == PropertyType.BINARY) {
            return addBinaryNodeData(name);
        }
        return new DefaultNodeData(this, name);
    }

    protected int determineNodeDataType(String name) {
        // FIXME: maybe delegate to NodeDataImplementations?
        try {
            if (this.node.hasProperty(name)) {
                return this.node.getProperty(name).getType();
            }
            if (hasBinaryNode(name)) {
                return PropertyType.BINARY;
            }
        } catch (RepositoryException e) {
            throw new IllegalStateException("Can't determine property type of [" + getHandle() + "/" + name + "]",
                    e);
        }
        return PropertyType.UNDEFINED;
    }

    @Override
    public MetaData getMetaData() {
        if (this.metaData == null) {
            this.metaData = new MetaData(this.node);
        }
        return this.metaData;
    }

    @Override
    public String getName() {
        try {
            return this.node.getName();
        } catch (RepositoryException e) {
            log.error(e.getMessage(), e);
        }
        return StringUtils.EMPTY;
    }

    @Override
    public Collection<Content> getChildren(ContentFilter filter, String namePattern,
            Comparator<Content> orderCriteria) {
        List<Content> children;
        children = new ArrayList<Content>();

        try {
            final NodeIterator nodeIterator;
            if (namePattern == null) {
                nodeIterator = this.node.getNodes();
            } else {
                nodeIterator = this.node.getNodes(namePattern);
            }

            while (nodeIterator.hasNext()) {
                Node subNode = (Node) nodeIterator.next();
                Content content = wrapAsContent(subNode);
                if (filter.accept(content)) {
                    children.add(content);
                }
            }
        } catch (RepositoryException re) {
            log.error("Exception caught", re);
        }

        if (orderCriteria != null) {
            // stable sort - do not reorder or remove equal items
            Collections.sort(children, orderCriteria);
        }
        return children;
    }

    protected Content wrapAsContent(Node node) {
        return new DefaultContent(node);
    }

    protected Content wrapAsContent(Node node, String name)
            throws AccessDeniedException, PathNotFoundException, RepositoryException {
        return new DefaultContent(node, name);
    }

    protected Content wrapAsContent(Node node, String name, String contentType)
            throws AccessDeniedException, PathNotFoundException, RepositoryException {
        return new DefaultContent(node, name, contentType);
    }

    @Override
    public Collection<NodeData> getNodeDataCollection(String namePattern) {
        final ArrayList<NodeData> all = new ArrayList<NodeData>();
        try {
            all.addAll(getPrimitiveNodeDatas(namePattern));
            all.addAll(getBinaryNodeDatas(namePattern));
        } catch (RepositoryException e) {
            throw new IllegalStateException("Can't read node datas of " + toString(), e);
        }
        return all;
    }

    protected Collection<NodeData> getPrimitiveNodeDatas(String namePattern) throws RepositoryException {
        final Collection<NodeData> nodeDatas = new ArrayList<NodeData>();
        final PropertyIterator propertyIterator;
        if (namePattern == null) {
            propertyIterator = this.node.getProperties();
        } else {
            propertyIterator = this.node.getProperties(namePattern);
        }
        while (propertyIterator.hasNext()) {
            Property property = (Property) propertyIterator.next();
            try {
                if (!property.getName().startsWith("jcr:") && !property.getName().startsWith("mgnl:")) {
                    nodeDatas.add(getNodeData(property.getName()));
                }
            } catch (PathNotFoundException e) {
                log.error("Exception caught", e);
            } catch (AccessDeniedException e) {
                // ignore, simply wont add content in a list
            }
        }
        return nodeDatas;
    }

    @Override
    public boolean hasContent(String name) throws RepositoryException {
        return this.node.hasNode(name);
    }

    @Override
    public String getHandle() {
        try {
            return this.node.getPath();
        } catch (RepositoryException e) {
            log.error("Failed to get handle: " + e.getMessage(), e);
            return StringUtils.EMPTY;
        }
    }

    @Override
    public Content getParent() throws PathNotFoundException, RepositoryException, AccessDeniedException {
        return wrapAsContent(this.node.getParent());
    }

    @Override
    public Content getAncestor(int level) throws PathNotFoundException, RepositoryException, AccessDeniedException {
        if (level > this.getLevel()) {
            throw new PathNotFoundException();
        }
        return wrapAsContent((Node) this.node.getAncestor(level));
    }

    @Override
    public Collection<Content> getAncestors() throws PathNotFoundException, RepositoryException {
        List<Content> allAncestors = new ArrayList<Content>();
        int level = this.getLevel();
        while (level != 0) {
            try {
                allAncestors.add(getAncestor(--level));
            } catch (AccessDeniedException e) {
                // valid
            }
        }
        return allAncestors;
    }

    @Override
    public int getLevel() throws PathNotFoundException, RepositoryException {
        return this.node.getDepth();
    }

    @Override
    public void orderBefore(String srcName, String beforeName) throws RepositoryException {
        this.node.orderBefore(srcName, beforeName);
    }

    @Override
    public int getIndex() throws RepositoryException {
        return this.node.getIndex();
    }

    @Override
    public Node getJCRNode() {
        return this.node;
    }

    @Override
    public boolean isNodeType(String type) {
        return isNodeType(this.node, type);
    }

    /**
     * private Helper method to evaluate primary node type of the given node.
     * @param node
     * @param type
     */
    protected boolean isNodeType(Node node, String type) {
        try {
            return NodeUtil.isNodeType(node, type);
        } catch (RepositoryException re) {
            log.error(re.getMessage());
            log.debug(re.getMessage(), re);
            return false;
        }
    }

    @Override
    public NodeType getNodeType() throws RepositoryException {
        return this.node.getPrimaryNodeType();
    }

    @Override
    public String getNodeTypeName() throws RepositoryException {

        if (this.node.hasProperty(ItemType.JCR_FROZEN_PRIMARY_TYPE)) {
            return this.node.getProperty(ItemType.JCR_FROZEN_PRIMARY_TYPE).getString();
        }
        return this.node.getProperty(ItemType.JCR_PRIMARY_TYPE).getString();
    }

    @Override
    public ItemType getItemType() throws RepositoryException {
        return new ItemType(getNodeTypeName());
    }

    @Override
    public void restore(String versionName, boolean removeExisting)
            throws VersionException, UnsupportedRepositoryOperationException, RepositoryException {
        Version version = this.getVersionHistory().getVersion(versionName);
        this.restore(version, removeExisting);
    }

    @Override
    public void restore(Version version, boolean removeExisting)
            throws VersionException, UnsupportedRepositoryOperationException, RepositoryException {
        VersionManager.getInstance().restore(this, version, removeExisting);
    }

    @Override
    public void restore(Version version, String relPath, boolean removeExisting)
            throws VersionException, UnsupportedRepositoryOperationException, RepositoryException {
        throw new UnsupportedRepositoryOperationException("Not implemented since 3.0 Beta");
    }

    @Override
    public void restoreByLabel(String versionLabel, boolean removeExisting)
            throws VersionException, UnsupportedRepositoryOperationException, RepositoryException {
        // FIXME: !!! why does it do something and then throws exception anyway?
        this.node.restoreByLabel(versionLabel, removeExisting);
        throw new UnsupportedRepositoryOperationException("Not implemented since 3.0 Beta");
    }

    @Override
    public Version addVersion() throws UnsupportedRepositoryOperationException, RepositoryException {
        return VersionManager.getInstance().addVersion(this.getJCRNode());
    }

    @Override
    public Version addVersion(Rule rule) throws UnsupportedRepositoryOperationException, RepositoryException {
        return VersionManager.getInstance().addVersion(this.getJCRNode(), rule);
    }

    public BinaryNodeData addBinaryNodeData(String name) {
        return new BinaryNodeData(this, name);
    }

    /**
     * Returns true if this node is either
     * <ul>
     * <li/>versionable and currently checked-out, <li/>non-versionable and its nearest versionable ancestor is
     * checked-out or <li/>non-versionable and it has no versionable ancestor.
     * </ul>
     * Returns false if this node is either
     * <ul>
     * <li/>versionable and currently checked-in or <li/>non-versionable and its nearest versionable ancestor is
     * checked-in.
     * </ul>
     * @return true if the node is checked out
     * @throws RepositoryException
     */
    protected boolean isCheckedOut() throws RepositoryException {
        return this.node.isCheckedOut();
    }

    @Override
    public boolean isModified() {
        return this.node.isModified();
    }

    @Override
    public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException {
        return VersionManager.getInstance().getVersionHistory(this.getJCRNode());
    }

    @Override
    public VersionIterator getAllVersions() throws UnsupportedRepositoryOperationException, RepositoryException {
        return VersionManager.getInstance().getAllVersions(this.getJCRNode());
    }

    @Override
    public ContentVersion getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException {
        return new ContentVersion(VersionManager.getInstance().getBaseVersion(this.getJCRNode()), this);
    }

    @Override
    public ContentVersion getVersionedContent(Version version) throws RepositoryException {
        return new ContentVersion(version, this);
    }

    @Override
    public ContentVersion getVersionedContent(String versionName) throws RepositoryException {
        return new ContentVersion(VersionManager.getInstance().getVersion(this.getJCRNode(), versionName), this);
    }

    @Override
    public void removeVersionHistory() throws AccessDeniedException, RepositoryException {
        VersionManager.getInstance().removeVersionHistory(this.node);
    }

    @Override
    public void save() throws RepositoryException {
        this.node.save();
    }

    @Override
    public void delete() throws RepositoryException {
        final String nodePath = Path.getAbsolutePath(this.node.getPath());
        final String workspaceName = this.node.getSession().getWorkspace().getName();
        log.debug("removing {} from {}", this.node.getPath(), workspaceName);
        final ItemType nodeType = this.getItemType();
        if (!workspaceName.startsWith("mgnl")) {
            MgnlContext.doInSystemContext(new Op<Void, RepositoryException>() {
                @Override
                public Void exec() throws RepositoryException {
                    try {
                        final String uuid = node.getUUID();
                        Session session = MgnlContext.getJCRSession("mgnlVersion");
                        Node versionedNode = session.getNodeByIdentifier(uuid);
                        log.debug("Located versioned node {}({})", uuid, nodePath);

                        VersionHistory history = versionedNode.getVersionHistory();

                        log.debug("Removing versioned node {}({})", uuid, nodePath);
                        versionedNode.remove();
                        session.save();

                        VersionIterator iter = history.getAllVersions();
                        // skip root version. It can't be deleted manually, but will be cleaned up automatically after removing all other versions (see JCR-134)
                        iter.nextVersion();
                        while (iter.hasNext()) {
                            Version version = iter.nextVersion();
                            log.debug("removing version {}", version.getName());
                            history.removeVersion(version.getName());
                        }
                        // at this point history should be deleted automatically (at least on JR)
                    } catch (ItemNotFoundException e) {
                        // doesn't exist in version store, ignore
                    } catch (UnsupportedRepositoryOperationException e) {
                        // not versionable or not referenceable ... either way ignore
                    }
                    return null;
                }
            });
        }
        this.node.remove();
        AuditLoggingUtil.log(AuditLoggingUtil.ACTION_DELETE, workspaceName, nodeType, nodePath);
    }

    @Override
    public void refresh(boolean keepChanges) throws RepositoryException {
        this.node.refresh(keepChanges);
    }

    @Override
    public String getUUID() {
        try {
            return this.node.getUUID();
        } catch (UnsupportedOperationException e) {
            log.error(e.getMessage());
        } catch (RepositoryException re) {
            log.error("Exception caught", re);
        }
        return StringUtils.EMPTY;
    }

    @Override
    public void addMixin(String type) throws RepositoryException {
        // TODO: there seems to be bug somewhere as we are able to add mixins even when the method below returns false
        if (!this.node.canAddMixin(type)) {
            log.debug("Node - " + this.node.getPath() + " does not allow mixin type - " + type);
        }
        try {
            this.node.addMixin(type);
        } catch (Exception e) {
            log.error("Failed to add  mixin type - " + type + " to a node " + this.node.getPath());
        }
    }

    @Override
    public void removeMixin(String type) throws RepositoryException {
        this.node.removeMixin(type);
    }

    @Override
    public NodeType[] getMixinNodeTypes() throws RepositoryException {
        return this.node.getMixinNodeTypes();
    }

    @Override
    public Lock lock(boolean isDeep, boolean isSessionScoped) throws LockException, RepositoryException {
        return this.node.lock(isDeep, isSessionScoped);
    }

    @Override
    public Lock lock(boolean isDeep, boolean isSessionScoped, long yieldFor)
            throws LockException, RepositoryException {
        long finalTime = System.currentTimeMillis() + yieldFor;
        LockException lockException = null;
        while (System.currentTimeMillis() <= finalTime) {
            try {
                return this.node.lock(isDeep, isSessionScoped);
            } catch (LockException e) {
                // its not an exception yet, still got time
                lockException = e;
            }
            Thread.yield();
        }
        // could not get lock
        throw lockException;
    }

    @Override
    public Lock getLock() throws LockException, RepositoryException {
        return this.node.getLock();
    }

    @Override
    public void unlock() throws LockException, RepositoryException {
        this.node.unlock();
    }

    @Override
    public boolean holdsLock() throws RepositoryException {
        return this.node.holdsLock();
    }

    @Override
    public boolean isLocked() throws RepositoryException {
        return this.node.isLocked();
    }

    @Override
    public boolean hasMetaData() {
        return true;
    }

    @Override
    public boolean hasMixin(String mixinName) throws RepositoryException {
        if (StringUtils.isBlank(mixinName)) {
            throw new IllegalArgumentException("Mixin name can't be empty.");
        }
        for (NodeType type : getMixinNodeTypes()) {
            if (mixinName.equals(type.getName())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public HierarchyManager getHierarchyManager() {
        try {
            return createHierarchyManager(node.getSession());
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Workspace getWorkspace() throws RepositoryException {
        return node.getSession().getWorkspace();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null || !(obj instanceof DefaultContent)) {
            return false;
        }
        DefaultContent otherContent = (DefaultContent) obj;
        return getJCRNode().equals(otherContent.getJCRNode());
    }

    protected HierarchyManager createHierarchyManager(Session session) {
        return new DefaultHierarchyManager(session);
    }

    protected boolean hasBinaryNode(String name) throws RepositoryException {
        return this.node.hasNode(name) && (this.node.getNode(name).isNodeType(ItemType.NT_RESOURCE)
                || (this.node.hasProperty("jcr:frozenPrimaryType") && this.node.getNode(name)
                        .getProperty("jcr:frozenPrimaryType").getValue().getString().equals(ItemType.NT_RESOURCE)));
    }

}