org.apereo.portal.portlet.registry.PortletEntityRegistryImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apereo.portal.portlet.registry.PortletEntityRegistryImpl.java

Source

/**
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apereo.portal.portlet.registry;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.portlet.WindowState;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.Validate;
import org.apereo.portal.EntityIdentifier;
import org.apereo.portal.IUserPreferencesManager;
import org.apereo.portal.PortalException;
import org.apereo.portal.layout.IUserLayoutManager;
import org.apereo.portal.layout.dao.IStylesheetDescriptorDao;
import org.apereo.portal.layout.node.IUserLayoutChannelDescription;
import org.apereo.portal.layout.node.IUserLayoutNodeDescription;
import org.apereo.portal.layout.node.IUserLayoutNodeDescription.LayoutNodeType;
import org.apereo.portal.layout.om.IStylesheetDescriptor;
import org.apereo.portal.portlet.dao.IPortletEntityDao;
import org.apereo.portal.portlet.dao.jpa.PortletPreferenceImpl;
import org.apereo.portal.portlet.om.IPortletDefinition;
import org.apereo.portal.portlet.om.IPortletDefinitionId;
import org.apereo.portal.portlet.om.IPortletDefinitionParameter;
import org.apereo.portal.portlet.om.IPortletDescriptorKey;
import org.apereo.portal.portlet.om.IPortletEntity;
import org.apereo.portal.portlet.om.IPortletEntityId;
import org.apereo.portal.portlet.om.IPortletPreference;
import org.apereo.portal.portlet.om.IPortletType;
import org.apereo.portal.portlet.om.IPortletWindowId;
import org.apereo.portal.portlet.om.PortletLifecycleState;
import org.apereo.portal.security.IAuthorizationPrincipal;
import org.apereo.portal.security.IPerson;
import org.apereo.portal.services.AuthorizationService;
import org.apereo.portal.url.IPortalRequestUtils;
import org.apereo.portal.user.IUserInstance;
import org.apereo.portal.user.IUserInstanceManager;
import org.apereo.portal.utils.ConcurrentMapUtils;
import org.apereo.portal.utils.web.PortalWebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import org.springframework.web.util.WebUtils;

import com.google.common.base.Function;

/**
 * Provides access to IPortletEntity objects and convenience methods for creating
 * and converting them and related objects.
 * 
 * The portlet adaptor channel will be responsible for listenting to unsubscribe events and cleaning up entity objects
 * 
 * @author Eric Dalquist
 */
@Service
public class PortletEntityRegistryImpl implements IPortletEntityRegistry {

    private static final String PORTLET_ENTITY_DATA_ATTRIBUTE = PortletEntityRegistryImpl.class.getName()
            + ".PORTLET_ENTITY_DATA";
    private static final String PORTLET_ENTITY_ATTRIBUTE = PortletEntityRegistryImpl.class.getName()
            + ".PORTLET_ENTITY.thread-";
    private static final String PORTLET_ENTITY_LOCK_MAP_ATTRIBUTE = PortletEntityRegistryImpl.class.getName()
            + ".PORTLET_ENTITY_LOCK_MAP_ATTRIBUTE";
    private static final String PORTLET_DEFINITION_LOOKUP_MAP_ATTRIBUTE = PortletEntityRegistryImpl.class.getName()
            + ".PORTLET_DEFINITION_LOOKUP_MAP_ATTRIBUTE";

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    private IUserInstanceManager userInstanceManager;
    private IPortletEntityDao portletEntityDao;
    private IPortletDefinitionRegistry portletDefinitionRegistry;
    private IPortalRequestUtils portalRequestUtils;
    private IStylesheetDescriptorDao stylesheetDescriptorDao;
    private Ehcache entityIdParseCache;

    @Autowired
    public void setStylesheetDescriptorDao(IStylesheetDescriptorDao stylesheetDescriptorDao) {
        this.stylesheetDescriptorDao = stylesheetDescriptorDao;
    }

    @Autowired
    public void setEntityIdParseCache(
            @Qualifier("org.apereo.portal.portlet.dao.jpa.PortletEntityImpl.idParseCache") Ehcache entityIdParseCache) {
        this.entityIdParseCache = entityIdParseCache;
    }

    @Autowired
    public void setUserInstanceManager(IUserInstanceManager userInstanceManager) {
        this.userInstanceManager = userInstanceManager;
    }

    @Autowired
    public void setPortletEntityDao(@Qualifier("transient") IPortletEntityDao portletEntityDao) {
        this.portletEntityDao = portletEntityDao;
    }

    @Autowired
    public void setPortletDefinitionRegistry(IPortletDefinitionRegistry portletDefinitionRegistry) {
        this.portletDefinitionRegistry = portletDefinitionRegistry;
    }

    @Autowired
    public void setPortalRequestUtils(IPortalRequestUtils portalRequestUtils) {
        this.portalRequestUtils = portalRequestUtils;
    }

