org.pentaho.platform.repository2.unified.jcr.JcrRepositoryFileDaoInst.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.platform.repository2.unified.jcr.JcrRepositoryFileDaoInst.java

Source

/*
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License, version 2 as published by the Free Software
 * Foundation.
 *
 * You should have received a copy of the GNU General Public License along with this
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html
 * or from the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * This program 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 General Public License for more details.
 *
 *
 * Copyright 2006 - 2013 Pentaho Corporation.  All rights reserved.
 */

package org.pentaho.platform.repository2.unified.jcr;

import org.apache.commons.lang.ArrayUtils;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.locale.IPentahoLocale;
import org.pentaho.platform.api.repository2.unified.IRepositoryAccessVoterManager;
import org.pentaho.platform.api.repository2.unified.IRepositoryDefaultAclHandler;
import org.pentaho.platform.api.repository2.unified.IRepositoryFileData;
import org.pentaho.platform.api.repository2.unified.RepositoryFile;
import org.pentaho.platform.api.repository2.unified.RepositoryFileAcl;
import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission;
import org.pentaho.platform.api.repository2.unified.RepositoryFileTree;
import org.pentaho.platform.api.repository2.unified.RepositoryRequest;
import org.pentaho.platform.api.repository2.unified.UnifiedRepositoryException;
import org.pentaho.platform.api.repository2.unified.VersionSummary;
import org.pentaho.platform.api.repository2.unified.data.node.DataNode;
import org.pentaho.platform.api.repository2.unified.data.node.NodeRepositoryFileData;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.repository2.messages.Messages;
import org.pentaho.platform.repository2.unified.IRepositoryFileAclDao;
import org.pentaho.platform.repository2.unified.ServerRepositoryPaths;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;

import javax.jcr.Item;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.Lock;

/**
 * CRUD operations against JCR. Note that there is no access control in this class (implicit or explicit).
 * 
 * @author mlowery
 */
public class JcrRepositoryFileDaoInst {

    // ~ Static fields/initializers
    // ======================================================================================

    // ~ Instance fields
    // =================================================================================================
    private List<ITransformer<IRepositoryFileData>> transformers;

    private ILockHelper lockHelper;

    private IDeleteHelper deleteHelper;

    private IPathConversionHelper pathConversionHelper;

    private IRepositoryFileAclDao aclDao;

    private IRepositoryDefaultAclHandler defaultAclHandler;

    private IRepositoryAccessVoterManager accessVoterManager;

    private String repositoryAdminUsername;

    // ~ Constructors
    // ====================================================================================================

    public JcrRepositoryFileDaoInst(final List<ITransformer<IRepositoryFileData>> transformers,
            final ILockHelper lockHelper, final IDeleteHelper deleteHelper,
            final IPathConversionHelper pathConversionHelper, final IRepositoryFileAclDao aclDao,
            final IRepositoryDefaultAclHandler defaultAclHandler, String repositoryAdminUsername) {
        super();
        Assert.notNull(transformers);
        this.transformers = transformers;
        this.lockHelper = lockHelper;
        this.deleteHelper = deleteHelper;
        this.pathConversionHelper = pathConversionHelper;
        this.aclDao = aclDao;
        this.defaultAclHandler = defaultAclHandler;
        this.repositoryAdminUsername = repositoryAdminUsername;
    }

    public JcrRepositoryFileDaoInst(final List<ITransformer<IRepositoryFileData>> transformers,
            final ILockHelper lockHelper, final IDeleteHelper deleteHelper,
            final IPathConversionHelper pathConversionHelper, final IRepositoryFileAclDao aclDao,
            final IRepositoryDefaultAclHandler defaultAclHandler,
            final IRepositoryAccessVoterManager accessVoterManager, String repositoryAdminUsername) {
        this(transformers, lockHelper, deleteHelper, pathConversionHelper, aclDao, defaultAclHandler,
                repositoryAdminUsername);
        this.accessVoterManager = accessVoterManager;
    }

    public void setDefaultAclHandler(IRepositoryDefaultAclHandler defaultAclHandler) {
        this.defaultAclHandler = defaultAclHandler;
    }

