/**
* 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 org.exoplatform.Constants;
import org.exoplatform.commons.utils.Text;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.portal.application.PortalRequestContext;
import org.exoplatform.portal.application.UserProfileLifecycle;
import org.exoplatform.portal.application.state.ContextualPropertyManager;
import org.exoplatform.portal.config.DataStorage;
import org.exoplatform.portal.config.NoSuchDataException;
import org.exoplatform.portal.config.model.ApplicationType;
import org.exoplatform.portal.pom.spi.portlet.Portlet;
import org.exoplatform.portal.pom.spi.wsrp.WSRP;
import org.exoplatform.portal.portlet.PortletExceptionHandleService;
import org.exoplatform.portal.webui.application.UIPortletActionListener.ChangePortletModeActionListener;
import org.exoplatform.portal.webui.application.UIPortletActionListener.ChangeWindowStateActionListener;
import org.exoplatform.portal.webui.application.UIPortletActionListener.EditPortletActionListener;
import org.exoplatform.portal.webui.application.UIPortletActionListener.ProcessActionActionListener;
import org.exoplatform.portal.webui.application.UIPortletActionListener.ProcessEventsActionListener;
import org.exoplatform.portal.webui.application.UIPortletActionListener.RenderActionListener;
import org.exoplatform.portal.webui.application.UIPortletActionListener.ServeResourceActionListener;
import org.exoplatform.portal.webui.portal.UIPortal;
import org.exoplatform.portal.webui.portal.UIPortalComponentActionListener.DeleteComponentActionListener;
import org.exoplatform.portal.webui.util.Util;
import org.exoplatform.portal.webui.workspace.UIPortalApplication;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.organization.UserProfile;
import org.exoplatform.services.portletcontainer.PortletContainerException;
import org.exoplatform.web.application.JavascriptManager;
import org.exoplatform.web.application.RequestContext;
import org.exoplatform.webui.application.WebuiRequestContext;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.config.annotation.EventConfig;
import org.exoplatform.webui.event.Event.Phase;
import org.gatein.common.i18n.LocalizedString;
import org.gatein.common.net.media.MediaType;
import org.gatein.common.util.MultiValuedPropertyMap;
import org.gatein.common.util.ParameterValidation;
import org.gatein.pc.api.Mode;
import org.gatein.pc.api.PortletContext;
import org.gatein.pc.api.PortletInvoker;
import org.gatein.pc.api.PortletInvokerException;
import org.gatein.pc.api.PortletStateType;
import org.gatein.pc.api.StateString;
import org.gatein.pc.api.StatefulPortletContext;
import org.gatein.pc.api.cache.CacheLevel;
import org.gatein.pc.api.info.EventInfo;
import org.gatein.pc.api.info.MetaInfo;
import org.gatein.pc.api.info.ModeInfo;
import org.gatein.pc.api.info.ParameterInfo;
import org.gatein.pc.api.info.PortletInfo;
import org.gatein.pc.api.invocation.ActionInvocation;
import org.gatein.pc.api.invocation.EventInvocation;
import org.gatein.pc.api.invocation.PortletInvocation;
import org.gatein.pc.api.invocation.RenderInvocation;
import org.gatein.pc.api.invocation.ResourceInvocation;
import org.gatein.pc.api.invocation.response.ErrorResponse;
import org.gatein.pc.api.invocation.response.FragmentResponse;
import org.gatein.pc.api.invocation.response.PortletInvocationResponse;
import org.gatein.pc.api.state.AccessMode;
import org.gatein.pc.api.state.PropertyChange;
import org.gatein.pc.portlet.impl.spi.AbstractClientContext;
import org.gatein.pc.portlet.impl.spi.AbstractPortalContext;
import org.gatein.pc.portlet.impl.spi.AbstractRequestContext;
import org.gatein.pc.portlet.impl.spi.AbstractSecurityContext;
import org.gatein.pc.portlet.impl.spi.AbstractWindowContext;
import org.gatein.portal.controller.resource.ResourceScope;
import org.w3c.dom.Element;
import javax.portlet.MimeResponse;
import javax.portlet.PortletMode;
import javax.portlet.WindowState;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.xml.namespace.QName;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
/**
* This UI component represent a portlet window on a page. <br/>
* Each user request to a portlet will be passed through this class then delegate call to the portlet container<br/>
* UIPortletLifecycle do the main request router: delegate the job to portlet action listeners according to the url parameters<br/>
*
* ProcessAction, ServeResource, Render action listeners will receive event if request url contain parameter
* point to them, those event will delegate call to portlet container to call JSR 286 portlet lifecycle method<br/>
*
* ProcessEvents, ChangePortletMode, ChangeWindowState listener will receive event after the portlet action invocation response.
* (dispatched during the process of ProcessActionListener)<br/>
*
* DeleteComponent, EditPortlet action listener is portal specific listener, come from the UI of portal
*
* @see UIPortletLifecycle
* @see UIPortletActionListener
*/
@ComponentConfig(lifecycle = UIPortletLifecycle.class, template = "system:/groovy/portal/webui/application/UIPortlet.gtmpl", events = {
@EventConfig(listeners = RenderActionListener.class), @EventConfig(listeners = ChangePortletModeActionListener.class),
@EventConfig(listeners = ChangeWindowStateActionListener.class),
@EventConfig(listeners = DeleteComponentActionListener.class, confirm = "UIPortlet.deletePortlet"),
@EventConfig(listeners = EditPortletActionListener.class),
@EventConfig(phase = Phase.PROCESS, listeners = ProcessActionActionListener.class),
@EventConfig(phase = Phase.PROCESS, listeners = ServeResourceActionListener.class),
@EventConfig(phase = Phase.PROCESS, listeners = ProcessEventsActionListener.class) })
public class UIPortlet<S, C extends Serializable> extends UIApplication {
protected static final Log log = ExoLogger.getLogger("portal:UIPortlet");
public static final String DEFAULT_THEME = "Default:DefaultTheme::Vista:VistaTheme::Mac:MacTheme";
public static final String JAVASCRIPT_DEPENDENCY = "org.gatein.javascript.dependency";
private static final String WSRP_URL = "wsrp-url";
private static final String WSRP_PREFER_OPERATION = "wsrp-preferOperation";
private static final String WSRP_REQUIRES_REWRITE = "wsrp-requiresRewrite";
private static final String WSRP_NAVIGATIONAL_VALUES = "wsrp-navigationalValues";
private static final AbstractPortalContext PORTAL_CONTEXT = new AbstractPortalContext(Collections.singletonMap(
"javax.portlet.markup.head.element.support", "true"));
/** . */
private String storageId;
/** . */
private String storageName;
/** . */
private ModelAdapter<S, C> adapter;
/** . */
private org.gatein.pc.api.Portlet producedOfferedPortlet;
/** . */
private PortletContext producerOfferedPortletContext;
/** A computed field that contains the runtime description of the portlet for edit mode. */
private LocalizedString displayName;
/** . */
private PortletState<S> state;
/** . */
private String applicationId;
private String theme_;
private String portletStyle;
private boolean showPortletMode = true;
private PortletMode currentPortletMode_ = PortletMode.VIEW;
private WindowState currentWindowState_ = WindowState.NORMAL;
private List<String> supportModes_;
private List<QName> supportedProcessingEvents_;
private List<QName> supportedPublishingEvents_;
private Map<QName, String> supportedPublicParams_;
private boolean portletInPortal_ = true;
private StateString navigationalState;
/** A field storing localized value of javax.portlet.title * */
private String configuredTitle;
public UIPortlet() {
// That value will be overriden when it is mapped onto a data storage
storageName = UUID.randomUUID().toString();
}
public String getStorageId() {
return storageId;
}
public void setStorageId(String storageId) {
this.storageId = storageId;
}
public String getStorageName() {
return storageName;
}
public void setStorageName(String storageName) {
this.storageName = storageName;
}
public String getWindowId() {
return storageName;
}
/**
* Retrieves the skin identifier associated with this portlet or <code>null</code> if there isn't one (for example, it
* doesn't make any sense in the WSRP scenario).
*
* @return the skin identifier associated with this portlet or <code>null</code> if there isn't one
*/
public String getSkinId() {
ApplicationType<S> type = state.getApplicationType();
if (type == ApplicationType.PORTLET) {
return applicationId;
} else if (type == ApplicationType.GADGET) {
return "dashboard/GadgetPortlet";
} else {
return null;
}
}
public String getId() {
return storageName;
}
public String getApplicationId() {
return applicationId;
}
/**
* portletStyle is 'Window' when it's in WebOS project - an GateIn extension,
* portletStyle is null if is not in WebOS
* @return a string represent current portlet style
*/
public String getPortletStyle() {
return portletStyle;
}
public void setPortletStyle(String s) {
portletStyle = s;
}
/**
* @return true if portlet is configured to show control icon that allow to change portlet mode
*/
public boolean getShowPortletMode() {
return showPortletMode;
}
/**
* Used by portal to show the icon that allow to change portlet mode
* @param b if show icon
*/
public void setShowPortletMode(Boolean b) {
showPortletMode = b;
}
/**
* Used internally by portal to change current state
* if portlet is in portal or in page
*/
public void setPortletInPortal(boolean b) {
portletInPortal_ = b;
}
/**
* Check if portlet is in portal
* @return true if portlet is in portal
*/
public boolean isPortletInPortal() {
return portletInPortal_;
}
/**
* Theme is composed of map between theme name and skin name.
* Theme format: {skinName}:{themeName}::{anotherSkin}:{anotherTheme}.
* For example: the default them is 'Default:DefaultTheme::Vista:VistaTheme::Mac:MacTheme'.
* Default theme means if portal skin is 'Default', this portlet's theme is 'DefaultTheme. If portal change skin to 'Vista',
* portlet theme will be change to 'VistaTheme'.
* @return current theme setting
*/
public String getTheme() {
if (theme_ == null || theme_.trim().length() < 1) {
return DEFAULT_THEME;
}
return theme_;
}
/**
* Used internally by Portal to change current portlet theme.
* Theme format: {skinName}:{themeName}::{anotherSkin}:{anotherTheme}.
* For example: 'Default:DefaultTheme::Vista:VistaTheme::Mac:MacTheme'
*/
public void setTheme(String theme) {
theme_ = theme;
}
/**
* Get theme name according to portal skin.
* If there is no coressponding theme. return 'DefaultTheme'
* @param skin - portal skin
* @return theme name
*/
public String getSuitedTheme(String skin) {
if (skin == null) {
skin = Util.getUIPortalApplication().getSkin();
}
Map<String, String> themeMap = stringToThemeMap(getTheme());
if (themeMap.containsKey(skin)) {
return themeMap.get(skin);
}
return DEFAULT_THEME.split(":")[1];
}
/**
* Add map between portlet theme and portal skin
* @param skin - portal skin name
* @param theme - portlet theme name
*/
public void putSuitedTheme(String skin, String theme) {
if (skin == null) {
skin = Util.getUIPortalApplication().getSkin();
}
Map<String, String> themeMap = stringToThemeMap(getTheme());
themeMap.put(skin, theme);
setTheme(themeMapToString(themeMap));
}
private String themeMapToString(Map<String, String> themeMap) {
StringBuffer builder = new StringBuffer();
Iterator<Entry<String, String>> itr = themeMap.entrySet().iterator();
while (itr.hasNext()) {
Entry<String, String> entry = itr.next();
builder.append(entry.getKey()).append(":").append(entry.getValue());
if (itr.hasNext()) {
builder.append("::");
}
}
return builder.toString();
}
private Map<String, String> stringToThemeMap(String themesString) {
Map<String, String> themeMap = new HashMap<String, String>();
String[] themeIds = themesString.split("::");
for (String ele : themeIds) {
String[] strs = ele.split(":");
themeMap.put(strs[0], strs[1]);
}
return themeMap;
}
public PortletMode getCurrentPortletMode() {
return currentPortletMode_;
}
public void setCurrentPortletMode(PortletMode mode) {
currentPortletMode_ = mode;
}
public WindowState getCurrentWindowState() {
return currentWindowState_;
}
public void setCurrentWindowState(WindowState state) {
currentWindowState_ = state;
}
public List<QName> getSupportedProcessingEvents() {
return supportedProcessingEvents_;
}
public void setSupportedProcessingEvents(List<QName> supportedProcessingEvents) {
supportedProcessingEvents_ = supportedProcessingEvents;
}
public Map<QName, String> getSupportedPublicRenderParameters() {
return supportedPublicParams_;
}
public void setSupportedPublicRenderParameters(Map<QName, String> supportedPublicRenderParameters) {
supportedPublicParams_ = supportedPublicRenderParameters;
}
/**
* Get localized displayName metadata configured in portlet.xml.<br/>
* If can't find localized displayName, return portlet name.<br/>
* If portlet doesn't exists anymore, return empty string.<br/>
* This value is cached in session, that means it only query to portlet container one time
* @return display name
*/
public String getDisplayName() {
if (displayName == null) {
org.gatein.pc.api.Portlet portlet = getProducedOfferedPortlet();
if (portlet != null) {
PortletInfo info = portlet.getInfo();
MetaInfo meta = info.getMeta();
displayName = meta.getMetaValue(MetaInfo.DISPLAY_NAME);
String value = null;
if (displayName != null) {
RequestContext i = PortalRequestContext.getCurrentInstance();
Locale locale = i.getLocale();
value = displayName.getString(locale, true);
}
if (value == null || value.length() == 0) {
value = info.getName();
}
return value;
} else {
return "";
}
} else {
RequestContext i = PortalRequestContext.getCurrentInstance();
Locale locale = i.getLocale();
String value = displayName.getString(locale, true);
if (ParameterValidation.isNullOrEmpty(value)) {
org.gatein.pc.api.Portlet portlet = getProducedOfferedPortlet();
PortletInfo info = portlet.getInfo();
value = info.getName();
}
return value;
}
}
public org.gatein.pc.api.Portlet getProducedOfferedPortlet() {
return producedOfferedPortlet;
}
public List<String> getSupportModes() {
if (supportModes_ != null) {
return supportModes_;
}
List<String> supportModes = new ArrayList<String>();
org.gatein.pc.api.Portlet portlet = getProducedOfferedPortlet();
// if we couldn't get the portlet that just return an empty modes list
if (portlet == null) {
return supportModes;
}
Set<ModeInfo> modes = portlet.getInfo().getCapabilities().getModes(MediaType.create("text/html"));
for (ModeInfo mode : modes) {
supportModes.add(mode.getModeName());
}
if (supportModes.size() > 0) {
supportModes.remove("view");
}
setSupportModes(supportModes);
return supportModes;
}
public void setSupportModes(List<String> supportModes) {
supportModes_ = supportModes;
}
/**
* Tells, according to the info located in portlet.xml, wether this portlet can handle a portlet event with the QName given
* as the method argument
*/
public boolean supportsProcessingEvent(QName name) {
if (supportedProcessingEvents_ == null) {
org.gatein.pc.api.Portlet portlet = getProducedOfferedPortlet();
if (portlet == null) {
if (producerOfferedPortletContext != null) {
log.info("Could not find portlet with ID : " + producerOfferedPortletContext.getId());
} else {
log.info("Could not find portlet. The producerOfferedPortletContext is null");
}
return false;
}
Map<QName, EventInfo> consumedEvents = (Map<QName, EventInfo>) portlet.getInfo().getEventing().getConsumedEvents();
if (consumedEvents == null) {
return false;
}
supportedProcessingEvents_ = new ArrayList<QName>(consumedEvents.keySet());
}
for (QName eventName : supportedProcessingEvents_) {
if (eventName.equals(name)) {
if (log.isDebugEnabled()) {
log.debug("The Portlet " + producerOfferedPortletContext + " supports comsuming the event : " + name);
}
return true;
}
}
if (log.isDebugEnabled()) {
log.debug("The portlet " + producerOfferedPortletContext + " doesn't support consuming the event : " + name);
}
return false;
}
public boolean supportsPublishingEvent(QName name) {
if (supportedPublishingEvents_ == null) {
org.gatein.pc.api.Portlet portlet = getProducedOfferedPortlet();
if (portlet == null) {
log.info("Could not find portlet with ID : " + producerOfferedPortletContext.getId());
return false;
}
Map<QName, EventInfo> producedEvents = (Map<QName, EventInfo>) portlet.getInfo().getEventing().getProducedEvents();
if (producedEvents == null) {
return false;
}
supportedPublishingEvents_ = new ArrayList<QName>(producedEvents.keySet());
}
for (QName eventName : supportedPublishingEvents_) {
if (eventName.equals(name)) {
if (log.isDebugEnabled()) {
log.debug("The Portlet " + producerOfferedPortletContext + " supports producing the event : " + name);
}
return true;
}
}
if (log.isDebugEnabled()) {
log.debug("The portlet " + producerOfferedPortletContext + " doesn't support producing the event : " + name);
}
return false;
}
/**
* Tells, according to the info located in portlet.xml, wether this portlet supports the public render parameter qname given
* as method argument. If the qname is supported, the public render parameter id is returned otherwise false is returned.
*
* @param supportedPublicParam the supported public parameter qname
* @return the supported public parameter id
*/
public String supportsPublicParam(QName supportedPublicParam) {
if (supportedPublicParams_ == null) {
//
if (producedOfferedPortlet == null) {
log.info("Could not find portlet with ID : " + producerOfferedPortletContext.getId());
return null;
}
//
Collection<ParameterInfo> parameters = (Collection<ParameterInfo>) producedOfferedPortlet.getInfo().getNavigation()
.getPublicParameters();
supportedPublicParams_ = new HashMap<QName, String>();
for (ParameterInfo parameter : parameters) {
supportedPublicParams_.put(parameter.getName(), parameter.getId());
}
}
//
String id = supportedPublicParams_.get(supportedPublicParam);
if (id != null) {
if (log.isDebugEnabled()) {
log.debug("The Portlet " + producerOfferedPortletContext.getId() + " supports the public render parameter : "
+ supportedPublicParam);
}
return id;
}
//
return null;
}
/**
* Tells, according to the info located in portlet.xml, wether this portlet supports the public render parameter given as a
* method argument
*/
public boolean supportsPublicParam(String supportedPublicParam) {
if (supportedPublicParams_ == null) {
//
if (producedOfferedPortlet == null) {
log.info("Could not find portlet with ID : " + producerOfferedPortletContext.getId());
return false;
}
//
Collection<ParameterInfo> parameters = (Collection<ParameterInfo>) producedOfferedPortlet.getInfo().getNavigation()
.getPublicParameters();
supportedPublicParams_ = new HashMap<QName, String>();
for (ParameterInfo parameter : parameters) {
supportedPublicParams_.put(parameter.getName(), parameter.getId());
}
}
//
for (String publicParam : supportedPublicParams_.values()) {
if (publicParam.equals(supportedPublicParam)) {
if (log.isDebugEnabled()) {
log.debug("The Portlet " + producerOfferedPortletContext.getId()
+ " supports the public render parameter : " + supportedPublicParam);
}
return true;
}
}
//
return false;
}
/**
* This methods return the public render parameters names supported by the targeted portlet; in other words, it sorts the
* full public render params list and only return the ones that the current portlet can handle
*/
public List<String> getPublicRenderParamNames() {
UIPortal uiPortal = Util.getUIPortal();
Map<String, String[]> publicParams = uiPortal.getPublicParameters();
List<String> publicParamsSupportedByPortlet = new ArrayList<String>();
if (publicParams != null) {
Set<String> keys = publicParams.keySet();
for (String key : keys) {
if (supportsPublicParam(key)) {
publicParamsSupportedByPortlet.add(key);
}
}
return publicParamsSupportedByPortlet;
}
return new ArrayList<String>();
}
public Map<String, String[]> getPublicParameters() {
Map<String, String[]> publicParamsMap = new HashMap<String, String[]>();
UIPortal uiPortal = Util.getUIPortal();
Map<String, String[]> publicParams = uiPortal.getPublicParameters();
Set<String> allPublicParamsNames = publicParams.keySet();
List<String> supportedPublicParamNames = getPublicRenderParamNames();
for (String oneOfAllParams : allPublicParamsNames) {
if (supportedPublicParamNames.contains(oneOfAllParams)) {
publicParamsMap.put(oneOfAllParams, publicParams.get(oneOfAllParams));
}
}
// Handle exposed portal contextual properties
ContextualPropertyManager propertyManager = this.getApplicationComponent(ContextualPropertyManager.class);
Map<QName, String[]> exposedPortalState = propertyManager.getProperties(this);
for (QName prpQName : exposedPortalState.keySet()) {
String prpId = supportsPublicParam(prpQName);
if (prpId != null) {
publicParamsMap.put(prpId, exposedPortalState.get(prpQName));
}
}
//
return publicParamsMap;
}
// This is code for integration with PC
/**
* Create the correct portlet invocation that will target the portlet represented by this UI component.
*
* @param type the invocation type
* @param prc the portal request context
* @param <I> the invocation type
* @return the portlet invocation
* @throws Exception any exception
*/
public <I extends PortletInvocation> I create(Class<I> type, PortalRequestContext prc) throws Exception {
ExoPortletInvocationContext pic = new ExoPortletInvocationContext(prc, this);
//
I invocation;
HttpServletRequest servletRequest = prc.getRequest();
HashMap<String, String[]> allParams = new HashMap<String, String[]>();
allParams.putAll(prc.getPortletParameters());
allParams.putAll(this.getPublicParameters());
allParams.remove(ExoPortletInvocationContext.NAVIGATIONAL_STATE_PARAM_NAME);
if (type.equals(ActionInvocation.class)) {
ActionInvocation actionInvocation = new ActionInvocation(pic);
actionInvocation.setRequestContext(new AbstractRequestContext(servletRequest));
String interactionState = servletRequest.getParameter(ExoPortletInvocationContext.INTERACTION_STATE_PARAM_NAME);
if (interactionState != null) {
actionInvocation.setInteractionState(StateString.create(interactionState));
// remove the interaction state from remaining params
allParams.remove(ExoPortletInvocationContext.INTERACTION_STATE_PARAM_NAME);
}
actionInvocation.setForm(allParams);
invocation = type.cast(actionInvocation);
} else if (type.equals(ResourceInvocation.class)) {
ResourceInvocation resourceInvocation = new ResourceInvocation(pic);
resourceInvocation.setRequestContext(new AbstractRequestContext(servletRequest));
String resourceId = servletRequest.getParameter(Constants.RESOURCE_ID_PARAMETER);
if (!ParameterValidation.isNullOrEmpty(resourceId)) {
resourceInvocation.setResourceId(resourceId);
} else if (!ParameterValidation.isNullOrEmpty(prc.getRequestParameter(Constants.RESOURCE_ID_PARAMETER))) {
resourceInvocation.setResourceId(prc.getRequestParameter(Constants.RESOURCE_ID_PARAMETER));
}
String cachability = servletRequest.getParameter(Constants.CACHELEVEL_PARAMETER);
if (!ParameterValidation.isNullOrEmpty(cachability)) {
// we need to convert the given value to upper case as it might come from WSRP in lower case
resourceInvocation.setCacheLevel(CacheLevel.create(cachability.toUpperCase(Locale.ENGLISH)));
}
String resourceState = servletRequest.getParameter(ExoPortletInvocationContext.RESOURCE_STATE_PARAM_NAME);
if (!ParameterValidation.isNullOrEmpty(resourceState)) {
resourceInvocation.setResourceState(StateString.create(resourceState));
}
// remove the resource state from remaining params
allParams.remove(ExoPortletInvocationContext.RESOURCE_STATE_PARAM_NAME);
// deal with WSRP-specific parameters: add them to the invocation attributes if they exist and remove them from form
// params
String url = servletRequest.getParameter(WSRP_URL);
if (!ParameterValidation.isNullOrEmpty(url)) {
resourceInvocation.setAttribute(WSRP_URL, url);
}
allParams.remove(WSRP_URL);
String preferOperation = servletRequest.getParameter(WSRP_PREFER_OPERATION);
if (!ParameterValidation.isNullOrEmpty(preferOperation)) {
resourceInvocation.setAttribute(WSRP_PREFER_OPERATION, preferOperation);
}
allParams.remove(WSRP_PREFER_OPERATION);
String requiresRewrite = servletRequest.getParameter(WSRP_REQUIRES_REWRITE);
if (!ParameterValidation.isNullOrEmpty(requiresRewrite)) {
resourceInvocation.setAttribute(WSRP_REQUIRES_REWRITE, requiresRewrite);
}
allParams.remove(WSRP_REQUIRES_REWRITE);
// End WSRP-specific parameters handling
resourceInvocation.setForm(allParams);
invocation = type.cast(resourceInvocation);
} else if (type.equals(EventInvocation.class)) {
invocation = type.cast(new EventInvocation(pic));
} else if (type.equals(RenderInvocation.class)) {
invocation = type.cast(new RenderInvocation(pic));
} else {
throw new AssertionError();
}
//
invocation.setRequest(servletRequest);
invocation.setResponse(prc.getResponse());
// Navigational state
invocation.setNavigationalState(navigationalState);
// Public navigational state
invocation.setPublicNavigationalState(this.getPublicParameters());
// WSRP-specific public navigational state handling needed when we have a URL coming from template
String navigationalValues = servletRequest.getParameter(WSRP_NAVIGATIONAL_VALUES);
if (!ParameterValidation.isNullOrEmpty(navigationalValues)) {
// add to the invocation attributes so that it can be retrieved and used by the WSRP component
invocation.setAttribute(WSRP_NAVIGATIONAL_VALUES, navigationalValues);
}
allParams.remove(WSRP_NAVIGATIONAL_VALUES);
// End WSRP-specific public navigational state handling
// Mode
invocation.setMode(Mode.create(getCurrentPortletMode().toString()));
// Window state
invocation.setWindowState(org.gatein.pc.api.WindowState.create(getCurrentWindowState().toString()));
StatefulPortletContext<C> preferencesPortletContext = getPortletContext();
if (preferencesPortletContext == null) {
return null;
}
// get the user profile cached in the prc during the start of the request
UserProfile userProfile = (UserProfile) prc.getAttribute(UserProfileLifecycle.USER_PROFILE_ATTRIBUTE_NAME);
// client context
AbstractClientContext clientContext;
Cookie[] cookies = servletRequest.getCookies();
if (cookies != null) {
clientContext = new AbstractClientContext(servletRequest, Arrays.asList(cookies));
} else {
clientContext = new AbstractClientContext(servletRequest);
}
invocation.setClientContext(clientContext);
// instance context
ExoPortletInstanceContext instanceContext;
// TODO: we should not be having these wsrp specific conditions through the code like
// this, it should either work the same was as normal portlets or abstracted out to another class.
if (ApplicationType.WSRP_PORTLET.equals(state.getApplicationType())) {
WSRP wsrp = (WSRP) preferencesPortletContext.getState();
AccessMode accessMode = AccessMode.CLONE_BEFORE_WRITE;
if (wsrp.getState() != null) {
StatefulPortletContext statefulPortletContext = StatefulPortletContext.create(
preferencesPortletContext.getId(), PortletStateType.OPAQUE, wsrp.getState());
invocation.setTarget(statefulPortletContext);
} else {
PortletContext portletContext = PortletContext.createPortletContext(preferencesPortletContext.getId());
invocation.setTarget(portletContext);
}
// if the portlet is a cloned one already, we can modify it directly instead of requesting a clone
if (wsrp.isCloned()) {
accessMode = AccessMode.READ_WRITE;
}
instanceContext = new ExoPortletInstanceContext(preferencesPortletContext.getId(), accessMode);
} else {
instanceContext = new ExoPortletInstanceContext(preferencesPortletContext.getId());
invocation.setTarget(preferencesPortletContext);
}
invocation.setInstanceContext(instanceContext);
invocation.setServerContext(new ExoServerContext(servletRequest, prc.getResponse()));
invocation.setUserContext(new ExoUserContext(servletRequest, userProfile));
invocation.setWindowContext(new AbstractWindowContext(storageName));
invocation.setPortalContext(PORTAL_CONTEXT);
invocation.setSecurityContext(new AbstractSecurityContext(servletRequest));
//
return invocation;
}
public void update(PropertyChange... changes) throws Exception {
PortletContext portletContext = getPortletContext();
//
PortletInvoker portletInvoker = getApplicationComponent(PortletInvoker.class);
// Get marshalled version
StatefulPortletContext<C> updatedCtx = (StatefulPortletContext<C>) portletInvoker
.setProperties(portletContext, changes);
//
C updateState = updatedCtx.getState();
// Now save it
update(updateState);
}
public PortletState<S> getState() {
return state;
}
public void setState(PortletState<S> state) {
if (state != null) {
try {
PortletInvoker portletInvoker = getApplicationComponent(PortletInvoker.class);
DataStorage dataStorage = getApplicationComponent(DataStorage.class);
String applicationId = dataStorage.getId(state.getApplicationState());
ModelAdapter<S, C> adapter = ModelAdapter.getAdapter(state.getApplicationType());
PortletContext producerOfferedPortletContext = adapter.getProducerOfferedPortletContext(applicationId);
org.gatein.pc.api.Portlet producedOfferedPortlet;
try {
producedOfferedPortlet = portletInvoker.getPortlet(producerOfferedPortletContext);
} catch (Exception e) {
// Whenever couldn't invoke the portlet object, set the request portlet to null for the error tobe
// properly handled and displayed when the portlet is rendered
producedOfferedPortlet = null;
log.error(e.getMessage(), e);
}
this.adapter = adapter;
this.producerOfferedPortletContext = producerOfferedPortletContext;
this.producedOfferedPortlet = producedOfferedPortlet;
this.applicationId = applicationId;
} catch (NoSuchDataException e) {
log.error(e.getMessage());
throw e;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
} else {
this.adapter = null;
this.producedOfferedPortlet = null;
this.producerOfferedPortletContext = null;
this.applicationId = null;
}
this.state = state;
}
/**
* Returns the state of the portlet as a set of preferences.
*
* @return the preferences of the portlet
* @throws Exception any exception
*/
public Portlet getPreferences() throws Exception {
WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
ExoContainer container = context.getApplication().getApplicationServiceContainer();
return adapter.getState(container, state.getApplicationState());
}
/**
* Returns the portlet context of the portlet.
*
* @return the portlet context
* @throws Exception any exception
*/
public StatefulPortletContext<C> getPortletContext() throws Exception {
WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
ExoContainer container = context.getApplication().getApplicationServiceContainer();
return adapter.getPortletContext(container, applicationId, state.getApplicationState());
}
/**
* Update the state of the portlet.
*
* @param updateState the state update
* @throws Exception any exception
*/
public void update(C updateState) throws Exception {
WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
ExoContainer container = context.getApplication().getApplicationServiceContainer();
state.setApplicationState(adapter.update(container, updateState, state.getApplicationState()));
setState(state);
}
/**
* Return modifed portlet stated (after portlet action invovation)
* @param modifiedContext
* @throws Exception
*/
public C getModifiedState(PortletContext modifiedContext) throws Exception {
return adapter.getStateFromModifiedContext(this.getPortletContext(), modifiedContext);
}
/**
* Return cloned portlet state (updated after portlet action invocation).
* This method is used in case WSRP
* @param clonedContext
* @throws Exception
*/
public C getClonedState(PortletContext clonedContext) throws Exception {
return adapter.getstateFromClonedContext(this.getPortletContext(), clonedContext);
}
/** This is used by the dashboard portlet and should not be used else where. It will be removed some day. */
private static final ThreadLocal<UIPortlet> currentPortlet = new ThreadLocal<UIPortlet>();
public static UIPortlet getCurrentUIPortlet() {
return currentPortlet.get();
}
/**
* Performs an invocation on this portlet.
*
* @param invocation the portlet invocation
* @return the portlet invocation response
* @throws PortletInvokerException any invoker exception
*/
public PortletInvocationResponse invoke(PortletInvocation invocation) throws PortletInvokerException {
PortletInvoker portletInvoker = getApplicationComponent(PortletInvoker.class);
currentPortlet.set(this);
try {
return portletInvoker.invoke(invocation);
} finally {
currentPortlet.set(null);
}
}
/**
* navigationalState - internal portlet container parameter (go with portlet url).
* called when navigation state updated
* @param navigationalState
*/
void setNavigationalState(StateString navigationalState) {
this.navigationalState = navigationalState;
}
/**
* configuredTitle - the localized title configured in portlet.xml.
* This value returned ans set after portlet container invocation.
* @param _configuredTitle - portlet title responsed from portlet container
*/
protected void setConfiguredTitle(String _configuredTitle) {
this.configuredTitle = _configuredTitle;
}
/**
* Returns the title showed on the InfoBar. The title is computed in following manner.
* <p/>
* 1. First, the method getTitle(), inherited from UIPortalComponent is called. The getTitle() returns what users set in the
* PortletSetting tab, the current method returns call result if it is not null.
* <p/>
* 2. configuredTitle, which is the localized value of javax.portlet.title is returned if it is not null.
* <p/>
* 3. If the method does not terminate at neither (1) nor (2), the configured display name is returned.
*
* @return
*/
public String getDisplayTitle() {
String displayedTitle = getTitle();
if (displayedTitle != null && displayedTitle.trim().length() > 0) {
return displayedTitle;
}
if (configuredTitle != null) {
return configuredTitle;
}
return getDisplayName();
}
/**
* Parsing response from portlet container. The response contains:<br/>
* html markup, portlet title, response properties:<br/>
* - JS resource dependency (defined in gatein-resources.xml)<br/>
* - html header<br/>
* - cookie<br/>
* - extra markup header<br/>
* If errors occur during portlet lifecycle processing. PortletExceptionHandleService is called.
* Add plugins to this service to customize portlet error handler
* @param pir - response object from portlet container
* @param context - request context
* @return markup to render on browser
* @see PortletExceptionHandleService
*/
public Text generateRenderMarkup(PortletInvocationResponse pir, WebuiRequestContext context) {
PortalRequestContext prcontext = (PortalRequestContext) context;
Text markup = null;
if (pir instanceof FragmentResponse) {
JavascriptManager jsMan = context.getJavascriptManager();
jsMan.loadScriptResource(ResourceScope.PORTLET, getApplicationId());
FragmentResponse fragmentResponse = (FragmentResponse) pir;
switch (fragmentResponse.getType()) {
case FragmentResponse.TYPE_CHARS:
markup = Text.create(fragmentResponse.getContent());
break;
case FragmentResponse.TYPE_BYTES:
markup = Text.create(fragmentResponse.getBytes(), Charset.forName("UTF-8"));
break;
case FragmentResponse.TYPE_EMPTY:
markup = Text.create("");
break;
}
setConfiguredTitle(fragmentResponse.getTitle());
// setup portlet properties
if (fragmentResponse.getProperties() != null) {
// setup transport headers
if (fragmentResponse.getProperties().getTransportHeaders() != null) {
MultiValuedPropertyMap<String> transportHeaders = fragmentResponse.getProperties().getTransportHeaders();
for (String key : transportHeaders.keySet()) {
if (JAVASCRIPT_DEPENDENCY.equals(key)) {
for (String value : transportHeaders.getValues(key)) {
jsMan.require(value);
}
} else {
for (String value : transportHeaders.getValues(key)) {
prcontext.getResponse().setHeader(key, value);
}
}
}
}
// setup up portlet cookies
if (fragmentResponse.getProperties().getCookies() != null) {
List<Cookie> cookies = fragmentResponse.getProperties().getCookies();
for (Cookie cookie : cookies) {
prcontext.getResponse().addCookie(cookie);
}
}
// setup markup headers
if (fragmentResponse.getProperties().getMarkupHeaders() != null) {
MultiValuedPropertyMap<Element> markupHeaders = fragmentResponse.getProperties().getMarkupHeaders();
List<Element> markupElements = markupHeaders.getValues(MimeResponse.MARKUP_HEAD_ELEMENT);
if (markupElements != null) {
for (Element element : markupElements) {
if (!context.useAjax() && "title".equals(element.getNodeName().toLowerCase())
&& element.getFirstChild() != null) {
String title = element.getFirstChild().getNodeValue();
prcontext.getRequest().setAttribute(PortalRequestContext.REQUEST_TITLE, title);
} else {
prcontext.addExtraMarkupHeader(element, getId());
}
}
}
}
}
} else {
PortletContainerException pcException;
if (pir instanceof ErrorResponse) {
ErrorResponse errorResponse = (ErrorResponse) pir;
pcException = new PortletContainerException(errorResponse.getMessage(), errorResponse.getCause());
} else {
pcException = new PortletContainerException("Unknown invocation response type [" + pir.getClass()
+ "]. Expected a FragmentResponse or an ErrorResponse");
}
//
PortletExceptionHandleService portletExceptionService = getApplicationComponent(PortletExceptionHandleService.class);
if (portletExceptionService != null) {
portletExceptionService.handle(pcException);
}
// Log the error
log.error("Portlet render threw an exception", pcException);
markup = Text.create(context.getApplicationResourceBundle().getString("UIPortlet.message.RuntimeError"));
}
return markup;
}
}