    /* (non-Javadoc)
     * @see org.apereo.portal.portlet.registry.IPortletEntityRegistry#getPortletEntity(org.apereo.portal.portlet.om.IPortletEntityId)
     */
    @Override
    public IPortletEntity getPortletEntity(HttpServletRequest request, IPortletEntityId portletEntityId) {
        Validate.notNull(portletEntityId, "portletEntityId can not be null");

        //Sync on the request map to make sure duplicate IPortletEntitys aren't ever created
        final PortletEntityCache<IPortletEntity> portletEntityCache = this.getPortletEntityMap(request);

        return this.getPortletEntity(request, portletEntityCache, portletEntityId, null, -1);
    }

    /* (non-Javadoc)
     * @see org.apereo.portal.portlet.registry.IPortletEntityRegistry#getPortletEntity(java.lang.String)
     */
    @Override
    public IPortletEntity getPortletEntity(HttpServletRequest request, String portletEntityIdString) {
        final IPortletEntityId portletEntityId = this.parseConsistentPortletEntityId(request,
                portletEntityIdString);
        return this.getPortletEntity(request, portletEntityId);
    }

    @Override
    public IPortletEntity getOrCreateDefaultPortletEntity(HttpServletRequest request,
            IPortletDefinitionId portletDefinitionId) {
        Validate.notNull(request, "HttpServletRequest cannot be null");
        Validate.notNull(portletDefinitionId, "portletDefinitionId cannot be null");

        final IPortletDefinition portletDefinition = this.getPortletDefinition(request, portletDefinitionId);
        if (portletDefinition == null) {
            throw new IllegalArgumentException("No portlet definition found for id '" + portletDefinitionId + "'.");
        }

        //Determine the appropriate portlet window ID for the definition
        final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
        final IUserPreferencesManager preferencesManager = userInstance.getPreferencesManager();
        final IUserLayoutManager userLayoutManager = preferencesManager.getUserLayoutManager();

        //Determine the subscribe ID
        final String portletFName = portletDefinition.getFName();
        final String layoutNodeId = userLayoutManager.getSubscribeId(portletFName);
        if (layoutNodeId == null) {
            throw new IllegalArgumentException("No layout node ID found for fname '" + portletFName + "'.");
        }

        this.logger.trace("Found layout node {} for portlet definition {}", layoutNodeId, portletFName);

        final IPerson person = userInstance.getPerson();
        final int personId = person.getID();
        return this.getOrCreatePortletEntity(request, portletDefinitionId, layoutNodeId, personId);
    }

    @Override
    public IPortletEntity getOrCreatePortletEntity(HttpServletRequest request, IUserInstance userInstance,
            String layoutNodeId) {
        final IUserPreferencesManager preferencesManager = userInstance.getPreferencesManager();
        final IUserLayoutManager userLayoutManager = preferencesManager.getUserLayoutManager();

        //Find the channel and portlet definitions
        final IUserLayoutChannelDescription channelNode = (IUserLayoutChannelDescription) userLayoutManager
                .getNode(layoutNodeId);
        if (channelNode == null) {
            this.logger
                    .warn("No layout node exists for id " + layoutNodeId + ", no portlet entity will be returned.");
            return null;
        }

        final String channelPublishId = channelNode.getChannelPublishId();

        final IPortletDefinition portletDefinition = this.getPortletDefinition(request, userInstance,
                channelPublishId);

        if (portletDefinition != null) {
            final IPerson person = userInstance.getPerson();
            return this.getOrCreatePortletEntity(request, portletDefinition.getPortletDefinitionId(), layoutNodeId,
                    person.getID());
        }

        // No permission to see the portlet
        return null;
    }

    /* (non-Javadoc)
     * @see org.apereo.portal.portlet.registry.IPortletEntityRegistry#getOrCreatePortletEntity(org.apereo.portal.portlet.om.IPortletDefinitionId, java.lang.String, int)
     */
    @Override
    public IPortletEntity getOrCreatePortletEntity(HttpServletRequest request,
            IPortletDefinitionId portletDefinitionId, String layoutNodeId, int userId) {
        final PortletEntityCache<IPortletEntity> portletEntityCache = getPortletEntityMap(request);

        //Try just getting an existing entity first
        IPortletEntity portletEntity = this.getPortletEntity(request, portletEntityCache, null, layoutNodeId,
                userId);

        //Found an existing entity!
        if (portletEntity != null) {
            //Verify the definition IDs match, this is a MUST in the case where the subscribed portlet changes
            final IPortletDefinition portletDefinition = portletEntity.getPortletDefinition();
            if (portletDefinitionId.equals(portletDefinition.getPortletDefinitionId())) {
                return portletEntity;
            }

            //Remove the entity if the definition IDs don't match
            this.logger.warn(
                    "Found portlet entity '{}' is not the correct entity for portlet definition id: {}. The entity will be deleted and a new one created.",
                    portletEntity, portletDefinitionId);
            this.deletePortletEntity(request, portletEntity, false);
        }

        //Create the entity data object and store it in the session map (if not already there)
        final PortletEntityCache<PortletEntityData> portletEntityDataMap = this.getPortletEntityDataMap(request);

        final IPortletEntityId portletEntityId = this.createConsistentPortletEntityId(portletDefinitionId,
                layoutNodeId, userId);
        PortletEntityData portletEntityData = new PortletEntityData(portletEntityId, portletDefinitionId,
                layoutNodeId, userId);
        portletEntityData = portletEntityDataMap.storeIfAbsentEntity(portletEntityData);

        portletEntity = wrapPortletEntityData(portletEntityData);

        //Stick the wrapper in the request map
        portletEntity = portletEntityCache.storeIfAbsentEntity(portletEntity);

        return portletEntity;
    }