    public VersionSummary getVersionSummary(final Session session, final Serializable fileId,
            final Serializable versionId) throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        return JcrRepositoryFileUtils.getVersionSummary(session, pentahoJcrConstants, fileId, versionId);
    }

    public RepositoryFile internalCreateFolder(final Session session, final Serializable parentFolderId,
            final RepositoryFile folder, final RepositoryFileAcl acl, final String versionMessage)
            throws RepositoryException {
        if (!hasAccess(session, parentFolderId, RepositoryFilePermission.WRITE)) {
            return null;
        }
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                parentFolderId);
        Node folderNode = JcrRepositoryFileUtils.createFolderNode(session, pentahoJcrConstants, parentFolderId,
                folder);
        // create a temporary folder object with correct path for default acl purposes.
        String path = JcrRepositoryFileUtils.getAbsolutePath(session, pentahoJcrConstants, folderNode);
        RepositoryFile tmpFolder = new RepositoryFile.Builder(folder).path(path).build();
        // we must create the acl during checkout
        JcrRepositoryFileAclUtils.createAcl(session, pentahoJcrConstants, folderNode.getIdentifier(),
                acl == null ? defaultAclHandler.createDefaultAcl(tmpFolder) : acl);
        session.save();
        if (folder.isVersioned()) {
            JcrRepositoryFileUtils.checkinNearestVersionableNodeIfNecessary(session, pentahoJcrConstants,
                    folderNode, versionMessage);
        }
        JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                parentFolderId,
                Messages.getInstance().getString("JcrRepositoryFileDao.USER_0001_VER_COMMENT_ADD_FOLDER", //$NON-NLS-1$
                        folder.getName(), (parentFolderId == null ? "root" : parentFolderId.toString()))); //$NON-NLS-1$
        return JcrRepositoryFileUtils.nodeToFile(session, pentahoJcrConstants, pathConversionHelper, lockHelper,
                folderNode);
    }

    public RepositoryFile internalCreateFile(Session session, final Serializable parentFolderId,
            final RepositoryFile file, final IRepositoryFileData content, final RepositoryFileAcl acl,
            final String versionMessage) throws RepositoryException {
        if (!hasAccess(session, parentFolderId, RepositoryFilePermission.WRITE)) {
            return null;
        }
        // Assert.notNull(content);
        DataNode emptyDataNode = new DataNode(file.getName());
        emptyDataNode.setProperty(" ", "content"); //$NON-NLS-1$ //$NON-NLS-2$
        final IRepositoryFileData emptyContent = new NodeRepositoryFileData(emptyDataNode);

        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                parentFolderId);
        Node fileNode = JcrRepositoryFileUtils.createFileNode(session, pentahoJcrConstants, parentFolderId, file,
                content == null ? emptyContent : content,
                findTransformerForWrite(content == null ? emptyContent.getClass() : content.getClass()));
        // create a tmp file with correct path for default acl creation purposes.
        String path = JcrRepositoryFileUtils.getAbsolutePath(session, pentahoJcrConstants, fileNode);
        RepositoryFile tmpFile = new RepositoryFile.Builder(file).path(path).build();
        // we must create the acl during checkout
        aclDao.createAcl(fileNode.getIdentifier(), acl == null ? defaultAclHandler.createDefaultAcl(tmpFile) : acl);
        session.save();
        if (file.isVersioned()) {
            JcrRepositoryFileUtils.checkinNearestVersionableNodeIfNecessary(session, pentahoJcrConstants, fileNode,
                    versionMessage, file.getCreatedDate(), false);

        }
        JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                parentFolderId,
                Messages.getInstance().getString("JcrRepositoryFileDao.USER_0002_VER_COMMENT_ADD_FILE", //$NON-NLS-1$
                        file.getName(), (parentFolderId == null ? "root" : parentFolderId.toString()))); //$NON-NLS-1$
        return JcrRepositoryFileUtils.nodeToFile(session, pentahoJcrConstants, pathConversionHelper, lockHelper,
                fileNode);
    }

    public RepositoryFile internalUpdateFile(final Session session, final RepositoryFile file,
            final IRepositoryFileData content, final String versionMessage) throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        if (!hasAccess(file, RepositoryFilePermission.WRITE)) {
            return null;
        }
        lockHelper.addLockTokenToSessionIfNecessary(session, pentahoJcrConstants, file.getId());
        JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                file.getId());
        JcrRepositoryFileUtils.updateFileNode(session, pentahoJcrConstants, file, content,
                findTransformerForWrite(content.getClass()));
        session.save();
        JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants, file.getId(),
                versionMessage, new java.util.Date(), true);
        lockHelper.removeLockTokenFromSessionIfNecessary(session, pentahoJcrConstants, file.getId());
        return JcrRepositoryFileUtils.nodeIdToFile(session, pentahoJcrConstants, pathConversionHelper, lockHelper,
                file.getId());
    }

    public RepositoryFile internalUpdateFolder(final Session session, final RepositoryFile folder,
            final String versionMessage) throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        lockHelper.addLockTokenToSessionIfNecessary(session, pentahoJcrConstants, folder.getId());
        JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                folder.getId());
        JcrRepositoryFileUtils.updateFolderNode(session, pentahoJcrConstants, folder);
        session.save();
        JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                folder.getId(), versionMessage);
        lockHelper.removeLockTokenFromSessionIfNecessary(session, pentahoJcrConstants, folder.getId());
        return JcrRepositoryFileUtils.nodeIdToFile(session, pentahoJcrConstants, pathConversionHelper, lockHelper,
                folder.getId());
    }

    public void undeleteFile(final Session session, final Serializable fileId, final String versionMessage)
            throws RepositoryException, IOException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        String absOrigParentFolderPath = deleteHelper.getOriginalParentFolderPath(session, pentahoJcrConstants,
                fileId);
        Serializable origParentFolderId = null;
        if (!hasAccess(session, fileId, RepositoryFilePermission.WRITE)) {
            return;
        }
        // original parent folder path may no longer exist!
        if (session.itemExists(JcrStringHelper.pathEncode(absOrigParentFolderPath))) {
            origParentFolderId = ((Node) session.getItem(JcrStringHelper.pathEncode(absOrigParentFolderPath)))
                    .getIdentifier();
        } else {
            // go through each of the segments of the original parent folder path, creating as necessary
            String[] segments = pathConversionHelper.absToRel(absOrigParentFolderPath)
                    .split(RepositoryFile.SEPARATOR);
            RepositoryFile lastParentFolder = internalGetFile(session,
                    ServerRepositoryPaths.getTenantRootFolderPath(), false, null);
            for (String segment : segments) {
                if (StringUtils.hasLength(segment)) {
                    RepositoryFile tmp = internalGetFile(session,
                            pathConversionHelper
                                    .relToAbs((lastParentFolder.getPath().equals(RepositoryFile.SEPARATOR) ? "" //$NON-NLS-1$
                                            : lastParentFolder.getPath()) + RepositoryFile.SEPARATOR + segment),
                            false, null);
                    if (tmp == null) {
                        lastParentFolder = internalCreateFolder(session, lastParentFolder.getId(),
                                new RepositoryFile.Builder(segment).folder(true).build(),
                                defaultAclHandler.createDefaultAcl(lastParentFolder), null);
                    } else {
                        lastParentFolder = tmp;
                    }
                }
            }
            origParentFolderId = lastParentFolder.getId();
        }
        JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                origParentFolderId);
        deleteHelper.undeleteFile(session, pentahoJcrConstants, fileId);
        session.save();
        JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                origParentFolderId, versionMessage);
    }

    public void deleteFile(final Session session, final Serializable fileId, final String versionMessage)
            throws RepositoryException {
        if (!hasAccess(session, fileId, RepositoryFilePermission.DELETE)) {
            return;
        }
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        Serializable parentFolderId = JcrRepositoryFileUtils.getParentId(session, fileId);
        JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                parentFolderId);
        deleteHelper.deleteFile(session, pentahoJcrConstants, fileId);
        session.save();
        JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                parentFolderId, versionMessage);
    }

    public void deleteFileAtVersion(final Session session, final Serializable fileId, final Serializable versionId)
            throws RepositoryException {
        if (!hasAccess(session, fileId, RepositoryFilePermission.DELETE)) {
            return;
        }
        Node fileToDeleteNode = session.getNodeByIdentifier(fileId.toString());
        session.getWorkspace().getVersionManager().getVersionHistory(fileToDeleteNode.getPath())
                .removeVersion(versionId.toString());
        session.save();
    }

    public List<RepositoryFile> getDeletedFiles(final Session session, final String origParentFolderPath,
            final String filter) throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        return deleteHelper.getDeletedFiles(session, pentahoJcrConstants, origParentFolderPath, filter);
    }

    public void permanentlyDeleteFile(final Session session, final Serializable fileId, final String versionMessage)
            throws RepositoryException {
        if (!hasAccess(session, fileId, RepositoryFilePermission.DELETE)) {
            return;
        }
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        deleteHelper.permanentlyDeleteFile(session, pentahoJcrConstants, fileId);
        session.save();
    }

    public List<RepositoryFile> getChildren(final Session session, final RepositoryRequest repositoryRequest)
            throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        return JcrRepositoryFileUtils.getChildren(session, pentahoJcrConstants, pathConversionHelper, lockHelper,
                repositoryRequest);
    }

    @SuppressWarnings("deprecation")
    public List<RepositoryFile> getChildren(final Session session, final Serializable folderId, final String filter,
            final Boolean showHiddenFiles) throws RepositoryException, IOException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        return JcrRepositoryFileUtils.getChildren(session, pentahoJcrConstants, pathConversionHelper, lockHelper,
                folderId, filter, showHiddenFiles);
    }

    public <T extends IRepositoryFileData> IRepositoryFileData getData(final Session session,
            final Serializable fileId, final Serializable versionId, final Class<T> contentClass)
            throws RepositoryException {
        if (!hasAccess(session, fileId, RepositoryFilePermission.READ)) {
            return null;
        }
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        return JcrRepositoryFileUtils.getContent(session, pentahoJcrConstants, fileId, versionId,
                findTransformerForRead(
                        JcrRepositoryFileUtils.getFileContentType(session, pentahoJcrConstants, fileId, versionId),
                        contentClass));
    }

    public List<RepositoryFile> getDeletedFiles(final Session session) throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        return deleteHelper.getDeletedFiles(session, pentahoJcrConstants);
    }

    protected ITransformer<IRepositoryFileData> findTransformerForRead(final String contentType,
            final Class<? extends IRepositoryFileData> clazz) {
        for (ITransformer<IRepositoryFileData> transformer : transformers) {
            if (transformer.canRead(contentType, clazz)) {
                return transformer;
            }
        }
        throw new IllegalArgumentException(
                Messages.getInstance().getString("JcrRepositoryFileDao.ERROR_0001_NO_TRANSFORMER")); //$NON-NLS-1$
    }

    protected ITransformer<IRepositoryFileData> findTransformerForWrite(
            final Class<? extends IRepositoryFileData> clazz) {
        for (ITransformer<IRepositoryFileData> transformer : transformers) {
            if (transformer.canWrite(clazz)) {
                return transformer;
            }
        }
        throw new IllegalArgumentException(
                Messages.getInstance().getString("JcrRepositoryFileDao.ERROR_0001_NO_TRANSFORMER")); //$NON-NLS-1$
    }

    public RepositoryFile checkAndGetFileById(Session session, final Serializable fileId, final boolean loadMaps,
            final IPentahoLocale locale) throws RepositoryException {
        RepositoryFile file = internalGetFileById(session, fileId, loadMaps, locale);
        if (file != null) {
            if (!hasAccess(file, RepositoryFilePermission.READ)) {
                return null;
            }
        }
        return file;
    }

    private RepositoryFile internalGetFileById(Session session, final Serializable fileId, final boolean loadMaps,
            final IPentahoLocale locale) throws RepositoryException {
        Assert.notNull(fileId);
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        Node fileNode = session.getNodeByIdentifier(fileId.toString());
        RepositoryFile file = fileNode != null
                ? JcrRepositoryFileUtils.nodeToFile(session, pentahoJcrConstants, pathConversionHelper, lockHelper,
                        fileNode, loadMaps, locale)
                : null;
        return file;
    }

    public RepositoryFile getFileByRelPath(final Session session, final String relPath, final boolean loadMaps,
            final IPentahoLocale locale) throws RepositoryException {
        String absPath = pathConversionHelper.relToAbs(relPath);
        return internalGetFile(session, absPath, loadMaps, locale);
    }

    public RepositoryFile internalGetFile(final Session session, final String absPath, final boolean loadMaps,
            final IPentahoLocale locale) throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        try {
            Item fileNode = session.getItem(JcrStringHelper.pathEncode(absPath));
            if (fileNode == null) {
                return null;
            }
            // items are nodes or properties; this must be a node
            Assert.isTrue(fileNode.isNode());
            RepositoryFile file = JcrRepositoryFileUtils.nodeToFile(session, pentahoJcrConstants,
                    pathConversionHelper, lockHelper, (Node) fileNode, loadMaps, locale);
            if (file == null || !hasAccess(file, RepositoryFilePermission.READ)) {
                return null;
            }
            return file;
        } catch (PathNotFoundException e) {
            return null;
        }
    }

    private boolean isUser() {
        IPentahoSession pentahoSession = PentahoSessionHolder.getSession();
        String name = pentahoSession.getName();
        return name != null && !name.equals(repositoryAdminUsername);
    }

    private boolean hasAccess(Session session, Serializable fileId, RepositoryFilePermission... permissions)
            throws RepositoryException {
        if (!isUser()) {
            return true;
        }
        return hasUserAccess(internalGetFileById(session, fileId, false, null), permissions);
    }

    private boolean hasAccess(RepositoryFile file, RepositoryFilePermission... permissions) {
        if (!isUser()) {
            return true;
        }
        return hasUserAccess(file, permissions);
    }

    private boolean hasUserAccess(RepositoryFile file, RepositoryFilePermission... permissions) {
        if (ArrayUtils.isEmpty(permissions)) {
            return false;
        }

        RepositoryFileAcl acl = aclDao.getAcl(file.getId());

        // Invoke accessVoterManager to see if we have access to perform this operation
        for (RepositoryFilePermission permission : permissions) {
            if (!accessVoterManager.hasAccess(file, permission, acl, PentahoSessionHolder.getSession())) {
                return false;
            }
        }
        return true;
    }

    public void internalCopyOrMove(Session session, final RepositoryFile file, final String destRelPath,
            final String versionMessage, final boolean copy) throws RepositoryException, IOException {
        if (!hasAccess(file, RepositoryFilePermission.WRITE)) {
            return;
        }
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        String destAbsPath = pathConversionHelper.relToAbs(destRelPath);
        String cleanDestAbsPath = destAbsPath;
        if (cleanDestAbsPath.endsWith(RepositoryFile.SEPARATOR)) {
            cleanDestAbsPath.substring(0, cleanDestAbsPath.length() - 1);
        }
        Node srcFileNode = session.getNodeByIdentifier(file.getId().toString());
        Serializable srcParentFolderId = JcrRepositoryFileUtils.getParentId(session, file.getId());
        boolean appendFileName = false;
        boolean destExists = true;
        Node destFileNode = null;
        Node destParentFolderNode = null;
        try {
            destFileNode = (Node) session.getItem(JcrStringHelper.pathEncode(cleanDestAbsPath));
        } catch (PathNotFoundException e) {
            destExists = false;
        }
        if (destExists) {
            // make sure it's a file or folder
            Assert.isTrue(JcrRepositoryFileUtils.isSupportedNodeType(pentahoJcrConstants, destFileNode));
            // existing item; make sure src is not a folder if dest is a file
            Assert.isTrue(
                    !(JcrRepositoryFileUtils.isPentahoFolder(pentahoJcrConstants, srcFileNode)
                            && JcrRepositoryFileUtils.isPentahoFile(pentahoJcrConstants, destFileNode)),
                    Messages.getInstance()
                            .getString("JcrRepositoryFileDao.ERROR_0002_CANNOT_OVERWRITE_FILE_WITH_FOLDER")); //$NON-NLS-1$
            if (JcrRepositoryFileUtils.isPentahoFolder(pentahoJcrConstants, destFileNode)) {
                // existing item; caller is not renaming file, only moving it
                appendFileName = true;
                destParentFolderNode = destFileNode;
            } else {
                // get parent of existing dest item
                int lastSlashIndex = cleanDestAbsPath.lastIndexOf(RepositoryFile.SEPARATOR);
                Assert.isTrue(lastSlashIndex > 1,
                        Messages.getInstance().getString("JcrRepositoryFileDao.ERROR_0003_ILLEGAL_DEST_PATH")); //$NON-NLS-1$
                String absPathToDestParentFolder = cleanDestAbsPath.substring(0, lastSlashIndex);
                destParentFolderNode = (Node) session
                        .getItem(JcrStringHelper.pathEncode(absPathToDestParentFolder));
            }
        } else {
            // destination doesn't exist; go up one level to a folder that does exist
            int lastSlashIndex = cleanDestAbsPath.lastIndexOf(RepositoryFile.SEPARATOR);
            Assert.isTrue(lastSlashIndex > 1,
                    Messages.getInstance().getString("JcrRepositoryFileDao.ERROR_0003_ILLEGAL_DEST_PATH")); //$NON-NLS-1$
            String absPathToDestParentFolder = cleanDestAbsPath.substring(0, lastSlashIndex);
            // Not need to check the name if we encoded it
            // JcrRepositoryFileUtils.checkName( cleanDestAbsPath.substring( lastSlashIndex + 1 ) );
            try {
                destParentFolderNode = (Node) session
                        .getItem(JcrStringHelper.pathEncode(absPathToDestParentFolder));
            } catch (PathNotFoundException e1) {
                Assert.isTrue(false,
                        Messages.getInstance().getString("JcrRepositoryFileDao.ERROR_0004_PARENT_MUST_EXIST")); //$NON-NLS-1$
            }
            Assert.isTrue(JcrRepositoryFileUtils.isPentahoFolder(pentahoJcrConstants, destParentFolderNode),
                    Messages.getInstance().getString("JcrRepositoryFileDao.ERROR_0005_PARENT_MUST_BE_FOLDER")); //$NON-NLS-1$
        }
        if (!copy) {
            JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                    srcParentFolderId);
        }
        JcrRepositoryFileUtils.checkoutNearestVersionableNodeIfNecessary(session, pentahoJcrConstants,
                destParentFolderNode);
        String finalEncodedSrcAbsPath = srcFileNode.getPath();
        String finalDestAbsPath = appendFileName && !file.isFolder()
                ? cleanDestAbsPath + RepositoryFile.SEPARATOR + srcFileNode.getName()
                : cleanDestAbsPath;
        try {
            if (copy) {
                session.getWorkspace().copy(finalEncodedSrcAbsPath, JcrStringHelper.pathEncode(finalDestAbsPath));
            } else {
                session.getWorkspace().move(finalEncodedSrcAbsPath, JcrStringHelper.pathEncode(finalDestAbsPath));
            }
        } catch (ItemExistsException iae) {
            throw new UnifiedRepositoryException((file.isFolder() ? "Folder " : "File ") + "with path ["
                    + cleanDestAbsPath + "] already exists in the repository");
        }

        JcrRepositoryFileUtils.checkinNearestVersionableNodeIfNecessary(session, pentahoJcrConstants,
                destParentFolderNode, versionMessage);
        // if it's a move within the same folder, then the next checkin is unnecessary
        if (!copy && !destParentFolderNode.getIdentifier().equals(srcParentFolderId.toString())) {
            JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                    srcParentFolderId, versionMessage);
        }
        session.save();
    }

    protected RepositoryFile getReferrerFile(final Session session, final PentahoJcrConstants pentahoJcrConstants,
            final Property referrerProperty) throws RepositoryException {
        Node currentNode = referrerProperty.getParent();
        while (!currentNode.isNodeType(pentahoJcrConstants.getPHO_NT_PENTAHOHIERARCHYNODE())) {
            currentNode = currentNode.getParent();
        }
        // if folder, then referrer is a lock token record (under the user's home folder) which will be cleaned up by
        // lockHelper; ignore it
        if (currentNode.isNodeType(pentahoJcrConstants.getPHO_NT_PENTAHOFOLDER())) {
            return null;
        }

        return JcrRepositoryFileUtils.nodeToFile(session, pentahoJcrConstants, pathConversionHelper, lockHelper,
                currentNode);
    }

    public List<RepositoryFile> getReferrers(Session session, final Serializable fileId)
            throws ItemNotFoundException, RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);

        Node fileNode = session.getNodeByIdentifier(fileId.toString());
        // guard against using a file retrieved from a more lenient session inside a more strict session
        Assert.notNull(fileNode);

        Set<RepositoryFile> referrers = new HashSet<RepositoryFile>();
        PropertyIterator refIter = fileNode.getReferences();
        if (refIter.hasNext()) {
            while (refIter.hasNext()) {
                // for each referrer property, march up the tree until we find the file node to which the property
                // belongs
                RepositoryFile referrer = getReferrerFile(session, pentahoJcrConstants, refIter.nextProperty());
                if (referrer != null) {
                    referrers.add(referrer);
                }
            }
        }
        session.save();
        return new ArrayList<RepositoryFile>(referrers);
    }

    public RepositoryFileTree getTree(Session session, final RepositoryRequest repositoryRequest)
            throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        String absPath = pathConversionHelper.relToAbs(repositoryRequest.getPath());
        return JcrRepositoryFileUtils.getTree(session, pentahoJcrConstants, pathConversionHelper, lockHelper,
                absPath, repositoryRequest, accessVoterManager);
    }

    public boolean canUnlockFile(final Session session, final Serializable fileId)
            throws ItemNotFoundException, RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        Node fileNode = session.getNodeByIdentifier(fileId.toString());
        Lock lock = session.getWorkspace().getLockManager().getLock(fileNode.getPath());
        return lockHelper.canUnlock(session, pentahoJcrConstants, lock);
    }

    public void restoreFileAtVersion(final Session session, final Serializable fileId, final Serializable versionId,
            final String versionMessage) throws ItemNotFoundException, RepositoryException {
        Node fileNode = session.getNodeByIdentifier(fileId.toString());
        session.getWorkspace().getVersionManager().restore(fileNode.getPath(), versionId.toString(), true);
    }

    public List<Locale> getAvailableLocalesForFile(RepositoryFile repositoryFile) {
        List<Locale> localeList = new ArrayList<Locale>();
        if (repositoryFile != null && repositoryFile.getLocalePropertiesMap() != null) {
            for (String localeName : repositoryFile.getLocalePropertiesMap().keySet()) {
                String[] localePieces = localeName.split("_");
                String language = localePieces[0];
                String country = (localePieces.length > 1) ? localePieces[1] : "";
                String variant = (localePieces.length > 2) ? localePieces[2] : "";
                Locale locale = new Locale(language, country, variant);
                localeList.add(locale);
            }
        }
        return localeList;
    }

    public Properties getLocalePropertiesForFile(RepositoryFile repositoryFile, String locale) {
        if (org.apache.commons.lang.StringUtils.isBlank(locale)) {
            locale = RepositoryFile.DEFAULT_LOCALE;
        }
        if (repositoryFile != null && repositoryFile.getLocalePropertiesMap() != null) {
            Properties properties = repositoryFile.getLocalePropertiesMap().get(locale);
            return properties;
        }
        return null;
    }

    public void setLocalePropertiesForFile(final Session session, final RepositoryFile repositoryFile,
            final String locale, final Properties properties) throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        String versionMessage = Messages.getInstance()
                .getString("JcrRepositoryFileDao.LOCALE_0001_UPDATE_PROPERTIES", repositoryFile.getId());
        lockHelper.addLockTokenToSessionIfNecessary(session, pentahoJcrConstants, repositoryFile.getId());
        JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                repositoryFile.getId());
        JcrRepositoryFileUtils.updateFileLocaleProperties(session, repositoryFile.getId(), locale, properties);
        session.save();
        JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                repositoryFile.getId(), versionMessage);
        lockHelper.removeLockTokenFromSessionIfNecessary(session, pentahoJcrConstants, repositoryFile.getId());
    }

    public void deleteLocalePropertiesForFile(final Session session, final RepositoryFile repositoryFile,
            final String locale) throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        String versionMessage = Messages.getInstance()
                .getString("JcrRepositoryFileDao.LOCALE_0002_DELETE_PROPERTIES", repositoryFile.getId());
        lockHelper.addLockTokenToSessionIfNecessary(session, pentahoJcrConstants, repositoryFile.getId());
        JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                repositoryFile.getId());
        JcrRepositoryFileUtils.deleteFileLocaleProperties(session, repositoryFile.getId(), locale);
        session.save();
        JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants,
                repositoryFile.getId(), versionMessage);
        lockHelper.removeLockTokenFromSessionIfNecessary(session, pentahoJcrConstants, repositoryFile.getId());
    }

    public void lockFile(final Session session, final Serializable fileId, final String message)
            throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        lockHelper.lockFile(session, pentahoJcrConstants, fileId, message);
    }

    public void unlockFile(final Session session, final Serializable fileId) throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        lockHelper.unlockFile(session, pentahoJcrConstants, fileId);
    }

    public List<VersionSummary> getVersionSummaries(final Session session, final Serializable fileId)
            throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        return JcrRepositoryFileUtils.getVersionSummaries(session, pentahoJcrConstants, fileId, true);
    }

    public RepositoryFile getFile(final Session session, final Serializable fileId, final Serializable versionId)
            throws RepositoryException {
        PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
        return JcrRepositoryFileUtils.getFileAtVersion(session, pentahoJcrConstants, pathConversionHelper,
                lockHelper, fileId, versionId);
    }
}