/** * Copyright (C) 2009 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.portal.webui.application; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.exoplatform.application.gadget.GadgetRegistryService; import org.exoplatform.container.ExoContainer; import org.exoplatform.portal.config.DataStorage; import org.exoplatform.portal.config.model.ApplicationState; import org.exoplatform.portal.config.model.ApplicationType; import org.exoplatform.portal.config.model.TransientApplicationState; import org.exoplatform.portal.pc.ExoPortletState; import org.exoplatform.portal.pc.ExoPortletStateType; import org.exoplatform.portal.pom.spi.gadget.Gadget; import org.exoplatform.portal.pom.spi.portlet.Portlet; import org.exoplatform.portal.pom.spi.portlet.PortletBuilder; import org.exoplatform.portal.pom.spi.portlet.Preference; import org.exoplatform.portal.pom.spi.wsrp.WSRP; import org.exoplatform.portal.pom.spi.wsrp.WSRPPortletStateType; import org.gatein.pc.api.PortletContext; import org.gatein.pc.api.PortletInvoker; import org.gatein.pc.api.StatefulPortletContext; /** * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> * @version $Revision$ */ public abstract class ModelAdapter<S, C extends Serializable> { private static final String LOCAL_STATE_ID = PortletContext.LOCAL_CONSUMER_CLONE.getId(); public static <S, C extends Serializable, I> ModelAdapter<S, C> getAdapter(ApplicationType<S> type) { if (type == ApplicationType.PORTLET) { @SuppressWarnings("unchecked") ModelAdapter<S, C> adapter = (ModelAdapter<S, C>) PORTLET; return adapter; } else if (type == ApplicationType.GADGET) { @SuppressWarnings("unchecked") ModelAdapter<S, C> adapter = (ModelAdapter<S, C>) GADGET; return adapter; } else if (type == ApplicationType.WSRP_PORTLET) { @SuppressWarnings("unchecked") ModelAdapter<S, C> adapter = (ModelAdapter<S, C>) WSRP; return adapter; } else { throw new AssertionError(); } } /** . */ private static final ModelAdapter<Portlet, ExoPortletState> PORTLET = new ModelAdapter<Portlet, ExoPortletState>() { @Override public StatefulPortletContext<ExoPortletState> getPortletContext(ExoContainer container, String applicationId, ApplicationState<Portlet> applicationState) throws Exception { DataStorage dataStorage = (DataStorage) container.getComponentInstanceOfType(DataStorage.class); Portlet preferences = dataStorage.load(applicationState, ApplicationType.PORTLET); PortletContext producerOfferedPortletContext = getProducerOfferedPortletContext(applicationId); ExoPortletState map = new ExoPortletState(producerOfferedPortletContext.getId()); if (preferences != null) { for (Preference pref : preferences) { map.getState().put(pref.getName(), pref.getValues()); } } return StatefulPortletContext.create(LOCAL_STATE_ID, ExoPortletStateType.getInstance(), map); } @Override public ApplicationState<Portlet> update(ExoContainer container, ExoPortletState updateState, ApplicationState<Portlet> applicationState) throws Exception { // Compute new preferences PortletBuilder builder = new PortletBuilder(); for (Map.Entry<String, List<String>> entry : updateState.getState().entrySet()) { builder.add(entry.getKey(), entry.getValue()); } if (applicationState instanceof TransientApplicationState) { TransientApplicationState<Portlet> transientState = (TransientApplicationState<Portlet>) applicationState; transientState.setContentState(builder.build()); return transientState; } else { DataStorage dataStorage = (DataStorage) container.getComponentInstanceOfType(DataStorage.class); return dataStorage.save(applicationState, builder.build()); } } @Override public PortletContext getProducerOfferedPortletContext(String applicationState) { int indexOfSeparator = applicationState.lastIndexOf("/"); String appName = applicationState.substring(0, indexOfSeparator); String portletName = applicationState.substring(indexOfSeparator + 1); return PortletContext.reference(PortletInvoker.LOCAL_PORTLET_INVOKER_ID, PortletContext.createPortletContext(appName, portletName)); } @Override public Portlet getState(ExoContainer container, ApplicationState<Portlet> applicationState) throws Exception { if (applicationState instanceof TransientApplicationState) { TransientApplicationState<Portlet> transientState = (TransientApplicationState<Portlet>) applicationState; Portlet pref = transientState.getContentState(); if (pref == null) { pref = new Portlet(); } return pref; } else { DataStorage dataStorage = (DataStorage) container.getComponentInstanceOfType(DataStorage.class); Portlet pref = dataStorage.load(applicationState, ApplicationType.PORTLET); if (pref == null) { pref = new Portlet(); } return pref; } } @Override public ExoPortletState getStateFromModifiedContext(PortletContext originalPortletContext, PortletContext modifiedPortletContext) { if (modifiedPortletContext != null && modifiedPortletContext instanceof StatefulPortletContext) { StatefulPortletContext statefulContext = (StatefulPortletContext) modifiedPortletContext; if (statefulContext.getState() instanceof ExoPortletState) { return (ExoPortletState) statefulContext.getState(); } } return null; } @Override public ExoPortletState getstateFromClonedContext(PortletContext originalPortletContext, PortletContext clonedPortletContext) { if (clonedPortletContext != null && clonedPortletContext instanceof StatefulPortletContext) { StatefulPortletContext statefulContext = (StatefulPortletContext) clonedPortletContext; if (statefulContext.getState() instanceof ExoPortletState) { return (ExoPortletState) statefulContext.getState(); } } return null; } }; private static final String DASHBOARD = "dashboard"; private static final String GADGET_PORTLET = "GadgetPortlet"; private static final PortletContext WRAPPER_CONTEXT = PortletContext.reference(PortletInvoker.LOCAL_PORTLET_INVOKER_ID, PortletContext.createPortletContext(DASHBOARD, GADGET_PORTLET)); private static final String WRAPPER_ID = WRAPPER_CONTEXT.getId(); private static final ModelAdapter<Gadget, ExoPortletState> GADGET = new ModelAdapter<Gadget, ExoPortletState>() { @Override public StatefulPortletContext<ExoPortletState> getPortletContext(ExoContainer container, String applicationId, ApplicationState<Gadget> applicationState) throws Exception { GadgetRegistryService gadgetService = (GadgetRegistryService) container .getComponentInstanceOfType(GadgetRegistryService.class); org.exoplatform.application.gadget.Gadget model = gadgetService.getGadget(applicationId); String url = GadgetUtil.reproduceUrl(model.getUrl(), model.isLocal()); ExoPortletState prefs = new ExoPortletState(WRAPPER_ID); prefs.getState().put("url", Arrays.asList(url)); DataStorage dataStorage = (DataStorage) container.getComponentInstanceOfType(DataStorage.class); Gadget gadget = dataStorage.load(applicationState, ApplicationType.GADGET); if (gadget != null && gadget.getUserPref() != null) { prefs.getState().put("userPref", Collections.singletonList(gadget.getUserPref())); } return StatefulPortletContext.create(LOCAL_STATE_ID, ExoPortletStateType.getInstance(), prefs); } @Override public ApplicationState<Gadget> update(ExoContainer container, ExoPortletState updateState, ApplicationState<Gadget> gadgetApplicationState) throws Exception { // Compute new preferences String userPref = null; for (Map.Entry<String, List<String>> entry : updateState.getState().entrySet()) { if (entry.getKey().equals("userPref") && entry.getValue().size() > 0) { userPref = entry.getValue().get(0); } } if (gadgetApplicationState instanceof TransientApplicationState<?>) { throw new UnsupportedOperationException("todo"); } else { if (userPref != null) { Gadget gadget = new Gadget(); gadget.addUserPref(userPref); DataStorage dataStorage = (DataStorage) container.getComponentInstanceOfType(DataStorage.class); dataStorage.save(gadgetApplicationState, gadget); } } // return gadgetApplicationState; } @Override public PortletContext getProducerOfferedPortletContext(String applicationState) { return WRAPPER_CONTEXT; } @Override public Portlet getState(ExoContainer container, ApplicationState<Gadget> applicationState) throws Exception { // For now we return null as it does not make sense to edit the gadget preferences return null; } @Override public ExoPortletState getStateFromModifiedContext(PortletContext originalPortletContext, PortletContext modifiedPortletContext) { if (modifiedPortletContext != null && modifiedPortletContext instanceof StatefulPortletContext) { StatefulPortletContext statefulContext = (StatefulPortletContext) modifiedPortletContext; if (statefulContext.getState() instanceof ExoPortletState) { return (ExoPortletState) statefulContext.getState(); } } return null; } @Override public ExoPortletState getstateFromClonedContext(PortletContext originalPortletContext, PortletContext clonedPortletContext) { if (clonedPortletContext != null && clonedPortletContext instanceof StatefulPortletContext) { StatefulPortletContext statefulContext = (StatefulPortletContext) clonedPortletContext; if (statefulContext.getState() instanceof ExoPortletState) { return (ExoPortletState) statefulContext.getState(); } } return null; } }; /** * todo: this ModelAdapter is not quite good, what is really needed is a ModelAdapter<WSRP, byte[]> so that the * StatefulPortletContext returned by getPortletContext is actually of type PortletStateType.OPAQUE so that it can be * properly handled in WSRP... This model needs to be revisited if we want to properly support consumer-side state * management. See GTNPORTAL-736. */ private static final ModelAdapter<WSRP, WSRP> WSRP = new ModelAdapter<WSRP, WSRP>() { @Override public Portlet getState(ExoContainer container, ApplicationState<WSRP> state) throws Exception { return null; // return null for now } @Override public PortletContext getProducerOfferedPortletContext(String applicationId) { return PortletContext.createPortletContext(applicationId); } @Override public StatefulPortletContext<WSRP> getPortletContext(ExoContainer container, String applicationId, ApplicationState<WSRP> state) throws Exception { DataStorage dataStorage = (DataStorage) container.getComponentInstanceOfType(DataStorage.class); WSRP wsrp = dataStorage.load(state, ApplicationType.WSRP_PORTLET); if (wsrp == null) { // create wsrp = new WSRP(); wsrp.setPortletId(applicationId); if (!(state instanceof TransientApplicationState)) { // only save state if it's not transient dataStorage.save(state, wsrp); } } return StatefulPortletContext.create(wsrp.getPortletId(), WSRPPortletStateType.instance, wsrp); } @Override public ApplicationState<WSRP> update(ExoContainer container, WSRP updateState, ApplicationState<WSRP> state) throws Exception { if (state instanceof TransientApplicationState) { TransientApplicationState<WSRP> transientState = (TransientApplicationState<WSRP>) state; transientState.setContentState(updateState); return transientState; } else { // todo: it is possible to get a CloneApplicationState for some reason, need to investigate DataStorage dataStorage = (DataStorage) container.getComponentInstanceOfType(DataStorage.class); return dataStorage.save(state, updateState); } } @Override public WSRP getStateFromModifiedContext(PortletContext originalPortletContext, PortletContext modifiedPortletContext) { WSRP wsrp = new WSRP(); wsrp.setPortletId(modifiedPortletContext.getId()); // from the originalPortletContext see if we are dealing with a cloned context or not. if (originalPortletContext instanceof StatefulPortletContext) { Object originalState = ((StatefulPortletContext) originalPortletContext).getState(); if (originalState instanceof WSRP) { wsrp.setCloned(((WSRP) originalState).isCloned()); } } if (modifiedPortletContext instanceof StatefulPortletContext) { Object modifiedState = ((StatefulPortletContext) modifiedPortletContext).getState(); if (modifiedState instanceof byte[]) { wsrp.setState((byte[]) modifiedState); } } return wsrp; } @Override public WSRP getstateFromClonedContext(PortletContext originalPortletContext, PortletContext clonedPortletContext) { WSRP wsrp = new WSRP(); wsrp.setPortletId(clonedPortletContext.getId()); wsrp.setCloned(true); // if we have an associated state, record it as well... if (clonedPortletContext instanceof StatefulPortletContext) { StatefulPortletContext statefulPortletContext = (StatefulPortletContext) clonedPortletContext; wsrp.setState((byte[]) statefulPortletContext.getState()); } return wsrp; } }; public abstract PortletContext getProducerOfferedPortletContext(String applicationId); public abstract StatefulPortletContext<C> getPortletContext(ExoContainer container, String applicationId, ApplicationState<S> applicationState) throws Exception; public abstract ApplicationState<S> update(ExoContainer container, C updateState, ApplicationState<S> applicationState) throws Exception; /** * Returns the state of the gadget as preferences or null if the preferences cannot be edited as such. * * @param container the container * @param applicationState the application state * @return the preferences * @throws Exception any exception */ public abstract Portlet getState(ExoContainer container, ApplicationState<S> applicationState) throws Exception; /** * Extracts the state based on what the current PortletContext is and the new modified PortletContext. * * @param originalPortletContext The current PortletContext for the Portlet * @param modifiedPortletContext The new modified PortletContext * @return */ public abstract C getStateFromModifiedContext(PortletContext originalPortletContext, PortletContext modifiedPortletContext); /** * Extracts the state based on what the current PortletContext is and the new cloned PortletContext * * @param originalPortletContext The current PortletContext for the Portlet * @param clonedPortletContext The new cloned PortletContext * @return */ public abstract C getstateFromClonedContext(PortletContext originalPortletContext, PortletContext clonedPortletContext); }