    @Override
    public IPortletEntity getOrCreatePortletEntityByFname(HttpServletRequest request, IUserInstance userInstance,
            String fname) {
        final IUserPreferencesManager preferencesManager = userInstance.getPreferencesManager();
        final IUserLayoutManager userLayoutManager = preferencesManager.getUserLayoutManager();
        final String subscribeId = userLayoutManager.getSubscribeId(fname);
        return this.getOrCreatePortletEntity(request, userInstance, subscribeId);
    }

    @Override
    public IPortletEntity getOrCreatePortletEntityByFname(HttpServletRequest request, IUserInstance userInstance,
            String fname, String preferredChannelSubscribeId) {
        try {
            final IPortletEntity portletEntity = this.getOrCreatePortletEntity(request, userInstance,
                    preferredChannelSubscribeId);

            if (portletEntity != null) {
                //Verify the fname matches before returning the entity
                final IPortletDefinition portletDefinition = portletEntity.getPortletDefinition();
                if (fname.equals(portletDefinition.getFName())) {
                    return portletEntity;
                }
            }
        } catch (PortalException pe) {
            //Ignored, can be the case if no layout node exists for the specified subscribe ID
        }

        //Either the layout node didn't exist or the entity for the node doesn't match the requested fname
        return this.getOrCreatePortletEntityByFname(request, userInstance, fname);
    }

    @Override
    public IPortletEntity getOrCreateDelegatePortletEntity(HttpServletRequest request,
            IPortletWindowId parentPortletWindowId, IPortletDefinitionId delegatePortletDefinitionId) {
        //Create a special synthetic layout node ID for the delegate entity
        final String layoutNodeId = PortletWindowIdStringUtils
                .convertToDelegateLayoutNodeId(parentPortletWindowId.toString());

        //Grab the current user
        final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
        final IPerson person = userInstance.getPerson();
        final int userId = person.getID();

        //Use the general API, the only thing special is the layout node id
        return getOrCreatePortletEntity(request, delegatePortletDefinitionId, layoutNodeId, userId);
    }

