/** * 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: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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 com.google.common.base.Function; 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; /** * Provides access to IPortletEntity objects and convenience methods for creating and converting * them and related objects. * * <p>The portlet adaptor channel will be responsible for listenting to unsubscribe events and * cleaning up entity objects * */ @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; } }; }