com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.HibernateRepositoryServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.HibernateRepositoryServiceImpl.java

Source

/*
 * Copyright (C) 2005 - 2014 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com.
 *
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License  as
 * published by the Free Software Foundation, either version 3 of  the
 * License, or (at your option) any later version.
 *
 * 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 Affero  General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public  License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate;

import com.jaspersoft.jasperserver.api.JSDuplicateResourceException;
import com.jaspersoft.jasperserver.api.JSException;
import com.jaspersoft.jasperserver.api.JSExceptionWrapper;
import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
import com.jaspersoft.jasperserver.api.common.domain.ValidationErrorFilter;
import com.jaspersoft.jasperserver.api.common.domain.ValidationErrors;
import com.jaspersoft.jasperserver.api.common.domain.impl.ExecutionContextImpl;
import com.jaspersoft.jasperserver.api.common.domain.impl.ValidationErrorsImpl;
import com.jaspersoft.jasperserver.api.common.service.ClassMappingsObjectFactory;
import com.jaspersoft.jasperserver.api.common.util.CollatorFactory;
import com.jaspersoft.jasperserver.api.common.util.DefaultCollatorFactory;
import com.jaspersoft.jasperserver.api.logging.access.context.AccessContext;
import com.jaspersoft.jasperserver.api.logging.access.domain.AccessEvent;
import com.jaspersoft.jasperserver.api.logging.audit.context.AuditContext;
import com.jaspersoft.jasperserver.api.logging.audit.domain.AuditEvent;
import com.jaspersoft.jasperserver.api.metadata.common.domain.*;
import com.jaspersoft.jasperserver.api.metadata.common.domain.impl.IdedRepoObject;
import com.jaspersoft.jasperserver.api.metadata.common.service.JSResourceNotFoundException;
import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryEventListener;
import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService;
import com.jaspersoft.jasperserver.api.metadata.common.service.ResourceFactory;
import com.jaspersoft.jasperserver.api.metadata.common.service.ResourceValidator;
import com.jaspersoft.jasperserver.api.metadata.common.service.impl.*;
import com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.persistent.*;
import com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.util.RepositoryUtils;
import com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.util.ResourceCriterionUtils;
import com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.util.SortingUtils;
import com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.util.UpdateDatesIndicator;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.ReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.ReportUnit;
import com.jaspersoft.jasperserver.api.metadata.view.domain.FilterCriteria;
import com.jaspersoft.jasperserver.api.metadata.view.domain.FilterElement;
import com.jaspersoft.jasperserver.api.search.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.*;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.sql.SQLException;
import java.text.Collator;
import java.util.*;

/**
 * @author Lucian Chirita (lucianc@users.sourceforge.net)
 * @version $Id: HibernateRepositoryServiceImpl.java 47331 2014-07-18 09:13:06Z kklein $
 */
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class HibernateRepositoryServiceImpl extends HibernateDaoImpl
        implements HibernateRepositoryService, ReferenceResolver, RepoManager, ApplicationContextAware {

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

    protected static final int RESOURCE_NAME_LENGHT = 100;
    protected int resourceUriLength = -1;

    protected static final String TEMP_NAME_PREFIX = "*";
    protected static final int TEMP_NAME_PREFIX_LENGTH = TEMP_NAME_PREFIX.length();
    protected static final String CHILDREN_FOLDER_SUFFIX = "_files";
    protected static final String COPY_GENERATED_NAME_SEPARATOR = "_";

    protected static final Map<String, Object> CLIENT_CLONE_OPTIONS;
    static {
        HashMap<String, Object> options = new HashMap<String, Object>();
        options.put(RepoResource.CLIENT_OPTION_FULL_DATA, null);
        options.put(RepoResource.CLIENT_OPTION_AS_NEW, null);
        CLIENT_CLONE_OPTIONS = Collections.unmodifiableMap(options);
    }

    private ResourceFactory resourceFactory;
    private ResourceFactory persistentClassMappings;
    private ClassMappingsObjectFactory validatorMappings;
    private ThreadLocal tempNameResources;

    private CollatorFactory collatorFactory = new DefaultCollatorFactory();

    private List repositoryListeners = new ArrayList();
    private RepositorySecurityChecker securityChecker;
    private boolean lockFoldersOnPathChange = true;

    private AuditContext auditContext;
    private AccessContext accessContext;

    private QueryModificationEvaluator queryModificationEvaluator;

    private HibernateSaveUpdateDeleteListener hibernateSaveUpdateDeleteListener;

    private ApplicationContext applicationContext; //needed for validation settings

    public HibernateRepositoryServiceImpl() {
        tempNameResources = new ThreadLocal();
    }

    /**
     *
     * @param sessionFactory
     * @return
     */
    @Override
    protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) {
        //return super.createHibernateTemplate(sessionFactory);
        return new HibernateDaoTemplate(sessionFactory) {
            @Override
            public Serializable save(Object entity) throws DataAccessException {
                if (hibernateSaveUpdateDeleteListener != null) {
                    hibernateSaveUpdateDeleteListener.beforeSave(entity, this);
                }
                Serializable ret = super.save(entity);
                if (hibernateSaveUpdateDeleteListener != null) {
                    hibernateSaveUpdateDeleteListener.afterSave(entity, this);
                }
                return ret;
            }

            @Override
            public void update(Object entity) throws DataAccessException {
                if (hibernateSaveUpdateDeleteListener != null) {
                    hibernateSaveUpdateDeleteListener.beforeUpdate(entity, this);
                }
                super.update(entity);
                if (hibernateSaveUpdateDeleteListener != null) {
                    hibernateSaveUpdateDeleteListener.afterUpdate(entity, this);
                }
            }

            @Override
            public void saveOrUpdate(Object entity) throws DataAccessException {
                if (hibernateSaveUpdateDeleteListener != null) {
                    hibernateSaveUpdateDeleteListener.beforeSaveOrUpdate(entity, this);
                }
                super.saveOrUpdate(entity);
                if (hibernateSaveUpdateDeleteListener != null) {
                    hibernateSaveUpdateDeleteListener.afterSaveOrUpdate(entity, this);
                }
            }

            @Override
            public void delete(Object entity) throws DataAccessException {
                if (hibernateSaveUpdateDeleteListener != null) {
                    hibernateSaveUpdateDeleteListener.beforeDelete(entity, this);
                }
                super.delete(entity);
                if (hibernateSaveUpdateDeleteListener != null) {
                    hibernateSaveUpdateDeleteListener.afterDelete(entity, this);
                }
            }
        };
    }

    public ResourceFactory getPersistentClassMappings() {
        return persistentClassMappings;
    }

    public AuditContext getAuditContext() {
        return auditContext;
    }

    public void setAuditContext(AuditContext auditContext) {
        this.auditContext = auditContext;
    }

    public AccessContext getAccessContext() {
        return accessContext;
    }

    public void setAccessContext(AccessContext accessContext) {
        this.accessContext = accessContext;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void setPersistentClassMappings(ResourceFactory persistentClassMappings) {
        this.persistentClassMappings = persistentClassMappings;
    }

    public ResourceFactory getResourceFactory() {
        return resourceFactory;
    }

    public void setResourceFactory(ResourceFactory resourceFactory) {
        this.resourceFactory = resourceFactory;
    }

    public ClassMappingsObjectFactory getValidatorMappings() {
        return validatorMappings;
    }

    public void setValidatorMappings(ClassMappingsObjectFactory validatorMappings) {
        this.validatorMappings = validatorMappings;
    }

    public ResourceValidator getValidator(Resource resource) {
        return resource == null ? null
                : (ResourceValidator) validatorMappings.getClassObject(resource.getResourceType());
    }

    public RepositorySecurityChecker getSecurityChecker() {
        return securityChecker;
    }

    public void setSecurityChecker(RepositorySecurityChecker securityChecker) {
        this.securityChecker = securityChecker;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Resource getResource(ExecutionContext context, final String uri) {
        return getResourceUnsecure(context, uri);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Resource getResourceUnsecure(ExecutionContext context, final String uri) {
        return (Resource) executeCallback(new DaoCallback() {
            public Object execute() {
                return loadResource(uri, null);
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Resource getResource(ExecutionContext context, final String uri, final Class resourceType) {
        return (Resource) executeCallback(new DaoCallback() {
            public Object execute() {
                return loadResource(uri, resourceType);
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public <T extends Resource> T makeResourceCopy(ExecutionContext context, final String uri, final int version,
            final Class<T> resourceType, final String copyURI) {
        return (T) executeCallback(new DaoCallback() {
            public Object execute() {
                Resource resourceCopy = loadResource(uri, resourceType, CLIENT_CLONE_OPTIONS, version);
                if (resourceCopy != null) {
                    String folderPath = RepositoryUtils.getParentPath(copyURI);
                    String name = RepositoryUtils.getName(copyURI);
                    resourceCopy.setParentFolder(folderPath);
                    resourceCopy.setName(name);
                }
                return resourceCopy;
            }
        });
    }

    private void auditResourceActivity(final String eventType, final String uri, final String resourceType) {
        auditContext.doInAuditContext(new AuditContext.AuditContextCallback() {
            public void execute() {
                auditContext.createAuditEvent(eventType, uri, resourceType);
            }
        });
    }

    private void auditResourceCopyAndMove(final String eventType, final String sourceUri, final String destUri,
            final String resourceType) {
        auditContext.doInAuditContext(new AuditContext.AuditContextCallback() {
            public void execute() {
                AuditEvent auditEvent = auditContext.createAuditEvent(eventType, sourceUri, resourceType);
                auditContext.addPropertyToAuditEvent("destFolderUri", destUri, auditEvent);
            }
        });
    }

    private void auditFolderActivity(final String eventType, final String uri) {
        auditContext.doInAuditContext(new AuditContext.AuditContextCallback() {
            public void execute() {
                auditContext.createAuditEvent(eventType, uri, Folder.class.getName());
            }
        });
    }

    private void auditFolderActivity(final String eventType, final Folder folder) {
        auditContext.doInAuditContext(new AuditContext.AuditContextCallback() {
            public void execute() {
                AuditEvent auditEvent = auditContext.createAuditEvent(eventType, folder.getURI(),
                        Folder.class.getName());
                auditContext.addPropertyToAuditEvent("folderName", folder.getName(), auditEvent);
                auditContext.addPropertyToAuditEvent("folderLabel", folder.getLabel(), auditEvent);
                auditContext.addPropertyToAuditEvent("folderDescription", folder.getDescription(), auditEvent);
            }
        });
    }

    private void auditFolderCopyAndMove(final String eventType, final String sourceUri, final String destUri) {
        auditContext.doInAuditContext(new AuditContext.AuditContextCallback() {
            public void execute() {
                AuditEvent auditEvent = auditContext.createAuditEvent(eventType, sourceUri, Folder.class.getName());
                auditContext.addPropertyToAuditEvent("destFolderUri", destUri, auditEvent);
            }
        });
    }

    private void closeAuditEvent(final String eventType) {
        auditContext.doInAuditContext(eventType, new AuditContext.AuditContextCallbackWithEvent() {
            public void execute(AuditEvent auditEvent) {
                auditContext.closeAuditEvent(auditEvent);
            }
        });
    }

    private void logAccessResource(final RepoResource repoResource, final boolean updating) {
        accessContext.doInAccessContext(new AccessContext.AccessContextCallback() {
            public void fillAccessEvent(AccessEvent accessEvent) {
                accessEvent.setResource((Resource) repoResource.toClient(resourceFactory));
                accessEvent.setUpdating(updating);
            }
        });
    }

    protected Resource loadResource(final String uri, Class resourceType) {
        return loadResource(uri, resourceType, null, null);
    }

    protected Resource loadResource(final String uri, Class resourceType, Map<String, Object> clientOptions,
            Integer expectedVersion) {
        Class persistentClass = resourcePersistentClass(resourceType);

        RepoResource repoResource = findByURI(persistentClass, uri, false);
        Resource resource;
        if (repoResource == null) {
            resource = null;
        } else {
            if (expectedVersion != null && expectedVersion.intValue() != repoResource.getVersion()) {
                throw new JSException("Resource " + uri + " has version " + repoResource.getVersion()
                        + ", was expecting " + expectedVersion);
            }

            auditResourceActivity("accessResource", uri, repoResource.getClientType().getName());
            logAccessResource(repoResource, false);
            resource = (Resource) repoResource.toClient(resourceFactory, clientOptions);
            closeAuditEvent("accessResource");
        }
        return resource;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public ResourceLookup getResourceLookupUnsecure(ExecutionContext context, final String uri) {
        return (ResourceLookup) executeCallback(new DaoCallback() {
            public Object execute() {
                return loadResourceLookup(uri, null);
            }
        });
    }

    protected ResourceLookup loadResourceLookup(final String uri, Class resourceType) {
        Class persistentClass = resourcePersistentClass(resourceType);

        RepoResource repoResource = findByURI(persistentClass, uri, false);
        ResourceLookup resource;
        if (repoResource == null) {
            resource = null;
        } else {
            resource = repoResource.toClientLookup();
        }
        return resource;
    }

    protected Class resourcePersistentClass(Class resourceType) {
        Class persistentClass;
        if (resourceType == null) {
            persistentClass = RepoResource.class;
        } else {
            persistentClass = getPersistentClassMappings().getImplementationClass(resourceType);
        }
        return persistentClass;
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void saveFolder(ExecutionContext context, final Folder folder) {
        final RepoManager manager = this;
        final ExecutionContext executionContext = context;

        executeWriteCallback(new DaoCallback() {
            public Object execute() {
                RepoFolder repoFolder = getFolder(folder.getURIString(), false);
                validateResourceUriLength(folder.getURIString().length());

                String eventType;

                if (folder.isNew()) {
                    eventType = "createFolder";
                    auditFolderActivity(eventType, folder);
                    if (repoFolder != null || resourceExists(folder.getURIString())) {
                        throw new JSDuplicateResourceException("jsexception.folder.already.exists",
                                new Object[] { folder.getURIString() });
                    }

                    repoFolder = createNewRepoFolder();

                    if (log.isDebugEnabled()) {
                        log.debug("Creating new folder " + folder.getURIString());
                    }
                } else {
                    eventType = "updateFolder";
                    auditFolderActivity(eventType, folder);
                    if (repoFolder == null) {
                        String quotedURI = "\"" + folder.getURIString() + "\"";
                        throw new JSException("jsexception.folder.not.found", new Object[] { quotedURI });
                    }
                    repoFolder.setUpdateDate(new Date());
                }

                String parentURI = folder.getParentFolder();
                RepoFolder parent;
                if (parentURI == null && folder.getName().equals(Folder.SEPARATOR)) {
                    parent = null;
                } else {
                    parent = getFolder(parentURI, true);
                }

                //TODO don't set parent, name when updating
                repoFolder.set(folder, parent, manager);

                // Set the original updateDate of the folder if it is being imported
                if (executionContext != null && executionContext.getAttributes() != null
                        && executionContext.getAttributes().contains(RepositoryService.IS_IMPORTING)
                        && folder.getUpdateDate() != null) {
                    repoFolder.setUpdateDate(folder.getUpdateDate());
                }

                getHibernateTemplate().saveOrUpdate(repoFolder);
                closeAuditEvent(eventType);
                return null;
            }
        });
    }

    protected RepoFolder createNewRepoFolder() {
        RepoFolder repoFolder = new RepoFolder();
        repoFolder.setCreationDate(getOperationTimestamp());
        repoFolder.setUpdateDate(getOperationTimestamp());
        return repoFolder;
    }

    protected RepoFolder getFolder(String uri, boolean required) {
        if (uri == null || uri.length() == 0 || uri.equals(Folder.SEPARATOR)) {
            return getRootFolder();
        }

        // Deal with URIs that come with "repo:" on the front

        final String repoURIPrefix = Resource.URI_PROTOCOL + ":";
        String workUri = uri.startsWith(repoURIPrefix) ? uri.substring(repoURIPrefix.length()) : uri;

        DetachedCriteria criteria = DetachedCriteria.forClass(RepoFolder.class);
        criteria.add(Restrictions.naturalId().set("URI", workUri));
        criteria.getExecutableCriteria(getSession()).setCacheable(true);
        List foldersList = getHibernateTemplate().findByCriteria(criteria);
        RepoFolder folder;
        if (foldersList.isEmpty()) {
            if (required) {
                String quotedURI = "\"" + uri + "\"";
                throw new JSResourceNotFoundException("jsexception.folder.not.found.at",
                        new Object[] { quotedURI });
            }

            log.debug("Folder not found at \"" + uri + "\"");
            folder = null;
        } else {
            folder = (RepoFolder) foldersList.get(0);
        }
        return folder;
    }

    protected RepoFolder getRootFolder() {
        DetachedCriteria criteria = DetachedCriteria.forClass(RepoFolder.class);
        criteria.add(Restrictions.naturalId().set("URI", Folder.SEPARATOR));
        criteria.getExecutableCriteria(getSession()).setCacheable(true);
        List foldersList = getHibernateTemplate().findByCriteria(criteria);
        RepoFolder root;
        if (foldersList.isEmpty()) {
            root = null;
        } else {
            root = (RepoFolder) foldersList.get(0);
        }
        return root;
    }

    public ValidationErrors validateResource(ExecutionContext context, final Resource resource,
            final ValidationErrorFilter filter) {
        return validate(resource, filter);
    }

    protected ValidationErrors validate(final Resource resource, final ValidationErrorFilter filter) {
        ResourceValidator validator = getValidator(resource);

        if (validator != null) {
            return validator.validate(resource, filter);
        }

        return new ValidationErrorsImpl();
    }

    public ValidationErrors validateFolder(ExecutionContext context, Folder folder, ValidationErrorFilter filter) {
        return validate(folder, filter);
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void saveResource(ExecutionContext executionContext, Resource res) {
        saveResourceWithFlush(executionContext, res, true);
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void saveResourceNoFlush(ExecutionContext executionContext, Resource res) {
        saveResourceWithFlush(executionContext, res, false);
    }

    private void saveResourceWithFlush(ExecutionContext executionContext, Resource res, boolean flush) {
        initTempNameResources();
        final ExecutionContext context = executionContext;
        final Resource resource = res;
        try {
            executeWriteCallback(new DaoCallback() {
                public Object execute() {
                    RepoResource repo = getRepoResource(resource);
                    String eventType;
                    boolean isImporting = context != null && context.getAttributes() != null
                            && context.getAttributes().contains(RepositoryService.IS_IMPORTING);
                    boolean isOverwriting = context != null && context.getAttributes() != null
                            && context.getAttributes().contains(RepositoryService.IS_OVERWRITING);

                    if (repo.isNew()) {
                        eventType = "saveResource";
                        auditResourceActivity(eventType, resource.getURI(), resource.getResourceType());
                    } else {
                        eventType = isOverwriting ? "overwriteResource" : "updateResource";
                        auditResourceActivity(eventType, resource.getURI(), repo.getClientType().getName());
                    }

                    // Set the appropriate creationDate and updateDate against the normal, import or overwrite modes.
                    // Now local resources are also affected in case of importing or overwriting.
                    if (isImporting || isOverwriting) {
                        UpdateDatesIndicator.required(getOperationTimestamp(), !isImporting);

                        try {
                            repo.copyFromClient(resource, HibernateRepositoryServiceImpl.this);

                            UpdateDatesIndicator.clean();
                        } catch (RuntimeException e) {
                            UpdateDatesIndicator.clean();

                            throw e;
                        }
                    } else { // For regular cases just set updateDate of current (top level) resource to operational.
                        repo.copyFromClient(resource, HibernateRepositoryServiceImpl.this);

                        repo.setUpdateDate(getOperationTimestamp());
                    }

                    RepoResource repositoryResource = repo;
                    getHibernateTemplate().saveOrUpdate(repositoryResource);

                    logAccessResource(repositoryResource, true);
                    closeAuditEvent(eventType);
                    return null;
                }
            }, flush);

            if (tempNameResources() != null && !tempNameResources().isEmpty()) {
                executeWriteCallback(new DaoCallback() {
                    public Object execute() {
                        HibernateTemplate template = getHibernateTemplate();
                        for (Iterator it = tempNameResources().iterator(); it.hasNext();) {
                            RepoResource res = (RepoResource) it.next();
                            res.setName(res.getName().substring(TEMP_NAME_PREFIX_LENGTH));

                            RepoFolder childrenFolder = res.getChildrenFolder();
                            if (childrenFolder != null) {
                                childrenFolder.setName(childrenFolder.getName().substring(TEMP_NAME_PREFIX_LENGTH));
                                refreshFolderPaths(childrenFolder);
                            }

                            template.save(res);
                        }
                        return null;
                    }
                });
            }
        } finally {
            resetTempNameResources();
        }
    }

    @Deprecated
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void saveResource(ExecutionContext context, final Resource resource, boolean updateCreationDate) {

        // Since this method is deprecated it invokes the main saveResource with appropriate context parameter.

        if (updateCreationDate) {
            if (context == null) {
                context = new ExecutionContextImpl();
            }
            if (context.getAttributes() == null) {
                context.setAttributes(new ArrayList());
            }
            context.getAttributes().add(RepositoryService.IS_OVERWRITING);
        }

        saveResource(context, resource);
    }

    protected void initTempNameResources() {
        // re-entrance support
        if (tempNameResources() == null) {
            tempNameResources.set(new HashSet());
        }
    }

    protected void resetTempNameResources() {
        tempNameResources.set(null);
    }

    protected Set tempNameResources() {
        return (Set) tempNameResources.get();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public RepoResource getRepoResource(Resource resource) {
        Class persistentClass = getPersistentClassMappings().getImplementationClass(resource.getClass());
        if (persistentClass == null) {
            String quotedResource = "\"" + resource.getClass().getName() + "\"";
            throw new JSException("jsexception.no.persistent.class.mapped.to", new Object[] { quotedResource });
        }

        RepoResource repo;
        if (resource.isNew()) {
            if (pathExists(resource.getURIString())) {
                String quotedResource = "\"" + resource.getURIString() + "\"";
                throw new JSDuplicateResourceException("jsexception.resource.already.exists",
                        new Object[] { quotedResource });
            }

            repo = createPersistentResource(persistentClass);

            RepoFolder parent = getFolder(resource.getParentFolder(), true);
            repo.setParent(parent);
        } else {
            repo = findByURI(persistentClass, resource.getURIString(), false);
            if (repo == null) {
                String quotedURI = "\"" + resource.getURIString() + "\"";
                throw new JSException("jsexception.resource.does.not.exist", new Object[] { quotedURI });
            }
        }
        return repo;
    }

    public boolean resourceExists(ExecutionContext executionContext, String uri) {
        return resourceExists(uri);
    }

    public boolean resourceExists(ExecutionContext executionContext, String uri, Class resourceType) {
        return resourceExists(uri, resourceType);
    }

    public boolean resourceExists(ExecutionContext executionContext, FilterCriteria filterCriteria) {
        DetachedCriteria criteria = translateFilterToCriteria(filterCriteria);
        boolean exists;
        if (criteria == null) {
            exists = false;
        } else {
            criteria.setProjection(Projections.rowCount());
            criteria.getExecutableCriteria(getSession()).setCacheable(true);
            List countList = getHibernateTemplate().findByCriteria(criteria);
            int count = ((Integer) countList.get(0)).intValue();
            exists = count > 0;
        }
        return exists;
    }

    protected boolean resourceExists(String uri) {
        return resourceExists(uri, null);
    }

    protected boolean resourceExists(String uri, Class type) {
        int sep = uri.lastIndexOf(Folder.SEPARATOR);
        boolean exists = false;
        if (sep >= 0) {
            String name = uri.substring(sep + Folder.SEPARATOR_LENGTH);
            String folderName = uri.substring(0, sep);
            if ("repo:".equals(folderName)) {
                folderName += Folder.SEPARATOR;
            }
            RepoFolder folder = getFolder(folderName, false);
            if (folder != null) {
                exists = resourceExists(folder, name, type);
            }
        }
        return exists;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public ResourceLookup[] findResource(final ExecutionContext context, final FilterCriteria filterCriteria) {
        return (ResourceLookup[]) executeCallback(new DaoCallback() {
            public Object execute() {
                return loadResources(context, filterCriteria);
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public ResourceLookup[] findResources(final ExecutionContext context, final FilterCriteria[] filterCriteria) {
        return (ResourceLookup[]) executeCallback(new DaoCallback() {
            public Object execute() {
                return loadResources(context, filterCriteria);
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List loadClientResources(FilterCriteria filterCriteria) {

        List repoResources = loadRepoResourceList(filterCriteria);

        List result = new ArrayList(repoResources.size());

        for (Iterator iter = repoResources.iterator(); iter.hasNext();) {
            RepoResourceBase repoResource = (RepoResourceBase) iter.next();
            result.add(repoResource.toClient(resourceFactory));
        }

        return result;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List loadResourcesList(FilterCriteria filterCriteria) {
        return loadResourcesList(null, filterCriteria);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List loadResourcesList(ExecutionContext context, FilterCriteria filterCriteria) {
        List repoResources = loadRepoResourceList(context, filterCriteria);
        return toLookups(repoResources, false);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List loadResourcesList(ExecutionContext context, FilterCriteria[] filterCriteria) {
        List repoResources;
        if (filterCriteria.length == 1) {
            repoResources = loadRepoResourceList(context, filterCriteria[0]);
        } else {
            repoResources = new ArrayList();
            for (int i = 0; i < filterCriteria.length; i++) {
                FilterCriteria criteria = filterCriteria[i];
                List criteriaRes = loadRepoResourceList(context, criteria, false);
                if (criteriaRes != null) {
                    repoResources.addAll(criteriaRes);
                }
            }

            sortRepoResourcesByURI(context, repoResources);
        }

        return toLookups(repoResources, false);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List<Long> getResourcesIds(ExecutionContext context, String text, Class type, Class aClass,
            List<SearchFilter> filters, Map<String, SearchFilter> typeSpecificFilters, SearchSorter sorter,
            TransformerFactory transformerFactory, int firstResult, int maxResult) {
        if (transformerFactory == null) {
            throw new IllegalArgumentException("Transformer factory is null.");
        }

        SearchCriteria criteria = getResourcesListCriteria(context, text, type, aClass, filters,
                typeSpecificFilters);

        ProjectionList distinctProperties = Projections.projectionList().add(Projections.id());
        criteria.addProjection(Projections.distinct(distinctProperties));

        if (sorter != null) {
            sorter.applyOrder(type.getName(), context, criteria);
        }

        List list = getHibernateTemplate().findByCriteria(criteria, firstResult, maxResult);

        List<Long> ids = new ArrayList<Long>();
        ResultTransformer transformer = transformerFactory.createTransformer(filters, sorter);
        if (transformer != null) {
            ids.addAll(transformer.transformToIdList(list));
        } else {
            throw new IllegalArgumentException("Result transformer is null.");
        }

        return ids;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List<ResourceLookup> getResourcesByIdList(List<Long> idList) {
        return getResourcesByIdList(idList, null);
    }

    protected List<ResourceLookup> getResourcesByIdList(List<Long> idList,
            SearchCriteriaFactory searchCriteriaFactory) {
        List resourceList = new ArrayList();

        if (!idList.isEmpty()) {
            DetachedCriteria criteria = searchCriteriaFactory != null
                    ? searchCriteriaFactory.create(ExecutionContextImpl.getRuntimeExecutionContext(), null)
                    : DetachedCriteria.forClass(RepoResource.class);
            criteria.add(Restrictions.in("id", idList));
            resourceList = getHibernateTemplate().findByCriteria(criteria);
        }

        // Reordering is required because the result (resource) list is not in the same order as id list.
        List orderedResourceList = orderByIdList(resourceList, idList);

        return toLookups(orderedResourceList, true);
    }

    private List orderByIdList(List<IdedRepoObject> resourceList, List<Long> idList) {
        List<IdedRepoObject> orderedList = new ArrayList<IdedRepoObject>();

        for (Long id : idList) {
            for (IdedRepoObject repoResource : resourceList) {
                if (repoResource.getId() == id) {
                    orderedList.add(repoResource);
                }
            }
        }

        return orderedList;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Map<Class, Integer> loadResourcesMapCount(ExecutionContext context, String text,
            List<Class> resourceTypeList, List<SearchFilter> filters, Map<String, SearchFilter> typeSpecificFilters,
            SearchSorter sorter, TransformerFactory transformerFactory) {
        if (transformerFactory == null) {
            throw new IllegalArgumentException("Transformer factory is null.");
        }

        Map<Class, Integer> result = new HashMap<Class, Integer>();

        Map<Class, Class> typeToPersistentClassMap = getTypeToPersistentClassMap(resourceTypeList);

        for (Map.Entry<Class, Class> entry : typeToPersistentClassMap.entrySet()) {
            SearchCriteria criteria = getResourcesListCriteria(context, text, entry.getKey(), entry.getValue(),
                    filters, typeSpecificFilters);
            criteria.setProjection(Projections.countDistinct("id"));

            //            if (sorter != null) {
            //                sorter.applyOrder(entry.getKey().getName(), context, criteria);
            //            }

            List resourceList = getHibernateTemplate().findByCriteria(criteria);

            ResultTransformer transformer = transformerFactory.createTransformer(filters, sorter);
            if (transformer != null) {
                result.put(entry.getKey(), transformer.transformToCount(resourceList));
            } else {
                throw new IllegalArgumentException("Result transformer is null.");
            }
        }

        return result;
    }

    private SearchCriteria getResourcesListCriteria(ExecutionContext context, String text, Class type, Class aClass,
            List<SearchFilter> filters, Map<String, SearchFilter> typeSpecificFilters) {
        SearchCriteria criteria = SearchCriteria.forClass(aClass);

        if (filters != null) {
            for (SearchFilter filter : filters) {
                filter.applyRestrictions(type.getName(), context, criteria);
            }
        }

        if (typeSpecificFilters != null && typeSpecificFilters.get(type.getName()) != null) {
            typeSpecificFilters.get(type.getName()).applyRestrictions(type.getName(), context, criteria);
        } else {
            String st = (text == null) ? "" : text;

            criteria.add(ResourceCriterionUtils.getTextCriterion(st));
        }

        String p = criteria.getAlias("parent", "p");
        criteria.add(Restrictions.eq(p + ".hidden", Boolean.FALSE));

        return criteria;
    }

    private Map<Class, Class> getTypeToPersistentClassMap(List<Class> resourceTypeList) {
        Map<Class, Class> typeToPersistentClassMap = new HashMap<Class, Class>();
        if (resourceTypeList == null) {
            typeToPersistentClassMap.put(null, RepoResource.class);
        } else {
            for (Class aClass : resourceTypeList) {
                Class persistentClass = getPersistentClassMappings().getImplementationClass(aClass);

                if (persistentClass == null) {
                    throw new JSResourceNotFoundException("jsexception.resource.of.type.not.found",
                            new Object[] { "", aClass.getName() });
                }

                typeToPersistentClassMap.put(aClass, persistentClass);
            }
        }

        return typeToPersistentClassMap;
    }

    protected List toLookups(List repoResources, boolean addIdAsAttr) {
        List result = new ArrayList(repoResources.size());

        for (Iterator iter = repoResources.iterator(); iter.hasNext();) {
            IdedRepoObject repoResource = (IdedRepoObject) iter.next();
            ResourceLookup resourceLookup = repoResource.toClientLookup();

            if (addIdAsAttr) {
                List attrList = resourceLookup.getAttributes();
                if (attrList == null) {
                    attrList = new ArrayList();
                    resourceLookup.setAttributes(attrList);
                }
                attrList.add(new IdAttribute(repoResource.getId()));
            }

            result.add(resourceLookup);
        }
        return result;
    }

    protected ResourceLookup[] loadResources(ExecutionContext context, FilterCriteria filterCriteria) {
        List repoResources = loadResourcesList(context, filterCriteria);

        ResourceLookup[] resourceLookups = new ResourceLookup[repoResources.size()];
        resourceLookups = (ResourceLookup[]) repoResources.toArray(resourceLookups);
        return resourceLookups;
    }

    protected ResourceLookup[] loadResources(ExecutionContext context, FilterCriteria[] filterCriteria) {
        List repoResources = loadResourcesList(context, filterCriteria);

        ResourceLookup[] resourceLookups = new ResourceLookup[repoResources.size()];
        resourceLookups = (ResourceLookup[]) repoResources.toArray(resourceLookups);
        return resourceLookups;
    }

    public List loadRepoResourceList(final FilterCriteria filterCriteria) {
        return loadRepoResourceList(null, filterCriteria);
    }

    protected List loadRepoResourceList(final ExecutionContext context, final FilterCriteria filterCriteria) {
        return loadRepoResourceList(context, filterCriteria, true);
    }

    public List loadRepoResourceList(final ExecutionContext context, final FilterCriteria filterCriteria,
            final boolean sort) {
        DetachedCriteria criteria = translateFilterToCriteria(filterCriteria);

        // If we don't have a mapping, ignore it
        if (criteria == null) {
            return new ArrayList();
        } else {
            criteria.getExecutableCriteria(getSession()).setCacheable(true);
        }

        List repoResources = getHibernateTemplate().findByCriteria(criteria);
        if (sort) {
            sortRepoResourcesByURI(context, repoResources);
        }

        return repoResources;
    }

    protected DetachedCriteria translateFilterToCriteria(FilterCriteria filterCriteria) {
        Class filterClass = filterCriteria == null ? null : filterCriteria.getFilterClass();
        Class persistentClass;
        if (filterClass == null) {
            persistentClass = RepoResource.class;
            //persistentClass = RepoOlapUnit.class;    
        } else {
            persistentClass = getPersistentClassMappings().getImplementationClass(filterClass);
        }

        DetachedCriteria criteria;
        if (persistentClass == null) {
            criteria = null;
        } else {
            criteria = DetachedCriteria.forClass(persistentClass);
            criteria.createAlias("parent", "parent");
            criteria.add(Restrictions.eq("parent.hidden", Boolean.FALSE));
            if (filterCriteria != null) {
                List filterElements = filterCriteria.getFilterElements();
                if (!filterElements.isEmpty()) {
                    Conjunction conjunction = Restrictions.conjunction();
                    HibernateFilter filter = new HibernateFilter(conjunction, this);
                    for (Iterator it = filterElements.iterator(); it.hasNext();) {
                        FilterElement filterElement = (FilterElement) it.next();
                        filterElement.apply(filter);
                    }
                    criteria.add(conjunction);
                }
            }
        }

        return criteria;
    }

    protected void sortRepoResourcesByURI(final ExecutionContext context, List repoResources) {
        SortingUtils.sortRepoResourcesByURI(getCollator(context), repoResources);
    }

    protected void sortRepoFoldersByURI(final ExecutionContext context, List repoResources) {
        SortingUtils.sortFoldersByURI(getCollator(context), repoResources);
    }

    public Resource newResource(ExecutionContext context, Class _class) {
        return resourceFactory.newResource(context, _class);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public RepoResource findByURI(Class persistentClass, String uri, boolean required) {
        if (uri == null) {
            throw new JSException("jsexception.null.uri");
        }

        // Deal with URIs that come with "repo:" on the front

        final String repoURIPrefix = Resource.URI_PROTOCOL + ":";
        String workUri = uri.startsWith(repoURIPrefix) ? uri.substring(repoURIPrefix.length()) : uri;

        int sep = workUri.lastIndexOf(Folder.SEPARATOR);
        RepoResource res = null;
        if (sep >= 0) {
            String name = workUri.substring(sep + Folder.SEPARATOR_LENGTH);
            String folderName = workUri.substring(0, sep);
            log.debug("Looking for name: " + name + " in folder: " + folderName);
            RepoFolder folder = getFolder(folderName, false);
            if (folder != null) {
                res = findByName(persistentClass, folder, name, required);
            } else {
                log.debug("No folder: " + folderName);
            }
        }

        if (res == null) {
            if (log.isDebugEnabled()) {
                log.debug("Resource of type " + persistentClass.getName() + " not found at " + uri);
            }

            if (required) {
                String quotedURI = "\"" + uri + "\"";
                throw new JSResourceNotFoundException("jsexception.resource.of.type.not.found",
                        new Object[] { quotedURI, persistentClass });
            }
        }

        return res;
    }

    protected RepoResource findByName(Class persistentClass, RepoFolder folder, String name, boolean required) {
        DetachedCriteria criteria = resourceNameCriteria(persistentClass, folder, name);
        criteria.getExecutableCriteria(getSession()).setCacheable(true);
        List resourceList = getHibernateTemplate().findByCriteria(criteria);

        RepoResource res;
        if (resourceList.isEmpty()) {
            if (required) {
                String uri = "\"" + folder.getURI() + Folder.SEPARATOR + name + "\"";
                throw new JSResourceNotFoundException("jsexception.resource.of.type.not.found",
                        new Object[] { uri, persistentClass });
            }

            res = null;
        } else {
            res = (RepoResource) resourceList.get(0);
        }

        return res;
    }

    protected DetachedCriteria resourceNameCriteria(Class persistentClass, RepoFolder folder, String name) {
        DetachedCriteria criteria = DetachedCriteria.forClass(persistentClass);
        criteria.add(Restrictions.naturalId().set("name", name).set("parent", folder));
        return criteria;
    }

    protected boolean resourceExists(RepoFolder folder, String name, Class resourceType) {
        Class persistentClass = resourcePersistentClass(resourceType);
        DetachedCriteria criteria = resourceNameCriteria(persistentClass, folder, name);
        criteria.setProjection(Projections.rowCount());
        criteria.getExecutableCriteria(getSession()).setCacheable(true);
        List countList = getHibernateTemplate().findByCriteria(criteria);
        int count = ((Integer) countList.get(0)).intValue();
        return count > 0;
    }

    protected long findResourceId(String uri, Class resourceType) {
        String folderPath = RepositoryUtils.getParentPath(uri);
        if (folderPath == null) {
            throw new JSException("Invalid resource path " + uri);
        }

        RepoFolder folder = getFolder(folderPath, true);

        String resourceName = RepositoryUtils.getName(uri);
        Class persistentClass = resourcePersistentClass(resourceType);

        DetachedCriteria criteria = resourceNameCriteria(persistentClass, folder, resourceName);
        criteria.setProjection(Projections.id());
        List<Long> idList = getHibernateTemplate().findByCriteria(criteria);
        if (idList == null || idList.isEmpty()) {
            throw new JSException("Resource of type " + resourceType.getName() + " not found at " + uri);
        }

        if (idList.size() > 1) {
            // should not get here, but safer to check
            throw new RuntimeException("Found " + idList.size() + " IDs for resource of type "
                    + resourceType.getName() + " at " + uri);
        }

        return idList.get(0);
    }

    /*   
       protected boolean resourceDisplayNameExists(String parentUri, String displayName) {
          FilterCriteria criteria = FilterCriteria.createFilter();
          criteria.addFilterElement(FilterCriteria.createParentFolderFilter(parentUri));
        
          List resources = loadResourcesList(null, criteria); 
          for (int i=0; i<resources.size(); i++) {
     ResourceLookupImpl res = (ResourceLookupImpl)resources.get(i);
     if (displayName.equalsIgnoreCase(res.getLabel())) {
        return true;                   
     }
          }
          return false;
       }   
           
    */
    protected boolean folderExists(RepoFolder parent, String name) {
        String uri;
        if (parent.isRoot()) {
            uri = Folder.SEPARATOR + name;
        } else {
            uri = parent.getURI() + Folder.SEPARATOR + name;
        }
        return folderExists(uri);
    }

    /*   
       protected boolean folderDisplayNameExists(String parentUri, String displayName) {
          List repoFolderList = getSubFolders(null, parentUri);
          for (int i=0; i<repoFolderList.size(); i++) {
     FolderImpl repoFolder = (FolderImpl)repoFolderList.get(i);
     if (displayName.equalsIgnoreCase(repoFolder.getLabel())) {
        return true;                   
     }
          }
          return false;
       }   
    */
    protected RepoResource createPersistentResource(Class persistentClass) {
        try {
            RepoResource repo = (RepoResource) persistentClass.newInstance();
            repo.setCreationDate(getOperationTimestamp());
            repo.setUpdateDate(getOperationTimestamp());
            return repo;
        } catch (InstantiationException e) {
            log.fatal("Error instantiating persistent resource", e);
            throw new JSExceptionWrapper(e);
        } catch (IllegalAccessException e) {
            log.fatal("Error instantiating persistent resource", e);
            throw new JSExceptionWrapper(e);
        }
    }

    protected RepoResource getRepositoryReference(RepoResource owner, Resource res) {
        Class persistentClass = getPersistentClassMappings().getImplementationClass(res.getClass());
        if (persistentClass == null) {
            String quotedClass = "\"" + res.getClass().getName() + "\"";
            throw new JSException("jsexception.no.persistent.class.mapped.to", new Object[] { quotedClass });
        }

        RepoResource repo = null;
        RepoFolder folder = owner.getChildrenFolder();
        if (res.isNew()) {
            //if a local resource with the same name already exists it will be orphaned and deleted
            boolean tempName = folder != null && !folder.isNew() && resourceExists(folder, res.getName(), null);
            repo = createPersistentResource(persistentClass);
            if (tempName) {
                tempNameResources().add(repo);
            }
        } else {
            if (folder != null && !folder.isNew()) {
                repo = findByName(persistentClass, folder, res.getName(), false);
            }
            if (repo == null) {
                String quotedResource = "\"" + res.getURIString() + "\"";
                throw new JSException("jsexception.resource.does.not.exist", new Object[] { quotedResource });
            }
        }

        return repo;
    }

    /* (non-Javadoc)
     * @see com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.ReferenceResolver#getReference(com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.persistent.RepoResource, com.jaspersoft.jasperserver.api.metadata.common.domain.Resource, java.lang.Class)
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public RepoResource getReference(RepoResource owner, ResourceReference resourceRef,
            Class persistentReferenceClass) {
        if (resourceRef == null) {
            return null;
        }
        RepoResource repoRes;
        if (resourceRef.isLocal()) {
            repoRes = getReference(owner, resourceRef.getLocalResource(), persistentReferenceClass);
        } else {
            repoRes = findByURI(persistentReferenceClass, resourceRef.getReferenceURI(), true);
        }
        return repoRes;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public RepoResource getReference(RepoResource owner, Resource resource, Class persistentReferenceClass) {
        return getReference(owner, resource, persistentReferenceClass, this);
    }

    protected RepoResource getReference(RepoResource owner, Resource resource, Class persistentReferenceClass,
            ReferenceResolver referenceResolver) {
        if (resource == null) {
            return null;
        }

        RepoResource repoRes = getRepositoryReference(owner, resource);

        RepoFolder local = owner.getChildrenFolder();
        if (local == null) {
            if (log.isInfoEnabled()) {
                log.info("Creating children folder for " + owner.getResourceURI());
            }

            local = createNewRepoFolder();
            local.setName(getChildrenFolderName(owner.getName()));
            local.setLabel(owner.getLabel());
            local.setDescription(owner.getDescription());
            local.setParent(owner.getParent());
            local.setHidden(true);
            local.refreshURI(this);
            owner.setChildrenFolder(local);
        }

        owner.addNewChild(repoRes);
        repoRes.copyFromClient(resource, referenceResolver);

        if (tempNameResources().contains(repoRes)) {
            repoRes.setName(TEMP_NAME_PREFIX + repoRes.getName());
            RepoFolder childrenFolder = repoRes.getChildrenFolder();
            if (childrenFolder != null) {
                childrenFolder.setName(TEMP_NAME_PREFIX + childrenFolder.getName());
                refreshFolderPaths(childrenFolder);
            }
        }

        return repoRes;
    }

    protected void refreshFolderPaths(RepoFolder folder) {
        // refreshing recursively using a queue
        LinkedList<RepoFolder> folders = new LinkedList<RepoFolder>();
        folders.addLast(folder);

        while (!folders.isEmpty()) {
            RepoFolder aFolder = folders.removeFirst();
            aFolder.refreshURI(this);

            Set resources = aFolder.getChildren();
            if (resources != null && !resources.isEmpty()) {
                for (Iterator it = resources.iterator(); it.hasNext();) {
                    RepoResource child = (RepoResource) it.next();
                    RepoFolder grandChildrenFolder = child.getChildrenFolder();
                    if (grandChildrenFolder != null) {
                        folders.addLast(grandChildrenFolder);
                    }
                }
            }
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List getAllFolders(final ExecutionContext context) {
        return (List) executeCallback(new DaoCallback() {
            public Object execute() {
                return loadAllSubfolders(context, null);
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public int getFoldersCount(final String parentURI) {
        return (Integer) executeCallback(new DaoCallback() {
            public Object execute() {
                DetachedCriteria criteria = DetachedCriteria.forClass(RepoFolder.class);
                criteria.add(Restrictions.eq("hidden", Boolean.FALSE));
                criteria.setProjection(Projections.rowCount());
                if (parentURI != null && !Folder.SEPARATOR.equals(parentURI)) {
                    criteria.add(Restrictions.or(
                            (parentURI.contains("%")) ? Restrictions.eq("URI", parentURI)
                                    : Restrictions.like("URI", parentURI),
                            Restrictions.like("URI", parentURI + "/%")));
                }
                return new BasicTransformer().transformToCount(getHibernateTemplate().findByCriteria(criteria));
            }
        });
    }

    protected List loadAllSubfolders(ExecutionContext context, String parentURI) {
        DetachedCriteria criteria = DetachedCriteria.forClass(RepoFolder.class);
        criteria.add(Restrictions.eq("hidden", Boolean.FALSE));

        if (parentURI != null && !Folder.SEPARATOR.equals(parentURI)) {
            criteria.add(Restrictions.or((parentURI.indexOf("%") == -1) ? Restrictions.eq("URI", parentURI)
                    : Restrictions.like("URI", parentURI), Restrictions.like("URI", parentURI + "/%")));
        }

        criteria.getExecutableCriteria(getSession()).setCacheable(true);
        List repoFolders = getHibernateTemplate().findByCriteria(criteria);
        List folders = new ArrayList(repoFolders.size());
        for (Iterator iter = repoFolders.iterator(); iter.hasNext();) {
            RepoFolder repoFolder = (RepoFolder) iter.next();
            Folder folder = repoFolder.toClient();
            folders.add(folder);
        }

        SortingUtils.sortFoldersByURI(getCollator(context), folders);

        return folders;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List getAllSubfolders(final ExecutionContext context, final String parentURI) {
        return (List) executeCallback(new DaoCallback() {
            public Object execute() {
                return loadAllSubfolders(context, parentURI);
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List getSubFolders(final ExecutionContext context, final String folderURI) {
        return (List) executeCallback(new DaoCallback() {
            public Object execute() {
                final RepoFolder folder = getFolder(folderURI, false);
                List subfolders;
                if (folder == null || folder.getSubFolders() == null) {
                    //return empty list for non-existing folder
                    subfolders = new ArrayList();
                } else {
                    List folders = getHibernateTemplate().executeFind(new HibernateCallback() {
                        public Object doInHibernate(Session session) throws HibernateException, SQLException {
                            return session.createFilter(folder.getSubFolders(), "where hidden = false").list();
                        }
                    });

                    subfolders = new ArrayList(folders.size());
                    for (Iterator it = folders.iterator(); it.hasNext();) {
                        RepoFolder repoFolder = (RepoFolder) it.next();
                        subfolders.add(repoFolder.toClient());
                    }

                    SortingUtils.sortFoldersByName(getCollator(context), subfolders);
                }
                return subfolders;
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public FileResourceData getResourceData(ExecutionContext context, final String uri)
            throws JSResourceNotFoundException {
        return (FileResourceData) executeCallback(new DaoCallback() {
            public Object execute() {
                RepoFileResource res = (RepoFileResource) findByURI(RepoFileResource.class, uri, true);
                while (res.isFileReference()) {
                    res = res.getReference();
                }
                return res.copyData();
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public FileResourceData getContentResourceData(ExecutionContext context, final String uri)
            throws JSResourceNotFoundException {
        return (FileResourceData) executeCallback(new DaoCallback() {
            public Object execute() {
                ContentRepoFileResource res = (ContentRepoFileResource) findByURI(ContentRepoFileResource.class,
                        uri, true);
                return res.copyData();
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public RepoResource getExternalReference(String uri, Class persistentReferenceClass) {
        return findByURI(persistentReferenceClass, uri, true);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public RepoResource getPersistentReference(String uri, Class clientReferenceClass) {
        return findByURI(resourcePersistentClass(clientReferenceClass), uri, true);
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void deleteResourceNoFlush(ExecutionContext context, final String uri) {
        deleteResource(context, uri, false);
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void deleteResource(ExecutionContext context, final String uri) {
        deleteResource(context, uri, true);
    }

    public void deleteResource(ExecutionContext context, final String uri, boolean flush) {
        executeWriteCallback(new DaoCallback() {
            public Object execute() {
                deleteResource(uri);
                return null;
            }
        }, flush);
    }

    protected void deleteResource(String uri) {
        RepoResource repoResource = findByURI(RepoResource.class, uri, true);
        auditResourceActivity("deleteResource", uri, repoResource.getClientType().getName());
        getHibernateTemplate().delete(repoResource);
        for (Iterator it = getRepositoryListeners().iterator(); it.hasNext();) {
            RepositoryListener listener = (RepositoryListener) it.next();
            if (listener instanceof RepositoryEventListener) {
                ((RepositoryEventListener) listener).onResourceDelete(repoResource.getClientType(), uri);
            }
        }
        closeAuditEvent("deleteResource");
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void deleteFolder(ExecutionContext context, final String uri) {
        executeWriteCallback(new DaoCallback() {
            public Object execute() {
                deleteFolder(uri);
                return null;
            }
        });
    }

    protected void deleteFolder(String uri) {
        auditFolderActivity("deleteFolder", uri);
        RepoFolder folder = getFolder(uri, true);
        if (folder.isRoot()) {
            throw new JSException("jsexception.folder.delete.root");
        }
        getHibernateTemplate().delete(folder);
        closeAuditEvent("deleteFolder");
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void delete(ExecutionContext context, final String[] resourceURIs, final String[] folderURIs) {
        executeWriteCallback(new DaoCallback() {
            public Object execute() {
                if (resourceURIs != null && resourceURIs.length > 0) {
                    for (int i = 0; i < resourceURIs.length; i++) {
                        deleteResource(resourceURIs[i]);
                    }
                }

                if (folderURIs != null && folderURIs.length > 0) {
                    for (int i = 0; i < folderURIs.length; i++) {
                        deleteFolder(folderURIs[i]);
                    }
                }

                return null;
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Folder getFolder(ExecutionContext context, final String uri) {

        return getFolderUnsecure(context, uri);
    }

    public boolean folderExists(ExecutionContext context, String uri) {
        return folderExists(uri);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Folder getFolderUnsecure(ExecutionContext context, final String uri) {
        return (Folder) executeCallback(new DaoCallback() {
            public Object execute() {
                RepoFolder repoFolder = getFolder(uri, false);
                Folder folder = repoFolder == null ? null : repoFolder.toClient();
                return folder;
            }
        });
    }

    protected boolean folderExists(String uri) {
        return getFolder(uri, false) != null;
    }

    public String getChildrenFolderName(String resourceName) {
        return resourceName + CHILDREN_FOLDER_SUFFIX;
    }

    public CollatorFactory getCollatorFactory() {
        return collatorFactory;
    }

    public void setCollatorFactory(CollatorFactory collatorFactory) {
        this.collatorFactory = collatorFactory;
    }

    protected Collator getCollator(ExecutionContext context) {
        return getCollatorFactory().getCollator(context);
    }

    protected boolean pathExists(String uri) {
        return resourceExists(uri) || folderExists(uri);
    }

    public boolean repositoryPathExists(ExecutionContext context, String uri) {
        return pathExists(uri);
    }

    /* (non-Javadoc)
     * @see com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService#hideFolder(java.lang.String)
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void hideFolder(String uri) {
        setFolderHiddenFlag(uri, true);
    }

    /* (non-Javadoc)
     * @see com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService#unhideFolder(java.lang.String)
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void unhideFolder(String uri) {
        setFolderHiddenFlag(uri, false);
    }

    private void setFolderHiddenFlag(String uri, boolean hidden) {
        RepoFolder folder = getFolder(uri, false);
        if (folder != null && folder.isHidden() != hidden) {
            folder.setHidden(hidden);
            validateResourceUriLength(folder.getURI().length());
            getHibernateTemplate().saveOrUpdate(folder);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, isolation = Isolation.READ_COMMITTED)
    public void moveFolder(ExecutionContext context, final String sourceURI, final String destinationFolderURI) {

        executeWriteCallback(new DaoCallback() {
            public Object execute() {
                auditFolderCopyAndMove("moveFolder", sourceURI, destinationFolderURI);
                RepoFolder source = getFolder(sourceURI, true);
                RepoFolder dest = getFolder(destinationFolderURI, true);

                if (isAncestorOrEqual(source.getResourceURI(), dest.getResourceURI())) {
                    throw new JSException("jsexception.move.folder.source.uri.ancestor.destination",
                            new Object[] { sourceURI, destinationFolderURI });
                }
                if (source.getParent().getResourceURI().equals(dest.getResourceURI())) {
                    throw new JSException("jsexception.move.folder.to.same.folder",
                            new Object[] { sourceURI, destinationFolderURI });
                }
                if (nameExistsInFolder(dest, source.getName())) {
                    throw new JSException("jsexception.move.folder.path.already.exists",
                            new Object[] { sourceURI, destinationFolderURI });
                }
                validateResourceUriLength(source.getName().length() + destinationFolderURI.length());

                if (log.isDebugEnabled()) {
                    log.debug("Moving folder " + source.getResourceURI() + " to " + dest.getResourceURI());
                }

                source.moveTo(dest, HibernateRepositoryServiceImpl.this);
                closeAuditEvent("moveFolder");
                return null;
            }
        });
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, isolation = Isolation.READ_COMMITTED)
    public void moveResource(ExecutionContext context, final String sourceURI, final String destinationFolderURI) {
        executeWriteCallback(new DaoCallback() {
            public Object execute() {
                RepoResource source = findByURI(RepoResource.class, sourceURI, true);
                RepoFolder dest = getFolder(destinationFolderURI, true);

                auditResourceCopyAndMove("moveResource", sourceURI, destinationFolderURI,
                        source.getClientType().getName());
                if (source.getParent().getResourceURI().equals(dest.getResourceURI())) {
                    throw new JSException("jsexception.move.resource.to.same.folder",
                            new Object[] { sourceURI, destinationFolderURI });
                }
                if (nameExistsInFolder(dest, source.getName())) {
                    throw new JSException("jsexception.move.resource.path.already.exists",
                            new Object[] { sourceURI, destinationFolderURI });
                }
                if (log.isDebugEnabled()) {
                    log.debug("Moving resource " + source.getResourceURI() + " to " + dest.getResourceURI());
                }

                source.moveTo(dest, HibernateRepositoryServiceImpl.this);
                closeAuditEvent("moveResource");
                return null;
            }
        });
    }

    protected boolean isAncestorOrEqual(String ancestorURI, String childURI) {
        return RepositoryUtils.isAncestorOrEqual(ancestorURI, childURI);
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void update(Object repoObject) {
        if (repoObject instanceof RepoResourceBase) {
            RepoResourceBase repoResource = (RepoResourceBase) repoObject;
            validateResourceUriLength(repoResource.getResourceURI().length());
        }
        getHibernateTemplate().update(repoObject);
    }

    public List getRepositoryListeners() {
        return repositoryListeners;
    }

    public void setRepositoryListeners(List repositoryListeners) {
        this.repositoryListeners = repositoryListeners;
    }

    protected boolean nameExistsInFolder(RepoFolder folder, String name) {
        return resourceExists(folder, name, null) || folderExists(folder, name);
    }

    public void folderMoved(RepoFolder folder, String oldBaseURI, String newBaseURI) {
        String newURI = folder.getResourceURI();
        String oldURI = getMovedOldURI(newURI, oldBaseURI, newBaseURI);

        if (log.isDebugEnabled()) {
            log.debug("Folder " + oldURI + " has been moved to " + newURI);
        }

        FolderMoveEvent folderMove = new FolderMoveEvent(oldBaseURI, newBaseURI, oldURI, newURI);
        for (Iterator it = getRepositoryListeners().iterator(); it.hasNext();) {
            RepositoryListener listener = (RepositoryListener) it.next();
            listener.folderMoved(folderMove);
        }
    }

    public void resourceMoved(RepoResource resource, String oldBaseURI, String newBaseURI) {
        String newURI = resource.getResourceURI();
        String oldURI = getMovedOldURI(newURI, oldBaseURI, newBaseURI);
        Class type = resource.getClientType();

        if (log.isDebugEnabled()) {
            log.debug("Resource " + oldURI + " has been moved to " + newURI);
        }

        ResourceMoveEvent resourceMove = new ResourceMoveEvent(type, oldBaseURI, newBaseURI, oldURI, newURI);
        for (Iterator it = getRepositoryListeners().iterator(); it.hasNext();) {
            RepositoryListener listener = (RepositoryListener) it.next();
            listener.resourceMoved(resourceMove);
        }
    }

    protected String getMovedOldURI(String newURI, String oldBaseURI, String newBaseURI) {
        if (!RepositoryUtils.isAncestorOrEqual(newBaseURI, newURI)) {
            throw new JSException("New folder URI " + newURI + " does not match new base URI " + newBaseURI);
        }

        String newPostBaseURI;
        if (newBaseURI.equals(Folder.SEPARATOR)) {
            newPostBaseURI = newURI;
        } else {
            newPostBaseURI = newURI.substring(newBaseURI.length());
        }

        String oldURI;
        if (oldBaseURI.equals(Folder.SEPARATOR)) {
            oldURI = newPostBaseURI;
        } else {
            oldURI = oldBaseURI + newPostBaseURI;
        }
        return oldURI;
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, isolation = Isolation.READ_COMMITTED)
    public Resource copyResource(ExecutionContext context, final String sourceURI, final String destinationURI) {
        initTempNameResources();
        try {
            Resource copy = (Resource) executeWriteCallback(new DaoCallback() {
                public Object execute() {
                    RepoResource repoCopy = copyResource(sourceURI, destinationURI);
                    return repoCopy.toClient(resourceFactory);
                }
            });
            return copy;
        } finally {
            resetTempNameResources();
        }
    }

    protected RepoResource copyResource(final String sourceURI, final String destinationURI) {
        RepoResource sourceRepoResource = findByURI(RepoResource.class, sourceURI, true);
        auditResourceCopyAndMove("copyResource", sourceURI, destinationURI,
                sourceRepoResource.getClientType().getName());
        String copyURI = getCopyURI(destinationURI, true);
        RepoResource repoCopy = copyResource(sourceRepoResource, copyURI);
        closeAuditEvent("copyResource");
        return repoCopy;
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, isolation = Isolation.READ_COMMITTED)
    public void copyResources(ExecutionContext context, final String[] resources, final String destinationFolder) {
        initTempNameResources();
        try {
            executeWriteCallback(new DaoCallback() {
                public Object execute() {
                    copyResources(resources, destinationFolder);
                    return null;
                }
            });
        } finally {
            resetTempNameResources();
        }
    }

    protected void copyResources(String[] sourceURIs, String destinationFolder) {
        RepoResource[] sources = new RepoResource[sourceURIs.length];
        for (int i = 0; i < sourceURIs.length; i++) {
            sources[i] = findByURI(RepoResource.class, sourceURIs[i], true);
        }

        RepoFolder destination = getFolder(destinationFolder, true);

        MultiResourceCopier copier = new MultiResourceCopier(sources, destination);
        copier.copy();
    }

    protected String getCopyURI(String destinationURI, boolean resource) {
        String parentURI = RepositoryUtils.getParentPath(destinationURI);
        RepoFolder parent = getFolder(parentURI, true);
        lockReadFolder(parent);
        String destinationName = RepositoryUtils.getName(destinationURI);

        String copyURI = makeCopyURI(parent, destinationName, resource);

        if (log.isDebugEnabled()) {
            log.debug("Using " + copyURI + " for copy at " + destinationURI);
        }

        return copyURI;
    }

    protected String makeCopyURI(RepoFolder destination, String name, boolean resource) {
        String copyURI;
        if (nameExistsInFolder(destination, name)) {
            int maxNameLenght = RESOURCE_NAME_LENGHT;
            if (resource) {
                maxNameLenght -= CHILDREN_FOLDER_SUFFIX.length();
            }

            String baseName = name;
            String copyName;
            int counter = 1;
            do {
                copyName = baseName + COPY_GENERATED_NAME_SEPARATOR + counter;
                if (copyName.length() > maxNameLenght) {
                    String truncatedBaseName = baseName.substring(0,
                            baseName.length() - copyName.length() + maxNameLenght);
                    copyName = truncatedBaseName + COPY_GENERATED_NAME_SEPARATOR + counter;
                }
                ++counter;
            } while (nameExistsInFolder(destination, copyName));

            copyURI = RepositoryUtils.concatenatePath(destination.getURI(), copyName);
        } else {
            copyURI = RepositoryUtils.concatenatePath(destination.getURI(), name);
        }
        return copyURI;
    }

    protected RepoResource copyResource(RepoResource resource, String copyURI) {
        Resource copy = getClientClone(resource);
        copy.setURIString(copyURI);
        resourceCopied(resource.getResourceURI(), copy);

        RepoResource repoCopy = getRepoResource(copy);
        repoCopy.copyFromClient(copy, this);
        repoCopy.setUpdateDate(resource.getUpdateDate());
        getHibernateTemplate().save(repoCopy);

        return repoCopy;
    }

    protected void resourceCopied(String sourceUri, Resource copy) {
        if (log.isDebugEnabled()) {
            log.debug("Resource " + copy.getURIString() + " copied from " + sourceUri);
        }

        ResourceCopiedEvent event = new ResourceCopiedEvent(sourceUri, copy);
        for (Iterator it = getRepositoryListeners().iterator(); it.hasNext();) {
            RepositoryListener listener = (RepositoryListener) it.next();
            listener.resourceCopied(event);
        }
    }

    protected Resource getClientClone(RepoResource resource) {
        Resource copy = (Resource) resource.toClient(resourceFactory, CLIENT_CLONE_OPTIONS);
        return copy;
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, isolation = Isolation.READ_COMMITTED)
    public Folder copyFolder(ExecutionContext context, final String sourceURI, final String destinationURI) {
        initTempNameResources();
        auditFolderCopyAndMove("copyFolder", sourceURI, destinationURI);
        try {
            Folder copy = (Folder) executeWriteCallback(new DaoCallback() {
                public Object execute() {
                    RepoFolder folderCopy = copyFolder(sourceURI, destinationURI);
                    return folderCopy.toClient();
                }
            });
            closeAuditEvent("copyFolder");
            return copy;
        } finally {
            resetTempNameResources();
        }
    }

    protected RepoFolder copyFolder(String sourceURI, String destinationURI) {
        RepoFolder repoSource = getFolder(sourceURI, true);

        String copyURI = getCopyURI(destinationURI, false);
        RecursiveCopier copier = new RecursiveCopier(repoSource, copyURI);
        copier.copy();
        return copier.getCopiedRootFolder();
    }

    /**
     * Returns maximum valid folder URI length
     *
     * The method is used for futher lazy initialization of resourceUriLength.
     * Loads sessionFactory bean from application context as LocalSessionFactoryBean class instance.
     * Bean is needed for getting hibernate Configuration object which stores configuration metadata.
     *
     * @return maximum valid folder URI length.
     *
     *
     * */
    private int getMaxUriLength() {
        if (resourceUriLength >= 0) {
            return resourceUriLength;
        }

        //If resourceUriLength is not initialized yet
        LocalSessionFactoryBean localSessionFactoryBean = (LocalSessionFactoryBean) applicationContext
                .getBean("&sessionFactory");

        String entityName = RepoFolder.class.getCanonicalName();
        PersistentClass persistentClass = localSessionFactoryBean.getConfiguration().getClassMapping(entityName);

        resourceUriLength = persistentClass.getTable().getColumn(new Column("URI")).getLength();
        return resourceUriLength;
    }

    private void validateResourceUriLength(int uriLength) {
        if (uriLength > getMaxUriLength()) {
            throw new JSException("jsexception.folder.too.long.uri", new Object[] { resourceUriLength });
        }
    }

    protected abstract class BaseResourceCopier implements ReferenceResolver {

        protected final LinkedHashMap copiedResources = new LinkedHashMap();
        private final Set copyingResourceStack = new HashSet();

        protected abstract boolean isCopiedResource(String uri);

        protected abstract RepoFolder getCopyParent(RepoResourceBase resource);

        protected abstract String getCopyURI(String uri);

        protected abstract RepoResource loadResourceToCopy(String uri, Class persistentClass);

        protected RepoResource copyResource(RepoResource resource) {
            String copyURI = getCopyURI(resource.getResourceURI());
            RepoResource resourceCopy = (RepoResource) copiedResources.get(copyURI);
            if (resourceCopy == null) {
                resourceCopy = copyResource(resource, copyURI);
            }
            return resourceCopy;
        }

        protected RepoResource copyResource(RepoResource resource, String copyURI) {
            auditResourceCopyAndMove("copyResource", resource.getResourceURI(), copyURI,
                    resource.getClientType().getName());
            if (log.isDebugEnabled()) {
                log.debug("Creating copy of resource " + resource.getResourceURI() + " at " + copyURI);
            }

            if (!copyingResourceStack.add(copyURI)) {
                throw new JSException("jsexception.copy.resource.circular.reference",
                        new Object[] { resource.getResourceURI() });
            }
            try {
                Resource copy = getClientClone(resource);
                copy.setURIString(copyURI);
                resourceCopied(resource.getResourceURI(), copy);

                Class persistentClass = resourcePersistentClass(copy.getClass());
                RepoResource resourceCopy = createPersistentResource(persistentClass);
                RepoFolder copyParent = getCopyParent(resource);
                resourceCopy.setParent(copyParent);
                resourceCopy.copyFromClient(copy, this);
                resourceCopy.setUpdateDate(resource.getUpdateDate());

                copiedResources.put(copyURI, resourceCopy);

                closeAuditEvent("copyResource");
                return resourceCopy;
            } finally {
                copyingResourceStack.remove(copyURI);
            }
        }

        protected RepoResource getCopiedResourceReference(String uri, Class persistentClass) {
            String copyURI = getCopyURI(uri);
            RepoResource resourceCopy = (RepoResource) copiedResources.get(copyURI);
            if (resourceCopy == null) {
                RepoResource resource = loadResourceToCopy(uri, persistentClass);
                resourceCopy = copyResource(resource, copyURI);
            }

            if (log.isDebugEnabled()) {
                log.debug("Reference to " + uri + " has been changed to " + copyURI);
            }

            return resourceCopy;
        }

        protected RepoResource getCopyReference(String uri, Class persistentReferenceClass) {
            RepoResource reference;
            if (isCopiedResource(uri)) {
                reference = getCopiedResourceReference(uri, persistentReferenceClass);
            } else {
                reference = findByURI(persistentReferenceClass, uri, true);
            }
            return reference;
        }

        public RepoResource getExternalReference(String uri, Class persistentReferenceClass) {
            return getCopyReference(uri, persistentReferenceClass);
        }

        public RepoResource getPersistentReference(String uri, Class clientReferenceClass) {
            return getCopyReference(uri, resourcePersistentClass(clientReferenceClass));
        }

        public RepoResource getReference(RepoResource owner, ResourceReference resourceReference,
                Class persistentReferenceClass) {
            if (resourceReference == null) {
                return null;
            }
            RepoResource repoRes;
            if (resourceReference.isLocal()) {
                repoRes = getReference(owner, resourceReference.getLocalResource(), persistentReferenceClass);
            } else {
                repoRes = getCopyReference(resourceReference.getReferenceURI(), persistentReferenceClass);
            }
            return repoRes;
        }

        public RepoResource getReference(RepoResource owner, Resource resource, Class persistentReferenceClass) {
            return HibernateRepositoryServiceImpl.this.getReference(owner, resource, persistentReferenceClass,
                    this);
        }
    }

    protected class RecursiveCopier extends BaseResourceCopier {

        private final RepoFolder sourceRoot;
        private final String destinationRootURI;

        private final LinkedHashMap copiedFolders = new LinkedHashMap();

        public RecursiveCopier(RepoFolder sourceRoot, String destinationRootURI) {
            this.sourceRoot = sourceRoot;
            this.destinationRootURI = destinationRootURI;
        }

        public void copy() {
            copyFolderRecursively(sourceRoot);
            save();
        }

        protected void save() {
            HibernateTemplate template = getHibernateTemplate();

            for (Iterator it = copiedFolders.values().iterator(); it.hasNext();) {
                RepoFolder folder = (RepoFolder) it.next();
                validateResourceUriLength(folder.getResourceURI().length());
                template.save(folder);
            }

            for (Iterator it = copiedResources.values().iterator(); it.hasNext();) {
                RepoResource resource = (RepoResource) it.next();
                template.save(resource);
            }
        }

        protected void copyFolderRecursively(RepoFolder folder) {
            if (log.isDebugEnabled()) {
                log.debug("Recursively copying folder " + folder.getURI());
            }

            copyFolder(folder);

            getHibernateTemplate().refresh(folder); // This ensures that folder object is synchronized
            // with database.  Synchronization populates in-memory
            // (persistence context) folder with the correct sub-folders

            Set subfolders = folder.getSubFolders();
            if (subfolders != null) {
                for (Iterator it = subfolders.iterator(); it.hasNext();) {
                    RepoFolder subfolder = (RepoFolder) it.next();
                    if (!subfolder.isHidden() && getSecurityChecker().isFolderReadable(subfolder.getURI())) {
                        copyFolderRecursively(subfolder);
                    }
                }
            }

            Set resources = folder.getChildren();
            if (resources != null) {
                for (Iterator it = resources.iterator(); it.hasNext();) {
                    RepoResource resource = (RepoResource) it.next();
                    if (getSecurityChecker().isResourceReadable(resource.getResourceURI())) {
                        copyResource(resource);
                    }
                }
            }
        }

        protected RepoFolder copyFolder(RepoFolder folder) {
            String copyURI = getCopyURI(folder.getURI());
            RepoFolder folderCopy = (RepoFolder) copiedFolders.get(copyURI);
            if (folderCopy == null) {
                if (log.isDebugEnabled()) {
                    log.debug("Creating copy of folder " + folder.getURI() + " at " + copyURI);
                }

                Folder copy = folder.toClient();
                copy.setVersion(Resource.VERSION_NEW);
                copy.setURIString(copyURI);

                folderCopy = createNewRepoFolder();
                RepoFolder copyParent = getCopyParent(folder);
                folderCopy.set(copy, copyParent, HibernateRepositoryServiceImpl.this);
                folderCopy.setUpdateDate(folder.getUpdateDate());

                copiedFolders.put(copyURI, folderCopy);
            }

            return folderCopy;
        }

        protected String getCopyURI(String uri) {
            String copyURI;
            if (sourceRoot.getURI().equals(uri)) {
                copyURI = destinationRootURI;
            } else if (sourceRoot.isRoot()) {
                copyURI = destinationRootURI + uri;
            } else {
                copyURI = destinationRootURI + uri.substring(sourceRoot.getURI().length());
            }
            return copyURI;
        }

        protected RepoFolder getCopyParent(RepoResourceBase resource) {
            String uri = resource.getResourceURI();
            RepoFolder parent;
            if (sourceRoot.getURI().equals(uri)) {
                String destinationParentURI = RepositoryUtils.getParentPath(destinationRootURI);
                parent = getFolder(destinationParentURI, true);
            } else {
                String copyParentURI = getCopyURI(resource.getParent().getURI());
                parent = (RepoFolder) copiedFolders.get(copyParentURI);
                if (parent == null) {
                    parent = copyFolder(resource.getParent());
                }
            }
            return parent;
        }

        protected boolean isCopiedResource(String uri) {
            return RepositoryUtils.isAncestorOrEqual(sourceRoot.getURI(), uri);
        }

        protected RepoResource loadResourceToCopy(String uri, Class persistentClass) {
            if (!getSecurityChecker().isResourceReadable(uri)) {
                throw new JSException("jsexception.copy.resource.referenced.not.readable", new Object[] { uri });
            }
            return findByURI(persistentClass, uri, true);
        }

        public RepoFolder getCopiedRootFolder() {
            return (RepoFolder) copiedFolders.get(destinationRootURI);
        }
    }

    protected class MultiResourceCopier extends BaseResourceCopier {

        private final Map sourcesMap;
        private final RepoFolder destination;
        private final Map copyURIs = new HashMap();

        public MultiResourceCopier(RepoResource[] sources, RepoFolder destination) {
            this.sourcesMap = new LinkedHashMap();
            for (int i = 0; i < sources.length; i++) {
                RepoResource source = sources[i];
                sourcesMap.put(source.getResourceURI(), source);
            }

            this.destination = destination;
        }

        public void copy() {
            copyResources();
            save();
        }

        protected void copyResources() {
            for (Iterator it = sourcesMap.values().iterator(); it.hasNext();) {
                RepoResource resource = (RepoResource) it.next();
                copyResource(resource);
            }
        }

        protected void save() {
            HibernateTemplate template = getHibernateTemplate();
            for (Iterator it = copiedResources.values().iterator(); it.hasNext();) {
                RepoResource resource = (RepoResource) it.next();
                template.save(resource);
            }
        }

        protected RepoFolder getCopyParent(RepoResourceBase resource) {
            return destination;
        }

        protected String getCopyURI(String uri) {
            String copyURI = (String) copyURIs.get(uri);
            if (copyURI == null) {
                String name = RepositoryUtils.getName(uri);
                copyURI = makeCopyURI(destination, name, true);

                if (log.isDebugEnabled()) {
                    log.debug("Using " + copyURI + " for copy of " + name + " at " + destination.getURI());
                }

                copyURIs.put(uri, copyURI);
            }
            return copyURI;
        }

        protected boolean isCopiedResource(String uri) {
            return sourcesMap.containsKey(uri);
        }

        protected RepoResource loadResourceToCopy(String uri, Class persistentClass) {
            return (RepoResource) sourcesMap.get(uri);
        }
    }

    public void lockPath(RepoFolder folder) {
        validateResourceUriLength(folder.getURI().length());
        if (!isLockFoldersOnPathChange()) {
            return;
        }

        HibernateTemplate template = getHibernateTemplate();
        RepoFolder parent = folder.getParent();
        if (parent != null && !parent.isNew()) {
            lockReadFolder(parent);
        }

        if (!folder.isNew()) {
            //lock the folder to indicate that its path has changed
            template.lock(folder, LockMode.UPGRADE);
        }
    }

    protected void lockReadFolder(RepoFolder folder) {
        HibernateTemplate template = getHibernateTemplate();
        Session session = template.getSessionFactory().getCurrentSession();
        LockMode folderLockMode = session.getCurrentLockMode(folder);
        if (LockMode.READ.equals(folderLockMode)) {
            String currentURI = folder.getURI();

            //refresh and lock the parent
            template.refresh(folder, LockMode.UPGRADE);

            //check whether the parent URI has changed
            if (!currentURI.equals(folder.getURI())) {
                throw new JSException("jsexception.folder.moved", new Object[] { currentURI, folder.getURI() });
            }
        }
    }

    public boolean isLockFoldersOnPathChange() {
        return lockFoldersOnPathChange;
    }

    public void setLockFoldersOnPathChange(boolean lockFoldersOnPathChange) {
        this.lockFoldersOnPathChange = lockFoldersOnPathChange;
    }

    /* (non-Javadoc)
     * @see com.jaspersoft.jasperserver.api.metadata.common.service.impl.hibernate.PersistentObjectResolver#getPersistentObject(java.lang.Object)
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public Object getPersistentObject(Object clientObject) {
        if (clientObject instanceof Folder) {
            return getFolder(((Folder) clientObject).getURIString(), false);
        } else if (clientObject instanceof Resource) {
            return findByURI(RepoResource.class, ((Resource) clientObject).getURIString(), false);
        }
        return null;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List<ResourceLookup> getResources(ExecutionContext context, SearchCriteriaFactory searchCriteriaFactory,
            List<SearchFilter> filters, SearchSorter sorter, TransformerFactory transformerFactory, int current,
            int max) {
        if (transformerFactory == null) {
            throw new IllegalArgumentException("Transformer factory is null.");
        }

        SearchCriteria criteria;
        List<ResourceLookup> result;
        if (queryModificationEvaluator.useFullResource(context)) {
            // we have to get ids only and make additional call, see:
            // https://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#Hibernate_does_not_return_distinct_results_for_a_query_with_outer_join_fetching_enabled_for_a_collection_even_if_I_use_the_distinct_keyword
            searchCriteriaFactory = searchCriteriaFactory.newFactory(Resource.class.getName());
            criteria = searchCriteriaFactory.create(context, filters);
            criteria.addProjection(Projections.distinct(Projections.id()));
            searchCriteriaFactory.applySorter(context, criteria, sorter);

            List list = getHibernateTemplate().findByCriteria(criteria, current, max);

            List<Long> ids = new ArrayList<Long>();
            ResultTransformer transformer = transformerFactory.createTransformer(filters, sorter);
            if (transformer != null) {
                ids.addAll(transformer.transformToIdList(list));
            } else {
                throw new IllegalArgumentException("Result transformer is null.");
            }

            result = getResourcesByIdList(ids, searchCriteriaFactory);
        } else {
            // just get the resources since no outer joins supposed to be here
            criteria = searchCriteriaFactory.create(context, filters);
            searchCriteriaFactory.applySorter(context, criteria, sorter);

            List<IdedRepoObject> list = getHibernateTemplate().findByCriteria(criteria, current, max);
            result = toLookups(list, true);
        }

        return result;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public int getResourcesCount(ExecutionContext context, SearchCriteriaFactory searchCriteriaFactory,
            List<SearchFilter> filters, SearchSorter sorter, TransformerFactory transformerFactory) {
        if (transformerFactory == null) {
            throw new IllegalArgumentException("Transformer factory is null.");
        }

        SearchCriteria criteria;
        if (queryModificationEvaluator.useFullResource(context)) {
            criteria = searchCriteriaFactory.newFactory(Resource.class.getName()).create(context, filters);
            criteria.setProjection(Projections.countDistinct("id"));
        } else {
            criteria = searchCriteriaFactory.create(context, filters);
            criteria.setProjection(Projections.rowCount());
        }

        List resourceList = getHibernateTemplate().findByCriteria(criteria);

        ResultTransformer transformer = transformerFactory.createTransformer(filters, sorter);
        if (transformer != null) {
            return transformer.transformToCount(resourceList);
        } else {
            throw new IllegalArgumentException("Result transformer is null.");
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public List<ResourceLookup> getDependentResources(ExecutionContext context, String uri,
            SearchCriteriaFactory searchCriteriaFactory, int current, int max) {

        final Resource resource = getResource(context, uri);

        if (resource == null) {
            throw new JSException("jsexception.resource.does.not.exist");
        }

        if (resource instanceof ReportDataSource) {
            //ReportDataSource can have dependent ReportUnit items
            final ReportDataSource dataSource = (ReportDataSource) resource;
            RepoResource repoDS = findByURI(RepoResource.class, uri, true);
            Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
            //TODO GetDependentResourcesIds query can be updated to handle ReportUnit objects too
            org.hibernate.Query query = session.getNamedQuery("GetDependentResourcesIds");
            query.setEntity("dependency", repoDS).setFirstResult(current);
            if (max > 0) {
                query.setMaxResults(max);
            }
            List<Long> depRes = query.list();
            List<ResourceLookup> depResLookup = getResourcesByIdList(depRes);
            return depResLookup;
            //TODO delete if code above proves to work fine
            //            SearchFilter filter = new SearchFilter() {
            //                public void applyRestrictions(String type, ExecutionContext context, SearchCriteria criteria) {
            //                    DetachedCriteria dsCriteria = criteria.createCriteria("dataSource", "ds");
            //                    dsCriteria.add(Restrictions.eq("ds.name", dataSource.getName()));
            //
            //                    DetachedCriteria dsParent = dsCriteria.createCriteria("parent", "dsParent");
            //                    dsParent.add(Restrictions.eq("dsParent.URI", dataSource.getParentFolder()));
            //
            //                    // will use it later on for sorting
            //                    DetachedCriteria folderCriteria = criteria.createCriteria("parent", "f");
            //                }
            //            };
            //
            //            SearchSorter sorter = new SearchSorter() {
            //                public void applyOrder(String type, ExecutionContext context, SearchCriteria criteria) {
            //                    criteria.addOrder(Order.asc("f.URI"));
            //                    criteria.addOrder(Order.asc("name"));
            //                    // by default we retrieving only id's and to be able sort  we had to add property to select clause
            //                    criteria.addProjection(Projections.property("f.URI"));
            //                    criteria.addProjection(Projections.property("name"));
            //                }
            //            };
            //            SearchCriteriaFactory criteriaFactory = searchCriteriaFactory.newFactory(ReportUnit.class.getName());
            //            List<ResourceLookup> resources = getResources(
            //                    context,
            //                    criteriaFactory,
            //                    Arrays.asList(filter),
            //                    sorter,
            //                    new BasicTransformerFactory(),
            //                    current, max);
            //
            //            return resources;

            //TODO: replace 'exist' with right solution, it's hardcoded fix for CE version
        } else if (resource instanceof ReportUnit && exist("com.jaspersoft.ji.adhoc.DashboardResource")) {
            //ReportUnit can have dependent DashboardResource items
            final RepoResource repoResource = getRepoResource(resource);
            SearchFilter filter = new SearchFilter() {
                public void applyRestrictions(String type, ExecutionContext context, SearchCriteria criteria) {
                    DetachedCriteria resourcesCriteria = criteria.createCriteria("resources", "res");
                    resourcesCriteria.add(Restrictions.eq("res.id", repoResource.getId()));
                }
            };

            SearchSorter sorter = new SearchSorter() {
                @Override
                protected void addOrder(String type, ExecutionContext context, SearchCriteria criteria) {
                    criteria.addOrder(Order.asc("name")).addOrder(Order.asc("id"));
                }

                @Override
                protected void addProjection(String type, ExecutionContext context, SearchCriteria criteria) {
                }
            };

            SearchCriteriaFactory criteriaFactory = searchCriteriaFactory
                    .newFactory("com.jaspersoft.ji.adhoc.DashboardResource");
            List<ResourceLookup> resources = getResources(context, criteriaFactory, Arrays.asList(filter), sorter,
                    new BasicTransformerFactory(), current, max);

            return resources;
        } else {
            return null; //unsupported resource
        }

    }
    /*
    @Transactional(propagation = Propagation.REQUIRED)
    public List<ResourceLookup> getDomainDependentResources(ExecutionContext context,
                                                  String uri, SearchCriteriaFactory searchCriteriaFactory,
                                                  int current, int max) {
    final Resource resource = getResource(context, uri);
        
    if(resource == null) {
        throw new JSException("jsexception.resource.does.not.exist");
    }
        
    if(resource instanceof ReportDataSource) {
        final ReportDataSource dataSource = (ReportDataSource) resource;
        RepoResource repoDS = findByURI(RepoResource.class, uri, true);
        Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
        org.hibernate.Query query = session.getNamedQuery("GetDomainDependentResourcesIds");
        query.setEntity("dependency", repoDS).setFirstResult(current);
        if(max > 0) {
            query.setMaxResults(max);
        }
        List<Long> depRes = query.list();
        List<ResourceLookup> depResLookup = getResourcesByIdList(depRes);
        return depResLookup;
    } else {
       return null; //unsupported resource
    }
    }
    */

    //TODO: replace this with  right solution
    //temporary solution
    private Boolean exist(String className) {
        Boolean result;
        try {
            Class.forName(className);
            result = true;
        } catch (ClassNotFoundException e) {
            result = false;
        }
        return result;
    }

    // used to have REQUIRES_NEW but that resulted in a deadlock on DB when called from 
    // EngineServiceImpl.autoUpdateJRXMLResource()/HibernateRepositoryCache.saveData()
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void replaceFileResourceData(final String uri, DataContainer data) {
        final long resourceId = findResourceId(uri, FileResource.class);

        if (log.isDebugEnabled()) {
            log.debug("replacing file resource data at " + uri);
        }

        final byte[] dataBytes = data.getData();

        // we're using a batch update so that the version does not get incremented.
        getHibernateTemplate().execute(new HibernateCallback<Void>() {
            public Void doInHibernate(Session session) throws HibernateException, SQLException {
                int count = session.getNamedQuery("FileResourceReplaceDataById").setLong("id", resourceId)
                        .setBinary("data", dataBytes).executeUpdate();

                if (log.isDebugEnabled()) {
                    log.debug("update " + count + " records for resource " + uri);
                }

                if (count == 0) {
                    throw new JSException(
                            "Failed to update file resource data at " + uri + " with id " + resourceId);
                }

                if (count > 1) {
                    // should not get here, but protect against any accidents
                    throw new RuntimeException(
                            "Replacing file resource data at " + uri + " updated " + count + " records");
                }

                return null;
            }
        });
    }

    @Override
    public List<RepoResource> findRepoResources(ExecutionContext context, FilterCriteria criteria) {
        DetachedCriteria detachedCriteria = translateFilterToCriteria(criteria);
        return getHibernateTemplate().findByCriteria(detachedCriteria);
    }

    @Override
    public List<RepoFolder> findRepoFolders(ExecutionContext context, FilterCriteria criteria) {
        // enforce Folder search
        criteria.setFilterClass(Folder.class);
        DetachedCriteria detachedCriteria = translateFilterToCriteria(criteria);
        return getHibernateTemplate().findByCriteria(detachedCriteria);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteAll(ExecutionContext context, List<RepoResource> resources, List<RepoFolder> folders) {
        // delete resource first
        if (resources != null && !resources.isEmpty()) {
            getHibernateTemplate().deleteAll(resources);
        }
        // delete folders then
        if (folders != null && !folders.isEmpty()) {
            getHibernateTemplate().deleteAll(folders);
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveOrUpdateAll(ExecutionContext context, List<RepoResource> resources, List<RepoFolder> folders) {
        // save folders first
        if (folders != null && !folders.isEmpty()) {
            getHibernateTemplate().saveOrUpdateAll(folders);
        }
        // save resource then
        if (resources != null && !resources.isEmpty()) {
            getHibernateTemplate().saveOrUpdateAll(resources);
        }
    }

    public QueryModificationEvaluator getQueryModificationEvaluator() {
        return queryModificationEvaluator;
    }

    public void setQueryModificationEvaluator(QueryModificationEvaluator queryModificationEvaluator) {
        this.queryModificationEvaluator = queryModificationEvaluator;
    }

    /**
     * Transform  resource lookup path to relative URIs
     * @param internalPath
     * @return  external path
     */
    public String transformPathToExternal(String internalPath) {
        return internalPath;
    }

    public HibernateSaveUpdateDeleteListener getHibernateSaveUpdateDeleteListener() {
        return hibernateSaveUpdateDeleteListener;
    }

    public void setHibernateSaveUpdateDeleteListener(
            HibernateSaveUpdateDeleteListener hibernateSaveUpdateDeleteListener) {
        this.hibernateSaveUpdateDeleteListener = hibernateSaveUpdateDeleteListener;
    }
}