    /* (non-Javadoc)
     * @see org.apereo.portal.portlet.registry.IPortletEntityRegistry#storePortletEntity(org.apereo.portal.portlet.om.IPortletEntity)
     */
    @Override
    public void storePortletEntity(HttpServletRequest request, final IPortletEntity portletEntity) {
        Validate.notNull(portletEntity, "portletEntity can not be null");

        final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
        final IPerson person = userInstance.getPerson();
        if (person.isGuest()) {
            //Never persist things for the guest user, just rely on in-memory storage
            return;
        }

        final IPortletEntityId wrapperPortletEntityId = portletEntity.getPortletEntityId();
        final Lock portletEntityLock = this.getPortletEntityLock(request, wrapperPortletEntityId);
        portletEntityLock.lock();
        try {
            final boolean shouldBePersisted = this.shouldBePersisted(portletEntity);

            if (portletEntity instanceof PersistentPortletEntityWrapper) {
                //Unwrap the persistent entity
                final IPortletEntity persistentEntity = ((PersistentPortletEntityWrapper) portletEntity)
                        .getPersistentEntity();

                //Already persistent entity that still has prefs 
                if (shouldBePersisted) {
                    try {
                        this.portletEntityDao.updatePortletEntity(persistentEntity);
                    } catch (HibernateOptimisticLockingFailureException e) {
                        //Check if this exception is from the entity being deleted from under us.
                        final boolean exists = this.portletEntityDao
                                .portletEntityExists(persistentEntity.getPortletEntityId());
                        if (!exists) {
                            this.logger.warn("The persistent portlet has already been deleted: " + persistentEntity
                                    + ". The passed entity should be persistent so a new persistent entity will be created");
                            this.deletePortletEntity(request, portletEntity, true);
                            this.createPersistentEntity(persistentEntity, wrapperPortletEntityId);
                        } else {
                            throw e;
                        }
                    }
                }
                //Already persistent entity that should not be, DELETE!
                else {
                    //Capture identifiers needed to recreate the entity as session persistent
                    final IPortletDefinitionId portletDefinitionId = portletEntity.getPortletDefinitionId();
                    final String layoutNodeId = portletEntity.getLayoutNodeId();
                    final int userId = portletEntity.getUserId();

                    //Delete the persistent entity
                    this.deletePortletEntity(request, portletEntity, false);

                    //Create a new entity and stick it in the cache
                    this.getOrCreatePortletEntity(request, portletDefinitionId, layoutNodeId, userId);
                }
            } else if (portletEntity instanceof SessionPortletEntityImpl) {
                //There are preferences on the interim entity, create an store it
                if (shouldBePersisted) {
                    //Remove the session scoped entity from the request and session caches
                    this.deletePortletEntity(request, portletEntity, false);

                    final IPortletEntity persistentEntity = createPersistentEntity(portletEntity,
                            wrapperPortletEntityId);

                    if (this.logger.isTraceEnabled()) {
                        this.logger.trace("Session scoped entity " + wrapperPortletEntityId
                                + " should now be persistent. Deleted it from session cache and created persistent portlet entity "
                                + persistentEntity.getPortletEntityId());
                    }
                }
                //Session scoped entity that is still session scoped,
                else {
                    //Look for a persistent entity and delete it
                    final String channelSubscribeId = portletEntity.getLayoutNodeId();
                    final int userId = portletEntity.getUserId();
                    IPortletEntity existingPersistentEntity = this.portletEntityDao
                            .getPortletEntity(channelSubscribeId, userId);
                    if (existingPersistentEntity != null) {
                        final IPortletEntityId consistentPortletEntityId = this
                                .createConsistentPortletEntityId(existingPersistentEntity);
                        existingPersistentEntity = new PersistentPortletEntityWrapper(existingPersistentEntity,
                                consistentPortletEntityId);

                        this.logger.warn("A persistent portlet entity already exists: " + existingPersistentEntity
                                + ". The passed entity has no preferences so the persistent version will be deleted");
                        this.deletePortletEntity(request, existingPersistentEntity, false);

                        //Add to request cache
                        final PortletEntityCache<IPortletEntity> portletEntityMap = this
                                .getPortletEntityMap(request);
                        portletEntityMap.storeIfAbsentEntity(portletEntity);

                        //Add to session cache
                        final PortletEntityCache<PortletEntityData> portletEntityDataMap = this
                                .getPortletEntityDataMap(request);
                        portletEntityDataMap.storeIfAbsentEntity(
                                ((SessionPortletEntityImpl) portletEntity).getPortletEntityData());
                    }
                }
            } else {
                throw new IllegalArgumentException(
                        "Invalid portlet entity implementation passed: " + portletEntity.getClass());
            }
        } finally {
            portletEntityLock.unlock();
        }
    }

    @Override
    public Lock getPortletEntityLock(HttpServletRequest request, IPortletEntityId portletEntityId) {
        final ConcurrentMap<IPortletEntityId, Lock> lockMap = getPortletEntityLockMap(request);

        //See if the lock already exist, return if it does
        Lock lock = lockMap.get(portletEntityId);
        if (lock != null) {
            return lock;
        }

        //Create a new lock and do a putIfAbsent to avoid synchronizing but still get a single lock instance for the session. 
        lock = createPortletEntityLock();
        return ConcurrentMapUtils.putIfAbsent(lockMap, portletEntityId, lock);
    }

    protected IPortletDefinition getPortletDefinition(HttpServletRequest request, String portletDefinitionIdStr) {
        final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
        return this.getPortletDefinition(request, userInstance, portletDefinitionIdStr);
    }

    protected IPortletDefinition getPortletDefinition(HttpServletRequest request,
            IPortletDefinitionId portletDefinitionId) {
        final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
        return this.getPortletDefinition(userInstance, portletDefinitionId);
    }

    protected IPortletDefinition getPortletDefinition(HttpServletRequest request, IUserInstance userInstance,
            String portletDefinitionIdStr) {
        request = this.portalRequestUtils.getOriginalPortalRequest(request);

        final ConcurrentMap<String, IPortletDefinition> portletDefinitions = PortalWebUtils
                .getMapRequestAttribute(request, PORTLET_DEFINITION_LOOKUP_MAP_ATTRIBUTE);

        IPortletDefinition portletDefinition = portletDefinitions.get(portletDefinitionIdStr);
        if (portletDefinition == NO_PERMISSION_PORTLET_DEFINITION) {
            return null;
        }
        if (portletDefinition != null) {
            return portletDefinition;
        }

        portletDefinition = this.portletDefinitionRegistry.getPortletDefinition(portletDefinitionIdStr);
        portletDefinition = checkPortletDefinitionRenderPermissions(userInstance, portletDefinition);
        if (portletDefinition == null) {
            portletDefinitions.put(portletDefinitionIdStr, NO_PERMISSION_PORTLET_DEFINITION);
        } else {
            portletDefinitions.put(portletDefinitionIdStr, portletDefinition);
        }

        return portletDefinition;
    }

