/** * 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.container.services; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.locks.Lock; import javax.portlet.ValidatorException; import javax.servlet.http.HttpServletRequest; import org.apache.pluto.container.PortletRequestContext; import org.apache.pluto.container.om.portlet.PortletDefinition; import org.apache.pluto.container.om.portlet.Preference; import org.apache.pluto.container.om.portlet.Preferences; 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.IPortletEntity; import org.apereo.portal.portlet.om.IPortletEntityId; import org.apereo.portal.portlet.om.IPortletPreference; import org.apereo.portal.portlet.registry.IPortletDefinitionRegistry; import org.apereo.portal.portlet.registry.IPortletEntityRegistry; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionOperations; /** * Preferences impl that manipulates the portlet entity level preference data * */ public class PortletEntityPreferencesImpl extends AbstractPortletPreferencesImpl<IPortletEntity> { private final PortletRequestContext portletRequestContext; private final IPortletEntityId portletEntityId; private final IPortletEntityRegistry portletEntityRegistry; private final IPortletDefinitionRegistry portletDefinitionRegistry; private final TransactionOperations transactionOperations; public PortletEntityPreferencesImpl( PortletRequestContext portletRequestContext, IPortletEntityRegistry portletEntityRegistry, IPortletDefinitionRegistry portletDefinitionRegistry, TransactionOperations transactionOperations, IPortletEntityId portletEntityId, boolean render) { super(render); this.portletRequestContext = portletRequestContext; this.portletEntityRegistry = portletEntityRegistry; this.portletDefinitionRegistry = portletDefinitionRegistry; this.transactionOperations = transactionOperations; this.portletEntityId = portletEntityId; } @Override protected IPortletEntity getInitializationContext() { final HttpServletRequest containerRequest = this.portletRequestContext.getContainerRequest(); return this.portletEntityRegistry.getPortletEntity(containerRequest, portletEntityId); } @Override protected Object getLogDescription() { return this.getInitializationContext(); } @Override protected void loadTargetPortletPreferences( IPortletEntity portletEntity, Map<String, IPortletPreference> targetPortletPreferences) { final List<IPortletPreference> entityPreferences = portletEntity.getPortletPreferences(); for (final IPortletPreference preference : entityPreferences) { targetPortletPreferences.put(preference.getName(), preference); } } @Override protected void loadBasePortletPreferences( IPortletEntity portletEntity, Map<String, IPortletPreference> basePortletPreferences) { final IPortletDefinition portletDefinition = portletEntity.getPortletDefinition(); //Add descriptor prefs to base Map final IPortletDefinitionId portletDefinitionId = portletDefinition.getPortletDefinitionId(); final PortletDefinition portletDescriptor = this.portletDefinitionRegistry.getParentPortletDescriptor(portletDefinitionId); final Preferences descriptorPreferences = portletDescriptor.getPortletPreferences(); for (final Preference preference : descriptorPreferences.getPortletPreferences()) { final IPortletPreference preferenceWrapper = new PortletPreferenceImpl(preference); basePortletPreferences.put(preferenceWrapper.getName(), preferenceWrapper); } //Add definition prefs to base Map final List<IPortletPreference> definitionPreferences = portletDefinition.getPortletPreferences(); for (final IPortletPreference preference : definitionPreferences) { basePortletPreferences.put(preference.getName(), preference); } } @Override protected boolean storeInternal() throws IOException, ValidatorException { final HttpServletRequest containerRequest = portletRequestContext.getContainerRequest(); final IPortletEntity portletEntity = this.portletEntityRegistry.getPortletEntity(containerRequest, portletEntityId); final IPortletEntityId portletEntityId = portletEntity.getPortletEntityId(); final Lock portletEntityLock = this.portletEntityRegistry.getPortletEntityLock(containerRequest, portletEntityId); //Do a tryLock first so that we can warn about concurrent preference modification if it fails boolean locked = portletEntityLock.tryLock(); try { if (!locked) { logger.warn( "Concurrent portlet preferences modification by: " + portletEntity + " " + "This has the potential for changes to preferences to be lost. " + "This portlet should be modified to synchronize its preference modifications appropriately", new Throwable()); portletEntityLock.lock(); locked = true; } return this.transactionOperations.execute( new TransactionCallback<Boolean>() { @Override public Boolean doInTransaction(TransactionStatus status) { //Refresh the entity to avoid optimistic locking errors final IPortletEntity portletEntity = portletEntityRegistry.getPortletEntity( containerRequest, portletEntityId); final Map<String, IPortletPreference> targetPortletPreferences = getTargetPortletPreferences(); final Collection<IPortletPreference> values = targetPortletPreferences.values(); final boolean modified = portletEntity.setPortletPreferences( new ArrayList<IPortletPreference>(values)); if (!modified) { //Nothing actually changed, skip the store return Boolean.FALSE; } portletEntityRegistry.storePortletEntity( containerRequest, portletEntity); return Boolean.TRUE; } }); } finally { //check if locked, needed due to slightly more complex logic around the tryLock and logging if (locked) { portletEntityLock.unlock(); } } } }