Java tutorial
/* * Copyright (c) 2012-2015, Andrea Funto'. All rights reserved. See LICENSE for details. */ package org.dihedron.strutlets; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.nio.file.Files; import java.security.Principal; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.ResourceBundle; import java.util.Set; import javax.portlet.ActionResponse; import javax.portlet.ClientDataRequest; import javax.portlet.Event; import javax.portlet.EventRequest; import javax.portlet.GenericPortlet; import javax.portlet.MimeResponse; import javax.portlet.PortalContext; import javax.portlet.PortletModeException; import javax.portlet.PortletPreferences; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.portlet.PortletURL; import javax.portlet.ReadOnlyException; import javax.portlet.RenderResponse; import javax.portlet.ResourceURL; import javax.portlet.StateAwareResponse; import javax.portlet.ValidatorException; import javax.portlet.WindowStateException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.xml.namespace.QName; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.RequestContext; import org.apache.commons.fileupload.disk.DiskFileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.portlet.PortletFileUpload; import org.dihedron.core.properties.Properties; import org.dihedron.core.regex.Regex; import org.dihedron.core.strings.Strings; import org.dihedron.strutlets.actions.PortletMode; import org.dihedron.strutlets.actions.WindowState; import org.dihedron.strutlets.containers.portlet.PortalServer; import org.dihedron.strutlets.containers.web.ApplicationServer; import org.dihedron.strutlets.exceptions.InvalidPhaseException; import org.dihedron.strutlets.exceptions.StrutletsException; import org.dihedron.strutlets.interceptors.Interceptor; import org.dihedron.strutlets.upload.FileUploadConfiguration; import org.dihedron.strutlets.upload.UploadedFile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This object provides mediated access to the underlying JSR-286 features, such * as session, parameters, remote user information and the like. This interface * is a superset of all available functionalities; other classes may provide a * restricted view base on the current phase, to help developers discover bugs at * compile time instead of having to catch exceptions at run time. * @author Andrea Funto' */ public class ActionContext { /** * The logger. */ private static final Logger logger = LoggerFactory.getLogger(ActionContext.class); /** * The default encoding for file names in multipart/form-data requests. */ private static final String DEFAULT_ENCODING = "UTF-8"; /** * The number of milliseconds in a second. */ private static final int MILLISECONDS_PER_SEC = 1000; /** * The 4 possible phases in the portlet lifecycle. * * @author Andrea Funto' */ public enum Phase { /** * The phase in which action processing occurs. */ ACTION(PortletRequest.ACTION_PHASE), /** * The phase in which the portlet handles events. */ EVENT(PortletRequest.EVENT_PHASE), /** * The phase in which the portlet is requested to repaint itself. */ RENDER(PortletRequest.RENDER_PHASE), /** * The phase in which the portlet is serving resources as is. */ RESOURCE(PortletRequest.RESOURCE_PHASE); /** * Returns the Strulets enumeraition value corresponding to the given * textual representation. * * @param value * a string representing the phase, e.g. "ACTION_PHASE". * @return * an enumeraion value if the input string represents a vaild phase. * @throws IllegalArgumentException * if the string does not represent a supported phase. */ public static Phase fromString(String value) { if (value != null) { for (Phase phase : Phase.values()) { if (value.equals(phase.toString())) { return phase; } } } throw new IllegalArgumentException("Invalid phase '" + value + "'."); } /** * Returns the standard (JSR-286) textual representation of the given phase. * * @see java.lang.Enum#toString() */ @Override public String toString() { return value; } /** * Constructor. * * @param value * the standard (JSR-286) name for the given phase. */ private Phase(String value) { this.value = value; } /** * The standard (JSR-286) name of the phase. */ private String value; }; /** * The scope for the attributes. * * @author Andrea Funto' */ public enum Scope { /** * Attributes at request level will be accessible to the portlet that set * them and to included JSPs and servlets until the next action request * comes. The data lifecycle encompasses event and resource serving * methods, up to the <em>next</em> action processing request, when they * will be reset. */ REQUEST(0x00), /** * Attributes set at <em>application</em> scope are accessible throughout the * application: all portlets, JSPs and servlets packaged in the same WAR * file will have access to these attributes on a per-user basis. JSPs * and servlets will have direct access to tese attributes through * <code>HttsSession</code> attributes. */ APPLICATION(PortletSession.APPLICATION_SCOPE), /** * Attributes set at <em>session</em> will be available to all resources * sharing the same window id, that is the very portlet that set them and * its included JSPs and servlets. JSPs and servlets will <em>not</em> * have direct access to the resource, because it will be stored in the * <code>HttpSession</code> object under a namespaced attribute key. * The fabricated attribute name will contain the window ID. */ PORTLET(PortletSession.PORTLET_SCOPE); /** * Returns the numeric value of the constant. * * @return * the numeric value of the constant. */ public int getValue() { return value; } /** * Constructor. * * @param value * the numeric value of the constant. */ private Scope(int value) { this.value = value; } /** * The numeric value of the constant. */ private int value; } /** * The key under which request-scoped attributes are stored in the portlet session. */ protected static final String REQUEST_SCOPED_ATTRIBUTES_KEY = "org.dihedron.strutlets.request_scoped_attributes"; protected static final String INTERCEPTOR_DATA_KEY = "org.dihedron.strutlets.interceptor_data"; /** * The per-thread instance. */ private static ThreadLocal<ActionContext> context = new ThreadLocal<ActionContext>() { @Override protected ActionContext initialValue() { logger.debug("creating action context instance for thread {}", Thread.currentThread().getId()); return new ActionContext(); } }; /** * The action controller portlet. */ private GenericPortlet portlet; /** * The portlet request (it might be an <code>ActionRequest</code>, a * <code>RenderRequest</code> or an <code>EventRequest</code>, depending on * the lifecycle and the phase). */ private PortletRequest request; /** * The portlet response (it might be an <code>ActionResponse</code>, a * <code>RenderResponse</code> or an <code>EventResponse</code>, depending on * the lifecycle and the phase). */ private PortletResponse response; /** * The actions' configuration; this map is read only and it's loaded at startup * if the URL of a properties file is provided in the portlet.xml. */ private Properties configuration; /** * A reference to the <code>ApplicationServer</code> plug-in; this is useful if * the portlets need to exploitm application.server specific APIs. */ private ApplicationServer server = null; /** * A reference to the <code> PortalServer</code> plug-in, if available; * this is useful if the portlets want to use portal-server-specific APIs. */ private PortalServer portal = null; /** * The encoding of uploaded file names. */ private String encoding = DEFAULT_ENCODING; /** * A map containing names and information about all the form fields in a * multipart/form-data request; if the form is not multipart, the form values * can be retrieved directly from the request in the ordinary way (see * {@link HttpServletRequest#getParameter(String)} for details); if the * request is multipart, then the standard says that parameters will all be * mixed up in the request, and the context will extract them and place them * in this map. Thus, when this map is null, the request is not multipart * and values will be picked from the request, when not null, all parameters * (be they form fields or uploaded files) will be available inside this map. */ private Map<String, FileItem> parts = null; /** * This boolean value is used to keep track of changes to the render parameters, * since a redirect can only be issued in action phase if no render parameters * were changed. This flag is updated if and only if the render parameters are * set through the <code>ActionContext</code>. */ private boolean renderParametersChanged = false; /** * Retrieves the per-thread instance. * * @return * the per-thread instance. */ private static ActionContext getContext() { return context.get(); } /** * Initialise the attributes map used to emulate the per-request attributes; * this map will simulate action-scoped request parameters, and will be populated * with attributes that must be visible to all the render, serve resource and * event handling requests coming after an action processing request. These * parameters will be reset by the <code>ActionController</code>as soon as a * new action processing request comes. * * @param response * the portlet response. * @param invocation * the optional <code>ActionInvocation</code> object, only available in the * context of an action or event processing, not in the render phase. * @throws StrutletsException */ static void bindContext(GenericPortlet portlet, PortletRequest request, PortletResponse response, Properties configuration, ApplicationServer server, PortalServer portal, FileUploadConfiguration uploadInfo) throws StrutletsException { logger.debug("initialising the action context for thread {}", Thread.currentThread().getId()); getContext().portlet = portlet; getContext().request = request; getContext().response = response; getContext().configuration = configuration; getContext().server = server; getContext().portal = portal; getContext().renderParametersChanged = false; // check if a map for REQUEST-scoped attributes is already available in // the PORTLET, if not instantiate it and load it into PORTLET scope PortletSession session = request.getPortletSession(); @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>) session.getAttribute(getRequestScopedAttributesKey(), PortletSession.PORTLET_SCOPE); if (map == null) { logger.trace("installing REQUEST scoped attributes map into PORTLET scope"); session.setAttribute(getRequestScopedAttributesKey(), new HashMap<String, Object>(), PortletSession.PORTLET_SCOPE); } // this might be a multipart/form-data request, in which case we enable // support for file uploads and read them from the input stream using // a tweaked version of Apache Commons FileUpload facilities (in order // to support file uploads in AJAX requests too!). if (request instanceof ClientDataRequest) { // this is where we try to retrieve all files (if there are any that were // uploaded) and store them as temporary files on disk; these objects will // be accessible as ordinary values under the "FORM" scope through a // custom "filename-to-file" map, which will be clened up when the context // is unbound String encoding = ((ClientDataRequest) request).getCharacterEncoding(); getContext().encoding = Strings.isValid(encoding) ? encoding : DEFAULT_ENCODING; logger.trace("request encoding is: '{}'", getContext().encoding); try { RequestContext context = new ClientDataRequestContext((ClientDataRequest) request); // check that we have a file upload request if (PortletFileUpload.isMultipartContent(context)) { getContext().parts = new HashMap<String, FileItem>(); logger.trace("handling multipart/form-data request"); // create a factory for disk-based file items DiskFileItemFactory factory = new DiskFileItemFactory(); // register a tracker to perform automatic file cleanup // FileCleaningTracker tracker = FileCleanerCleanup.getFileCleaningTracker(getContext().filter.getServletContext()); // factory.setFileCleaningTracker(tracker); // configure the repository (to ensure a secure temporary location // is used and the size of the ) factory.setRepository(uploadInfo.getRepository()); factory.setSizeThreshold(uploadInfo.getInMemorySizeThreshold()); // create a new file upload handler PortletFileUpload upload = new PortletFileUpload(factory); upload.setSizeMax(uploadInfo.getMaxUploadableTotalSize()); upload.setFileSizeMax(uploadInfo.getMaxUploadableFileSize()); // parse the request & process the uploaded items List<FileItem> items = upload.parseRequest(context); logger.trace("{} items in the multipart/form-data request", items.size()); for (FileItem item : items) { // parameters would be stored with their fully-qualified // name if we didn't remove the portlet namespace String fieldName = item.getFieldName().replaceFirst(getPortletNamespace(), ""); logger.trace("storing field '{}' (type: '{}') into parts map", fieldName, item.isFormField() ? "field" : "file"); getContext().parts.put(fieldName, item); } } else { logger.trace("handling plain form request"); } } catch (FileUploadException e) { logger.warn("error handling uploaded file", e); throw new StrutletsException("Error handling uploaded file", e); } } } /** * Returns a reference to the action controller; for private use only, do not * mess around with the action controller or you might end up with unpredictable * results. * * @return * a reference to the action controller. */ @Deprecated public static ActionController getActionController() { return (ActionController) getContext().portlet; } /** * Cleans up the internal status of the <code>ActionContextImpl</code> in order to * avoid memory leaks due to persisting portal objects stored in the per-thread * local storage; afterwards it removes the thread local entry altogether, so * the application server does not complain about left-over data in TLS when * re-deploying the portlet. */ static void unbindContext() { logger.debug("removing action context for thread {}", Thread.currentThread().getId()); getContext().request = null; getContext().response = null; getContext().portlet = null; getContext().configuration = null; getContext().server = null; getContext().portal = null; // remove all files if this is a multipart/form-data request, because // the file tracker does not seem to work as expected if (isMultiPartRequest()) { for (Entry<String, FileItem> entry : getContext().parts.entrySet()) { if (!entry.getValue().isFormField() && !entry.getValue().isInMemory()) { File file = ((DiskFileItem) entry.getValue()).getStoreLocation(); try { logger.trace("removing uploaded file '{}' from disk...", file.getAbsolutePath()); Files.delete(file.toPath()); logger.trace("... file deleted"); } catch (IOException e) { logger.trace("... error deleting file", e); } } } } getContext().parts = null; // if(getContext().parts != null) { // for(Entry<String, FileItem> entry : getContext().parts.entrySet()) { // FileItem part = entry.getValue(); // if(!part.isFormField() && !part.isInMemory()) { // logger.trace("releasing on-disk uploaded file '{}'", part.getFieldName()); // // } // } // } context.remove(); } /** * Returns a reference to the current portal-server-specific plug-in, if * available. If no plug-in was loaded, returns null. * * @return * a reference to the current portal-server-specific plug-in. */ public static PortalServer getPortalServer() { return getContext().portal; } /** * Returns a reference to the current application-server-specific plug-in, if * available. If no plug-in was loaded, returns null. * * @return * a reference to the current application-server-specific plug-in. */ public static ApplicationServer getApplicationServer() { return getContext().server; } /** * Returns the current phase of the action request lifecycle. * * @return * the current phase of the action request lifecycle. */ public static Phase getCurrentPhase() { String currentPhase = (String) getContext().request.getAttribute(PortletRequest.LIFECYCLE_PHASE); return Phase.fromString(currentPhase); } /** * Returns whether the portlet is currently in the action phase. * * @return * whether the portlet is currently in the action phase. */ public static boolean isActionPhase() { return getContext().request.getAttribute(PortletRequest.LIFECYCLE_PHASE) .equals(PortletRequest.ACTION_PHASE); } /** * Returns whether the portlet is currently in the event phase. * * @return * whether the portlet is currently in the event phase. */ public static boolean isEventPhase() { return getContext().request.getAttribute(PortletRequest.LIFECYCLE_PHASE).equals(PortletRequest.EVENT_PHASE); } /** * Returns whether the portlet is currently in the resource phase. * * @return * whether the portlet is currently in the resource phase. */ public static boolean isResourcePhase() { return getContext().request.getAttribute(PortletRequest.LIFECYCLE_PHASE) .equals(PortletRequest.RESOURCE_PHASE); } /** * Returns whether the portlet is currently in the render phase. * * @return * whether the portlet is currently in the render phase. */ public static boolean isRenderPhase() { return getContext().request.getAttribute(PortletRequest.LIFECYCLE_PHASE) .equals(PortletRequest.RENDER_PHASE); } /** * Returns the current portlet's name. * * @return * the current portlet's name. */ public static String getPortletName() { return getContext().portlet.getPortletName(); } /** * Returns the portlet namespace; this value can be prefixed to DOM elements * and Javascript functions. * * @return * the portlet namespace. */ public static String getPortletNamespace() { return getContext().response.getNamespace(); } /** * Returns the value of the given portlet's initialisation parameter. * * @param name * the name of the parameter. * @return * the value of the given portlet's initialisation parameter. */ public static String getPortletInitialisationParameter(String name) { return getContext().portlet.getInitParameter(name); } /** * Factory method: creates a new Action URL. NOTE: this method returns a * valid result only when called in the action and render phases, otherwise * it returns null. * * @return * returns a new Action URL if invoked in the action or render phase, returns * null otherwise. */ public static PortletURL createActionURL() { if (getContext().response instanceof MimeResponse) { return ((MimeResponse) getContext().response).createActionURL(); } return null; } /** * Factory method: creates a new Render URL. NOTE: this method returns a * valid result only when called in the action and render phases, otherwise * it returns null. * * @return * returns a new Render URL if invoked in the action or render phase, returns * null otherwise. */ public static PortletURL createRenderURL() { if (getContext().response instanceof MimeResponse) { return ((MimeResponse) getContext().response).createRenderURL(); } return null; } /** * Factory method: creates a new Resource URL. NOTE: this method returns a * valid result only when called in the action and render phases, otherwise * it returns null. * * @return * returns a new Resource URL if invoked in the action or render phase, * returns null otherwise. */ public static ResourceURL createResourceURL() { if (getContext().response instanceof MimeResponse) { return ((MimeResponse) getContext().response).createResourceURL(); } return null; } // TODO: get other stuff from portlet.xml and web.xml // PortletContext and PorteltConfig (see Ashish Sarin pages 119-120) /** * In case of an <code>EventRequest</code>, returns the name of the event. * * @return * the name of the event. * @throws InvalidPhaseException * if invoked out of the "event" phase. */ public static String getEventName() throws InvalidPhaseException { Event event = getEvent(); if (event != null) { return event.getName(); } return null; } /** * In case of an <code>EventRequest</code>, returns the <code>QName</code> * of the event. * * @return * the <code>QName</code> of the event. * @throws InvalidPhaseException * if invoked out of the "event" phase. */ public static QName getEventQName() throws InvalidPhaseException { Event event = getEvent(); if (event != null) { return event.getQName(); } return null; } /** * In case of an <code>EventRequest</code>, returns the serializable payload * of the event. * * @return * the serializable payload of the event. * @throws InvalidPhaseException * if invoked out of the "event" phase. */ public static Serializable getEventPayload() throws InvalidPhaseException { Event event = getEvent(); if (event != null) { return event.getValue(); } return null; } /** * Fires an event, for inter-portlet communication. * * @param name * the fully-qualified name of the event. * @param payload * the event payload, as a serialisable object. * @throws InvalidPhaseException * if the operation is attempted while in the render phase. */ public static void fireEvent(String name, Serializable payload) throws InvalidPhaseException { if (isActionPhase() || isEventPhase()) { ((StateAwareResponse) getContext().response).setEvent(name, payload); } else { logger.error("trying to fire an event in the render phase"); throw new InvalidPhaseException("Events cannot be fired in the render phase."); } } /** * Fires an event, for inter-portlet communication. * * @param name * the name of the event. * @param namespace * the event namespace. * @param payload * the event payload, as a serialisable object. * @throws InvalidPhaseException * if the operation is attempted while in the render phase. */ public static void fireEvent(String name, String namespace, Serializable payload) throws InvalidPhaseException { QName qname = new QName(namespace, name); fireEvent(qname, payload); } /** * Fires an event, for inter-portlet communication. * * @param qname * an object representing the fully-qualified name of the event. * @param payload * the event payload, as a serialisable object. * @throws InvalidPhaseException * if the operation is attempted while in the render phase. */ public static void fireEvent(QName qname, Serializable payload) throws InvalidPhaseException { if (isActionPhase() || isEventPhase()) { ((StateAwareResponse) getContext().response).setEvent(qname, payload); } else { logger.error("trying to fire an event in the render phase"); throw new InvalidPhaseException("Events cannot be fired in the render phase."); } } /** * Returns a string representing the authentication type. * * @return * a string representing the authentication type. */ public static String getAuthType() { return getContext().request.getAuthType(); } /** * Checks whether the client request came through a secured connection. * * @return * whether the client request came through a secured connection. */ public static boolean isSecure() { return getContext().request.isSecure(); } /** * Returns the name of the remote authenticated user. * * @return * the name of the remote authenticated user. */ public static String getRemoteUser() { return getContext().request.getRemoteUser(); } /** * Returns the user principal associated with the request. * * @return * the user principal. */ public static Principal getUserPrincipal() { return getContext().request.getUserPrincipal(); } /** * Checks whether the user has the given role. * * @param role * the name of the role * @return * whether the user has the given role. */ public static boolean isUserInRole(String role) { return getContext().request.isUserInRole(role); } /** * Returns the locale associated with the user's request. * * @return * the request locale. */ public static Locale getLocale() { return getContext().request.getLocale(); } /** * Returns an Enumeration of Locale objects indicating, in decreasing order * starting with the preferred locale in which the portal will accept content * for this request. The Locales may be based on the Accept-Language header of the client. * * @return * an Enumeration of Locales, in decreasing order, in which the portal will * accept content for this request */ public static Enumeration<Locale> getLocales() { return getContext().request.getLocales(); } /** * Returns the set of available information for the current user as per the * portlet's configuration in portlet.xml. * * In order to have user information available in the portlet, the portlet.xml * must include the following lines after all the portlets have been defined: * <pre> * <portlet-app ...> * <portlet> * <portlet-name>MyPortlet</portlet-name> * ... * </portlet> * ... * <user-attribute> * <description>User First Name</description> * <name>user.name.given</name> * </user-attribute> * <user-attribute> * <description>User Last Name</description> * <name>user.name.family</name> * </user-attribute> * </portlet-app> * </pre> * where {@code user.name.given} and {@code user.name.family} are two of the * possible values; the following is a pretty complete list of acceptable * values:<ul> * <li>user.bdate</li> * <li>user.gender</li> * <li>user.employer</li> * <li>user.department</li> * <li>user.jobtitle</li> * <li>user.name.prefix</li> * <li>user.name.given</li> * <li>user.name.family</li> * <li>user.name.middle</li> * <li>user.name.suffix</li> * <li>user.name.nickName</li> * <li>user.home-info.postal.name</li> * <li>user.home-info.postal.street</li> * <li>user.home-info.postal.city</li> * <li>user.home-info.postal.stateprov</li> * <li>user.home-info.postal.postalcode</li> * <li>user.home-info.postal.country</li> * <li>user.home-info.postal.organization</li> * <li>user.home-info.telecom.telephone.intcode</li> * <li>user.home-info.telecom.telephone.loccode</li> * <li>user.home-info.telecom.telephone.number</li> * <li>user.home-info.telecom.telephone.ext</li> * <li>user.home-info.telecom.telephone.comment</li> * <li>user.home-info.telecom.fax.intcode</li> * <li>user.home-info.telecom.fax.loccode</li> * <li>user.home-info.telecom.fax.number</li> * <li>user.home-info.telecom.fax.ext</li> * <li>user.home-info.telecom.fax.comment</li> * <li>user.home-info.telecom.mobile.intcode</li> * <li>user.home-info.telecom.mobile.loccode</li> * <li>user.home-info.telecom.mobile.number</li> * <li>user.home-info.telecom.mobile.ext</li> * <li>user.home-info.telecom.mobile.comment</li> * <li>user.home-info.telecom.pager.intcode</li> * <li>user.home-info.telecom.pager.loccode</li> * <li>user.home-info.telecom.pager.number</li> * <li>user.home-info.telecom.pager.ext</li> * <li>user.home-info.telecom.pager.comment</li> * <li>user.home-info.online.email</li> * <li>user.home-info.online.uri</li> * <li>user.business-info.postal.name</li> * <li>user.business-info.postal.street</li> * <li>user.business-info.postal.city</li> * <li>user.business-info.postal.stateprov</li> * <li>user.business-info.postal.postalcode</li> * <li>user.business-info.postal.country</li> * <li>user.business-info.postal.organization</li> * <li>user.business-info.telecom.telephone.intcode</li> * <li>user.business-info.telecom.telephone.loccode</li> * <li>user.business-info.telecom.telephone.number</li> * <li>user.business-info.telecom.telephone.ext</li> * <li>user.business-info.telecom.telephone.comment</li> * <li>user.business-info.telecom.fax.intcode</li> * <li>user.business-info.telecom.fax.loccode</li> * <li>user.business-info.telecom.fax.number</li> * <li>user.business-info.telecom.fax.ext</li> * <li>user.business-info.telecom.fax.comment</li> * <li>user.business-info.telecom.mobile.intcode</li> * <li>user.business-info.telecom.mobile.loccode</li> * <li>user.business-info.telecom.mobile.number</li> * <li>user.business-info.telecom.mobile.ext</li> * <li>user.business-info.telecom.mobile.comment</li> * <li>user.business-info.telecom.pager.intcode</li> * <li>user.business-info.telecom.pager.loccode</li> * <li>user.business-info.telecom.pager.number</li> * <li>user.business-info.telecom.pager.ext</li> * <li>user.business-info.telecom.pager.comment</li> * <li>user.business-info.online.email</li> * <li>user.business-info.online.uri</li> * </ul> * * @return * a map representing the user information available to the portlets in * the current application. */ @SuppressWarnings("unchecked") public static Map<String, Object> getUserInformation() { return (Map<String, Object>) getContext().request.getAttribute(PortletRequest.USER_INFO); } /** * Returns the current portal context, containing information about the * current portal server. * * @return * the portal context. */ public static PortalContext getPortalContext() { return getContext().request.getPortalContext(); } /** * Returns the current portlet mode. * * @return * the current portlet mode. */ public static PortletMode getPortletMode() { return PortletMode.fromString(getContext().request.getPortletMode().toString()); } /** * Sets the current portlet mode; it is preferable not to use this method * directly and let the framework set the portlet mode instead, by * specifying it in the action's results settings. * * @param mode * the new portlet mode. * @throws PortletModeException * if the new portlet mode is not supported by the current portal server * runtime environment. * @throws InvalidPhaseException * if the operation is attempted while in the render phase. */ @Deprecated public static void setPortletMode(PortletMode mode) throws PortletModeException, InvalidPhaseException { if (isActionPhase() || isEventPhase()) { if (getContext().request.isPortletModeAllowed(mode)) { logger.trace("changing portlet mode to '{}'", mode); ((StateAwareResponse) getContext().response).setPortletMode(mode); } else { logger.warn("unsupported portlet mode '{}'", mode); } } else { logger.error("trying to change portlet mode in the render phase"); throw new InvalidPhaseException("Portlet mode cannot be changed in the render phase."); } } /** * Returns the current portlet window state. * * @return * the current portlet window state. */ public static WindowState getWindowState() { return WindowState.fromString(getContext().request.getWindowState().toString()); } /** * Sets the current window state; it is preferable not to use this method * directly and let the framework set the portlet window state instead, by * specifying it in the action's results settings. * * @param state * the new window state. * @throws WindowStateException * if the new window state is not supported by the current portal server * runtime environment. * @throws InvalidPhaseException * if the operation is attempted in the render phase. */ @Deprecated public static void setWindowState(WindowState state) throws WindowStateException, InvalidPhaseException { if (isActionPhase() || isEventPhase()) { if (getContext().request.isWindowStateAllowed(state)) { logger.trace("changing window state to '{}'", state); ((StateAwareResponse) getContext().response).setWindowState(state); } else { logger.warn("unsupported window state '{}'", state); } } else { logger.error("trying to change window state in the render phase"); throw new InvalidPhaseException("Windows state cannot be changed in the render phase."); } } /** * Returns the portlet window ID. The portlet window ID is unique for this * portlet window and is constant for the lifetime of the portlet window. * This ID is the same that is used by the portlet container for scoping * the portlet-scope session attributes. * * @return * the portlet window ID. */ public static String getPortletWindowId() { if (getContext().request != null) { return getContext().request.getWindowID(); } return null; } /** * Returns the session ID indicated in the client request. This session ID * may not be a valid one, it may be an old one that has expired or has been * invalidated. If the client request did not specify a session ID, this * method returns null. * * @return * a String specifying the session ID, or null if the request did not * specify a session ID. * @see isRequestedSessionIdValid() */ public static String getRequestedSessionId() { return getContext().request.getRequestedSessionId(); } /** * Checks whether the requested session ID is still valid. * * @return * true if this request has an id for a valid session in the current * session context; false otherwise. * @see getRequestedSessionId() * @see getPortletSession() */ public static boolean isRequestedSessionIdValid() { return getContext().request.isRequestedSessionIdValid(); } /** * Returns whether the <code>PortletSession</code> is still valid. * * @return * whether the <code>PortletSession</code> is still valid. */ public static boolean isSessionValid() { PortletSession session = getContext().request.getPortletSession(); long elapsed = System.currentTimeMillis() - session.getLastAccessedTime(); return (elapsed < session.getMaxInactiveInterval() * MILLISECONDS_PER_SEC); } /** * Returns the number of seconds left before the session gets invalidated * by the container. * * @return * the number of seconds left before the session gets invalidated by the * container. */ public static long getSecondsToSessionInvalid() { PortletSession session = getContext().request.getPortletSession(); long elapsed = System.currentTimeMillis() - session.getLastAccessedTime(); return (long) ((elapsed - session.getMaxInactiveInterval() * MILLISECONDS_PER_SEC) / MILLISECONDS_PER_SEC); } /** * Returns the number of seconds since the last access to the session object. * * @return * the number of seconds since the last access to the session object. */ public static long getTimeOfLastAccessToSession() { return getContext().request.getPortletSession().getLastAccessedTime(); } /** * Returns the maximum amount of inactivity seconds before the session is * considered stale. * * @return * the maximum number of seconds before the session is considered stale. */ public static int getMaxInactiveSessionInterval() { return getContext().request.getPortletSession().getMaxInactiveInterval(); } /** * Sets the session timeout duration in seconds. * * @param time * the session timeout duration, in seconds. */ public static void setMaxInactiveSessionInterval(int time) { getContext().request.getPortletSession().setMaxInactiveInterval(time); } /** * Returns whether the request is a multipart/form-data request. * * @return * whether the request is a multipart/form-data request. */ public static boolean isMultiPartRequest() { return getContext().parts != null; } /** * Sets the title of the portlet; this method can only be invoked in the render * phase. * * @param title * the new title of the portlet. * @throws InvalidPhaseException * if the method is invoked out of the "render" phase. */ public static void setPortletTitle(String title) throws InvalidPhaseException { if (isRenderPhase() && getContext().response instanceof RenderResponse) { logger.trace("setting the portlet title to '{}'", title); ((RenderResponse) getContext().response).setTitle(title); } else { logger.error("cannot set the title out of the render phase"); throw new InvalidPhaseException("Cannot set the portlet title when not in render phase"); } } /** * Encodes the given URL; ths URL is not prefixed with the current context * path, and is therefore considered as absolute. An example of such URLs is * <code>/MyApplication/myServlet</code>. * * @param url * the absolute URL to be encoded. * @return * the URL, in encoded form. */ public static String encodeAbsoluteURL(String url) { String encoded = getContext().response.encodeURL(url); logger.trace("url '{}' encoded as '{}'", url, encoded); return encoded; } /** * Encodes the given URL; the URL is prefixed with the current context path, * and is therefore considered as relative to it. An example of such URLs is * <code>/css/myStyleSheet.css</code>. * * @param url * the relative URL to be encoded. * @return * the URL, in encoded form. */ public static String encodeRelativeURL(String url) { String unencoded = getContext().request.getContextPath() + url; String encoded = getContext().response.encodeURL(unencoded); logger.trace("url '{}' encoded as '{}'", unencoded, encoded); return encoded; } /** * Redirects to a different URL, with no referrer URL unless it is specified * in the URL itself. * * @param url * the URL to redirect the browser to (via a 302 HTTP status response). * @throws IOException * if the redirect operation fails. * @throws InvalidPhaseException * if the method is invoked out of the "action" phase. */ public static void sendRedirect(String url) throws IOException, InvalidPhaseException { if (isActionPhase() && getContext().response instanceof ActionResponse) { ((ActionResponse) getContext().response).sendRedirect(url); } else { logger.warn("trying to redirect while not in action phase"); throw new InvalidPhaseException("Cannot redirect browser when not in action phase"); } } /** * Redirects to a different URL, adding a referrer to provide a "back" address * to the destination page. * * @param url * the URL to redirect the browser to (via a 302 HTTP status response). * @param referrer * the referrer URL, to provide a "back" link. * @throws IOException * if the redirect operation fails. * @throws InvalidPhaseException * if the method is invoked out of the "action" phase. */ public static void sendRedirect(String url, String referrer) throws IOException, InvalidPhaseException { if (isActionPhase() && getContext().response instanceof ActionResponse) { ((ActionResponse) getContext().response).sendRedirect(url, referrer); } else { logger.warn("trying to redirect while not in action phase"); throw new InvalidPhaseException("Cannot redirect browser when not in action phase"); } } /** * Returns the resource bundle associated with the underlying portlet, for * the given locale. * * @param locale * the selected locale. * @return * the portlet's configured resource bundle. */ public static ResourceBundle getResouceBundle(Locale locale) { return getContext().portlet.getResourceBundle(locale); } /** * Returns the per-user portlet preferences. * * @return * the per-user portlet preferences. */ public static PortletPreferences getPortletPreferences() { return getContext().request.getPreferences(); } /** * Returns the names of all portlet preference values stored for the current * portlet. * * @return * the names of all portlet preference values stored for the current * portlet. */ public static List<String> getPortletPreferenceNames() { List<String> names = new ArrayList<String>(); Enumeration<String> enumeration = getContext().request.getPreferences().getNames(); while (enumeration.hasMoreElements()) { names.add(enumeration.nextElement()); } return names; } /** * Returns the map of all portlet preference keys and values (as a string * array) for the current portlet. * * @return * the map of all portlet preference keys and values (as a string array) * for the current portlet. */ public static Map<String, String[]> getPortletPreferenceMap() { return getContext().request.getPreferences().getMap(); } /** * Returns the per-user portlet preference value corresponding to the given * key, or null if no preference exists under the given key. * * @param key * the key of the portlet preference value. * @return * the value of the preference, or the default value if no valid value was * found. */ public static String getPortletPreferenceValue(String key) { return getPortletPreferenceValue(key, null); } /** * Returns the per-user portlet preference value corresponding to the given * key, or the given default value if no preference exists under the given key. * * @param key * the key of the portlet preference value. * @param defaultValue * the default value, if no valid prefrence value could be found under the * given key. * @return * the value of the preference, or the default value if no valid value was * found. */ public static String getPortletPreferenceValue(String key, String defaultValue) { PortletPreferences preferences = getPortletPreferences(); return preferences.getValue(key, defaultValue); } /** * Returns the per-user portlet preference values corresponding to the given * key, or null if no preference exists under the given key. * * @param key * the key of the portlet preference value set (as an array of strings). * @return * the values of the preference, or null if no valid values were found. */ public static String[] getPortletPreferenceValues(String key) { return getPortletPreferenceValues(key, null); } /** * Returns the per-user portlet preference value corresponding to the given * key, or the given default value if no preference exists under the given key. * * @param key * the key of the portlet preference value set (as a string array). * @param defaultValues * the default values, if no valid preference values could be found under * the given key. * @return * the values of the preference, or the default values if no valid values * were found. */ public static String[] getPortletPreferenceValues(String key, String[] defaultValues) { PortletPreferences preferences = getPortletPreferences(); return preferences.getValues(key, defaultValues); } /** * Sets a new value for a portlet preference. * * @param key * the name under which the preference is stored. * @param value * the value of the preference. * @throws StrutletsException * if the preference is read-only, it canot be validated or an I/O error * occurs writing it down to persistent store. */ public static void setPortletPreferenceValue(String key, String value) throws StrutletsException { if (Strings.isValid(key)) { PortletPreferences preferences = getPortletPreferences(); try { preferences.setValue(key, value); preferences.store(); } catch (ReadOnlyException e) { logger.error("invalid attempt to store read-only preference '" + key + "'", e); throw new StrutletsException("Invalid attempt to store read-only preference '" + key + "'", e); } catch (ValidatorException e) { logger.error("error validating preference '" + key + "', value '" + value + "'", e); throw new StrutletsException("Error validating preference '" + key + "', value '" + value + "'", e); } catch (IOException e) { logger.error("error storing preference '" + key + "', value '" + value + "'", e); throw new StrutletsException("Error storing preference '" + key + "', value '" + value + "'", e); } } } /** * Sets a new sets of values for a portlet preference. * * @param key * the name under which the preference is stored. * @param values * the values of the preference. * @throws StrutletsException * if the preference is read-only, it canot be validated or an I/O error * occurs writing it down to persistent store. */ public static void setPortletPreferenceValues(String key, String[] values) throws StrutletsException { if (Strings.isValid(key)) { PortletPreferences preferences = getPortletPreferences(); try { preferences.setValues(key, values); preferences.store(); } catch (ReadOnlyException e) { logger.error("invalid attempt to store read-only preference '" + key + "'", e); throw new StrutletsException("Invalid attempt to store read-only preference '" + key + "'", e); } catch (ValidatorException e) { logger.error("error validating preference '" + key + "'", e); throw new StrutletsException("Error validating preference '" + key + "'", e); } catch (IOException e) { logger.error("error storing preference '" + key + "'", e); throw new StrutletsException("Error storing preference '" + key + "'", e); } } } /** * Resets the value(s) of the given prefefence. * * @param key * the name of the preference. * @throws StrutletsException * if the preference is read-only. */ public static void resetPortletPreference(String key) throws StrutletsException { if (Strings.isValid(key)) { PortletPreferences preferences = getPortletPreferences(); try { preferences.reset(key); } catch (ReadOnlyException e) { logger.error("invalid attempt to reset read-only preference '" + key + "'", e); throw new StrutletsException("Invalid attempt to reset read-only preference '" + key + "'", e); } } } /** * Returns whether the given portlet preference is a read-only value. * * @param key * the name of the portlet preference. * @return * whether the given portlet preference is a read-only value. */ public static boolean isReadOnlyPortletPreference(String key) { return getPortletPreferences().isReadOnly(key); } /** * Looks for a parameter in any of the provided scopes, in the given order. * * @param key * the name of the parameter to look for. * @param scopes * the ordered list of scopes to look into. * @return * the value of the parameter, as soon as it is found; null otherwise. * @throws StrutletsException * if the scopes include any other value besides FORM, REQUEST, PORTLET, * APPLICATION and CONFIGURATION. */ public static Object findValueInScopes(String key, org.dihedron.strutlets.annotations.Scope... scopes) throws StrutletsException { // now, depending on the scope, try to locate the parameter in the appropriate context Object value = null; loop: for (org.dihedron.strutlets.annotations.Scope scope : scopes) { logger.trace("scanning input scope '{}' for parameter '{}'...", scope.name(), key); switch (scope) { case FORM: value = ActionContext.getParameterValues(key); if (value != null) { logger.trace("... value for '{}' found in FORM parameters: '{}'", key, value); break loop; } break; case REQUEST: value = ActionContext.getRequestAttribute(key); if (value != null) { logger.trace("... value for '{}' found in REQUEST attributes: '{}'", key, value); break loop; } break; case PORTLET: value = ActionContext.getPortletAttribute(key); if (value != null) { logger.trace("... value for '{}' found in PORTLET attributes: '{}'", key, value); break loop; } break; case APPLICATION: value = ActionContext.getApplicationAttribute(key); if (value != null) { logger.trace("... value for '{}' found in APPLICATION attributes: '{}'", key, value); break loop; } break; case CONFIGURATION: value = ActionContext.getConfigurationValue(key); if (value != null) { logger.trace("... value for '{}' found in CONFIGURATION properties: '{}'", key, value); break loop; } break; case HTTP: if (getContext().portal != null) { value = ActionContext.getHttpParameterValue(key); if (value != null) { logger.trace("... value for '{}' found in HTTP parameters: '{}'", key, value); break loop; } } break; default: logger.error("cannot extract an input value from the {} scope: this is probably a bug!", scope.name()); throw new StrutletsException("Cannot extract an input value from the " + scope.name() + " scope: this is probably a bug!"); } } return value; } /** * Looks for all the parameters matching the given pattern in the given set * of scopes, one at a time in the given order. * * @param pattern * a pattern (regular expression) to identify the parameters or attributes * to pick from the given scope. * @param scopes * the ordered list of scopes to look into. * @return * a map of parameter or attribute names and their corresponding values. * @throws StrutletsException * if the scopes include any other value besides FORM, REQUEST, PORTLET, * APPLICATION, CONFIGURATION and HTTP. */ public static Map<String, Object> matchValuesInScopes(String pattern, org.dihedron.strutlets.annotations.Scope... scopes) throws StrutletsException { // now, depending on the scope, try to locate the parameter in the appropriate context Map<String, Object> values = new HashMap<String, Object>(); for (org.dihedron.strutlets.annotations.Scope scope : scopes) { logger.trace("scanning input scope '{}' for parameters matching '{}'...", scope.name(), pattern); Map<String, Object> map = matchValuesInScope(pattern, scope); if (map != null && !map.isEmpty()) { for (String key : map.keySet()) { if (!values.containsKey(key)) { Object value = map.get(key); logger.trace("... adding parameter '{}' from scope {} (value '{}')", key, scope.name(), value); values.put(key, value); } } } } return values; } /** * Looks for all the parameters matching the given pattern in the given * scope. * * @param pattern * a pattern (regular expression) to identify the parameters or attributes * to pick from the given scope. * @param scope * the scope to look into. * @return * a map of parameter or attribute names and their corresponding values. * @throws StrutletsException * if the scopes include any other value besides FORM, REQUEST, PORTLET, * APPLICATION, CONFIGURATION and HTTP. */ @SuppressWarnings("unchecked") public static Map<String, Object> matchValuesInScope(String pattern, org.dihedron.strutlets.annotations.Scope scope) throws StrutletsException { Regex regex = new Regex(pattern); logger.trace("retrieving values from scope '{}'...", scope.name()); Map<?, ?> map = null; switch (scope) { case FORM: map = ActionContext.getParameters(); break; case REQUEST: map = ActionContext.getAttributes(Scope.REQUEST); break; case PORTLET: map = ActionContext.getAttributes(Scope.PORTLET); break; case APPLICATION: map = ActionContext.getAttributes(Scope.APPLICATION); break; case CONFIGURATION: map = ActionContext.getConfigurationEntries(); break; case HTTP: map = ActionContext.getHttpParametersMap(); break; default: logger.error("cannot extract an input value from the {} scope: this is probably a bug!", scope.name()); throw new StrutletsException( "Cannot extract an input value from the " + scope.name() + " scope: this is probably a bug!"); } Set<String> keys = (Set<String>) map.keySet(); Map<String, Object> values = new HashMap<String, Object>(); for (String key : keys) { if (regex.matches(key)) { Object value = map.get(key); if (value != null) { logger.trace("... parameter '{}' has value '{}' in scope {}", key, value, scope.name()); values.put(key, value); } } } return values; } /** * Stores a value into the given scope. * * @param key * the name of the parameter to store. * @param scope * the scope into which the value must be stored. * @param value * the value to be stored. * @throws StrutletsException * if the scope includes any other value besides RENDER, REQUEST, PORTLET, * and APPLICATION. */ public static void storeValueIntoScope(String key, org.dihedron.strutlets.annotations.Scope scope, Object value) throws StrutletsException { logger.trace("storing parameter '{}' into scope '{}', value '{}'...", key, scope.name(), value); switch (scope) { case RENDER: String string = value != null ? value.toString() : null; setRenderParameter(key, string); break; case REQUEST: setRequestAttribute(key, value); break; case PORTLET: setPortletAttribute(key, value); break; case APPLICATION: setApplicationAttribute(key, value); break; default: logger.error("cannot store an output value into {} scope: this is probably a bug!", scope.name()); throw new StrutletsException( "Cannot store an output value into the " + scope.name() + " scope: this is probably a bug!"); } } /** * Removes a value from the given scope (if the value exists). * * @param key * the name of the parameter to remove. * @param scope * the scope from which the value must be removed. * @throws StrutletsException * if the scope includes any other value besides RENDER, REQUEST, PORTLET, * and APPLICATION. */ public static void removeValueFromScope(String key, org.dihedron.strutlets.annotations.Scope scope) throws StrutletsException { logger.trace("removing parameter '{}' from scope '{}'...", key, scope.name()); switch (scope) { case RENDER: removeRenderParameter(key); break; case REQUEST: removeRequestAttribute(key); break; case PORTLET: removePortletAttribute(key); break; case APPLICATION: removeApplicationAttribute(key); break; default: logger.error("cannot remove a value from the {} scope: this is probably a bug!", scope.name()); throw new StrutletsException( "Cannot remove a value from the " + scope.name() + " scope: this is probably a bug!"); } } /** * Retrieves the list of all parameter names in the original HTTP request. * * @return * the list of all parameter names in the original HTTP request. */ public static List<String> getHttpParameterNames() { List<String> names = new ArrayList<String>(); if (getContext().portal != null) { HttpServletRequest servlet = getContext().portal.getHTTPServletRequest(getContext().request); @SuppressWarnings("unchecked") Enumeration<String> enumeration = (Enumeration<String>) servlet.getParameterNames(); while (enumeration.hasMoreElements()) { names.add(enumeration.nextElement()); } } return names; } /** * Returns the complete map of all HTTP paramaters along with their values. * * @return * the complete map of all HTTP paramaters along with their values. */ public static Map<String, String[]> getHttpParametersMap() { Map<String, String[]> parameters = new HashMap<String, String[]>(); if (getContext().portal != null) { HttpServletRequest servlet = getContext().portal.getHTTPServletRequest(getContext().request); for (Object entry : servlet.getParameterMap().entrySet()) { String key = (String) (((Entry<?, ?>) entry).getKey()); String[] values = (String[]) (((Entry<?, ?>) entry).getValue()); parameters.put(key, values); } } return parameters; } /** * Returns the value of the HTTP paramneter corresponfding to the given key. * * @param key * the name of the parameter. * @return * the parameter value, or null if no portal server plugin is available or * no parameter corresponds to the given key. */ public static String getHttpParameterValue(String key) { if (getContext().portal != null) { HttpServletRequest servlet = getContext().portal.getHTTPServletRequest(getContext().request); return servlet.getParameter(key); } return null; } /** * Returns the set of values for the given HTTP parameter key, or null. * * @param key * the name of the paramter. * @return * the set of values for the given HTTP parameter, or null if no portal * server plugin is active or no parameter exists with the given name. */ public static String[] getHttpParameterValues(String key) { if (getContext().portal != null) { HttpServletRequest servlet = getContext().portal.getHTTPServletRequest(getContext().request); return servlet.getParameterValues(key); } return null; } /** * Returns the list of all HTTP request attribute names. * * @return * the list of all HTTP request attribute names. */ public static List<String> getHttpAttributeNames() { List<String> names = new ArrayList<String>(); if (getContext().portal != null) { HttpServletRequest servlet = getContext().portal.getHTTPServletRequest(getContext().request); @SuppressWarnings("unchecked") Enumeration<String> enumeration = (Enumeration<String>) servlet.getAttributeNames(); while (enumeration.hasMoreElements()) { names.add(enumeration.nextElement()); } } return names; } /** * Returns the complete map of all HTTP attributes along with their values. * * @return * the complete map of all HTTP attributes along with their values. */ public static Map<String, Object> getHttpAttributesMap() { Map<String, Object> attributes = new HashMap<String, Object>(); if (getContext().portal != null) { HttpServletRequest servlet = getContext().portal.getHTTPServletRequest(getContext().request); for (String attribute : getHttpAttributeNames()) { Object value = servlet.getAttribute(attribute); attributes.put(attribute, value); } } return attributes; } /** * Returns the application-scoped attribute corresponding to the given key. * * @param key * the attribute key. * @return * the attribute value. */ public static Object getApplicationAttribute(String key) { PortletSession session = getContext().request.getPortletSession(); Object value = session.getAttribute(key, PortletSession.APPLICATION_SCOPE); logger.trace("application attribute '{}' has value '{}' (class '{}')", key, value, value != null ? value.getClass().getSimpleName() : Strings.NULL); return value; } /** * Adds or replaces an attribute in the map of attributes at application scope. * The attribute will be shared among all portlets, JSPs and servlets belonging * to the same application, on a per-user basis. * * @param key * the attribute key. * @param value * the attribute value. */ public static void setApplicationAttribute(String key, Object value) { PortletSession session = getContext().request.getPortletSession(); session.setAttribute(key, value, PortletSession.APPLICATION_SCOPE); logger.trace("application attribute '{}' set to value '{}' (class '{}')", key, value, value != null ? value.getClass().getSimpleName() : Strings.NULL); } /** * Removes the application-scoped attribute corresponding to the given key. * * @param key * the attribute key. * @return * the previous value of the attribute, or null if not set. */ public static Object removeApplicationAttribute(String key) { PortletSession session = getContext().request.getPortletSession(); Object value = session.getAttribute(key, PortletSession.APPLICATION_SCOPE); session.removeAttribute(key, PortletSession.APPLICATION_SCOPE); logger.trace("application attribute '{}' removed, previous value '{}' (class '{}')", key, value, value != null ? value.getClass().getSimpleName() : Strings.NULL); return value; } /** * Removes all application-level attributes from the session. */ public static void clearApplicationAttributes() { PortletSession session = getContext().request.getPortletSession(); Map<String, Object> attributes = session.getAttributeMap(PortletSession.APPLICATION_SCOPE); for (Entry<String, Object> attribute : attributes.entrySet()) { removeApplicationAttribute(attribute.getKey()); } logger.trace("all attributes at application scope cleared"); } /** * Returns the portlet-scoped attribute corresponding to the given key. * * @param key * the attribute key. * @return * the attribute value. */ public static Object getPortletAttribute(String key) { PortletSession session = getContext().request.getPortletSession(); Object value = session.getAttribute(key, PortletSession.PORTLET_SCOPE); logger.trace("portlet attribute '{}' has value '{}' (class '{}')", key, value, value != null ? value.getClass().getSimpleName() : Strings.NULL); return value; } /** * Adds or replaces an attribute in the map of attributes at portlet scope. * The attribute will be visible to the portlet itself (but not to other * instances of the same portlet), and to JSPs and servlets included by the * portlet, on a per-user basis. * * @param key * the attribute key. * @param value * the attribute value. */ public static void setPortletAttribute(String key, Object value) { PortletSession session = getContext().request.getPortletSession(); session.setAttribute(key, value, PortletSession.PORTLET_SCOPE); logger.trace("portlet attribute '{}' set to value '{}' (class '{}')", key, value, value != null ? value.getClass().getSimpleName() : Strings.NULL); } /** * Removes the portlet-scoped attribute corresponding to the given key. * * @param key * the attribute key. * @return * the previous value of the attribute, or null if not set. */ public static Object removePortletAttribute(String key) { PortletSession session = getContext().request.getPortletSession(); Object value = session.getAttribute(key, PortletSession.PORTLET_SCOPE); session.removeAttribute(key, PortletSession.PORTLET_SCOPE); logger.trace("portlet attribute '{}' removed, previous value '{}' (class '{}')", key, value, value != null ? value.getClass().getSimpleName() : Strings.NULL); return value; } /** * Removes all portlet-level attributes from the session. */ public static void clearPortletAttributes() { PortletSession session = getContext().request.getPortletSession(); Map<String, Object> attributes = session.getAttributeMap(PortletSession.PORTLET_SCOPE); for (Entry<String, Object> attribute : attributes.entrySet()) { removePortletAttribute(attribute.getKey()); } logger.trace("all attributes at portlet scope cleared"); } /** * Returns the value of the request-scoped attribute. * * @param key * the attribute key. * @return * the value of the request-scoped attribute, or null if not set. */ public static Object getRequestAttribute(String key) { @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>) getPortletAttribute(getRequestScopedAttributesKey()); Object value = map.get(key); logger.trace("request attribute '{}' has value '{}' (class '{}')", key, value, value != null ? value.getClass().getSimpleName() : Strings.NULL); return value; } /** * Adds or replaces an attribute in the map of attributes at request scope. * The attribute will be available to all following render requests until a * new action request comes to reset them. * * @param key * the attribute key. * @param value * the attribute value. */ public static void setRequestAttribute(String key, Object value) { @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>) getPortletAttribute(getRequestScopedAttributesKey()); map.put(key, value); logger.trace("request attribute '{}' set to value '{}' (class '{}')", key, value, value != null ? value.getClass().getSimpleName() : Strings.NULL); } /** * Removes the request-scoped attribute corresponding to the given key. * * @param key * the attribute key. * @return * the previous value of the attribute, or null if not set. */ public static Object removeRequestAttribute(String key) { @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>) getPortletAttribute(getRequestScopedAttributesKey()); Object value = map.get(key); map.remove(key); logger.trace("request attribute '{}' removed, previous value '{}' (class '{}')", key, value, value != null ? value.getClass().getSimpleName() : Strings.NULL); return value; } /** * Removes all request-level attributes from the session. */ public static void clearRequestAttributes() { logger.trace("clearing request attributes"); PortletSession session = getContext().request.getPortletSession(); @SuppressWarnings("unchecked") Map<String, Object> attributes = (Map<String, Object>) session .getAttribute(getRequestScopedAttributesKey()); attributes.clear(); logger.trace("all attributes at request scope cleared"); } /** * Returns the value of the given attribute in the proper application-, * session- or portlet-level map, depending on the scope. * * @param key * the attribute key. * @param scope * the requested scope. * @return * the requested attribute value, or null if not found. */ public static Object getAttribute(String key, Scope scope) { switch (scope) { case REQUEST: return getRequestAttribute(key); case PORTLET: return getPortletAttribute(key); case APPLICATION: return getApplicationAttribute(key); } return null; } /** * Returns the proper map of attributes at application-, session- or portlet- * level, depending on the requested scope. * * @param scope * the requested scope. * @return * the <em>immutable</em> map of attributes at the requested scope. */ @SuppressWarnings("unchecked") public static Map<String, Object> getAttributes(Scope scope) { Map<String, Object> map = null; if (getContext().request != null) { PortletSession session = getContext().request.getPortletSession(); switch (scope) { case APPLICATION: logger.trace("getting application attributes map"); map = session.getAttributeMap(PortletSession.APPLICATION_SCOPE); break; case PORTLET: logger.trace("getting portlet attributes map"); map = session.getAttributeMap(PortletSession.PORTLET_SCOPE); break; case REQUEST: logger.trace("getting request attributes map"); map = (Map<String, Object>) getPortletAttribute(getRequestScopedAttributesKey()); break; } } return map; } /** * Stores the given attribute in the proper map, depending on the requested * scope. * * @param key * the attribute key. * @param value * the attribute value. * @param scope * the requested scope. */ public static void setAttribute(String key, Object value, Scope scope) { switch (scope) { case REQUEST: setRequestAttribute(key, value); break; case PORTLET: setPortletAttribute(key, value); break; case APPLICATION: setApplicationAttribute(key, value); break; } } /** * Merges all the entries in the given map into the appropriate attributes * map; depending on the scope, the destination map will be the set of * attributes contained in the <code>PortletSession</code> or in the * <code>PortletRequest</code>, with the following semantics:<ul> * <li>in the case of <em>application</em> scope, the attributes are stored * in the attributes map at the <code>PortletSession</code>'s * <code>APPLICATION_SCOPE</code>; these attributes will be visible throughout * the application to all portlets, JSPs and servlets on a per-user basis</li> * <li>in the case of <em>portlet</em> scope, the attributes are stored * in the attributes map at the <code>PortletSession</code>'s * <code>PORTLET_SCOPE</code>; these attributes will be visible to the portlet * itself (but not to other instances of the same portlet), and to JSPs and * servlets included by the portlet, on a per-user basis</li> * <li>in the case of <em>request</em> scope, the attributes will be stored * and made available to all following render requests until a new action * request comes to reset them; this involves a bit of management by the * <code>ActionController</code>, which has to reset action scoped request * attributes when a new action comes</li>. * </ul>. * <em>NOTE</em>: Liferay 6.x and many other portlet containers do not support * action-request-scoped attributes, so instead of making request attributes * available to all the following render requests, they make these attributes * available only to the one render request that immediately follows, or to none * at all. The expected behaviour has been simulated in this method by putting * these request-scoped parameters in a reserved and dedicated area in the * <code>PortletSession</code>; if you want to leverage the container's * native behaviour, use the deprecated setActionScopedAttributes() instead * and remember to enable the <em>actionScopedRequestParmeters</em> runtime * option. * * @param attributes * a map of attributes to be set at application level. * @param scope * the scope at which the attributes should be set. */ public static void setAttributes(Map<String, Object> attributes, Scope scope) { for (Entry<String, Object> attribute : attributes.entrySet()) { setAttribute(attribute.getKey(), attribute.getValue(), scope); } } /** * Removes the give attribute from the proper map, depending on the requested * scope. * * @param key * the attribute key. * @param scope * the requested scope. * @return * the previous value of the attribute, or null if not found. */ public static Object removeAttribute(String key, Scope scope) { switch (scope) { case REQUEST: return removeRequestAttribute(key); case PORTLET: return removePortletAttribute(key); case APPLICATION: return removeApplicationAttribute(key); } return null; } /** * Removes all attributes from the map at the requested scope. * * @param scope * the requested scope. */ public static void clearAttributes(Scope scope) { switch (scope) { case REQUEST: clearRequestAttributes(); break; case PORTLET: clearPortletAttributes(); break; case APPLICATION: clearApplicationAttributes(); break; } } /** * Returns the map of all parameters set in the client request. * * @return * the map of input parameters. */ public static Map<String, String[]> getParameters() { if (getContext().request != null) { return getContext().request.getParameterMap(); } return null; } /** * Returns the names of all request parameters. * * @return * the names of all request parameters. */ public static Set<String> getParameterNames() { Set<String> set = new HashSet<String>(); Enumeration<String> e = getContext().request.getParameterNames(); while (e.hasMoreElements()) { set.add(e.nextElement()); } return set; } /** * Returns the set of values associated with the given parameter key. * * @param key * the name of the parameter. * @return * the array of parameter values. */ public static Object getParameterValues(String key) { if (isMultiPartRequest()) { logger.trace("looking for parameter named '{}' in multipart/form-data", key); FileItem item = getContext().parts.get(key); if (item != null) { if (item.isFormField()) { return new String[] { item.getString() }; } else { return new UploadedFile(item); } } } else { logger.trace("looking for parameter named '{}' in plain form", key); return getContext().request.getParameterValues(key); } return null; } /** * Returns only the first of the set of values associated with the given * parameter key. * * @param key * the name of the parameter. * @return * the first value of the array, or null if not found. */ public static Object getFirstParameterValue(String key) { if (isMultiPartRequest()) { FileItem item = getContext().parts.get(key); if (item != null) { if (item.isFormField()) { return item.getString(); } else { return new UploadedFile(item); } } } else { return getContext().request.getParameter(key); } return null; } /** * Returns the map of all public parameters set in the client request. * * @return * the map of input public parameters. */ public static Map<String, String[]> getPublicParameters() { if (getContext().request != null) { return getContext().request.getPublicParameterMap(); } return null; } /** * Returns the names of all public request parameters. * * @return * the names of all request public parameters. */ public static Set<String> getPublicParameterNames() { if (getContext().request != null) { return getContext().request.getPublicParameterMap().keySet(); } return null; } /** * Returns the set of values associated with the given public parameter key. * * @param key * the name of the public parameter. * @return * the array of parameter values. */ public static String[] getPublicParameterValues(String key) { if (getContext().request != null) { return getContext().request.getPublicParameterMap().get(key); } return null; } /** * Returns only the first of the set of values associated with the given * public parameter key. * * @param key * the name of the public parameter. * @return * the first value of the array, or null if not found. */ public static String getFirstPublicParameterValue(String key) { if (getContext().request != null) { String[] values = getContext().request.getPublicParameterMap().get(key); if (values != null && values.length > 0) { return values[0]; } } return null; } /** * Returns the map of all private parameters set in the client request. * * @return * the map of input private parameters. */ public static Map<String, String[]> getPrivateParameters() { if (getContext().request != null) { return getContext().request.getPrivateParameterMap(); } return null; } /** * Returns the names of all private request parameters. * * @return * the names of all request private parameters. */ public static Set<String> getPrivateParameterNames() { if (getContext().request != null) { return getContext().request.getPrivateParameterMap().keySet(); } return null; } /** * Returns the set of values associated with the given private parameter key. * * @param key * the name of the private parameter. * @return * the array of parameter values. */ public static String[] getPrivateParameterValues(String key) { if (getContext().request != null) { return getContext().request.getPrivateParameterMap().get(key); } return null; } /** * Returns only the first of the set of values associated with the given * private parameter key. * * @param key * the name of the private parameter. * @return * the first value of the array, or null if not found. */ public static String getFirstPrivateParameterValue(String key) { if (getContext().request != null) { String[] values = getContext().request.getPrivateParameterMap().get(key); if (values != null && values.length > 0) { return values[0]; } } return null; } /** * Returns the map of currently set render parameters. * * @return * a map of render parameters names an values, or null if unsupported by * the current type of request/response. */ public static Map<String, String[]> getRenderParameterMap() { Map<String, String[]> parameters = null; if (getContext().response instanceof StateAwareResponse) { parameters = ((StateAwareResponse) getContext().response).getRenderParameterMap(); } else if (getContext().request instanceof PortletRequest) { logger.trace("retrieving the render parameter map in the render phase..."); parameters = new HashMap<String, String[]>(); PortletRequest request = (PortletRequest) getContext().request; Enumeration<String> names = request.getParameterNames(); while (names.hasMoreElements()) { String name = names.nextElement(); String[] values = request.getParameterValues(name); logger.trace("... parameter '{}' has value '{}'", name, values); parameters.put(name, values); } } return parameters; } /** * Replaces the render parameters map with the one supplied. * * @param parameters * the new render parameters map. * @throws InvalidPhaseException * if invoked out of the action or event phase. */ public static void setRenderParameterMap(Map<String, String[]> parameters) throws InvalidPhaseException { if (getContext().response instanceof StateAwareResponse) { ((StateAwareResponse) getContext().response).setRenderParameters(parameters); getContext().renderParametersChanged = true; } else { logger.error("trying to set the render parameters map in render phase"); throw new InvalidPhaseException("Render parameters cannot be set in the render phase"); } } /** * Clears the render parameter map. * * @throws InvalidPhaseException * if invoked out of the action or event phase. */ public static void clearRenderParameterMap() throws InvalidPhaseException { setRenderParameterMap(new HashMap<String, String[]>()); } /** * Sets a render parameter. The parameter value(s) must all be string(s). * * @param key * the name of the parameter. * @param values * the parameter value(s). * @throws InvalidPhaseException * if invoked outside of the action or event phase. */ public static void setRenderParameter(String key, String... values) throws InvalidPhaseException { if (Strings.isValid(key) && values != null && (isActionPhase() || isEventPhase()) && getContext().response instanceof StateAwareResponse) { getContext().renderParametersChanged = true; logger.trace("setting render parameter '{}'...", key); if (values.length == 1) { logger.trace(" ... value is '{}'", values[0]); ((StateAwareResponse) getContext().response).setRenderParameter(key, values[0]); } else { ((StateAwareResponse) getContext().response).setRenderParameter(key, values); for (String value : values) { logger.trace(" ... value is '{}'", value); } } } else { logger.error("trying to set render parameter '{}' in render phase", key); throw new InvalidPhaseException("Render parameters cannot be set in the render phase"); } } /** * Removes a render parameter from the render parameters map. * * @param key * the key of the parameter to remove. * @throws InvalidPhaseException * if invoked outside of the action or event phase. */ public static void removeRenderParameter(String key) throws InvalidPhaseException { if (Strings.isValid(key) && (isActionPhase() || isEventPhase()) && getContext().response instanceof StateAwareResponse) { getContext().renderParametersChanged = true; logger.trace("removing render parameter '{}'...", key); Map<String, String[]> parameters = getRenderParameterMap(); parameters.remove(key); setRenderParameterMap(parameters); } else { logger.error("trying to remove render parameter '{}' in render phase", key); throw new InvalidPhaseException("Render parameters cannot be removed in the render phase"); } } /** * Returns the names of all request attributes; these are the attributes normally * available through the request, they cannot be set by portlets and are provided * by the portal instead. * * @return * a list of attribute names. */ public static List<String> getAttributeNames() { Enumeration<String> enumeration = getContext().request.getAttributeNames(); List<String> names = new ArrayList<String>(); while (enumeration.hasMoreElements()) { names.add(enumeration.nextElement()); } return names; } /** * Returns the value of the request attribute corresponding to the given name. * This attribute is not to be confused with those set in the variuos scopes, * and is a value provided by the portal server. * * @param key * the name of the request attribute. * @return * the request attribute value. */ public static Object getAttribute(String key) { return getContext().request.getAttribute(key); } /** * Returns the values in the actions configuration file as a map of string * keys and values. * * @return * the values in the actions configuration file as a map of string keys * and values. */ public static Map<String, String> getConfigurationEntries() { Map<String, String> entries = new HashMap<String, String>(); if (getContext().configuration != null) { for (Entry<String, String> entry : getContext().configuration.entrySet()) { entries.put(entry.getKey(), entry.getValue()); } } return entries; } /** * Returns the list of all configuration keys in the actions configuration * properties file, if available, or an emty list otherwise. * * @return * the list of all configuration keys in the actions configuration * properties file, if available, or an emty list otherwise. */ public static List<String> getConfigurationKeys() { List<String> keys = new ArrayList<String>(); if (getContext().configuration != null) { keys.addAll(getContext().configuration.keySet()); } return keys; } /** * Returns the value in the actions configuration properties file corresponding * to the given key, if available; null otherwise. * * @param key * the property key. * @return * the value in the actions configuration properties file corresponding * to the given key, if available; null otherwise. */ public static String getConfigurationValue(String key) { if (getContext().configuration != null) { return getContext().configuration.get(key); } return null; } /** * Returns an array containing all of the Cookie properties. This method * returns null if no cookies exist. * * @return * the array of cookie properties, or null if no cookies exist. */ public static Cookie[] getCookies() { if (getContext().request != null) { return getContext().request.getCookies(); } return null; } /** * Adds a cookie to the client. * * @param cookie * the cookie to be added to the client. */ public static void setCookie(Cookie cookie) { if (getContext().response != null) { getContext().response.addProperty(cookie); } } /** * Returns the underlying portlet request object. * * @return * the underlying portlet request object. */ @Deprecated public static PortletRequest getPortletRequest() { return getContext().request; } /** * Returns the underlying portlet response object. * * @return * the underlying portlet response object. */ @Deprecated public static PortletResponse getPortletResponse() { return getContext().response; } /** * Returns the original HTTP request object. * * @return * the original HTTP request object. */ @Deprecated public static HttpServletRequest getHttpServletRequest() { if (getContext().portal != null) { return getContext().portal.getHTTPServletRequest(getContext().request); } return null; } // /** // * Returns the underlying HTTP server request object, which is common to all // * portlets on the same page. Note that portlet-specific parameters in the // * URL are encoded with a portlet-specific namespace. // * // * @return // * the underlying HTTP server request object. // */ // public static HttpServletRequest getHttpServletRequest() { // Enumeration<String> names = getContext().request.getAttributeNames(); // while(names.hasMoreElements()) { // String name = names.nextElement(); // Object value = getContext().request.getAttribute(name); // logger.trace("request attribute '{}' = '{}'", name, value); // } // // return (HttpServletRequest)getContext().request.getAttribute("javax.servlet.request"); // } /** * Returns the underlying portlet session object. * * @return * the underlying portlet session object. */ @Deprecated public static PortletSession getPortletSession() { return getContext().request.getPortletSession(); } /** * Returns the event, if this invocation was due to an inter-portlet communication * even being fired ("event phase"). * * @return * the event object. * @throws InvalidPhaseException */ @Deprecated public static Event getEvent() throws InvalidPhaseException { if (isEventPhase()) { return ((EventRequest) getContext().request).getEvent(); } else { logger.error("trying to get event out of event phase"); throw new InvalidPhaseException("Events are not available out of event phase"); } } /** * Sets interceptor-specific data into the action context; this information * is available through different calls and can be used to keep track of * system status, such as number of calls for target, or number of accesses * by the same user etc. This method should only be used by interceptors, * and the associated data should not be tampered with, to avoid unpredictable * behaviour. * * @param interceptorId * the namepaced id of the interceptor this data belongs to (see * {@link Interceptor#getId()} for details). * @param data * the data object. */ public static void setInterceptorData(String interceptorId, Object data) { @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>) getPortletAttribute(INTERCEPTOR_DATA_KEY); if (map == null) { map = Collections.synchronizedMap(new HashMap<String, Object>()); setPortletAttribute(INTERCEPTOR_DATA_KEY, map); } map.put(interceptorId, data); } /** * Retrieves interceptor-specific data stored by the given interceptor. This * method should only be used by interceptors, and the associated data should * not be tampered with, to avoid unpredictable behaviour. * * @param interceptorId * the namespaced id of the interceptor owning the stored data. * @return * the data, or null if none found. */ public static Object getInterceptorData(String interceptorId) { @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>) getPortletAttribute(INTERCEPTOR_DATA_KEY); Object data = null; if (map != null) { data = map.get(interceptorId); } return data; } /** * Returns the data stored by the given interceptor. This method should only * be used by interceptors, and the associated data should not be tampered * with, to avoid unpredictable behaviour. * * @param interceptorId * the namespaced id of the interceptor requesting the data. * @param clazz * the type of the data to be retrieved, so it can be automatically cast. * @return * the data, already cast to the given type, or null if nothing found. */ public static <T> T getInterceptorData(String interceptorId, Class<? extends T> clazz) { Object data = getInterceptorData(interceptorId); return data != null ? clazz.cast(data) : null; } /** * This method returns a portlet-specific key for request-scoped attributes * for the current portlet. * * @return * a portlet-specific key for request-scoped attributes. */ public static String getRequestScopedAttributesKey() { return getRequestScopedAttributesKey(getPortletName()); } /** * This method returns the portlet-specific key for request-scoped attributes * for the given portlet name. * * @param portletName * the name of the portlet whose request-scoped attributes key is being * asked for. * @return * the portlet-specific key for request-scoped attributes. */ public static String getRequestScopedAttributesKey(String portletName) { return ActionContext.REQUEST_SCOPED_ATTRIBUTES_KEY + "[" + portletName.toUpperCase() + ":" + ActionContext.getPortletWindowId() + "]"; } /** * This method checks if during the invocation the render parameters have been * changed, e g. by setting their values. This method is reliable if and only * if the only way to set the render parameters is through the appropriate * <code>ActionContext</code> method: if you use the portlet response method * directly the framework has no way of detecting changes to the render * parameters. * Tracking this information is relevant because you cannot redirect to another * page (in the action phase) if any render parameter has been changed; if * you help the framework keep track of what you do with the parameters, it * might help you spot errors by giving a warning instead of simply letting * the portal fail with an illegal state exception. * * @return * whether the render parameters have been set during this invocation. */ public static boolean hasChangedRenderParameters() { return getContext().renderParametersChanged; } /** * Protected constructor, so this object cannot be instantiated by anyone * except extending classes, which are supposed to provide a phase-related * filter on available portlat functionalities. */ protected ActionContext() { } }