    protected IPortletDefinition getPortletDefinition(IUserInstance userInstance,
            IPortletDefinitionId portletDefinitionId) {
        final IPortletDefinition portletDefinition = this.portletDefinitionRegistry
                .getPortletDefinition(portletDefinitionId);
        return checkPortletDefinitionRenderPermissions(userInstance, portletDefinition);
    }

    private IPortletDefinition checkPortletDefinitionRenderPermissions(IUserInstance userInstance,
            final IPortletDefinition portletDefinition) {
        if (portletDefinition == null) {
            return null;
        }

        final IPerson person = userInstance.getPerson();
        final EntityIdentifier ei = person.getEntityIdentifier();
        final IAuthorizationPrincipal ap = AuthorizationService.instance().newPrincipal(ei.getKey(), ei.getType());
        if (ap.canRender(portletDefinition.getPortletDefinitionId().getStringId())) {
            return portletDefinition;
        }

        return null;
    }

    protected IPortletEntity createPersistentEntity(final IPortletEntity portletEntity,
            final IPortletEntityId wrapperPortletEntityId) {
        final IPortletDefinitionId portletDefinitionId = portletEntity.getPortletDefinitionId();
        final String layoutNodeId = portletEntity.getLayoutNodeId();
        final int userId = portletEntity.getUserId();

        IPortletEntity persistentEntity = this.portletEntityDao.getPortletEntity(layoutNodeId, userId);
        if (persistentEntity != null) {
            this.logger.warn("A persistent portlet entity already exists: " + persistentEntity
                    + ". The data from the passed in entity will be copied to the persistent entity: "
                    + portletEntity);
        } else {
            persistentEntity = this.portletEntityDao.createPortletEntity(portletDefinitionId, layoutNodeId, userId);
        }

        //Copy over preferences to avoid modifying any part of the interim entity by reference
        final List<IPortletPreference> existingPreferences = portletEntity.getPortletPreferences();
        final List<IPortletPreference> persistentPreferences = persistentEntity.getPortletPreferences();

        //Only do the copy if the List objects are not the same instance
        if (persistentPreferences != existingPreferences) {
            persistentPreferences.clear();
            for (final IPortletPreference preference : existingPreferences) {
                persistentPreferences.add(new PortletPreferenceImpl(preference));
            }
        }

        //Copy over WindowStates
        final Map<Long, WindowState> windowStates = portletEntity.getWindowStates();
        for (Map.Entry<Long, WindowState> windowStateEntry : windowStates.entrySet()) {
            final Long stylesheetDescriptorId = windowStateEntry.getKey();
            final IStylesheetDescriptor stylesheetDescriptor = stylesheetDescriptorDao
                    .getStylesheetDescriptor(stylesheetDescriptorId);
            final WindowState windowState = windowStateEntry.getValue();
            persistentEntity.setWindowState(stylesheetDescriptor, windowState);
        }

        this.portletEntityDao.updatePortletEntity(persistentEntity);

        return persistentEntity;
    }

    /**
     * Delete a portlet entity, removes it from the request, session and persistent stores
     */
    protected void deletePortletEntity(HttpServletRequest request, IPortletEntity portletEntity,
            boolean cacheOnly) {
        final IPortletEntityId portletEntityId = portletEntity.getPortletEntityId();

        //Remove from request cache
        final PortletEntityCache<IPortletEntity> portletEntityMap = this.getPortletEntityMap(request);
        portletEntityMap.removeEntity(portletEntityId);

        //Remove from session cache
        final PortletEntityCache<PortletEntityData> portletEntityDataMap = this.getPortletEntityDataMap(request);
        portletEntityDataMap.removeEntity(portletEntityId);

        if (!cacheOnly && portletEntity instanceof PersistentPortletEntityWrapper) {
            final IPortletEntity persistentEntity = ((PersistentPortletEntityWrapper) portletEntity)
                    .getPersistentEntity();

            try {
                this.portletEntityDao.deletePortletEntity(persistentEntity);
            } catch (HibernateOptimisticLockingFailureException e) {
                this.logger.warn("This persistent portlet has already been deleted: " + persistentEntity
                        + ", trying to find and delete by layout node and user.");
                final IPortletEntity existingPersistentEntity = this.portletEntityDao
                        .getPortletEntity(persistentEntity.getLayoutNodeId(), persistentEntity.getUserId());
                if (existingPersistentEntity != null) {
                    this.portletEntityDao.deletePortletEntity(existingPersistentEntity);
                }
            }
        }
    }

    /**
     * Lookup the portlet entity by layoutNodeId and userId
     */
    protected IPortletEntity getPortletEntity(HttpServletRequest request,
            PortletEntityCache<IPortletEntity> portletEntityCache, IPortletEntityId portletEntityId,
            String layoutNodeId, int userId) {

        IPortletEntity portletEntity;

        //First look in the request map
        if (portletEntityId != null) {
            portletEntity = portletEntityCache.getEntity(portletEntityId);
        } else {
            portletEntity = portletEntityCache.getEntity(layoutNodeId, userId);
        }

        if (portletEntity != null) {
            logger.trace("Found IPortletEntity {} in request cache", portletEntity.getPortletEntityId());
            return portletEntity;
        }

        //Didn't find it, next look in the session map
        final PortletEntityCache<PortletEntityData> portletEntityDataMap = this.getPortletEntityDataMap(request);
        final PortletEntityData portletEntityData;
        if (portletEntityId != null) {
            portletEntityData = portletEntityDataMap.getEntity(portletEntityId);
        } else {
            portletEntityData = portletEntityDataMap.getEntity(layoutNodeId, userId);
        }

        if (portletEntityData != null) {

            //Stick the entity wrapper in the request map (if it wasn't already added by another thread)
            portletEntity = portletEntityCache.storeIfAbsentEntity(portletEntityData.getPortletEntityId(),
                    new Function<IPortletEntityId, IPortletEntity>() {
                        @Override
                        public IPortletEntity apply(IPortletEntityId input) {
                            //Found a session stored entity, wrap it to make it a real IPortletEntity
                            logger.trace(
                                    "Found PortletEntityData {} in session cache, caching wrapper in the request",
                                    portletEntityData.getPortletEntityId());

                            return wrapPortletEntityData(portletEntityData);
                        }
                    });

            return portletEntity;
        }

        //Still didn't find it, look in the persistent store
        if (portletEntityId != null) {
            if (portletEntityId instanceof PortletEntityIdImpl) {
                final PortletEntityIdImpl consistentPortletEntityId = (PortletEntityIdImpl) portletEntityId;
                final String localLayoutNodeId = consistentPortletEntityId.getLayoutNodeId();
                final int localUserId = consistentPortletEntityId.getUserId();

                portletEntity = this.portletEntityDao.getPortletEntity(localLayoutNodeId, localUserId);
            } else {
                portletEntity = this.portletEntityDao.getPortletEntity(portletEntityId);
            }
        } else {
            portletEntity = this.portletEntityDao.getPortletEntity(layoutNodeId, userId);
        }

        //Found a persistent entity, wrap it to make the id consistent between the persistent and session stored entities 
        if (portletEntity != null) {
            final IPortletEntityId consistentPortletEntityId = this.createConsistentPortletEntityId(portletEntity);

            final IPortletEntity anonPortletEntity = portletEntity;

            //Stick the entity wrapper in the request map (if it wasn't already added by another thread)
            portletEntity = portletEntityCache.storeIfAbsentEntity(consistentPortletEntityId,
                    new Function<IPortletEntityId, IPortletEntity>() {
                        @Override
                        public IPortletEntity apply(IPortletEntityId input) {
                            logger.trace(
                                    "Found persistent IPortletEntity {}, mapped id to {}, caching the wrapper in the request",
                                    anonPortletEntity.getPortletEntityId(), consistentPortletEntityId);
                            return new PersistentPortletEntityWrapper(anonPortletEntity, consistentPortletEntityId);
                        }
                    });

            return portletEntity;
        }

        //Didn't find an entity, just return null
        return null;
    }

    protected IPortletEntityId createConsistentPortletEntityId(IPortletEntity portletEntity) {
        final IPortletDefinition portletDefinition = portletEntity.getPortletDefinition();
        final IPortletDefinitionId portletDefinitionId = portletDefinition.getPortletDefinitionId();
        final String layoutNodeId = portletEntity.getLayoutNodeId();
        final int userId = portletEntity.getUserId();
        return this.createConsistentPortletEntityId(portletDefinitionId, layoutNodeId, userId);
    }

    protected IPortletEntityId createConsistentPortletEntityId(IPortletDefinitionId portletDefinitionId,
            String layoutNodeId, int userId) {
        return new PortletEntityIdImpl(portletDefinitionId, layoutNodeId, userId);
    }

    protected IPortletEntityId parseConsistentPortletEntityId(HttpServletRequest request,
            String consistentEntityIdString) {
        Validate.notNull(consistentEntityIdString, "consistentEntityIdString can not be null");

        //Check in the cache first
        final Element element = this.entityIdParseCache.get(consistentEntityIdString);
        if (element != null) {
            final Object value = element.getObjectValue();
            if (value != null) {
                return (IPortletEntityId) value;
            }
        }

        if (!PortletEntityIdStringUtils.hasCorrectNumberOfParts(consistentEntityIdString)) {
            throw new IllegalArgumentException(
                    "consistentEntityIdString does not have 3 parts and is invalid: " + consistentEntityIdString);
        }

        //Verify the portlet definition id
        final String portletDefinitionIdString = PortletEntityIdStringUtils
                .parsePortletDefinitionId(consistentEntityIdString);
        final IPortletDefinition portletDefinition = this.getPortletDefinition(request, portletDefinitionIdString);
        if (portletDefinition == null) {
            throw new IllegalArgumentException("No parent IPortletDefinition found for " + portletDefinitionIdString
                    + " from entity id string: " + consistentEntityIdString);
        }
        final IPortletDefinitionId portletDefinitionId = portletDefinition.getPortletDefinitionId();

        final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
        final IUserPreferencesManager preferencesManager = userInstance.getPreferencesManager();
        final IUserLayoutManager userLayoutManager = preferencesManager.getUserLayoutManager();

        //Verify non-delegate layout node id exists and is for a portlet
        final String layoutNodeId = PortletEntityIdStringUtils.parseLayoutNodeId(consistentEntityIdString);
        if (!PortletEntityIdStringUtils.isDelegateLayoutNode(layoutNodeId)) {
            final IUserLayoutNodeDescription node = userLayoutManager.getNode(layoutNodeId);
            if (node == null || node.getType() != LayoutNodeType.PORTLET) {
                throw new IllegalArgumentException("No portlet layout node found for " + layoutNodeId
                        + " from entity id string: " + consistentEntityIdString);
            }

            //TODO is this doable for delegation?
            //Verify the portlet definition matches
            final IUserLayoutChannelDescription portletNode = (IUserLayoutChannelDescription) node;
            final String channelPublishId = portletNode.getChannelPublishId();
            if (!portletDefinitionId.getStringId().equals(channelPublishId)) {
                throw new IllegalArgumentException("The portlet layout node found for " + layoutNodeId
                        + " does not match the IPortletDefinitionId " + portletDefinitionId
                        + " specified in entity id string: " + consistentEntityIdString);
            }
        }

        //TODO when there is a JPA backed user dao actually verify this mapping
        //User just conver to an int
        final int userId;
        final String userIdAsString = PortletEntityIdStringUtils.parseUserIdAsString(consistentEntityIdString);
        try {
            userId = Integer.parseInt(userIdAsString);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("The user id " + userIdAsString
                    + " is not a valid integer from entity id string: " + consistentEntityIdString, e);
        }

        final IPortletEntityId portletEntityId = createConsistentPortletEntityId(portletDefinitionId, layoutNodeId,
                userId);

        //Cache the resolution
        this.entityIdParseCache.put(new Element(consistentEntityIdString, portletEntityId));

        return portletEntityId;
    }

    protected Lock createPortletEntityLock() {
        return new ReentrantLock(true);
    }

    protected ConcurrentMap<IPortletEntityId, Lock> getPortletEntityLockMap(HttpServletRequest request) {
        request = portalRequestUtils.getOriginalPortalRequest(request);
        final HttpSession session = request.getSession();
        return PortalWebUtils.getMapSessionAttribute(session, PORTLET_ENTITY_LOCK_MAP_ATTRIBUTE);
    }

    protected PortletEntityCache<IPortletEntity> getPortletEntityMap(HttpServletRequest request) {
        request = portalRequestUtils.getOriginalPortletOrPortalRequest(request);

        //create the thread specific cache name
        final String entityMapAttribute = PORTLET_ENTITY_ATTRIBUTE + Thread.currentThread().getId();

        //No need to do this in a request attribute mutex since the map is scoped to a specific thread
        @SuppressWarnings("unchecked")
        PortletEntityCache<IPortletEntity> cache = (PortletEntityCache<IPortletEntity>) request
                .getAttribute(entityMapAttribute);
        if (cache == null) {
            cache = new PortletEntityCache<IPortletEntity>(false);
            request.setAttribute(PORTLET_ENTITY_ATTRIBUTE, cache);
        }

        return cache;
    }

    protected PortletEntityCache<PortletEntityData> getPortletEntityDataMap(HttpServletRequest request) {
        request = portalRequestUtils.getOriginalPortalRequest(request);
        final HttpSession session = request.getSession();
        final Object mutex = WebUtils.getSessionMutex(session);
        synchronized (mutex) {
            @SuppressWarnings("unchecked")
            PortletEntityCache<PortletEntityData> cache = (PortletEntityCache<PortletEntityData>) session
                    .getAttribute(PORTLET_ENTITY_DATA_ATTRIBUTE);
            if (cache == null) {
                cache = new PortletEntityCache<PortletEntityData>();
                session.setAttribute(PORTLET_ENTITY_DATA_ATTRIBUTE, cache);
            }
            return cache;
        }
    }

    protected IPortletEntity wrapPortletEntityData(final PortletEntityData portletEntityData) {
        final IPortletDefinitionId portletDefinitionId = portletEntityData.getPortletDefinitionId();
        final IPortletDefinition portletDefinition = this.portletDefinitionRegistry
                .getPortletDefinition(portletDefinitionId);
        return new SessionPortletEntityImpl(portletDefinition, portletEntityData);
    }

    @Override
    public boolean shouldBePersisted(IPortletEntity portletEntity) {
        //Delegate entities should NEVER be persisted
        final String layoutNodeId = portletEntity.getLayoutNodeId();
        if (PortletEntityIdStringUtils.isDelegateLayoutNode(layoutNodeId)) {
            return false;
        }

        //Only non delegate entities with preferences or a non-null window state should be persisted
        final List<IPortletPreference> preferences = portletEntity.getPortletPreferences();
        return CollectionUtils.isNotEmpty(preferences) || !portletEntity.getWindowStates().isEmpty();
    }

    private static final IPortletDefinition NO_PERMISSION_PORTLET_DEFINITION = new IPortletDefinition() {
        @Override
        public String getDataTitle() {
            return null;
        }

        @Override
        public String getDataId() {
            return null;
        }

        @Override
        public String getDataDescription() {
            return null;
        }

        @Override
        public void setType(IPortletType channelType) {
        }

        @Override
        public void setTitle(String title) {
        }

        @Override
        public void setTimeout(int timeout) {
        }

        @Override
        public void setResourceTimeout(Integer resourceTimeout) {
        }

        @Override
        public void setRenderTimeout(Integer renderTimeout) {
        }

        @Override
        public void setPublisherId(int publisherId) {
        }

        @Override
        public void setPublishDate(Date publishDate) {
        }

        @Override
        public boolean setPortletPreferences(List<IPortletPreference> portletPreferences) {
            return false;
        }

        @Override
        public void setParameters(Set<IPortletDefinitionParameter> parameters) {
        }

        @Override
        public void setName(String name) {
        }

        @Override
        public void setFName(String fname) {
        }

        @Override
        public void setExpirerId(int expirerId) {
        }

        @Override
        public void setExpirationDate(Date expirationDate) {
        }

        @Override
        public void setEventTimeout(Integer eventTimeout) {
        }

        @Override
        public void setDescription(String descr) {
        }

        @Override
        public void setApproverId(int approvalId) {
        }

        @Override
        public void setApprovalDate(Date approvalDate) {
        }

        @Override
        public void setActionTimeout(Integer actionTimeout) {
        }

        @Override
        public void removeParameter(String name) {
        }

        @Override
        public void removeParameter(IPortletDefinitionParameter parameter) {
        }

        @Override
        public IPortletType getType() {
            return null;
        }

        @Override
        public String getTitle(String locale) {
            return null;
        }

        @Override
        public String getAlternativeMaximizedLink() {
            return null;
        }

        @Override
        public String getTitle() {
            return null;
        }

        @Override
        public int getTimeout() {
            return 0;
        }

        @Override
        public Integer getResourceTimeout() {
            return null;
        }

        @Override
        public Integer getRenderTimeout() {
            return null;
        }

        @Override
        public int getPublisherId() {
            return 0;
        }

        @Override
        public Date getPublishDate() {
            return null;
        }

        @Override
        public List<IPortletPreference> getPortletPreferences() {
            return null;
        }

        @Override
        public IPortletDescriptorKey getPortletDescriptorKey() {
            return null;
        }

        @Override
        public IPortletDefinitionId getPortletDefinitionId() {
            return null;
        }

        @Override
        public Map<String, IPortletDefinitionParameter> getParametersAsUnmodifiableMap() {
            return null;
        }

        @Override
        public Set<IPortletDefinitionParameter> getParameters() {
            return null;
        }

        @Override
        public IPortletDefinitionParameter getParameter(String key) {
            return null;
        }

        @Override
        public String getName(String locale) {
            return null;
        }

        @Override
        public String getName() {
            return null;
        }

        @Override
        public PortletLifecycleState getLifecycleState() {
            return null;
        }

        @Override
        public String getFName() {
            return null;
        }

        @Override
        public int getExpirerId() {
            return 0;
        }

        @Override
        public Date getExpirationDate() {
            return null;
        }

        @Override
        public Integer getEventTimeout() {
            return null;
        }

        @Override
        public EntityIdentifier getEntityIdentifier() {
            return null;
        }

        @Override
        public String getDescription(String locale) {
            return null;
        }

        @Override
        public String getDescription() {
            return null;
        }

        @Override
        public int getApproverId() {
            return 0;
        }

        @Override
        public Date getApprovalDate() {
            return null;
        }

        @Override
        public Integer getActionTimeout() {
            return null;
        }

        @Override
        public void addParameter(String name, String value) {
        }

        @Override
        public void addParameter(IPortletDefinitionParameter parameter) {
        }

        @Override
        public void addLocalizedTitle(String locale, String chanTitle) {
        }

        @Override
        public void addLocalizedName(String locale, String chanName) {
        }

        @Override
        public void addLocalizedDescription(String locale, String chanDesc) {
        }

        @Override
        public Double getRating() {
            return null;
        }

        @Override
        public void setRating(Double rating) {
        }

        @Override
        public Long getUsersRated() {
            return null;
        }

        @Override
        public void setUsersRated(Long usersRated) {
        }

        @Override
        public String getTarget() {
            return null;
        }
    };
}