Moves plugins
git-svn-id: https://svn.apache.org/repos/asf/struts/archive/trunk@1672653 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/plugins/struts2-portlet-plugin/pom.xml b/plugins/struts2-portlet-plugin/pom.xml
new file mode 100644
index 0000000..b9435cf
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/pom.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.struts</groupId>
+ <artifactId>struts2-plugins</artifactId>
+ <version>2.3.1-SNAPSHOT</version>
+ </parent>
+ <groupId>org.apache.struts</groupId>
+ <artifactId>struts2-portlet-plugin</artifactId>
+ <packaging>jar</packaging>
+ <name>Struts 2 Portlet Plugin</name>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/struts/struts2/trunk/plugins/portlet/</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/struts/struts2/trunk/plugins/portlet/</developerConnection>
+ <url>http://svn.apache.org/viewcvs.cgi/struts/struts2/trunk/plugins/portlet/</url>
+ </scm>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>struts2-junit-plugin</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>jsp-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
+
+ <!-- Velocity -->
+ <dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity-tools</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <!-- Portlet -->
+ <dependency>
+ <groupId>portlet-api</groupId>
+ <artifactId>portlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>mockobjects</groupId>
+ <artifactId>mockobjects-jdk1.3-j2ee1.3</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>jmock</groupId>
+ <artifactId>jmock</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>jmock</groupId>
+ <artifactId>jmock-cglib</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>mockobjects</groupId>
+ <artifactId>mockobjects-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- Mocks for unit testing (by Spring) -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <version>2.5.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webmvc-portlet</artifactId>
+ <version>2.5.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ <version>2.5.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-fileupload</groupId>
+ <artifactId>commons-fileupload</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java
new file mode 100644
index 0000000..1ffb9ec
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java
@@ -0,0 +1,186 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.components;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.inject.Inject;
+import org.apache.commons.lang.StringUtils;
+import org.apache.struts2.StrutsException;
+import org.apache.struts2.dispatcher.mapper.ActionMapper;
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.apache.struts2.portlet.util.PortletUrlHelper;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Implementation of the {@link UrlRenderer} interface that renders URLs for portlet environments.
+ *
+ * @see UrlRenderer
+ *
+ */
+public class PortletUrlRenderer implements UrlRenderer {
+
+ /**
+ * The servlet renderer used when not executing in a portlet context.
+ */
+ private UrlRenderer servletRenderer = null;
+
+ public PortletUrlRenderer() {
+ this.servletRenderer = new ServletUrlRenderer();
+ }
+
+ @Inject
+ public void setActionMapper(ActionMapper actionMapper) {
+ servletRenderer.setActionMapper(actionMapper);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void renderUrl(Writer writer, UrlProvider urlComponent) {
+ if(PortletActionContext.getPortletContext() == null || "none".equalsIgnoreCase(urlComponent.getPortletUrlType())) {
+ servletRenderer.renderUrl(writer, urlComponent);
+ }
+ else {
+ String action = null;
+ if(urlComponent.getAction() != null) {
+ action = urlComponent.findString(urlComponent.getAction());
+ }
+ String scheme = urlComponent.getHttpServletRequest().getScheme();
+
+ if (urlComponent.getScheme() != null) {
+ scheme = urlComponent.getScheme();
+ }
+
+ String result;
+ urlComponent.setNamespace(urlComponent.determineNamespace(urlComponent.getNamespace(), urlComponent.getStack(), urlComponent.getHttpServletRequest()));
+ if (onlyActionSpecified(urlComponent)) {
+ result = PortletUrlHelper.buildUrl(action, urlComponent.getNamespace(), urlComponent.getMethod(), urlComponent.getParameters(), urlComponent.getPortletUrlType(),
+ urlComponent.getPortletMode(), urlComponent.getWindowState());
+ } else if(onlyValueSpecified(urlComponent)){
+ result = PortletUrlHelper.buildResourceUrl(urlComponent.getValue(), urlComponent.getParameters());
+ }
+ else {
+ result = createDefaultUrl(urlComponent);
+ }
+ String anchor = urlComponent.getAnchor();
+ if (StringUtils.isNotEmpty(anchor)) {
+ result += '#' + urlComponent.findString(anchor);
+ }
+
+ String var = urlComponent.getVar();
+
+ if (var != null) {
+ urlComponent.putInContext(result);
+
+ // add to the request and page scopes as well
+ urlComponent.getHttpServletRequest().setAttribute(var, result);
+ } else {
+ try {
+ writer.write(result);
+ } catch (IOException e) {
+ throw new StrutsException("IOError: " + e.getMessage(), e);
+ }
+ }
+ }
+ }
+
+ private String createDefaultUrl(UrlProvider urlComponent) {
+ String result;
+ ActionInvocation ai = (ActionInvocation)urlComponent.getStack().getContext().get(
+ ActionContext.ACTION_INVOCATION);
+ String action = ai.getProxy().getActionName();
+ result = PortletUrlHelper.buildUrl(action, urlComponent.getNamespace(), urlComponent.getMethod(), urlComponent.getParameters(),
+ urlComponent.getPortletUrlType(), urlComponent.getPortletMode(), urlComponent.getWindowState());
+ return result;
+ }
+
+ private boolean onlyValueSpecified(UrlProvider urlComponent) {
+ return urlComponent.getValue() != null && urlComponent.getAction() == null;
+ }
+
+ private boolean onlyActionSpecified(UrlProvider urlComponent) {
+ return urlComponent.getValue() == null && urlComponent.getAction() != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void renderFormUrl(Form formComponent) {
+ if(PortletActionContext.getPortletContext() == null) {
+ servletRenderer.renderFormUrl(formComponent);
+ }
+ else {
+ String namespace = formComponent.determineNamespace(formComponent.namespace, formComponent.getStack(),
+ formComponent.request);
+ String action = null;
+ if (formComponent.action != null) {
+ action = formComponent.findString(formComponent.action);
+ }
+ else {
+ ActionInvocation ai = (ActionInvocation) formComponent.getStack().getContext().get(ActionContext.ACTION_INVOCATION);
+ action = ai.getProxy().getActionName();
+ }
+ String type = "action";
+ if (StringUtils.isNotEmpty(formComponent.method)) {
+ if ("GET".equalsIgnoreCase(formComponent.method.trim())) {
+ type = "render";
+ }
+ }
+ if (action != null) {
+ String result = PortletUrlHelper.buildUrl(action, namespace, null,
+ formComponent.getParameters(), type, formComponent.portletMode, formComponent.windowState);
+ formComponent.addParameter("action", result);
+
+
+ // name/id: cut out anything between / and . should be the id and
+ // name
+ String id = formComponent.getId();
+ if (id == null) {
+ int slash = action.lastIndexOf('/');
+ int dot = action.indexOf('.', slash);
+ if (dot != -1) {
+ id = action.substring(slash + 1, dot);
+ } else {
+ id = action.substring(slash + 1);
+ }
+ formComponent.addParameter("id", formComponent.escape(id));
+ }
+ }
+ }
+
+ }
+
+ public void beforeRenderUrl(UrlProvider urlComponent) {
+ if(PortletActionContext.getPortletContext() == null) {
+ servletRenderer.beforeRenderUrl(urlComponent);
+ }
+ }
+
+ public void setServletRenderer(UrlRenderer nonPortletRenderer) {
+ this.servletRenderer = nonPortletRenderer;
+
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java
new file mode 100644
index 0000000..be2daff
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletActionConstants.java
@@ -0,0 +1,140 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.portlet.dispatcher.DispatcherServlet;
+
+/**
+ * Interface defining some constants used in the Struts portlet implementation
+ *
+ */
+public interface PortletActionConstants {
+ /**
+ * Default action name to use when no default action has been configured in the portlet
+ * init parameters.
+ */
+ String DEFAULT_ACTION_NAME = "default";
+
+ /**
+ * Action name parameter name
+ */
+ String ACTION_PARAM = "struts.portlet.action";
+
+ /**
+ * Key for parameter holding the last executed portlet mode.
+ */
+ String MODE_PARAM = "struts.portlet.mode";
+
+ /**
+ * Key used for looking up and storing the portlet phase
+ */
+ String PHASE = "struts.portlet.phase";
+
+ /**
+ * Constant used for the render phase (
+ * {@link javax.portlet.Portlet#render(javax.portlet.RenderRequest, javax.portlet.RenderResponse)})
+ */
+ Integer RENDER_PHASE = new Integer(1);
+
+ /**
+ * Constant used for the event phase (
+ * {@link javax.portlet.Portlet#processAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse)})
+ */
+ Integer EVENT_PHASE = new Integer(2);
+
+ /**
+ * Key used for looking up and storing the
+ * {@link javax.portlet.PortletRequest}
+ */
+ String REQUEST = "struts.portlet.request";
+
+ /**
+ * Key used for looking up and storing the
+ * {@link javax.portlet.PortletResponse}
+ */
+ String RESPONSE = "struts.portlet.response";
+
+ /**
+ * Key used for looking up and storing the action that was invoked in the event phase.
+ */
+ String EVENT_ACTION = "struts.portlet.eventAction";
+
+ /**
+ * Key used for looking up and storing the
+ * {@link javax.portlet.PortletConfig}
+ */
+ String PORTLET_CONFIG = "struts.portlet.config";
+
+ /**
+ * Name of the action used as error handler
+ */
+ String ERROR_ACTION = "errorHandler";
+
+ /**
+ * Key for the portlet namespace stored in the
+ * {@link org.apache.struts2.portlet.context.PortletActionContext}.
+ */
+ String PORTLET_NAMESPACE = "struts.portlet.portletNamespace";
+
+ /**
+ * Key for the mode-to-namespace map stored in the
+ * {@link org.apache.struts2.portlet.context.PortletActionContext}.
+ */
+ String MODE_NAMESPACE_MAP = "struts.portlet.modeNamespaceMap";
+
+ /**
+ * Key for the default action name for the portlet, stored in the
+ * {@link org.apache.struts2.portlet.context.PortletActionContext}.
+ */
+ String DEFAULT_ACTION_FOR_MODE = "struts.portlet.defaultActionForMode";
+
+ /**
+ * Key for request attribute indicating if the action has been reset.
+ */
+ String ACTION_RESET = "struts.portlet.actionReset";
+
+ /**
+ * Key for session attribute indicating the location of the render direct action.
+ */
+ String RENDER_DIRECT_LOCATION = "struts.portlet.renderDirectLocation";
+
+ /**
+ * Key for the dispatch instruction for the {@link DispatcherServlet}
+ */
+ String DISPATCH_TO = "struts.portlet.dispatchTo";
+
+ /**
+ * Session key where the value stack from the event phase is stored.
+ */
+ String STACK_FROM_EVENT_PHASE = "struts.portlet.valueStackFromEventPhase";
+
+ /**
+ * Default name of dispatcher servlet in web.xml
+ */
+ String DEFAULT_DISPATCHER_SERVLET_NAME = "Struts2PortletDispatcherServlet";
+
+ /**
+ * Key for the action mapping in the context
+ */
+ String ACTION_MAPPING = ServletActionContext.ACTION_MAPPING;
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletApplicationMap.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletApplicationMap.java
new file mode 100644
index 0000000..436c2ae
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletApplicationMap.java
@@ -0,0 +1,207 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet;
+
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.portlet.PortletContext;
+
+/**
+ * Portlet specific {@link java.util.Map} implementation representing the
+ * {@link javax.portlet.PortletContext} of a Portlet.
+ *
+ */
+public class PortletApplicationMap extends AbstractMap implements Serializable {
+
+ private static final long serialVersionUID = 2296107511063504414L;
+
+ private PortletContext context;
+
+ private Set<Object> entries;
+
+ /**
+ * Creates a new map object given the {@link PortletContext}.
+ *
+ * @param ctx The portlet context.
+ */
+ public PortletApplicationMap(PortletContext ctx) {
+ this.context = ctx;
+ }
+
+ /**
+ * Removes all entries from the Map and removes all attributes from the
+ * portlet context.
+ */
+ public void clear() {
+ entries = null;
+
+ Enumeration e = context.getAttributeNames();
+
+ while (e.hasMoreElements()) {
+ context.removeAttribute(e.nextElement().toString());
+ }
+ }
+
+ /**
+ * Creates a Set of all portlet context attributes as well as context init
+ * parameters.
+ *
+ * @return a Set of all portlet context attributes as well as context init
+ * parameters.
+ */
+ public Set entrySet() {
+ if (entries == null) {
+ entries = new HashSet<Object>();
+
+ // Add portlet context attributes
+ Enumeration enumeration = context.getAttributeNames();
+
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement().toString();
+ final Object value = context.getAttribute(key);
+ entries.add(new Map.Entry() {
+ public boolean equals(Object obj) {
+ Map.Entry entry = (Map.Entry) obj;
+
+ return ((key == null) ? (entry.getKey() == null) : key
+ .equals(entry.getKey()))
+ && ((value == null) ? (entry.getValue() == null)
+ : value.equals(entry.getValue()));
+ }
+
+ public int hashCode() {
+ return ((key == null) ? 0 : key.hashCode())
+ ^ ((value == null) ? 0 : value.hashCode());
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object obj) {
+ context.setAttribute(key.toString(), obj);
+
+ return value;
+ }
+ });
+ }
+
+ // Add portlet context init params
+ enumeration = context.getInitParameterNames();
+
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement().toString();
+ final Object value = context.getInitParameter(key);
+ entries.add(new Map.Entry() {
+ public boolean equals(Object obj) {
+ Map.Entry entry = (Map.Entry) obj;
+
+ return ((key == null) ? (entry.getKey() == null) : key
+ .equals(entry.getKey()))
+ && ((value == null) ? (entry.getValue() == null)
+ : value.equals(entry.getValue()));
+ }
+
+ public int hashCode() {
+ return ((key == null) ? 0 : key.hashCode())
+ ^ ((value == null) ? 0 : value.hashCode());
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object obj) {
+ context.setAttribute(key.toString(), obj);
+
+ return value;
+ }
+ });
+ }
+ }
+
+ return entries;
+ }
+
+ /**
+ * Returns the portlet context attribute or init parameter based on the
+ * given key. If the entry is not found, <tt>null</tt> is returned.
+ *
+ * @param key
+ * the entry key.
+ * @return the portlet context attribute or init parameter or <tt>null</tt>
+ * if the entry is not found.
+ */
+ public Object get(Object key) {
+ // Try context attributes first, then init params
+ // This gives the proper shadowing effects
+ String keyString = key.toString();
+ Object value = context.getAttribute(keyString);
+
+ return (value == null) ? context.getInitParameter(keyString) : value;
+ }
+
+ /**
+ * Sets a portlet context attribute given a attribute name and value.
+ *
+ * @param key
+ * the name of the attribute.
+ * @param value
+ * the value to set.
+ * @return the attribute that was just set.
+ */
+ public Object put(Object key, Object value) {
+ entries = null;
+ context.setAttribute(key.toString(), value);
+
+ return get(key);
+ }
+
+ /**
+ * Removes the specified portlet context attribute.
+ *
+ * @param key
+ * the attribute to remove.
+ * @return the entry that was just removed.
+ */
+ public Object remove(Object key) {
+ entries = null;
+
+ Object value = get(key);
+ context.removeAttribute(key.toString());
+
+ return value;
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletRequestMap.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletRequestMap.java
new file mode 100644
index 0000000..1ff6acb
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletRequestMap.java
@@ -0,0 +1,158 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet;
+
+import java.util.AbstractMap;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.portlet.PortletRequest;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * A simple implementation of the {@link java.util.Map} interface to handle a collection of request attributes.
+ *
+ */
+public class PortletRequestMap extends AbstractMap {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PortletRequestMap.class);
+
+ private Set<Object> entries = null;
+ private PortletRequest request = null;
+
+ /**
+ * Saves the request to use as the backing for getting and setting values
+ *
+ * @param request the portlet request.
+ */
+ public PortletRequestMap(PortletRequest request) {
+ this.request = request;
+ }
+
+ /**
+ * Removes all attributes from the request as well as clears entries in this
+ * map.
+ */
+ public void clear() {
+ entries = null;
+ Enumeration keys = request.getAttributeNames();
+
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ request.removeAttribute(key);
+ }
+ }
+
+ /**
+ * Returns a Set of attributes from the portlet request.
+ *
+ * @return a Set of attributes from the portlet request.
+ */
+ public Set entrySet() {
+ if (entries == null) {
+ entries = new HashSet<Object>();
+
+ Enumeration enumeration = request.getAttributeNames();
+
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement().toString();
+ final Object value = request.getAttribute(key);
+ entries.add(new Entry() {
+ public boolean equals(Object obj) {
+ Entry entry = (Entry) obj;
+
+ return ((key == null) ? (entry.getKey() == null) : key
+ .equals(entry.getKey()))
+ && ((value == null) ? (entry.getValue() == null)
+ : value.equals(entry.getValue()));
+ }
+
+ public int hashCode() {
+ return ((key == null) ? 0 : key.hashCode())
+ ^ ((value == null) ? 0 : value.hashCode());
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object obj) {
+ request.setAttribute(key, obj);
+
+ return value;
+ }
+ });
+ }
+ }
+
+ return entries;
+ }
+
+ /**
+ * Returns the request attribute associated with the given key or
+ * <tt>null</tt> if it doesn't exist.
+ *
+ * @param key the name of the request attribute.
+ * @return the request attribute or <tt>null</tt> if it doesn't exist.
+ */
+ public Object get(Object key) {
+ return request.getAttribute(key.toString());
+ }
+
+ /**
+ * Saves an attribute in the request.
+ *
+ * @param key the name of the request attribute.
+ * @param value the value to set.
+ * @return the object that was just set.
+ */
+ public Object put(Object key, Object value) {
+ entries = null;
+ request.setAttribute(key.toString(), value);
+
+ return get(key);
+ }
+
+ /**
+ * Removes the specified request attribute.
+ *
+ * @param key the name of the attribute to remove.
+ * @return the value that was removed or <tt>null</tt> if the value was
+ * not found (and hence, not removed).
+ */
+ public Object remove(Object key) {
+ entries = null;
+
+ Object value = get(key);
+ request.removeAttribute(key.toString());
+
+ return value;
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletSessionMap.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletSessionMap.java
new file mode 100644
index 0000000..c11d7fe
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/PortletSessionMap.java
@@ -0,0 +1,163 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet;
+
+import java.util.AbstractMap;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletSession;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * A simple implementation of the {@link java.util.Map} interface to handle a collection of portlet session
+ * attributes. The {@link #entrySet()} method enumerates over all session attributes and creates a Set of entries.
+ * Note, this will occur lazily - only when the entry set is asked for.
+ *
+ */
+public class PortletSessionMap extends AbstractMap {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PortletSessionMap.class);
+
+ private PortletSession session = null;
+ private Set<Object> entries = null;
+
+ /**
+ * Creates a new session map given a portlet request.
+ *
+ * @param request the portlet request object.
+ */
+ public PortletSessionMap(PortletRequest request) {
+ this.session = request.getPortletSession();
+ }
+
+ /**
+ * @see java.util.Map#entrySet()
+ */
+ public Set entrySet() {
+ synchronized (session) {
+ if (entries == null) {
+ entries = new HashSet<Object>();
+
+ Enumeration enumeration = session.getAttributeNames();
+
+ while (enumeration.hasMoreElements()) {
+ final String key = enumeration.nextElement().toString();
+ final Object value = session.getAttribute(key);
+ entries.add(new Map.Entry() {
+ public boolean equals(Object obj) {
+ Map.Entry entry = (Map.Entry) obj;
+
+ return ((key == null) ? (entry.getKey() == null)
+ : key.equals(entry.getKey()))
+ && ((value == null) ? (entry.getValue() == null)
+ : value.equals(entry.getValue()));
+ }
+
+ public int hashCode() {
+ return ((key == null) ? 0 : key.hashCode())
+ ^ ((value == null) ? 0 : value.hashCode());
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object obj) {
+ session.setAttribute(key, obj);
+
+ return value;
+ }
+ });
+ }
+ }
+ }
+
+ return entries;
+ }
+
+ /**
+ * Returns the session attribute associated with the given key or
+ * <tt>null</tt> if it doesn't exist.
+ *
+ * @param key the name of the session attribute.
+ * @return the session attribute or <tt>null</tt> if it doesn't exist.
+ */
+ public Object get(Object key) {
+ synchronized (session) {
+ return session.getAttribute(key.toString());
+ }
+ }
+
+ /**
+ * Saves an attribute in the session.
+ *
+ * @param key the name of the session attribute.
+ * @param value the value to set.
+ * @return the object that was just set.
+ */
+ public Object put(Object key, Object value) {
+ synchronized (session) {
+ entries = null;
+ session.setAttribute(key.toString(), value);
+
+ return get(key);
+ }
+ }
+
+ /**
+ * @see java.util.Map#clear()
+ */
+ public void clear() {
+ synchronized (session) {
+ entries = null;
+ session.invalidate();
+ }
+ }
+
+ /**
+ * Removes the specified session attribute.
+ *
+ * @param key the name of the attribute to remove.
+ * @return the value that was removed or <tt>null</tt> if the value was
+ * not found (and hence, not removed).
+ */
+ public Object remove(Object key) {
+ synchronized (session) {
+ entries = null;
+
+ Object value = get(key);
+ session.removeAttribute(key.toString());
+
+ return value;
+ }
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/context/PortletActionContext.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/context/PortletActionContext.java
new file mode 100644
index 0000000..edb9e31
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/context/PortletActionContext.java
@@ -0,0 +1,241 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.context;
+
+import java.util.Map;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletResponse;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.portlet.PortletActionConstants;
+
+import com.opensymphony.xwork2.ActionContext;
+
+
+/**
+ * PortletActionContext. ActionContext thread local for the portlet environment.
+ *
+ * @version $Revision$ $Date$
+ */
+public class PortletActionContext implements PortletActionConstants {
+
+ /**
+ * Get the PortletConfig of the portlet that is executing.
+ *
+ * @return The PortletConfig of the executing portlet.
+ */
+ public static PortletConfig getPortletConfig() {
+ return (PortletConfig) getContext().get(PORTLET_CONFIG);
+ }
+
+ /**
+ * Get the RenderRequest. Can only be invoked in the render phase.
+ *
+ * @return The current RenderRequest.
+ * @throws IllegalStateException If the method is invoked in the wrong phase.
+ */
+ public static RenderRequest getRenderRequest() {
+ if (!isRender()) {
+ throw new IllegalStateException(
+ "RenderRequest cannot be obtained in event phase");
+ }
+ return (RenderRequest) getContext().get(REQUEST);
+ }
+
+ /**
+ * Get the RenderResponse. Can only be invoked in the render phase.
+ *
+ * @return The current RenderResponse.
+ * @throws IllegalStateException If the method is invoked in the wrong phase.
+ */
+ public static RenderResponse getRenderResponse() {
+ if (!isRender()) {
+ throw new IllegalStateException(
+ "RenderResponse cannot be obtained in event phase");
+ }
+ return (RenderResponse) getContext().get(RESPONSE);
+ }
+
+ /**
+ * Get the ActionRequest. Can only be invoked in the event phase.
+ *
+ * @return The current ActionRequest.
+ * @throws IllegalStateException If the method is invoked in the wrong phase.
+ */
+ public static ActionRequest getActionRequest() {
+ if (!isEvent()) {
+ throw new IllegalStateException(
+ "ActionRequest cannot be obtained in render phase");
+ }
+ return (ActionRequest) getContext().get(REQUEST);
+ }
+
+ /**
+ * Get the ActionRequest. Can only be invoked in the event phase.
+ *
+ * @return The current ActionRequest.
+ * @throws IllegalStateException If the method is invoked in the wrong phase.
+ */
+ public static ActionResponse getActionResponse() {
+ if (!isEvent()) {
+ throw new IllegalStateException(
+ "ActionResponse cannot be obtained in render phase");
+ }
+ return (ActionResponse) getContext().get(RESPONSE);
+ }
+
+ /**
+ * Get the action namespace of the portlet. Used to organize actions for multiple portlets in
+ * the same portlet application.
+ *
+ * @return The portlet namespace as defined in <code>portlet.xml</code> and <code>struts.xml</code>
+ */
+ public static String getPortletNamespace() {
+ return (String)getContext().get(PORTLET_NAMESPACE);
+ }
+
+ /**
+ * Get the current PortletRequest.
+ *
+ * @return The current PortletRequest.
+ */
+ public static PortletRequest getRequest() {
+ return (PortletRequest) getContext().get(REQUEST);
+ }
+
+ /**
+ * Convenience setter for the portlet request.
+ * @param request
+ */
+ public static void setRequest(PortletRequest request) {
+ getContext().put(REQUEST, request);
+ }
+
+ /**
+ * Get the current PortletResponse
+ *
+ * @return The current PortletResponse.
+ */
+ public static PortletResponse getResponse() {
+ return (PortletResponse) getContext().get(RESPONSE);
+ }
+
+ /**
+ * Convenience setter for the portlet response.
+ * @param response
+ */
+ public static void setResponse(PortletResponse response) {
+ getContext().put(RESPONSE, response);
+ }
+
+ /**
+ * Get the phase that the portlet is executing in.
+ *
+ * @return {@link PortletActionConstants#RENDER_PHASE} in render phase, and
+ * {@link PortletActionConstants#EVENT_PHASE} in the event phase.
+ */
+ public static Integer getPhase() {
+ return (Integer) getContext().get(PHASE);
+ }
+
+ /**
+ * @return <code>true</code> if the Portlet is executing in render phase.
+ */
+ public static boolean isRender() {
+ return PortletActionConstants.RENDER_PHASE.equals(getPhase());
+ }
+
+ /**
+ * @return <code>true</code> if the Portlet is executing in the event phase.
+ */
+ public static boolean isEvent() {
+ return PortletActionConstants.EVENT_PHASE.equals(getPhase());
+ }
+
+ /**
+ * @return The current ActionContext.
+ */
+ private static ActionContext getContext() {
+ return ActionContext.getContext();
+ }
+
+ /**
+ * Check to see if the current request is a portlet request.
+ *
+ * @return <code>true</code> if the current request is a portlet request.
+ */
+ public static boolean isPortletRequest() {
+ return getRequest() != null;
+ }
+
+ /**
+ * Get the default action mapping for the current mode.
+ *
+ * @return The default action mapping for the current portlet mode.
+ */
+ public static ActionMapping getDefaultActionForMode() {
+ return (ActionMapping)getContext().get(DEFAULT_ACTION_FOR_MODE);
+ }
+
+ /**
+ * Get the namespace to mode mappings.
+ *
+ * @return The map of the namespaces for each mode.
+ */
+ public static Map getModeNamespaceMap() {
+ return (Map)getContext().get(MODE_NAMESPACE_MAP);
+ }
+
+ /**
+ * Get the portlet context.
+ * @return The portlet context.
+ */
+ public static PortletContext getPortletContext() {
+ return (PortletContext)getContext().get(StrutsStatics.STRUTS_PORTLET_CONTEXT);
+ }
+
+ /**
+ * Convenience setter for the portlet context.
+ * @param context
+ */
+ public static void setPortletContext(PortletContext context) {
+ getContext().put(StrutsStatics.STRUTS_PORTLET_CONTEXT, context);
+ }
+
+ /**
+ * Gets the action mapping for this context
+ *
+ * @return The action mapping
+ */
+ public static ActionMapping getActionMapping() {
+ return (ActionMapping) getContext().get(ACTION_MAPPING);
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/context/PreparatorServlet.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/context/PreparatorServlet.java
new file mode 100644
index 0000000..a181ef6
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/context/PreparatorServlet.java
@@ -0,0 +1,52 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.context;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+import org.apache.struts2.StrutsStatics;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * @deprecated
+ *
+ * This servlet has been deprecated. Do not use it (WW-2101)
+ *
+ */
+public class PreparatorServlet extends HttpServlet implements StrutsStatics {
+
+ private static final long serialVersionUID = 1853399729352984089L;
+
+ private final static Logger LOG = LoggerFactory.getLogger(PreparatorServlet.class);
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("The preparator servlet has been deprecated. It can safely be removed from your web.xml file");
+ }
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/context/ServletContextHolderListener.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/context/ServletContextHolderListener.java
new file mode 100644
index 0000000..4ab4275
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/context/ServletContextHolderListener.java
@@ -0,0 +1,69 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.context;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * @deprecated
+ *
+ * This listener has been deprecated. Do not use it. (WW-2101)
+ *
+ */
+public class ServletContextHolderListener implements ServletContextListener {
+
+ private static ServletContext context = null;
+
+ private final static Logger LOG = LoggerFactory.getLogger(ServletContextHolderListener.class);
+
+ /**
+ * @return The current servlet context
+ */
+ public static ServletContext getServletContext() {
+ return context;
+ }
+
+ /**
+ * Stores the reference to the {@link ServletContext}.
+ *
+ * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
+ */
+ public void contextInitialized(ServletContextEvent event) {
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("The ServletContextHolderListener has been deprecated. It can safely be removed from your web.xml file");
+ }
+ context = event.getServletContext();
+ }
+
+ /**
+ * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
+ */
+ public void contextDestroyed(ServletContextEvent event) {
+ context = null;
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/dispatcher/DirectRenderFromEventAction.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/dispatcher/DirectRenderFromEventAction.java
new file mode 100644
index 0000000..9e2ac6c
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/dispatcher/DirectRenderFromEventAction.java
@@ -0,0 +1,73 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.dispatcher;
+
+import com.opensymphony.xwork2.Action;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.apache.struts2.interceptor.SessionAware;
+import org.apache.struts2.portlet.PortletActionConstants;
+
+/**
+ * When a portlet is targetted for an <code>event</code>, the portlet will receive two
+ * portlet requests, one for the <code>event</code> phase, and then followed by a <code>render</code>
+ * operation. When in the <code>event</code> phase, the action that is executed can't render
+ * any output. This means that if an action in the XWork configuration is executed in the event
+ * phase, and the action is set up with a result that should render something, the result can't
+ * immediately be executed. The portlet needs to "wait" to the render phase to do the
+ * rendering.
+ * <p/>
+ * When the {@link org.apache.struts2.portlet.result.PortletResult} detects such a
+ * scenario, instead of executing the actual view, it prepares a couple of render parameters
+ * specifying this action and the location of the view, which then will be executed in the
+ * following render request.
+ */
+public class DirectRenderFromEventAction implements SessionAware, PortletActionConstants, Action, Serializable {
+
+ private static final long serialVersionUID = -1814807772308405785L;
+
+ private String location = null;
+
+ /**
+ * Get the location of the view.
+ *
+ * @return Returns the location.
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * Always return success.
+ *
+ * @return SUCCESS
+ */
+ public String execute() throws Exception {
+ return SUCCESS;
+ }
+
+ public void setSession(Map session) {
+ location = (String)session.get(RENDER_DIRECT_LOCATION);
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/dispatcher/DispatcherServlet.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/dispatcher/DispatcherServlet.java
new file mode 100644
index 0000000..29a77e5
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/dispatcher/DispatcherServlet.java
@@ -0,0 +1,56 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.dispatcher;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.struts2.dispatcher.StrutsRequestWrapper;
+import org.apache.struts2.portlet.PortletActionConstants;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class DispatcherServlet extends HttpServlet implements PortletActionConstants {
+
+ private static final long serialVersionUID = -266147033645951967L;
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ String dispatchTo = (String) request.getAttribute(DISPATCH_TO);
+ HttpServletRequest wrapper = wrapRequestIfNecessary(request);
+ if(StringUtils.isNotEmpty(dispatchTo)) {
+ request.getRequestDispatcher(dispatchTo).include(wrapper, response);
+ }
+ }
+
+ private HttpServletRequest wrapRequestIfNecessary(HttpServletRequest request) {
+ if(!(request instanceof StrutsRequestWrapper)) {
+ return new StrutsRequestWrapper(request);
+ }
+ else {
+ return request;
+ }
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java
new file mode 100644
index 0000000..1a1ba4a
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java
@@ -0,0 +1,628 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.dispatcher;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.GenericPortlet;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletException;
+import javax.portlet.PortletMode;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletResponse;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+import javax.portlet.WindowState;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.StrutsException;
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.dispatcher.ApplicationMap;
+import org.apache.struts2.dispatcher.Dispatcher;
+import org.apache.struts2.dispatcher.RequestMap;
+import org.apache.struts2.dispatcher.SessionMap;
+import org.apache.struts2.dispatcher.mapper.ActionMapper;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.PortletApplicationMap;
+import org.apache.struts2.portlet.PortletRequestMap;
+import org.apache.struts2.portlet.PortletSessionMap;
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.apache.struts2.portlet.servlet.PortletServletContext;
+import org.apache.struts2.portlet.servlet.PortletServletRequest;
+import org.apache.struts2.portlet.servlet.PortletServletResponse;
+import org.apache.struts2.util.AttributeMap;
+import org.apache.commons.lang.StringUtils;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.ActionProxyFactory;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.util.FileManager;
+import com.opensymphony.xwork2.util.LocalizedTextUtil;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ * <p>
+ * Struts JSR-168 portlet dispatcher. Similar to the WW2 Servlet dispatcher,
+ * but adjusted to a portal environment. The portlet is configured through the <tt>portlet.xml</tt>
+ * descriptor. Examples and descriptions follow below:
+ * </p>
+ * <!-- END SNIPPET: javadoc -->
+ *
+ * @author Nils-Helge Garli
+ * @author Rainer Hermanns
+ *
+ * <p><b>Init parameters</b></p>
+ * <!-- START SNIPPET: params -->
+ * <table class="confluenceTable">
+ * <tr>
+ * <th class="confluenceTh">Name</th>
+ * <th class="confluenceTh">Description</th>
+ * <th class="confluenceTh">Default value</th>
+ * </tr>
+ * <tr>
+ * <td class="confluenceTd">portletNamespace</td><td class="confluenceTd">The namespace for the portlet in the xwork configuration. This
+ * namespace is prepended to all action lookups, and makes it possible to host multiple
+ * portlets in the same portlet application. If this parameter is set, the complete namespace
+ * will be <tt>/portletNamespace/modeNamespace/actionName</tt></td><td class="confluenceTd">The default namespace</td>
+ * </tr>
+ * <tr>
+ * <td class="confluenceTd">viewNamespace</td><td class="confluenceTd">Base namespace in the xwork configuration for the <tt>view</tt> portlet
+ * mode</td><td class="confluenceTd">The default namespace</td>
+ * </tr>
+ * <tr>
+ * <td class="confluenceTd">editNamespace</td><td class="confluenceTd">Base namespace in the xwork configuration for the <tt>edit</tt> portlet
+ * mode</td><td class="confluenceTd">The default namespace</td>
+ * </tr>
+ * <tr>
+ * <td class="confluenceTd">helpNamespace</td><td class="confluenceTd">Base namespace in the xwork configuration for the <tt>help</tt> portlet
+ * mode</td><td class="confluenceTd">The default namespace</td>
+ * </tr>
+ * <tr>
+ * <td class="confluenceTd">defaultViewAction</td><td class="confluenceTd">Default action to invoke in the <tt>view</tt> portlet mode if no action is
+ * specified</td><td class="confluenceTd"><tt>default</tt></td>
+ * </tr>
+ * <tr>
+ * <td class="confluenceTd">defaultEditAction</td><td class="confluenceTd">Default action to invoke in the <tt>edit</tt> portlet mode if no action is
+ * specified</td><td class="confluenceTd"><tt>default</tt></td>
+ * </tr>
+ * <tr>
+ * <td class="confluenceTd">defaultHelpAction</td><td class="confluenceTd">Default action to invoke in the <tt>help</tt> portlet mode if no action is
+ * specified</td><td class="confluenceTd"><tt>default</tt></td>
+ * </tr>
+ * </table>
+ * <!-- END SNIPPET: params -->
+ * <p><b>Example:</b></p>
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ *
+ * <init-param>
+ * <!-- The view mode namespace. Maps to a namespace in the xwork config file -->
+ * <name>viewNamespace</name>
+ * <value>/view</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The default action to invoke in view mode -->
+ * <name>defaultViewAction</name>
+ * <value>index</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The view mode namespace. Maps to a namespace in the xwork config file -->
+ * <name>editNamespace</name>
+ * <value>/edit</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The default action to invoke in view mode -->
+ * <name>defaultEditAction</name>
+ * <value>index</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The view mode namespace. Maps to a namespace in the xwork config file -->
+ * <name>helpNamespace</name>
+ * <value>/help</value>
+ * </init-param>
+ * <init-param>
+ * <!-- The default action to invoke in view mode -->
+ * <name>defaultHelpAction</name>
+ * <value>index</value>
+ * </init-param>
+ *
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ */
+public class Jsr168Dispatcher extends GenericPortlet implements StrutsStatics,
+ PortletActionConstants {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Jsr168Dispatcher.class);
+
+ private ActionProxyFactory factory = null;
+
+ private Map<PortletMode,String> modeMap = new HashMap<PortletMode,String>(3);
+
+ private Map<PortletMode,ActionMapping> actionMap = new HashMap<PortletMode,ActionMapping>(3);
+
+ private String portletNamespace = null;
+
+ private Dispatcher dispatcherUtils;
+
+ private ActionMapper actionMapper;
+
+ private Container container;
+
+ /**
+ * Initialize the portlet with the init parameters from <tt>portlet.xml</tt>
+ */
+ public void init(PortletConfig cfg) throws PortletException {
+ super.init(cfg);
+ if (LOG.isDebugEnabled()) LOG.debug("Initializing portlet " + getPortletName());
+
+ Map<String,String> params = new HashMap<String,String>();
+ for (Enumeration e = cfg.getInitParameterNames(); e.hasMoreElements(); ) {
+ String name = (String) e.nextElement();
+ String value = cfg.getInitParameter(name);
+ params.put(name, value);
+ }
+
+ dispatcherUtils = new Dispatcher(new PortletServletContext(cfg.getPortletContext()), params);
+ dispatcherUtils.init();
+
+ // For testability
+ if (factory == null) {
+ factory = dispatcherUtils.getConfigurationManager().getConfiguration().getContainer().getInstance(ActionProxyFactory.class);
+ }
+ portletNamespace = cfg.getInitParameter("portletNamespace");
+ if (LOG.isDebugEnabled()) LOG.debug("PortletNamespace: " + portletNamespace);
+ parseModeConfig(actionMap, cfg, PortletMode.VIEW, "viewNamespace",
+ "defaultViewAction");
+ parseModeConfig(actionMap, cfg, PortletMode.EDIT, "editNamespace",
+ "defaultEditAction");
+ parseModeConfig(actionMap, cfg, PortletMode.HELP, "helpNamespace",
+ "defaultHelpAction");
+ parseModeConfig(actionMap, cfg, new PortletMode("config"), "configNamespace",
+ "defaultConfigAction");
+ parseModeConfig(actionMap, cfg, new PortletMode("about"), "aboutNamespace",
+ "defaultAboutAction");
+ parseModeConfig(actionMap, cfg, new PortletMode("print"), "printNamespace",
+ "defaultPrintAction");
+ parseModeConfig(actionMap, cfg, new PortletMode("preview"), "previewNamespace",
+ "defaultPreviewAction");
+ parseModeConfig(actionMap, cfg, new PortletMode("edit_defaults"),
+ "editDefaultsNamespace", "defaultEditDefaultsAction");
+ if (StringUtils.isEmpty(portletNamespace)) {
+ portletNamespace = "";
+ }
+ LocalizedTextUtil
+ .addDefaultResourceBundle("org/apache/struts2/struts-messages");
+
+ container = dispatcherUtils.getContainer();
+ //check for configuration reloading
+ if ("true".equalsIgnoreCase(container.getInstance(String.class, StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD))) {
+ FileManager.setReloadingConfigs(true);
+ }
+
+ actionMapper = container.getInstance(ActionMapper.class);
+ }
+
+ /**
+ * Parse the mode to namespace mappings configured in portlet.xml
+ * @param actionMap The map with mode <-> default action mapping.
+ * @param portletConfig The PortletConfig.
+ * @param portletMode The PortletMode.
+ * @param nameSpaceParam Name of the init parameter where the namespace for the mode
+ * is configured.
+ * @param defaultActionParam Name of the init parameter where the default action to
+ * execute for the mode is configured.
+ */
+ void parseModeConfig(Map<PortletMode, ActionMapping> actionMap, PortletConfig portletConfig,
+ PortletMode portletMode, String nameSpaceParam,
+ String defaultActionParam) {
+ String namespace = portletConfig.getInitParameter(nameSpaceParam);
+ if (StringUtils.isEmpty(namespace)) {
+ namespace = "";
+ }
+ modeMap.put(portletMode, namespace);
+ String defaultAction = portletConfig
+ .getInitParameter(defaultActionParam);
+ String method = null;
+ if (StringUtils.isEmpty(defaultAction)) {
+ defaultAction = DEFAULT_ACTION_NAME;
+ }
+ if(defaultAction.indexOf('!') >= 0) {
+ method = defaultAction.substring(defaultAction.indexOf('!') + 1);
+ defaultAction = defaultAction.substring(0, defaultAction.indexOf('!'));
+ }
+ StringBuffer fullPath = new StringBuffer();
+ if (StringUtils.isNotEmpty(portletNamespace)) {
+ fullPath.append(portletNamespace);
+ }
+ if (StringUtils.isNotEmpty(namespace)) {
+ fullPath.append(namespace).append("/");
+ } else {
+ fullPath.append("/");
+ }
+ fullPath.append(defaultAction);
+ ActionMapping mapping = new ActionMapping();
+ mapping.setName(getActionName(fullPath.toString()));
+ mapping.setNamespace(getNamespace(fullPath.toString()));
+ if(method != null) {
+ mapping.setMethod(method);
+ }
+ actionMap.put(portletMode, mapping);
+ }
+
+ /**
+ * Service an action from the <tt>event</tt> phase.
+ *
+ * @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest,
+ * javax.portlet.ActionResponse)
+ */
+ public void processAction(ActionRequest request, ActionResponse response)
+ throws PortletException, IOException {
+ if (LOG.isDebugEnabled()) LOG.debug("Entering processAction");
+ resetActionContext();
+ try {
+ serviceAction(request, response, getRequestMap(request), getParameterMap(request),
+ getSessionMap(request), getApplicationMap(),
+ portletNamespace, EVENT_PHASE);
+ if (LOG.isDebugEnabled()) LOG.debug("Leaving processAction");
+ } finally {
+ ActionContext.setContext(null);
+ }
+ }
+
+ /**
+ * Service an action from the <tt>render</tt> phase.
+ *
+ * @see javax.portlet.Portlet#render(javax.portlet.RenderRequest,
+ * javax.portlet.RenderResponse)
+ */
+ public void render(RenderRequest request, RenderResponse response)
+ throws PortletException, IOException {
+
+ if (LOG.isDebugEnabled()) LOG.debug("Entering render");
+ resetActionContext();
+ response.setTitle(getTitle(request));
+ if(!request.getWindowState().equals(WindowState.MINIMIZED)) {
+ try {
+ // Check to see if an event set the render to be included directly
+ serviceAction(request, response, getRequestMap(request), getParameterMap(request),
+ getSessionMap(request), getApplicationMap(),
+ portletNamespace, RENDER_PHASE);
+ if (LOG.isDebugEnabled()) LOG.debug("Leaving render");
+ } finally {
+ resetActionContext();
+ }
+ }
+ }
+
+ /**
+ * Reset the action context.
+ */
+ private void resetActionContext() {
+ ActionContext.setContext(null);
+ }
+
+ /**
+ * Merges all application and portlet attributes into a single
+ * <tt>HashMap</tt> to represent the entire <tt>Action</tt> context.
+ *
+ * @param requestMap a Map of all request attributes.
+ * @param parameterMap a Map of all request parameters.
+ * @param sessionMap a Map of all session attributes.
+ * @param applicationMap a Map of all servlet context attributes.
+ * @param request the PortletRequest object.
+ * @param response the PortletResponse object.
+ * @param portletConfig the PortletConfig object.
+ * @param phase The portlet phase (render or action, see
+ * {@link PortletActionConstants})
+ * @return a HashMap representing the <tt>Action</tt> context.
+ */
+ public HashMap<String, Object> createContextMap(Map<String, Object> requestMap, Map<String, String[]> parameterMap,
+ Map<String, Object> sessionMap, Map<String, Object> applicationMap, PortletRequest request,
+ PortletResponse response, HttpServletRequest servletRequest, HttpServletResponse servletResponse, ServletContext servletContext, PortletConfig portletConfig, Integer phase) throws IOException {
+
+ // TODO Must put http request/response objects into map for use with
+ container.inject(servletRequest);
+
+ // ServletActionContext
+ HashMap<String, Object> extraContext = new HashMap<String, Object>();
+ // The dummy servlet objects. Eases reuse of existing interceptors that uses the servlet objects.
+ extraContext.put(StrutsStatics.HTTP_REQUEST, servletRequest);
+ extraContext.put(StrutsStatics.HTTP_RESPONSE, servletResponse);
+ extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
+ // End dummy servlet objects
+ extraContext.put(ActionContext.PARAMETERS, parameterMap);
+ extraContext.put(ActionContext.SESSION, sessionMap);
+ extraContext.put(ActionContext.APPLICATION, applicationMap);
+
+ String defaultLocale = dispatcherUtils.getContainer().getInstance(String.class, StrutsConstants.STRUTS_LOCALE);
+ Locale locale = null;
+ if (defaultLocale != null) {
+ locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
+ } else {
+ locale = request.getLocale();
+ }
+ extraContext.put(ActionContext.LOCALE, locale);
+
+ extraContext.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, getPortletContext());
+ extraContext.put(REQUEST, request);
+ extraContext.put(RESPONSE, response);
+ extraContext.put(PORTLET_CONFIG, portletConfig);
+ extraContext.put(PORTLET_NAMESPACE, portletNamespace);
+ extraContext.put(DEFAULT_ACTION_FOR_MODE, actionMap.get(request.getPortletMode()));
+ // helpers to get access to request/session/application scope
+ extraContext.put("request", requestMap);
+ extraContext.put("session", sessionMap);
+ extraContext.put("application", applicationMap);
+ extraContext.put("parameters", parameterMap);
+ extraContext.put(MODE_NAMESPACE_MAP, modeMap);
+
+ extraContext.put(PHASE, phase);
+
+ AttributeMap attrMap = new AttributeMap(extraContext);
+ extraContext.put("attr", attrMap);
+
+ return extraContext;
+ }
+
+ /**
+ * Loads the action and executes it. This method first creates the action
+ * context from the given parameters then loads an <tt>ActionProxy</tt>
+ * from the given action name and namespace. After that, the action is
+ * executed and output channels throught the response object.
+ *
+ * @param request the HttpServletRequest object.
+ * @param response the HttpServletResponse object.
+ * @param requestMap a Map of request attributes.
+ * @param parameterMap a Map of request parameters.
+ * @param sessionMap a Map of all session attributes.
+ * @param applicationMap a Map of all application attributes.
+ * @param portletNamespace the namespace or context of the action.
+ * @param phase The portlet phase (render or action, see
+ * {@link PortletActionConstants})
+ */
+ public void serviceAction(PortletRequest request, PortletResponse response, Map<String, Object> requestMap, Map<String, String[]> parameterMap,
+ Map<String, Object> sessionMap, Map<String, Object> applicationMap, String portletNamespace,
+ Integer phase) throws PortletException {
+ if (LOG.isDebugEnabled()) LOG.debug("serviceAction");
+ Dispatcher.setInstance(dispatcherUtils);
+ String actionName = null;
+ String namespace = null;
+ try {
+ ServletContext servletContext = new PortletServletContext(getPortletContext());
+ HttpServletRequest servletRequest = new PortletServletRequest(request, getPortletContext());
+ HttpServletResponse servletResponse = new PortletServletResponse(response);
+ if(EVENT_PHASE.equals(phase)) {
+ servletRequest = dispatcherUtils.wrapRequest(servletRequest, servletContext);
+ if(servletRequest instanceof MultiPartRequestWrapper) {
+ // Multipart request. Request parameters are encoded in the multipart data,
+ // so we need to manually add them to the parameter map.
+ parameterMap.putAll(servletRequest.getParameterMap());
+ }
+ }
+ container.inject(servletRequest);
+ ActionMapping mapping = getActionMapping(request, servletRequest);
+ actionName = mapping.getName();
+ namespace = mapping.getNamespace();
+ HashMap<String, Object> extraContext = createContextMap(requestMap, parameterMap,
+ sessionMap, applicationMap, request, response, servletRequest, servletResponse,
+ servletContext, getPortletConfig(), phase);
+ extraContext.put(PortletActionConstants.ACTION_MAPPING, mapping);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Creating action proxy for name = " + actionName + ", namespace = " + namespace);
+ }
+ ActionProxy proxy = factory.createActionProxy(namespace,
+ actionName, mapping.getMethod(), extraContext);
+ request.setAttribute("struts.valueStack", proxy.getInvocation()
+ .getStack());
+ proxy.execute();
+ } catch (ConfigurationException e) {
+ LOG.error("Could not find action", e);
+ throw new PortletException("Could not find action " + actionName, e);
+ } catch (Exception e) {
+ LOG.error("Could not execute action", e);
+ throw new PortletException("Error executing action " + actionName,
+ e);
+ } finally {
+ Dispatcher.setInstance(null);
+ }
+ }
+
+ /**
+ * Returns a Map of all application attributes. Copies all attributes from
+ * the {@link PortletActionContext}into an {@link ApplicationMap}.
+ *
+ * @return a Map of all application attributes.
+ */
+ protected Map getApplicationMap() {
+ return new PortletApplicationMap(getPortletContext());
+ }
+
+ /**
+ * Gets the namespace of the action from the request. The namespace is the
+ * same as the portlet mode. E.g, view mode is mapped to namespace
+ * <code>view</code>, and edit mode is mapped to the namespace
+ * <code>edit</code>
+ *
+ * @param request the PortletRequest object.
+ * @return the namespace of the action.
+ */
+ protected ActionMapping getActionMapping(final PortletRequest portletRequest, final HttpServletRequest servletRequest) {
+ ActionMapping mapping = null;
+ String actionPath = null;
+ if (resetAction(portletRequest)) {
+ mapping = (ActionMapping) actionMap.get(portletRequest.getPortletMode());
+ } else {
+ actionPath = servletRequest.getParameter(ACTION_PARAM);
+ if (StringUtils.isEmpty(actionPath)) {
+ mapping = (ActionMapping) actionMap.get(portletRequest
+ .getPortletMode());
+ } else {
+
+ // Use the usual action mapper, but it is expecting an action extension
+ // on the uri, so we add the default one, which should be ok as the
+ // portlet is a portlet first, a servlet second
+ mapping = actionMapper.getMapping(servletRequest, dispatcherUtils.getConfigurationManager());
+ }
+ }
+
+ if (mapping == null) {
+ throw new StrutsException("Unable to locate action mapping for request, probably due to " +
+ "an invalid action path: "+actionPath);
+ }
+ return mapping;
+ }
+
+ /**
+ * Get the namespace part of the action path.
+ * @param actionPath Full path to action
+ * @return The namespace part.
+ */
+ String getNamespace(String actionPath) {
+ int idx = actionPath.lastIndexOf('/');
+ String namespace = "";
+ if (idx >= 0) {
+ namespace = actionPath.substring(0, idx);
+ }
+ return namespace;
+ }
+
+ /**
+ * Get the action name part of the action path.
+ * @param actionPath Full path to action
+ * @return The action name.
+ */
+ String getActionName(String actionPath) {
+ int idx = actionPath.lastIndexOf('/');
+ String action = actionPath;
+ if (idx >= 0) {
+ action = actionPath.substring(idx + 1);
+ }
+ return action;
+ }
+
+ /**
+ * Returns a Map of all request parameters. This implementation just calls
+ * {@link PortletRequest#getParameterMap()}.
+ *
+ * @param request the PortletRequest object.
+ * @return a Map of all request parameters.
+ * @throws IOException if an exception occurs while retrieving the parameter
+ * map.
+ */
+ protected Map<String, String[]> getParameterMap(PortletRequest request) throws IOException {
+ return new HashMap<String, String[]>(request.getParameterMap());
+ }
+
+ /**
+ * Returns a Map of all request attributes. The default implementation is to
+ * wrap the request in a {@link RequestMap}. Override this method to
+ * customize how request attributes are mapped.
+ *
+ * @param request the PortletRequest object.
+ * @return a Map of all request attributes.
+ */
+ protected Map getRequestMap(PortletRequest request) {
+ return new PortletRequestMap(request);
+ }
+
+ /**
+ * Returns a Map of all session attributes. The default implementation is to
+ * wrap the reqeust in a {@link SessionMap}. Override this method to
+ * customize how session attributes are mapped.
+ *
+ * @param request the PortletRequest object.
+ * @return a Map of all session attributes.
+ */
+ protected Map getSessionMap(PortletRequest request) {
+ return new PortletSessionMap(request);
+ }
+
+ /**
+ * Convenience method to ease testing.
+ * @param factory
+ */
+ protected void setActionProxyFactory(ActionProxyFactory factory) {
+ this.factory = factory;
+ }
+
+ /**
+ * Check to see if the action parameter is valid for the current portlet mode. If the portlet
+ * mode has been changed with the portal widgets, the action name is invalid, since the
+ * action name belongs to the previous executing portlet mode. If this method evaluates to
+ * <code>true</code> the <code>default<Mode>Action</code> is used instead.
+ * @param request The portlet request.
+ * @return <code>true</code> if the action should be reset.
+ */
+ private boolean resetAction(PortletRequest request) {
+ boolean reset = false;
+ Map paramMap = request.getParameterMap();
+ String[] modeParam = (String[]) paramMap.get(MODE_PARAM);
+ if (modeParam != null && modeParam.length == 1) {
+ String originatingMode = modeParam[0];
+ String currentMode = request.getPortletMode().toString();
+ if (!currentMode.equals(originatingMode)) {
+ reset = true;
+ }
+ }
+ if(reset) {
+ request.setAttribute(ACTION_RESET, Boolean.TRUE);
+ }
+ else {
+ request.setAttribute(ACTION_RESET, Boolean.FALSE);
+ }
+ return reset;
+ }
+
+ public void destroy() {
+ if (dispatcherUtils == null) {
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("something is seriously wrong, DispatcherUtil is not initialized (null) ");
+ }
+ } else {
+ dispatcherUtils.cleanup();
+ }
+ }
+
+ /**
+ * @param actionMapper the actionMapper to set
+ */
+ public void setActionMapper(ActionMapper actionMapper) {
+ this.actionMapper = actionMapper;
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptor.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptor.java
new file mode 100644
index 0000000..b9075c0
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptor.java
@@ -0,0 +1,92 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletResponse;
+
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.interceptor.PrincipalAware;
+import org.apache.struts2.portlet.PortletActionConstants;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+public class PortletAwareInterceptor extends AbstractInterceptor implements PortletActionConstants, StrutsStatics {
+
+ private static final long serialVersionUID = 2476509721059587700L;
+
+ private static final Logger LOG = LoggerFactory.getLogger(PortletAwareInterceptor.class);
+
+ /**
+ * Sets action properties based on the interfaces an action implements. Things like application properties,
+ * parameters, session attributes, etc are set based on the implementing interface.
+ *
+ * @param invocation an encapsulation of the action execution state.
+ * @throws Exception if an error occurs when setting action properties.
+ */
+ public String intercept(ActionInvocation invocation) throws Exception {
+ final Object action = invocation.getAction();
+ final ActionContext context = invocation.getInvocationContext();
+
+ if (action instanceof PortletRequestAware) {
+ PortletRequest request = (PortletRequest) context.get(REQUEST);
+ ((PortletRequestAware) action).setPortletRequest(request);
+ }
+
+ if (action instanceof PortletResponseAware) {
+ PortletResponse response = (PortletResponse) context.get(RESPONSE);
+ ((PortletResponseAware) action).setPortletResponse(response);
+ }
+ if (action instanceof PrincipalAware) {
+ PortletRequest request = (PortletRequest) context.get(REQUEST);
+ ((PrincipalAware) action).setPrincipalProxy(new PortletPrincipalProxy(request));
+ }
+ if (action instanceof PortletContextAware) {
+ PortletContext portletContext = (PortletContext) context.get(STRUTS_PORTLET_CONTEXT);
+ ((PortletContextAware) action).setPortletContext(portletContext);
+ }
+ if (action instanceof PortletConfigAware) {
+ PortletConfig portletConfig = (PortletConfig)context.get(PORTLET_CONFIG);
+ ((PortletConfigAware) action).setPortletConfig(portletConfig);
+ }
+ if (action instanceof PortletPreferencesAware) {
+ PortletRequest request = (PortletRequest) context.get(REQUEST);
+
+ // Check if running in a servlet environment
+ if (request == null) {
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("This portlet preferences implementation should only be used during development");
+ }
+ ((PortletPreferencesAware)action).setPortletPreferences(new ServletPortletPreferences(ActionContext.getContext().getSession()));
+ } else {
+ ((PortletPreferencesAware)action).setPortletPreferences(request.getPreferences());
+ }
+ }
+ return invocation.invoke();
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletConfigAware.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletConfigAware.java
new file mode 100644
index 0000000..432de25
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletConfigAware.java
@@ -0,0 +1,34 @@
+/*
+ * $Id: $
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.portlet.interceptor;
+
+import javax.portlet.PortletConfig;
+
+
+/**
+ * Actions that wants a reference to the PortletConfig object can
+ * implement this interface.
+ *
+ */
+public interface PortletConfigAware {
+
+ void setPortletConfig(PortletConfig portletConfig);
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletContextAware.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletContextAware.java
new file mode 100644
index 0000000..59b613e
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletContextAware.java
@@ -0,0 +1,30 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import javax.portlet.PortletContext;
+
+public interface PortletContextAware {
+
+ void setPortletContext(PortletContext portletContext);
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletPreferencesAware.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletPreferencesAware.java
new file mode 100644
index 0000000..f692b9c
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletPreferencesAware.java
@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import javax.portlet.PortletPreferences;
+
+
+/**
+ * All Actions that want to have access to the portlet preferences should
+ * implement this interface. If running in a servlet environment, an
+ * appropriate testing implementation will be provided.
+ */
+public interface PortletPreferencesAware {
+
+ /**
+ * Sets the HTTP request object in implementing classes.
+ *
+ * @param request the HTTP request.
+ */
+ public void setPortletPreferences(PortletPreferences prefs);
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletPrincipalProxy.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletPrincipalProxy.java
new file mode 100644
index 0000000..e1cd082
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletPrincipalProxy.java
@@ -0,0 +1,94 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import org.apache.struts2.interceptor.PrincipalProxy;
+
+import javax.portlet.PortletRequest;
+import javax.servlet.http.HttpServletRequest;
+import java.security.Principal;
+
+/**
+ * PrincipalProxy implementation for using PortletRequest Principal related methods.
+ */
+public class PortletPrincipalProxy implements PrincipalProxy {
+
+ private PortletRequest request;
+
+ /**
+ * Constructs a proxy
+ *
+ * @param request The underlying request
+ */
+ public PortletPrincipalProxy(PortletRequest request) {
+ this.request = request;
+ }
+
+ /**
+ * True if the user is in the given role
+ *
+ * @param role The role
+ * @return True if the user is in that role
+ */
+ public boolean isUserInRole(String role) {
+ return request.isUserInRole(role);
+ }
+
+ /**
+ * Gets the user principal
+ *
+ * @return The principal
+ */
+ public Principal getUserPrincipal() {
+ return request.getUserPrincipal();
+ }
+
+ /**
+ * Gets the user id
+ *
+ * @return The user id
+ */
+ public String getRemoteUser() {
+ return request.getRemoteUser();
+ }
+
+ /**
+ * Is the request using https?
+ *
+ * @return True if using https
+ */
+ public boolean isRequestSecure() {
+ return request.isSecure();
+ }
+
+ /**
+ * Gets the request.
+ *
+ * @return The request
+ * @throws UnsupportedOperationException not supported in this implementation.
+ * @deprecated To obtain the HttpServletRequest in your action, use
+ * {@link org.apache.struts2.servlet.ServletRequestAware}, since this method will be dropped in future.
+ */
+ public HttpServletRequest getRequest() {
+ throw new UnsupportedOperationException("Usage of getRequest() method is deprecadet and not supported for this implementation");
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletRequestAware.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletRequestAware.java
new file mode 100644
index 0000000..5b3f9b4
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletRequestAware.java
@@ -0,0 +1,30 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import javax.portlet.PortletRequest;
+
+public interface PortletRequestAware {
+
+ void setPortletRequest(PortletRequest request);
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletResponseAware.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletResponseAware.java
new file mode 100644
index 0000000..75a12e8
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletResponseAware.java
@@ -0,0 +1,30 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import javax.portlet.PortletResponse;
+
+public interface PortletResponseAware {
+
+ void setPortletResponse(PortletResponse response);
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptor.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptor.java
new file mode 100644
index 0000000..c6fe6c7
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptor.java
@@ -0,0 +1,95 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import org.apache.commons.lang.StringUtils;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.dispatcher.DirectRenderFromEventAction;
+
+import javax.portlet.ActionResponse;
+import javax.portlet.RenderRequest;
+import java.util.Map;
+
+public class PortletStateInterceptor extends AbstractInterceptor implements PortletActionConstants {
+
+ private final static Logger LOG = LoggerFactory.getLogger(PortletStateInterceptor.class);
+
+ private static final long serialVersionUID = 6138452063353911784L;
+
+ @Override
+ public String intercept(ActionInvocation invocation) throws Exception {
+ Integer phase = (Integer) invocation.getInvocationContext().get(PHASE);
+ if (RENDER_PHASE.equals(phase)) {
+ restoreStack(invocation);
+ return invocation.invoke();
+ } else if (EVENT_PHASE.equals(phase)) {
+ try {
+ return invocation.invoke();
+ } finally {
+ saveStack(invocation);
+ }
+ } else {
+ return invocation.invoke();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void saveStack(ActionInvocation invocation) {
+ Map session = invocation.getInvocationContext().getSession();
+ session.put(STACK_FROM_EVENT_PHASE, invocation.getStack());
+ ActionResponse actionResponse = (ActionResponse) invocation.getInvocationContext().get(RESPONSE);
+ actionResponse.setRenderParameter(EVENT_ACTION, "true");
+ }
+
+ @SuppressWarnings("unchecked")
+ private void restoreStack(ActionInvocation invocation) {
+ RenderRequest request = (RenderRequest) invocation.getInvocationContext().get(REQUEST);
+ if (StringUtils.isNotEmpty(request.getParameter(EVENT_ACTION))) {
+ if(!isProperPrg(invocation)) {
+ if (LOG.isDebugEnabled()) LOG.debug("Restoring value stack from event phase");
+ ValueStack oldStack = (ValueStack) invocation.getInvocationContext().getSession().get(
+ STACK_FROM_EVENT_PHASE);
+ if (oldStack != null) {
+ CompoundRoot oldRoot = oldStack.getRoot();
+ ValueStack currentStack = invocation.getStack();
+ CompoundRoot root = currentStack.getRoot();
+ root.addAll(0, oldRoot);
+ if (LOG.isDebugEnabled()) LOG.debug("Restored stack");
+ }
+ }
+ else {
+ if (LOG.isDebugEnabled()) LOG.debug("Won't restore stack from event phase since it's a proper PRG request");
+ }
+ }
+ }
+
+ private boolean isProperPrg(ActionInvocation invocation) {
+ return !(invocation.getAction() instanceof DirectRenderFromEventAction);
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/ServletPortletPreferences.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/ServletPortletPreferences.java
new file mode 100644
index 0000000..9a914b3
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/interceptor/ServletPortletPreferences.java
@@ -0,0 +1,96 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.portlet.PortletPreferences;
+import javax.portlet.ReadOnlyException;
+import javax.portlet.ValidatorException;
+
+/**
+ * Simple portlet preferences implementation that uses a map in the Session
+ * as storage.
+ */
+public class ServletPortletPreferences implements PortletPreferences {
+
+ private Map session;
+ private String PREFERENCES_KEY = "_portlet-preferences";
+
+ public ServletPortletPreferences(Map session) {
+ this.session = session;
+ }
+
+ public Map getMap() {
+ Map map = (Map) session.get(PREFERENCES_KEY);
+ if (map == null) {
+ map = new HashMap();
+ session.put(PREFERENCES_KEY, map);
+ }
+ return map;
+ }
+
+ public Enumeration getNames() {
+ return new Vector(getMap().keySet()).elements();
+ }
+
+ public String getValue(String key, String def) {
+ String val = (String) getMap().get(key);
+ if (val == null) {
+ val = def;
+ }
+ return val;
+ }
+
+ public String[] getValues(String key, String[] def) {
+ String[] val = (String[]) getMap().get(key);
+ if (val == null) {
+ val = def;
+ }
+ return val;
+ }
+
+ public boolean isReadOnly(String arg0) {
+ return false;
+ }
+
+ public void reset(String arg0) throws ReadOnlyException {
+ session.put(PREFERENCES_KEY, new HashMap());
+ }
+
+ public void setValue(String key, String value) throws ReadOnlyException {
+ getMap().put(key, value);
+ }
+
+ public void setValues(String key, String[] value) throws ReadOnlyException {
+ getMap().put(key, value);
+ }
+
+ public void store() throws IOException, ValidatorException {
+
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java
new file mode 100644
index 0000000..d794a1f
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java
@@ -0,0 +1,269 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.result;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.portlet.PortletMode;
+
+import org.apache.struts2.dispatcher.ServletActionRedirectResult;
+import org.apache.struts2.dispatcher.mapper.ActionMapper;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.views.util.UrlHelper;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler;
+import com.opensymphony.xwork2.util.reflection.ReflectionException;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.config.entities.ResultConfig;
+import com.opensymphony.xwork2.inject.Inject;
+
+/**
+ *
+ * Portlet modification of the {@link ServletActionRedirectResult}.
+ *
+ * <!-- START SNIPPET: description -->
+ *
+ * This result uses the {@link ActionMapper} provided by the
+ * {@link ActionMapperFactory} to instruct the render phase to invoke the
+ * specified action and (optional) namespace. This is better than the
+ * {@link PortletResult} because it does not require you to encode the URL
+ * patterns processed by the {@link ActionMapper} in to your struts.xml
+ * configuration files. This means you can change your URL patterns at any point
+ * and your application will still work. It is strongly recommended that if you
+ * are redirecting to another action, you use this result rather than the
+ * standard redirect result.
+ *
+ * See examples below for an example of how request parameters could be passed
+ * in.
+ *
+ * <!-- END SNIPPET: description -->
+ *
+ * <b>This result type takes the following parameters:</b>
+ *
+ * <!-- START SNIPPET: params -->
+ *
+ * <ul>
+ *
+ * <li><b>actionName (default)</b> - the name of the action that will be
+ * redirect to</li>
+ *
+ * <li><b>namespace</b> - used to determine which namespace the action is in
+ * that we're redirecting to . If namespace is null, this defaults to the
+ * current namespace</li>
+ *
+ * </ul>
+ *
+ * <!-- END SNIPPET: params -->
+ *
+ * <b>Example:</b>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * <package name="public" extends="struts-default">
+ * <action name="login" class="...">
+ * <!-- Redirect to another namespace -->
+ * <result type="redirectAction">
+ * <param name="actionName">dashboard</param>
+ * <param name="namespace">/secure</param>
+ * </result>
+ * </action>
+ * </package>
+ *
+ * <package name="secure" extends="struts-default" namespace="/secure">
+ * <-- Redirect to an action in the same namespace -->
+ * <action name="dashboard" class="...">
+ * <result>dashboard.jsp</result>
+ * <result name="error" type="redirectAction">error</result>
+ * </action>
+ *
+ * <action name="error" class="...">
+ * <result>error.jsp</result>
+ * </action>
+ * </package>
+ *
+ * <package name="passingRequestParameters" extends="struts-default" namespace="/passingRequestParameters">
+ * <-- Pass parameters (reportType, width and height) -->
+ * <!--
+ * The redirectAction url generated will be :
+ * /genReport/generateReport.action?reportType=pie&width=100&height=100
+ * -->
+ * <action name="gatherReportInfo" class="...">
+ * <result name="showReportResult" type="redirectAction">
+ * <param name="actionName">generateReport</param>
+ * <param name="namespace">/genReport</param>
+ * <param name="reportType">pie</param>
+ * <param name="width">100</param>
+ * <param name="height">100</param>
+ * </result>
+ * </action>
+ * </package>
+ *
+ *
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @see ActionMapper
+ */
+public class PortletActionRedirectResult extends PortletResult implements ReflectionExceptionHandler {
+
+ private static final long serialVersionUID = -7627388936683562557L;
+
+ private static final Logger LOG = LoggerFactory.getLogger(PortletActionRedirectResult.class);
+
+ /** The default parameter */
+ public static final String DEFAULT_PARAM = "actionName";
+
+ protected String actionName;
+
+ protected String namespace;
+
+ protected String method;
+
+ private Map<String, String> requestParameters = new LinkedHashMap<String, String>();
+
+ private ActionMapper actionMapper;
+
+ public PortletActionRedirectResult() {
+ super();
+ }
+
+ public PortletActionRedirectResult(String actionName) {
+ this(null, actionName, null);
+ }
+
+ public PortletActionRedirectResult(String actionName, String method) {
+ this(null, actionName, method);
+ }
+
+ public PortletActionRedirectResult(String namespace, String actionName, String method) {
+ super(null);
+ this.namespace = namespace;
+ this.actionName = actionName;
+ this.method = method;
+ }
+
+ protected List<String> prohibitedResultParam = Arrays.asList(new String[] { DEFAULT_PARAM, "namespace", "method",
+ "encode", "parse", "location", "prependServletContext" });
+
+ @Inject
+ public void setActionMapper(ActionMapper actionMapper) {
+ this.actionMapper = actionMapper;
+ }
+
+ /**
+ * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation)
+ */
+ public void execute(ActionInvocation invocation) throws Exception {
+ actionName = conditionalParse(actionName, invocation);
+ if (portletMode != null) {
+ Map<PortletMode, String> namespaceMap = (Map<PortletMode, String>) invocation.getInvocationContext().get(
+ PortletActionConstants.MODE_NAMESPACE_MAP);
+ namespace = namespaceMap.get(portletMode);
+ }
+ if (namespace == null) {
+ namespace = invocation.getProxy().getNamespace();
+ } else {
+ namespace = conditionalParse(namespace, invocation);
+ }
+ if (method == null) {
+ method = "";
+ } else {
+ method = conditionalParse(method, invocation);
+ }
+
+ String resultCode = invocation.getResultCode();
+ if (resultCode != null) {
+ ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(resultCode);
+ Map resultConfigParams = resultConfig.getParams();
+ for (Iterator i = resultConfigParams.entrySet().iterator(); i.hasNext();) {
+ Map.Entry e = (Map.Entry) i.next();
+ if (!prohibitedResultParam.contains(e.getKey())) {
+ requestParameters.put(e.getKey().toString(), e.getValue() == null ? "" : conditionalParse(e
+ .getValue().toString(), invocation));
+ }
+ }
+ }
+
+ StringBuilder tmpLocation = new StringBuilder(actionMapper.getUriFromActionMapping(new ActionMapping(actionName,
+ namespace, method, null)));
+ UrlHelper.buildParametersString(requestParameters, tmpLocation, "&");
+
+ setLocation(tmpLocation.toString());
+
+ super.execute(invocation);
+ }
+
+ /**
+ * Sets the action name
+ *
+ * @param actionName
+ * The name
+ */
+ public void setActionName(String actionName) {
+ this.actionName = actionName;
+ }
+
+ /**
+ * Sets the namespace
+ *
+ * @param namespace
+ * The namespace
+ */
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ /**
+ * Sets the method
+ *
+ * @param method
+ * The method
+ */
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ /**
+ * Adds a request parameter to be added to the redirect url
+ *
+ * @param key
+ * The parameter name
+ * @param value
+ * The parameter value
+ */
+ public PortletActionRedirectResult addParameter(String key, Object value) {
+ requestParameters.put(key, String.valueOf(value));
+ return this;
+ }
+
+ public void handle(ReflectionException ex) {
+ // Only log as debug as they are probably parameters to be appended to the url
+ if (LOG.isDebugEnabled()) LOG.debug(ex.getMessage(), ex);
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/result/PortletResult.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/result/PortletResult.java
new file mode 100644
index 0000000..7e949ac
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/result/PortletResult.java
@@ -0,0 +1,243 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.result;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import org.apache.commons.lang.StringUtils;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.dispatcher.StrutsResultSupport;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletException;
+import javax.portlet.PortletMode;
+import javax.portlet.PortletRequestDispatcher;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * Result type that includes a JSP to render.
+ *
+ */
+public class PortletResult extends StrutsResultSupport implements PortletActionConstants {
+
+ private static final long serialVersionUID = 434251393926178567L;
+
+ private boolean useDispatcherServlet;
+
+ private String dispatcherServletName = DEFAULT_DISPATCHER_SERVLET_NAME;
+
+ /**
+ * Logger instance.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(PortletResult.class);
+
+ private String contentType = "text/html";
+
+ private String title;
+
+ protected PortletMode portletMode;
+
+ public PortletResult() {
+ super();
+ }
+
+ public PortletResult(String location) {
+ super(location);
+ }
+
+ /**
+ * Execute the result. Obtains the
+ * {@link javax.portlet.PortletRequestDispatcher}from the
+ * {@link PortletActionContext}and includes the JSP.
+ *
+ * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation)
+ */
+ public void doExecute(String finalLocation, ActionInvocation actionInvocation) throws Exception {
+
+ if (PortletActionContext.isRender()) {
+ executeRenderResult(finalLocation);
+ } else if (PortletActionContext.isEvent()) {
+ executeActionResult(finalLocation, actionInvocation);
+ } else {
+ executeRegularServletResult(finalLocation, actionInvocation);
+ }
+ }
+
+ /**
+ * Executes the regular servlet result.
+ *
+ * @param finalLocation
+ * @param actionInvocation
+ */
+ private void executeRegularServletResult(String finalLocation, ActionInvocation actionInvocation)
+ throws ServletException, IOException {
+ ServletContext ctx = ServletActionContext.getServletContext();
+ HttpServletRequest req = ServletActionContext.getRequest();
+ HttpServletResponse res = ServletActionContext.getResponse();
+ try {
+ ctx.getRequestDispatcher(finalLocation).include(req, res);
+ } catch (ServletException e) {
+ LOG.error("ServletException including " + finalLocation, e);
+ throw e;
+ } catch (IOException e) {
+ LOG.error("IOException while including result '" + finalLocation + "'", e);
+ throw e;
+ }
+ }
+
+ /**
+ * Executes the action result.
+ *
+ * @param finalLocation
+ * @param invocation
+ */
+ protected void executeActionResult(String finalLocation, ActionInvocation invocation) throws Exception {
+ if (LOG.isDebugEnabled()) LOG.debug("Executing result in Event phase");
+ ActionResponse res = PortletActionContext.getActionResponse();
+ Map sessionMap = invocation.getInvocationContext().getSession();
+ if (LOG.isDebugEnabled()) LOG.debug("Setting event render parameter: " + finalLocation);
+ if (finalLocation.indexOf('?') != -1) {
+ convertQueryParamsToRenderParams(res, finalLocation.substring(finalLocation.indexOf('?') + 1));
+ finalLocation = finalLocation.substring(0, finalLocation.indexOf('?'));
+ }
+ if (finalLocation.endsWith(".action")) {
+ // View is rendered with a view action...luckily...
+ finalLocation = finalLocation.substring(0, finalLocation.lastIndexOf("."));
+ res.setRenderParameter(ACTION_PARAM, finalLocation);
+ } else {
+ // View is rendered outside an action...uh oh...
+ String namespace = invocation.getProxy().getNamespace();
+ if ( namespace != null && namespace.length() > 0 && !namespace.endsWith("/")) {
+ namespace += "/";
+
+ }
+ res.setRenderParameter(ACTION_PARAM, namespace + "renderDirect");
+ sessionMap.put(RENDER_DIRECT_LOCATION, finalLocation);
+ }
+ if(portletMode != null) {
+ res.setPortletMode(portletMode);
+ res.setRenderParameter(PortletActionConstants.MODE_PARAM, portletMode.toString());
+ }
+ else {
+ res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext.getRequest().getPortletMode()
+ .toString());
+ }
+ }
+
+ /**
+ * Converts the query params to render params.
+ *
+ * @param response
+ * @param queryParams
+ */
+ protected static void convertQueryParamsToRenderParams(ActionResponse response, String queryParams) {
+ StringTokenizer tok = new StringTokenizer(queryParams, "&");
+ while (tok.hasMoreTokens()) {
+ String token = tok.nextToken();
+ String key = token.substring(0, token.indexOf('='));
+ String value = token.substring(token.indexOf('=') + 1);
+ response.setRenderParameter(key, value);
+ }
+ }
+
+ /**
+ * Executes the render result.
+ *
+ * @param finalLocation
+ * @throws PortletException
+ * @throws IOException
+ */
+ protected void executeRenderResult(final String finalLocation) throws PortletException, IOException {
+ if (LOG.isDebugEnabled()) LOG.debug("Executing result in Render phase");
+ PortletContext ctx = PortletActionContext.getPortletContext();
+ RenderRequest req = PortletActionContext.getRenderRequest();
+ RenderResponse res = PortletActionContext.getRenderResponse();
+ res.setContentType(contentType);
+ if (StringUtils.isNotEmpty(title)) {
+ res.setTitle(title);
+ }
+ if (LOG.isDebugEnabled()) LOG.debug("Location: " + finalLocation);
+ if (useDispatcherServlet) {
+ req.setAttribute(DISPATCH_TO, finalLocation);
+ PortletRequestDispatcher dispatcher = ctx.getNamedDispatcher(dispatcherServletName);
+ if(dispatcher == null) {
+ throw new PortletException("Could not locate dispatcher servlet \"" + dispatcherServletName + "\". Please configure it in your web.xml file");
+ }
+ dispatcher.include(req, res);
+ } else {
+ PortletRequestDispatcher dispatcher = ctx.getRequestDispatcher(finalLocation);
+ if (dispatcher == null) {
+ throw new PortletException("Could not locate dispatcher for '" + finalLocation + "'");
+ }
+ dispatcher.include(req, res);
+ }
+ }
+
+ /**
+ * Sets the content type.
+ *
+ * @param contentType
+ * The content type to set.
+ */
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ /**
+ * Sets the title.
+ *
+ * @param title
+ * The title to set.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setPortletMode(String portletMode) {
+ if(portletMode != null) {
+ this.portletMode = new PortletMode(portletMode);
+ }
+ }
+
+ @Inject("struts.portlet.useDispatcherServlet")
+ public void setUseDispatcherServlet(String useDispatcherServlet) {
+ this.useDispatcherServlet = "true".equalsIgnoreCase(useDispatcherServlet);
+ }
+
+ @Inject("struts.portlet.dispatcherServletName")
+ public void setDispatcherServletName(String dispatcherServletName) {
+ this.dispatcherServletName = dispatcherServletName;
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java
new file mode 100644
index 0000000..33defd8
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java
@@ -0,0 +1,291 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.result;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.dispatcher.StrutsResultSupport;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.apache.struts2.views.JspSupportServlet;
+import org.apache.struts2.views.velocity.VelocityManager;
+import org.apache.velocity.Template;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+
+import javax.portlet.ActionResponse;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.PageContext;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * <!-- START SNIPPET: description -->
+ *
+ * Using the Servlet container's {@link JspFactory}, this result mocks a JSP
+ * execution environment and then displays a Velocity template that will be
+ * streamed directly to the servlet output.
+ *
+ * <!-- END SNIPPET: description --> <p/><b>This result type takes the
+ * following parameters: </b>
+ *
+ * <!-- START SNIPPET: params -->
+ *
+ * <ul>
+ *
+ * <li><b>location (default) </b>- the location of the template to process.
+ * </li>
+ *
+ * <li><b>parse </b>- true by default. If set to false, the location param
+ * will not be parsed for Ognl expressions.</li>
+ *
+ * </ul>
+ * <p>
+ * This result follows the same rules from {@link StrutsResultSupport}.
+ * </p>
+ *
+ * <!-- END SNIPPET: params -->
+ *
+ * <b>Example: </b>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * <result name="success" type="velocity">
+ * <param name="location">foo.vm</param>
+ * </result>
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ */
+public class PortletVelocityResult extends StrutsResultSupport {
+
+ private static final long serialVersionUID = -8241086555872212274L;
+
+ private static final Logger LOG = LoggerFactory.getLogger(PortletVelocityResult.class);
+
+ private String defaultEncoding;
+ private VelocityManager velocityManager;
+ private JspFactory jspFactory = JspFactory.getDefaultFactory();
+
+ public PortletVelocityResult() {
+ super();
+ }
+
+ public PortletVelocityResult(String location) {
+ super(location);
+ }
+
+ @Inject
+ public void setVelocityManager(VelocityManager mgr) {
+ this.velocityManager = mgr;
+ }
+
+ @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
+ public void setDefaultEncoding(String encoding) {
+ this.defaultEncoding = encoding;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.struts2.dispatcher.StrutsResultSupport#doExecute(java.lang.String, com.opensymphony.xwork2.ActionInvocation)
+ */
+ public void doExecute(String location, ActionInvocation invocation)
+ throws Exception {
+ if (PortletActionContext.isEvent()) {
+ executeActionResult(location, invocation);
+ } else if (PortletActionContext.isRender()) {
+ executeRenderResult(location, invocation);
+ }
+ }
+
+ /**
+ * Executes the result
+ *
+ * @param location The location string
+ * @param invocation The action invocation
+ */
+ private void executeActionResult(String location,
+ ActionInvocation invocation) {
+ ActionResponse res = PortletActionContext.getActionResponse();
+ // View is rendered outside an action...uh oh...
+ String namespace = invocation.getProxy().getNamespace();
+ if ( namespace != null && namespace.length() > 0 && !namespace.endsWith("/")) {
+ namespace += "/";
+
+ }
+ res.setRenderParameter(PortletActionConstants.ACTION_PARAM, namespace + "freemarkerDirect");
+ res.setRenderParameter("location", location);
+ res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext
+ .getRequest().getPortletMode().toString());
+
+ }
+
+ /**
+ * Creates a Velocity context from the action, loads a Velocity template and
+ * executes the template. Output is written to the servlet output stream.
+ *
+ * @param finalLocation the location of the Velocity template
+ * @param invocation an encapsulation of the action execution state.
+ * @throws Exception if an error occurs when creating the Velocity context,
+ * loading or executing the template or writing output to the
+ * servlet response stream.
+ */
+ public void executeRenderResult(String finalLocation,
+ ActionInvocation invocation) throws Exception {
+ ValueStack stack = ActionContext.getContext().getValueStack();
+
+ HttpServletRequest request = ServletActionContext.getRequest();
+ HttpServletResponse response = ServletActionContext.getResponse();
+ ServletContext servletContext = ServletActionContext
+ .getServletContext();
+ Servlet servlet = JspSupportServlet.jspSupportServlet;
+
+ velocityManager.init(servletContext);
+
+ boolean usedJspFactory = false;
+ PageContext pageContext = (PageContext) ActionContext.getContext().get(
+ ServletActionContext.PAGE_CONTEXT);
+
+ if (pageContext == null && servlet != null) {
+ pageContext = jspFactory.getPageContext(servlet, request, response,
+ null, true, 8192, true);
+ ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT,
+ pageContext);
+ usedJspFactory = true;
+ }
+
+ try {
+ String encoding = getEncoding(finalLocation);
+ String contentType = getContentType(finalLocation);
+
+ if (encoding != null) {
+ contentType = contentType + ";charset=" + encoding;
+ }
+ response.setContentType(contentType);
+ Template t = getTemplate(stack,
+ velocityManager.getVelocityEngine(), invocation,
+ finalLocation, encoding);
+
+ Context context = createContext(velocityManager, stack, request,
+ response, finalLocation);
+ Writer writer = new OutputStreamWriter(response.getOutputStream(),
+ encoding);
+
+ t.merge(context, writer);
+
+ // always flush the writer (we used to only flush it if this was a
+ // jspWriter, but someone asked
+ // to do it all the time (WW-829). Since Velocity support is being
+ // deprecated, we'll oblige :)
+ writer.flush();
+ } catch (Exception e) {
+ LOG.error("Unable to render Velocity Template, '" + finalLocation
+ + "'", e);
+ throw e;
+ } finally {
+ if (usedJspFactory) {
+ jspFactory.releasePageContext(pageContext);
+ }
+ }
+
+ return;
+ }
+
+ /**
+ * Retrieve the content type for this template. <p/>People can override
+ * this method if they want to provide specific content types for specific
+ * templates (eg text/xml).
+ *
+ * @return The content type associated with this template (default
+ * "text/html")
+ */
+ protected String getContentType(String templateLocation) {
+ return "text/html";
+ }
+
+ /**
+ * Retrieve the encoding for this template. <p/>People can override this
+ * method if they want to provide specific encodings for specific templates.
+ *
+ * @return The encoding associated with this template (defaults to the value
+ * of 'struts.i18n.encoding' property)
+ */
+ protected String getEncoding(String templateLocation) {
+ String encoding = defaultEncoding;
+ if (encoding == null) {
+ encoding = System.getProperty("file.encoding");
+ }
+ if (encoding == null) {
+ encoding = "UTF-8";
+ }
+ return encoding;
+ }
+
+ /**
+ * Given a value stack, a Velocity engine, and an action invocation, this
+ * method returns the appropriate Velocity template to render.
+ *
+ * @param stack the value stack to resolve the location again (when parse
+ * equals true)
+ * @param velocity the velocity engine to process the request against
+ * @param invocation an encapsulation of the action execution state.
+ * @param location the location of the template
+ * @param encoding the charset encoding of the template
+ * @return the template to render
+ * @throws Exception when the requested template could not be found
+ */
+ protected Template getTemplate(ValueStack stack,
+ VelocityEngine velocity, ActionInvocation invocation,
+ String location, String encoding) throws Exception {
+ if (!location.startsWith("/")) {
+ location = invocation.getProxy().getNamespace() + "/" + location;
+ }
+
+ Template template = velocity.getTemplate(location, encoding);
+
+ return template;
+ }
+
+ /**
+ * Creates the VelocityContext that we'll use to render this page.
+ *
+ * @param velocityManager a reference to the velocityManager to use
+ * @param stack the value stack to resolve the location against (when parse
+ * equals true)
+ * @param location the name of the template that is being used
+ * @return the a minted Velocity context.
+ */
+ protected Context createContext(VelocityManager velocityManager,
+ ValueStack stack, HttpServletRequest request,
+ HttpServletResponse response, String location) {
+ return velocityManager.createContext(stack, request, response);
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletHttpSession.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletHttpSession.java
new file mode 100644
index 0000000..ebc39fa
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletHttpSession.java
@@ -0,0 +1,215 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.servlet;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.portlet.PortletSession;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+
+/**
+ * Wrapper object exposing a {@link PortletSession} as a {@link HttpSession} instance.
+ * Clients accessing this session object will in fact operate on the
+ * {@link PortletSession} object wrapped by this session object.
+ */
+public class PortletHttpSession implements HttpSession {
+
+ private PortletSession portletSession;
+
+ public PortletHttpSession(PortletSession portletSession) {
+ this.portletSession = portletSession;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String)
+ */
+ public Object getAttribute(String name) {
+ return portletSession.getAttribute(name);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#getAttributeNames()
+ */
+ public Enumeration getAttributeNames() {
+ return portletSession.getAttributeNames();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#getCreationTime()
+ */
+ public long getCreationTime() {
+ return portletSession.getCreationTime();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#getId()
+ */
+ public String getId() {
+ return portletSession.getId();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#getLastAccessedTime()
+ */
+ public long getLastAccessedTime() {
+ return portletSession.getLastAccessedTime();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#getMaxInactiveInterval()
+ */
+ public int getMaxInactiveInterval() {
+ return portletSession.getMaxInactiveInterval();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#getServletContext()
+ */
+ public ServletContext getServletContext() {
+ return new PortletServletContext(portletSession.getPortletContext());
+ }
+
+ /**
+ * @see javax.servlet.http.HttpSession#getSessionContext()
+ * @throws IllegalStateException
+ * Not supported in a portlet.
+ */
+ public HttpSessionContext getSessionContext() {
+ throw new IllegalStateException("Not supported in a portlet");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#getValue(java.lang.String)
+ */
+ public Object getValue(String name) {
+ return getAttribute(name);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#getValueNames()
+ */
+ public String[] getValueNames() {
+ List<String> names = new ArrayList<String>();
+ Enumeration attrNames = getAttributeNames();
+ while (attrNames.hasMoreElements()) {
+ names.add((String) attrNames.nextElement());
+ }
+ return names.toArray(new String[0]);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#invalidate()
+ */
+ public void invalidate() {
+ portletSession.invalidate();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#isNew()
+ */
+ public boolean isNew() {
+ return portletSession.isNew();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#putValue(java.lang.String,
+ * java.lang.Object)
+ */
+ public void putValue(String name, Object value) {
+ setAttribute(name, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String)
+ */
+ public void removeAttribute(String name) {
+ portletSession.removeAttribute(name);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#removeValue(java.lang.String)
+ */
+ public void removeValue(String name) {
+ removeAttribute(name);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#setAttribute(java.lang.String,
+ * java.lang.Object)
+ */
+ public void setAttribute(String name, Object value) {
+ portletSession.setAttribute(name, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int)
+ */
+ public void setMaxInactiveInterval(int interval) {
+ portletSession.setMaxInactiveInterval(interval);
+ }
+
+ /**
+ * Get the wrapped portlet session.
+ *
+ * @return The wrapped portlet session.
+ */
+ public PortletSession getPortletSession() {
+ return portletSession;
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletConfig.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletConfig.java
new file mode 100644
index 0000000..ea09237
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletConfig.java
@@ -0,0 +1,82 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.servlet;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletContext;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+/**
+ * Wrapper object exposing a {@link PortletConfig} as a {@link ServletConfig} instance.
+ * Clients accessing this config object will in fact operate on the
+ * {@link PortletConfig} object wrapped by this config object.
+ */
+public class PortletServletConfig implements ServletConfig {
+
+ private PortletConfig portletConfig;
+
+ public PortletServletConfig(PortletConfig portletConfig) {
+ this.portletConfig = portletConfig;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletConfig#getInitParameter(java.lang.String)
+ */
+ public String getInitParameter(String name) {
+ return portletConfig.getInitParameter(name);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletConfig#getInitParameterNames()
+ */
+ public Enumeration getInitParameterNames() {
+ return portletConfig.getInitParameterNames();
+ }
+
+ /**
+ * Get the {@link PortletContext} as a {@link PortletServletContext} instance.
+ * @see javax.servlet.ServletConfig#getServletContext()
+ */
+ public ServletContext getServletContext() {
+ return new PortletServletContext(portletConfig.getPortletContext());
+ }
+
+ /**
+ * Will return the portlet name.
+ * @see javax.servlet.ServletConfig#getServletName()
+ */
+ public String getServletName() {
+ return portletConfig.getPortletName();
+ }
+
+ /**
+ * Get the wrapped {@link PortletConfig} instance.
+ * @return The wrapped {@link PortletConfig} instance.
+ */
+ public PortletConfig getPortletConfig() {
+ return portletConfig;
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletContext.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletContext.java
new file mode 100644
index 0000000..6b86287
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletContext.java
@@ -0,0 +1,236 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.servlet;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Set;
+
+import javax.portlet.PortletContext;
+import javax.portlet.PortletRequestDispatcher;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+/**
+ * Wrapper object exposing a {@link PortletContext} as a {@link ServletContext} instance.
+ * Clients accessing this context object will in fact operate on the
+ * {@link PortletContext} object wrapped by this context object.
+ */
+public class PortletServletContext implements ServletContext {
+
+ private PortletContext portletContext;
+
+ public PortletServletContext(PortletContext portletContext) {
+ this.portletContext = portletContext;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
+ */
+ public Object getAttribute(String name) {
+ return portletContext.getAttribute(name);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getAttributeNames()
+ */
+ public Enumeration getAttributeNames() {
+ return portletContext.getAttributeNames();
+ }
+
+ /**
+ * @see javax.servlet.ServletContext#getContext(java.lang.String)
+ * @throws IllegalStateException Not supported in a portlet.
+ */
+ public ServletContext getContext(String uripath) {
+ throw new IllegalStateException("Not supported in a portlet");
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
+ */
+ public String getInitParameter(String name) {
+ return portletContext.getInitParameter(name);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getInitParameterNames()
+ */
+ public Enumeration getInitParameterNames() {
+ return portletContext.getInitParameterNames();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getMajorVersion()
+ */
+ public int getMajorVersion() {
+ return portletContext.getMajorVersion();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
+ */
+ public String getMimeType(String file) {
+ return portletContext.getMimeType(file);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getMinorVersion()
+ */
+ public int getMinorVersion() {
+ return portletContext.getMinorVersion();
+ }
+
+ /**
+ * Returns a {@link PortletServletRequestDispatcher} wrapping the {@link PortletRequestDispatcher}
+ * as a {@link RequestDispatcher} instance.
+ * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
+ * @return PortletServletRequestDispatcher
+ */
+ public RequestDispatcher getNamedDispatcher(String name) {
+ return new PortletServletRequestDispatcher(portletContext.getNamedDispatcher(name));
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
+ */
+ public String getRealPath(String path) {
+ return portletContext.getRealPath(path);
+ }
+
+ /**
+ * Returns a {@link PortletServletRequestDispatcher} wrapping the {@link PortletRequestDispatcher}
+ * as a {@link RequestDispatcher} instance.
+ * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
+ * @return PortletServletRequestDispatcher
+ */
+ public RequestDispatcher getRequestDispatcher(String path) {
+ return new PortletServletRequestDispatcher(portletContext.getRequestDispatcher(path));
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getResource(java.lang.String)
+ */
+ public URL getResource(String path) throws MalformedURLException {
+ return portletContext.getResource(path);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
+ */
+ public InputStream getResourceAsStream(String path) {
+ return portletContext.getResourceAsStream(path);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
+ */
+ public Set getResourcePaths(String path) {
+ return portletContext.getResourcePaths(path);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getServerInfo()
+ */
+ public String getServerInfo() {
+ return portletContext.getServerInfo();
+ }
+
+ /**
+ * @see javax.servlet.ServletContext#getServlet(java.lang.String)
+ * @throws IllegalStateException Not supported in a portlet.
+ */
+ public Servlet getServlet(String name) throws ServletException {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#getServletContextName()
+ */
+ public String getServletContextName() {
+ return portletContext.getPortletContextName();
+ }
+
+ /**
+ * @see javax.servlet.ServletContext#getServletNames()
+ * @throws IllegalStateException Not supported in a portlet.
+ */
+ public Enumeration getServletNames() {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * @see javax.servlet.ServletContext#getServlets()
+ * @throws IllegalStateException Not supported in a portlet.
+ */
+ public Enumeration getServlets() {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#log(java.lang.String)
+ */
+ public void log(String msg) {
+ portletContext.log(msg);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
+ */
+ public void log(Exception exception, String msg) {
+ log(msg, exception);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
+ */
+ public void log(String message, Throwable throwable) {
+ portletContext.log(message, throwable);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
+ */
+ public void removeAttribute(String name) {
+ portletContext.removeAttribute(name);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
+ */
+ public void setAttribute(String name, Object object) {
+ portletContext.setAttribute(name, object);
+ }
+
+ /**
+ * Get the wrapped {@link PortletContext} instance.
+ * @return The wrapped {@link PortletContext} instance.
+ */
+ public PortletContext getPortletContext() {
+ return portletContext;
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletInputStream.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletInputStream.java
new file mode 100644
index 0000000..a55f966
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletInputStream.java
@@ -0,0 +1,122 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.servlet;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ServletInputStream;
+
+/**
+ * Wrapper object exposing a {@link InputStream} from a portlet as a {@link ServletInputStream} instance.
+ * Clients accessing this stream object will in fact operate on the
+ * {@link InputStream} object wrapped by this stream object.
+ */
+public class PortletServletInputStream extends ServletInputStream {
+
+ private InputStream portletInputStream;
+
+ public PortletServletInputStream(InputStream portletInputStream) {
+ this.portletInputStream = portletInputStream;
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.InputStream#read()
+ */
+ @Override
+ public int read() throws IOException {
+ return portletInputStream.read();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.InputStream#available()
+ */
+ @Override
+ public int available() throws IOException {
+ return portletInputStream.available();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.InputStream#close()
+ */
+ @Override
+ public void close() throws IOException {
+ portletInputStream.close();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.InputStream#mark(int)
+ */
+ @Override
+ public synchronized void mark(int readlimit) {
+ portletInputStream.mark(readlimit);
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.InputStream#markSupported()
+ */
+ @Override
+ public boolean markSupported() {
+ return portletInputStream.markSupported();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.InputStream#read(byte[], int, int)
+ */
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return portletInputStream.read(b, off, len);
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.InputStream#read(byte[])
+ */
+ @Override
+ public int read(byte[] b) throws IOException {
+ return portletInputStream.read(b);
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.InputStream#reset()
+ */
+ @Override
+ public synchronized void reset() throws IOException {
+ portletInputStream.reset();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.InputStream#skip(long)
+ */
+ @Override
+ public long skip(long n) throws IOException {
+ return portletInputStream.skip(n);
+ }
+
+ /**
+ * Get the wrapped {@link InputStream} instance.
+ * @return The wrapped {@link InputStream} instance.
+ */
+ public InputStream getInputStream() {
+ return portletInputStream;
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletOutputStream.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletOutputStream.java
new file mode 100644
index 0000000..871b3d2
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletOutputStream.java
@@ -0,0 +1,89 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.servlet;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * Wrapper object exposing a {@link OutputStream} from a portlet as a {@link ServletOutputStream} instance.
+ * Clients accessing this stream object will in fact operate on the
+ * {@link OutputStream} object wrapped by this stream object.
+ */
+public class PortletServletOutputStream extends ServletOutputStream {
+
+ private OutputStream portletOutputStream;
+
+ public PortletServletOutputStream(OutputStream portletOutputStream) {
+ this.portletOutputStream = portletOutputStream;
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#write(int)
+ */
+ @Override
+ public void write(int ch) throws IOException {
+ portletOutputStream.write(ch);
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#close()
+ */
+ @Override
+ public void close() throws IOException {
+ portletOutputStream.close();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#flush()
+ */
+ @Override
+ public void flush() throws IOException {
+ portletOutputStream.flush();
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#write(byte[])
+ */
+ @Override
+ public void write(byte[] b) throws IOException {
+ portletOutputStream.write(b);
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.OutputStream#write(byte[], int, int)
+ */
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ portletOutputStream.write(b, off, len);
+ }
+
+ /**
+ * Get the wrapped {@link OutputStream} instance.
+ * @return The wrapped {@link OutputStream} instance.
+ */
+ public OutputStream getOutputStream() {
+ return portletOutputStream;
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletRequest.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletRequest.java
new file mode 100644
index 0000000..b0a7303
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletRequest.java
@@ -0,0 +1,679 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.servlet;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletRequestDispatcher;
+import javax.portlet.PortletSession;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+
+import com.opensymphony.xwork2.inject.Inject;
+
+/**
+ * Wrapper object exposing a {@link PortletRequest} as a
+ * {@link HttpServletRequest} instance. Clients accessing this request object
+ * will in fact operate on the {@link PortletRequest} object wrapped by this
+ * request object.
+ */
+public class PortletServletRequest implements HttpServletRequest, PortletActionConstants {
+
+ private PortletRequest portletRequest;
+
+ private PortletContext portletContext;
+
+ private String extension;
+
+ public PortletServletRequest(PortletRequest portletRequest, PortletContext portletContext) {
+ this.portletRequest = portletRequest;
+ this.portletContext = portletContext;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#getAuthType()
+ */
+ public String getAuthType() {
+ return portletRequest.getAuthType();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#getContextPath()
+ */
+ public String getContextPath() {
+ return portletRequest.getContextPath();
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public Cookie[] getCookies() {
+ if (portletRequest instanceof HttpServletRequest) {
+ return ((HttpServletRequest) portletRequest).getCookies();
+ }
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public long getDateHeader(String name) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Gets a property from the {@link PortletRequest}. Note that a
+ * {@link PortletRequest} is not guaranteed to map properties to headers.
+ *
+ * @see PortletRequest#getProperty(String)
+ * @see javax.servlet.http.HttpServletRequest#getHeader(java.lang.String)
+ */
+ public String getHeader(String name) {
+ return portletRequest.getProperty(name);
+ }
+
+ /**
+ * Gets the property names from the {@link PortletRequest}. Note that a
+ * {@link PortletRequest} is not guaranteed to map properties to headers.
+ *
+ * @see PortletRequest#getPropertyNames()
+ * @see javax.servlet.http.HttpServletRequest#getHeaderNames()
+ */
+ public Enumeration getHeaderNames() {
+ return portletRequest.getPropertyNames();
+ }
+
+ /**
+ * Gets the values for the specified property from the
+ * {@link PortletRequest}. Note that a {@link PortletRequest} is not
+ * guaranteed to map properties to headers.
+ *
+ * @see PortletRequest#getProperties(String)
+ * @see HttpServletRequest#getHeaders(String)
+ */
+ public Enumeration getHeaders(String name) {
+ return portletRequest.getProperties(name);
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public int getIntHeader(String name) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#getMethod()
+ */
+ public String getMethod() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#getPathInfo()
+ */
+ public String getPathInfo() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#getPathTranslated()
+ */
+ public String getPathTranslated() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#getQueryString()
+ */
+ public String getQueryString() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#getRemoteUser()
+ */
+ public String getRemoteUser() {
+ return portletRequest.getRemoteUser();
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public String getRequestURI() {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public StringBuffer getRequestURL() {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#getRequestedSessionId()
+ */
+ public String getRequestedSessionId() {
+ return portletRequest.getRequestedSessionId();
+ }
+
+ /**
+ * A {@link PortletRequest} has no servlet path. But for compatibility with
+ * Struts 2 components and interceptors, the action parameter on the request
+ * is mapped to the servlet path.
+ *
+ * @see javax.servlet.http.HttpServletRequest#getServletPath()
+ */
+ public String getServletPath() {
+ String actionPath = getParameter(ACTION_PARAM);
+ if (!hasExtension(actionPath)) {
+ actionPath += "." + extension;
+ }
+ return actionPath;
+ }
+
+ private boolean hasExtension(String actionPath) {
+ return extension == null || "".equals(extension)
+ || (actionPath != null && actionPath.endsWith("." + extension));
+ }
+
+ /**
+ * Get the {@link PortletSession} as a {@link PortletHttpSession} instance.
+ *
+ * @see javax.servlet.http.HttpServletRequest#getSession()
+ */
+ public HttpSession getSession() {
+ return new PortletHttpSession(portletRequest.getPortletSession());
+ }
+
+ /**
+ * Get the {@link PortletSession} as a {@link PortletHttpSession} instance.
+ *
+ * @see javax.servlet.http.HttpServletRequest#getSession(boolean)
+ */
+ public HttpSession getSession(boolean create) {
+ return new PortletHttpSession(portletRequest.getPortletSession(create));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
+ */
+ public Principal getUserPrincipal() {
+ return portletRequest.getUserPrincipal();
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public boolean isRequestedSessionIdFromCookie() {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public boolean isRequestedSessionIdFromURL() {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public boolean isRequestedSessionIdFromUrl() {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdValid()
+ */
+ public boolean isRequestedSessionIdValid() {
+ return portletRequest.isRequestedSessionIdValid();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServletRequest#isUserInRole(java.lang.String)
+ */
+ public boolean isUserInRole(String role) {
+ return portletRequest.isUserInRole(role);
+ }
+
+ /**
+ * Gets an attribute value on the {@link PortletRequest}. If the attribute
+ * name is <tt>javax.servlet.include.servlet_path</tt>, it returns the
+ * same as {@link PortletServletRequest#getServletPath()}
+ *
+ * @see javax.servlet.ServletRequest#getAttribute(java.lang.String)
+ */
+ public Object getAttribute(String name) {
+ if ("javax.servlet.include.servlet_path".equals(name)) {
+ return getServletPath();
+ } else {
+ return portletRequest.getAttribute(name);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getAttributeNames()
+ */
+ public Enumeration getAttributeNames() {
+ return portletRequest.getAttributeNames();
+ }
+
+ /**
+ * Can only be invoked in the event phase.
+ *
+ * @see ServletRequest#getCharacterEncoding()
+ * @throws IllegalStateException
+ * If the portlet is not in the event phase.
+ */
+ public String getCharacterEncoding() {
+ if (portletRequest instanceof ActionRequest) {
+ return ((ActionRequest) portletRequest).getCharacterEncoding();
+ } else {
+ throw new IllegalStateException("Not allowed in render phase");
+ }
+ }
+
+ /**
+ * Can only be invoked in the event phase.
+ *
+ * @see ServletRequest#getContentLength()
+ * @throws IllegalStateException
+ * If the portlet is not in the event phase.
+ */
+ public int getContentLength() {
+ if (portletRequest instanceof ActionRequest) {
+ return ((ActionRequest) portletRequest).getContentLength();
+ } else {
+ throw new IllegalStateException("Not allowed in render phase");
+ }
+ }
+
+ /**
+ * Can only be invoked in the event phase.
+ *
+ * @see ServletRequest#getContentType()
+ * @throws IllegalStateException
+ * If the portlet is not in the event phase.
+ */
+ public String getContentType() {
+ if (portletRequest instanceof ActionRequest) {
+ return ((ActionRequest) portletRequest).getContentType();
+ } else {
+ throw new IllegalStateException("Not allowed in render phase");
+ }
+ }
+
+ /**
+ * Can only be invoked in the event phase. When invoked in the event phase,
+ * it will wrap the portlet's {@link InputStream} as a
+ * {@link PortletServletInputStream}.
+ *
+ * @see ServletRequest#getInputStream()
+ * @throws IllegalStateException
+ * If the portlet is not in the event phase.
+ */
+ public ServletInputStream getInputStream() throws IOException {
+ if (portletRequest instanceof ActionRequest) {
+ return new PortletServletInputStream(((ActionRequest) portletRequest).getPortletInputStream());
+ } else {
+ throw new IllegalStateException("Not allowed in render phase");
+ }
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public String getLocalAddr() {
+ if (portletRequest instanceof HttpServletRequest) {
+ return ((HttpServletRequest) portletRequest).getLocalAddr();
+ }
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public String getLocalName() {
+ if (portletRequest instanceof HttpServletRequest) {
+ return ((HttpServletRequest) portletRequest).getLocalName();
+ }
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public int getLocalPort() {
+ if (portletRequest instanceof HttpServletRequest) {
+ return ((HttpServletRequest) portletRequest).getLocalPort();
+ }
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getLocale()
+ */
+ public Locale getLocale() {
+ return portletRequest.getLocale();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getLocales()
+ */
+ public Enumeration getLocales() {
+ return portletRequest.getLocales();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
+ */
+ public String getParameter(String name) {
+ return portletRequest.getParameter(name);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getParameterMap()
+ */
+ public Map getParameterMap() {
+ return portletRequest.getParameterMap();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getParameterNames()
+ */
+ public Enumeration getParameterNames() {
+ return portletRequest.getParameterNames();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
+ */
+ public String[] getParameterValues(String name) {
+ return portletRequest.getParameterValues(name);
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public String getProtocol() {
+ if (portletRequest instanceof HttpServletRequest) {
+ return ((HttpServletRequest) portletRequest).getProtocol();
+ }
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Can only be invoked in the event phase.
+ *
+ * @see ServletRequest#getReader()
+ * @throws IllegalStateException
+ * If the portlet is not in the event phase.
+ */
+ public BufferedReader getReader() throws IOException {
+ if (portletRequest instanceof ActionRequest) {
+ return ((ActionRequest) portletRequest).getReader();
+ } else {
+ throw new IllegalStateException("Not allowed in render phase");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getRealPath(java.lang.String)
+ */
+ public String getRealPath(String path) {
+ return portletContext.getRealPath(path);
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public String getRemoteAddr() {
+ if (portletRequest instanceof HttpServletRequest) {
+ return ((HttpServletRequest) portletRequest).getRemoteAddr();
+ }
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public String getRemoteHost() {
+ if (portletRequest instanceof HttpServletRequest) {
+ return ((HttpServletRequest) portletRequest).getRemoteHost();
+ }
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public int getRemotePort() {
+ if (portletRequest instanceof HttpServletRequest) {
+ return ((HttpServletRequest) portletRequest).getRemotePort();
+ }
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /**
+ * Get the {@link PortletRequestDispatcher} as a
+ * {@link PortletServletRequestDispatcher} instance.
+ *
+ * @see javax.servlet.ServletRequest#getRequestDispatcher(java.lang.String)
+ */
+ public RequestDispatcher getRequestDispatcher(String path) {
+ return new PortletServletRequestDispatcher(portletContext.getRequestDispatcher(path));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getScheme()
+ */
+ public String getScheme() {
+ return portletRequest.getScheme();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#getServerName()
+ */
+ public String getServerName() {
+ return portletRequest.getServerName();
+ }
+
+ /**
+ * Not allowed in a portlet.
+ *
+ * @throws IllegalStateException
+ * Not allowed in a portlet.
+ */
+ public int getServerPort() {
+ if (portletRequest instanceof HttpServletRequest) {
+ return ((HttpServletRequest) portletRequest).getServerPort();
+ }
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#isSecure()
+ */
+ public boolean isSecure() {
+ return portletRequest.isSecure();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#removeAttribute(java.lang.String)
+ */
+ public void removeAttribute(String name) {
+ portletRequest.removeAttribute(name);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletRequest#setAttribute(java.lang.String,
+ * java.lang.Object)
+ */
+ public void setAttribute(String name, Object o) {
+ portletRequest.setAttribute(name, o);
+ }
+
+ /**
+ * Can only be invoked in the event phase.
+ *
+ * @see ServletRequest#setCharacterEncoding(String)
+ * @throws IllegalStateException
+ * If the portlet is not in the event phase.
+ */
+ public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
+ if (portletRequest instanceof ActionRequest) {
+ ((ActionRequest) portletRequest).setCharacterEncoding(env);
+ } else {
+ throw new IllegalStateException("Not allowed in render phase");
+ }
+ }
+
+ /**
+ * Get the wrapped {@link PortletRequest} instance.
+ *
+ * @return The wrapped {@link PortletRequest} instance.
+ */
+ public PortletRequest getPortletRequest() {
+ return portletRequest;
+ }
+
+ @Inject(StrutsConstants.STRUTS_ACTION_EXTENSION)
+ public void setExtension(String extension) {
+ if (extension != null) {
+ this.extension = extension.split(",")[0];
+ }
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletRequestDispatcher.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletRequestDispatcher.java
new file mode 100644
index 0000000..54238f9
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletRequestDispatcher.java
@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.servlet;
+
+import java.io.IOException;
+
+import javax.portlet.PortletException;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletRequestDispatcher;
+import javax.portlet.PortletResponse;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+public class PortletServletRequestDispatcher implements RequestDispatcher {
+
+ private PortletRequestDispatcher portletRequestDispatcher;
+
+ public PortletServletRequestDispatcher(PortletRequestDispatcher portletRequestDispatcher) {
+ this.portletRequestDispatcher = portletRequestDispatcher;
+ }
+
+ public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+ throw new IllegalStateException("Not allowed in a portlet");
+
+ }
+
+ public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+ if(request instanceof PortletServletRequest && response instanceof PortletServletResponse) {
+ PortletRequest req = ((PortletServletRequest)request).getPortletRequest();
+ PortletResponse resp = ((PortletServletResponse)response).getPortletResponse();
+ if(req instanceof RenderRequest && resp instanceof RenderResponse) {
+ try {
+ portletRequestDispatcher.include((RenderRequest)req, (RenderResponse)resp);
+ }
+ catch(PortletException e) {
+ throw new ServletException(e);
+ }
+ }
+ else {
+ throw new IllegalStateException("Can only be invoked in the render phase");
+ }
+ }
+ else {
+ throw new IllegalStateException("Can only be invoked in a portlet");
+ }
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletResponse.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletResponse.java
new file mode 100644
index 0000000..7e4c12b
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/PortletServletResponse.java
@@ -0,0 +1,234 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+import javax.portlet.PortletResponse;
+import javax.portlet.RenderResponse;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+public class PortletServletResponse implements HttpServletResponse {
+
+ private PortletResponse portletResponse;
+
+ public PortletServletResponse(PortletResponse portletResponse) {
+ this.portletResponse = portletResponse;
+ }
+
+ public void addCookie(Cookie cookie) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void addDateHeader(String name, long date) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void addHeader(String name, String value) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void addIntHeader(String name, int value) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public boolean containsHeader(String name) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public String encodeRedirectURL(String url) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public String encodeRedirectUrl(String url) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public String encodeURL(String url) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public String encodeUrl(String url) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void sendError(int sc) throws IOException {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void sendError(int sc, String msg) throws IOException {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void sendRedirect(String location) throws IOException {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void setDateHeader(String name, long date) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void setHeader(String name, String value) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void setIntHeader(String name, int value) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void setStatus(int sc) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void setStatus(int sc, String sm) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void flushBuffer() throws IOException {
+ if(portletResponse instanceof RenderResponse) {
+ ((RenderResponse)portletResponse).flushBuffer();
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public int getBufferSize() {
+ if(portletResponse instanceof RenderResponse) {
+ return ((RenderResponse)portletResponse).getBufferSize();
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public String getCharacterEncoding() {
+ if(portletResponse instanceof RenderResponse) {
+ return ((RenderResponse)portletResponse).getCharacterEncoding();
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public String getContentType() {
+ if(portletResponse instanceof RenderResponse) {
+ return ((RenderResponse)portletResponse).getContentType();
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public Locale getLocale() {
+ if(portletResponse instanceof RenderResponse) {
+ return ((RenderResponse)portletResponse).getLocale();
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException {
+ if(portletResponse instanceof RenderResponse) {
+ return new PortletServletOutputStream(((RenderResponse)portletResponse).getPortletOutputStream());
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ if(portletResponse instanceof RenderResponse) {
+ return ((RenderResponse)portletResponse).getWriter();
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public boolean isCommitted() {
+ if(portletResponse instanceof RenderResponse) {
+ return ((RenderResponse)portletResponse).isCommitted();
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public void reset() {
+ if(portletResponse instanceof RenderResponse) {
+ ((RenderResponse)portletResponse).reset();
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public void resetBuffer() {
+ if(portletResponse instanceof RenderResponse) {
+ ((RenderResponse)portletResponse).resetBuffer();
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public void setBufferSize(int size) {
+ if(portletResponse instanceof RenderResponse) {
+ ((RenderResponse)portletResponse).setBufferSize(size);
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public void setCharacterEncoding(String charset) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void setContentLength(int len) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public void setContentType(String type) {
+ if(portletResponse instanceof RenderResponse) {
+ ((RenderResponse)portletResponse).setContentType(type);
+ }
+ else {
+ throw new IllegalStateException("Not allowed in event phase");
+ }
+ }
+
+ public void setLocale(Locale loc) {
+ throw new IllegalStateException("Not allowed in a portlet");
+ }
+
+ public PortletResponse getPortletResponse() {
+ return portletResponse;
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/package.html b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/package.html
new file mode 100644
index 0000000..81981fe
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/servlet/package.html
@@ -0,0 +1,27 @@
+<!--
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+-->
+<body>
+ Portlet wrapper objects for some of the servlet api objects. The wrappers simply delegate to the
+ underlying, corresponding portlet objects. The portlet wrapper objects makes it easier to reuse
+ interceptors and components from struts2 core.
+</body>
\ No newline at end of file
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/util/PortletUrlHelper.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/util/PortletUrlHelper.java
new file mode 100644
index 0000000..2c80778
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/portlet/util/PortletUrlHelper.java
@@ -0,0 +1,300 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.util;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import org.apache.commons.lang.StringUtils;
+import org.apache.struts2.StrutsException;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+
+import javax.portlet.*;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * Helper class for creating Portlet URLs. Portlet URLs are fundamentally different from regular
+ * servlet URLs since they never target the application itself; all requests go through the portlet
+ * container and must therefore be programatically constructed using the
+ * {@link javax.portlet.RenderResponse#createActionURL()} and
+ * {@link javax.portlet.RenderResponse#createRenderURL()} APIs.
+ *
+ */
+public class PortletUrlHelper {
+ public static final String ENCODING = "UTF-8";
+
+ private static final Logger LOG = LoggerFactory.getLogger(PortletUrlHelper.class);
+
+ /**
+ * Create a portlet URL with for the specified action and namespace.
+ *
+ * @param action The action the URL should invoke.
+ * @param namespace The namespace of the action to invoke.
+ * @param method The method of the action to invoke.
+ * @param params The parameters of the URL.
+ * @param type The type of the url, either <tt>action</tt> or <tt>render</tt>
+ * @param mode The PortletMode of the URL.
+ * @param state The WindowState of the URL.
+ * @return The URL String.
+ */
+ public static String buildUrl(String action, String namespace, String method, Map params,
+ String type, String mode, String state) {
+ return buildUrl(action, namespace, method, params, null, type, mode, state,
+ true, true);
+ }
+
+ /**
+ * Create a portlet URL with for the specified action and namespace.
+ *
+ * @see #buildUrl(String, String, Map, String, String, String)
+ */
+ public static String buildUrl(String action, String namespace, String method, Map params,
+ String scheme, String type, String portletMode, String windowState,
+ boolean includeContext, boolean encodeResult) {
+ StringBuffer resultingAction = new StringBuffer();
+ RenderRequest request = PortletActionContext.getRenderRequest();
+ RenderResponse response = PortletActionContext.getRenderResponse();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Creating url. Action = " + action + ", Namespace = "
+ + namespace + ", Type = " + type);
+ }
+ namespace = prependNamespace(namespace, portletMode);
+ if (StringUtils.isEmpty(portletMode)) {
+ portletMode = PortletActionContext.getRenderRequest().getPortletMode().toString();
+ }
+ String result = null;
+ int paramStartIndex = action.indexOf('?');
+ if (paramStartIndex > 0) {
+ String value = action;
+ action = value.substring(0, value.indexOf('?'));
+ String queryStr = value.substring(paramStartIndex + 1);
+ StringTokenizer tok = new StringTokenizer(queryStr, "&");
+ while (tok.hasMoreTokens()) {
+ String paramVal = tok.nextToken();
+ String key = paramVal.substring(0, paramVal.indexOf('='));
+ String val = paramVal.substring(paramVal.indexOf('=') + 1);
+ params.put(key, new String[] { val });
+ }
+ }
+ if (StringUtils.isNotEmpty(namespace)) {
+ resultingAction.append(namespace);
+ if(!action.startsWith("/") && !namespace.endsWith("/")) {
+ resultingAction.append("/");
+ }
+ }
+ resultingAction.append(action);
+ if(StringUtils.isNotEmpty(method)) {
+ resultingAction.append("!").append(method);
+ }
+ if (LOG.isDebugEnabled()) LOG.debug("Resulting actionPath: " + resultingAction);
+ params.put(PortletActionConstants.ACTION_PARAM, new String[] { resultingAction.toString() });
+
+ PortletURL url = null;
+ if ("action".equalsIgnoreCase(type)) {
+ if (LOG.isDebugEnabled()) LOG.debug("Creating action url");
+ url = response.createActionURL();
+ } else {
+ if (LOG.isDebugEnabled()) LOG.debug("Creating render url");
+ url = response.createRenderURL();
+ }
+
+ params.put(PortletActionConstants.MODE_PARAM, portletMode);
+ url.setParameters(ensureParamsAreStringArrays(params));
+
+ if ("HTTPS".equalsIgnoreCase(scheme)) {
+ try {
+ url.setSecure(true);
+ } catch (PortletSecurityException e) {
+ LOG.error("Cannot set scheme to https", e);
+ }
+ }
+ try {
+ url.setPortletMode(getPortletMode(request, portletMode));
+ url.setWindowState(getWindowState(request, windowState));
+ } catch (Exception e) {
+ LOG.error("Unable to set mode or state:" + e.getMessage(), e);
+ }
+ result = url.toString();
+ // TEMP BUG-WORKAROUND FOR DOUBLE ESCAPING OF AMPERSAND
+ if(result.indexOf("&") >= 0) {
+ result = result.replace("&", "&");
+ }
+ return result;
+
+ }
+
+ /**
+ *
+ * Prepend the namespace configuration for the specified namespace and PortletMode.
+ *
+ * @param namespace The base namespace.
+ * @param portletMode The PortletMode.
+ *
+ * @return prepended namespace.
+ */
+ private static String prependNamespace(String namespace, String portletMode) {
+ StringBuffer sb = new StringBuffer();
+ PortletMode mode = PortletActionContext.getRenderRequest().getPortletMode();
+ if(StringUtils.isNotEmpty(portletMode)) {
+ mode = new PortletMode(portletMode);
+ }
+ String portletNamespace = PortletActionContext.getPortletNamespace();
+ String modeNamespace = (String)PortletActionContext.getModeNamespaceMap().get(mode);
+ if (LOG.isDebugEnabled()) LOG.debug("PortletNamespace: " + portletNamespace + ", modeNamespace: " + modeNamespace);
+ if(StringUtils.isNotEmpty(portletNamespace)) {
+ sb.append(portletNamespace);
+ }
+ if(StringUtils.isNotEmpty(modeNamespace)) {
+ if(!modeNamespace.startsWith("/")) {
+ sb.append("/");
+ }
+ sb.append(modeNamespace);
+ }
+ if(StringUtils.isNotEmpty(namespace)) {
+ if(!namespace.startsWith("/")) {
+ sb.append("/");
+ }
+ sb.append(namespace);
+ }
+ if (LOG.isDebugEnabled()) LOG.debug("Resulting namespace: " + sb);
+ return sb.toString();
+ }
+
+ /**
+ * Encode an url to a non Struts action resource, like stylesheet, image or
+ * servlet.
+ *
+ * @param value
+ * @return encoded url to non Struts action resources.
+ */
+ public static String buildResourceUrl(String value, Map<String, Object> params) {
+ StringBuffer sb = new StringBuffer();
+ // Relative URLs are not allowed in a portlet
+ if (!value.startsWith("/")) {
+ sb.append("/");
+ }
+ sb.append(value);
+ if(params != null && params.size() > 0) {
+ sb.append("?");
+ Iterator<Map.Entry<String, Object>> it = params.entrySet().iterator();
+ try {
+ while(it.hasNext()) {
+ Map.Entry<String, Object> entry = it.next();
+
+ sb.append(URLEncoder.encode(entry.getKey(), ENCODING)).append("=");
+ sb.append(URLEncoder.encode(entry.getValue().toString(), ENCODING));
+ if(it.hasNext()) {
+ sb.append("&");
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new StrutsException("Encoding "+ENCODING+" not found");
+ }
+ }
+ RenderResponse resp = PortletActionContext.getRenderResponse();
+ RenderRequest req = PortletActionContext.getRenderRequest();
+ return resp.encodeURL(req.getContextPath() + sb.toString());
+ }
+
+ /**
+ * Will ensure that all entries in <code>params</code> are String arrays,
+ * as requried by the setParameters on the PortletURL.
+ *
+ * @param params The parameters to the URL.
+ * @return A Map with all parameters as String arrays.
+ */
+ public static Map ensureParamsAreStringArrays(Map<String, Object> params) {
+ Map<String, String[]> result = null;
+ if (params != null) {
+ result = new LinkedHashMap<String, String[]>(params.size());
+ Iterator<Map.Entry<String, Object>> it = params.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, Object> entry = it.next();
+ Object val = entry.getValue();
+ if (val instanceof String[]) {
+ result.put(entry.getKey(), (String[])val);
+ } else {
+ result.put(entry.getKey(), new String[] { val.toString() });
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Convert the given String to a WindowState object.
+ *
+ * @param portletReq The RenderRequest.
+ * @param windowState The WindowState as a String.
+ * @return The WindowState that mathces the <tt>windowState</tt> String, or if
+ * the String is blank, the current WindowState.
+ */
+ private static WindowState getWindowState(RenderRequest portletReq,
+ String windowState) {
+ WindowState state = portletReq.getWindowState();
+ if (StringUtils.isNotEmpty(windowState)) {
+ if ("maximized".equalsIgnoreCase(windowState)) {
+ state = WindowState.MAXIMIZED;
+ } else if ("normal".equalsIgnoreCase(windowState)) {
+ state = WindowState.NORMAL;
+ } else if ("minimized".equalsIgnoreCase(windowState)) {
+ state = WindowState.MINIMIZED;
+ }
+ }
+ if(state == null) {
+ state = WindowState.NORMAL;
+ }
+ return state;
+ }
+
+ /**
+ * Convert the given String to a PortletMode object.
+ *
+ * @param portletReq The RenderRequest.
+ * @param portletMode The PortletMode as a String.
+ * @return The PortletMode that mathces the <tt>portletMode</tt> String, or if
+ * the String is blank, the current PortletMode.
+ */
+ private static PortletMode getPortletMode(RenderRequest portletReq,
+ String portletMode) {
+ PortletMode mode = portletReq.getPortletMode();
+
+ if (StringUtils.isNotEmpty(portletMode)) {
+ if ("edit".equalsIgnoreCase(portletMode)) {
+ mode = PortletMode.EDIT;
+ } else if ("view".equalsIgnoreCase(portletMode)) {
+ mode = PortletMode.VIEW;
+ } else if ("help".equalsIgnoreCase(portletMode)) {
+ mode = PortletMode.HELP;
+ }
+ }
+ if(mode == null) {
+ mode = PortletMode.VIEW;
+ }
+ return mode;
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/views/freemarker/PortletFreemarkerResult.java b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/views/freemarker/PortletFreemarkerResult.java
new file mode 100644
index 0000000..1d27f79
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/java/org/apache/struts2/views/freemarker/PortletFreemarkerResult.java
@@ -0,0 +1,282 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.views.freemarker;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Locale;
+
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletException;
+import javax.portlet.PortletRequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.dispatcher.StrutsResultSupport;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.apache.struts2.views.util.ResourceUtil;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.ValueStack;
+
+import freemarker.template.Configuration;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ */
+public class PortletFreemarkerResult extends StrutsResultSupport implements PortletActionConstants{
+
+ private static final long serialVersionUID = -5570612389289887543L;
+
+ protected ActionInvocation invocation;
+
+ protected Configuration configuration;
+
+ protected ObjectWrapper wrapper;
+ protected FreemarkerManager freemarkerManager;
+
+ /*
+ * Struts results are constructed for each result execeution
+ *
+ * the current context is availible to subclasses via these protected fields
+ */
+ protected String location;
+
+ private String pContentType = "text/html";
+
+ public PortletFreemarkerResult() {
+ super();
+ }
+
+ public PortletFreemarkerResult(String location) {
+ super(location);
+ }
+
+ @Inject
+ public void setFreemarkerManager(FreemarkerManager mgr) {
+ this.freemarkerManager = mgr;
+ }
+
+ public void setContentType(String aContentType) {
+ pContentType = aContentType;
+ }
+
+ /**
+ * allow parameterization of the contentType the default being text/html
+ */
+ public String getContentType() {
+ return pContentType;
+ }
+
+ /**
+ * Execute this result, using the specified template location. <p/>The
+ * template location has already been interoplated for any variable
+ * substitutions <p/>this method obtains the freemarker configuration and
+ * the object wrapper from the provided hooks. It them implements the
+ * template processing workflow by calling the hooks for preTemplateProcess
+ * and postTemplateProcess
+ */
+ public void doExecute(String location, ActionInvocation invocation)
+ throws IOException, TemplateException, PortletException {
+ if (PortletActionContext.isEvent()) {
+ executeActionResult(location, invocation);
+ } else if (PortletActionContext.isRender()) {
+ executeRenderResult(location, invocation);
+ }
+ }
+
+ /**
+ * @param location
+ * @param invocation
+ */
+ private void executeActionResult(String location,
+ ActionInvocation invocation) {
+ ActionResponse res = PortletActionContext.getActionResponse();
+ // View is rendered outside an action...uh oh...
+ invocation.getInvocationContext().getSession().put(RENDER_DIRECT_LOCATION, location);
+ res.setRenderParameter(PortletActionConstants.ACTION_PARAM, "freemarkerDirect");
+ res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext
+ .getRequest().getPortletMode().toString());
+
+ }
+
+ /**
+ * @param location
+ * @param invocation
+ * @throws TemplateException
+ * @throws IOException
+ * @throws TemplateModelException
+ */
+ private void executeRenderResult(String location,
+ ActionInvocation invocation) throws TemplateException, IOException,
+ TemplateModelException, PortletException {
+ this.location = location;
+ this.invocation = invocation;
+ this.configuration = getConfiguration();
+ this.wrapper = getObjectWrapper();
+
+ HttpServletRequest req = ServletActionContext.getRequest();
+
+ if (!location.startsWith("/")) {
+ String base = ResourceUtil.getResourceBase(req);
+ location = base + "/" + location;
+ }
+
+ Template template = configuration.getTemplate(location, deduceLocale());
+ TemplateModel model = createModel();
+ // Give subclasses a chance to hook into preprocessing
+ if (preTemplateProcess(template, model)) {
+ try {
+ // Process the template
+ PortletActionContext.getRenderResponse().setContentType(pContentType);
+ template.process(model, getWriter());
+ } finally {
+ // Give subclasses a chance to hook into postprocessing
+ postTemplateProcess(template, model);
+ }
+ }
+ }
+
+ /**
+ * This method is called from {@link #doExecute(String, ActionInvocation)}
+ * to obtain the FreeMarker configuration object that this result will use
+ * for template loading. This is a hook that allows you to custom-configure
+ * the configuration object in a subclass, or to fetch it from an IoC
+ * container. <p/><b>The default implementation obtains the configuration
+ * from the ConfigurationManager instance. </b>
+ */
+ protected Configuration getConfiguration() throws TemplateException {
+ return freemarkerManager.getConfiguration(
+ ServletActionContext.getServletContext());
+ }
+
+ /**
+ * This method is called from {@link #doExecute(String, ActionInvocation)}
+ * to obtain the FreeMarker object wrapper object that this result will use
+ * for adapting objects into template models. This is a hook that allows you
+ * to custom-configure the wrapper object in a subclass. <p/><b>The default
+ * implementation returns {@link Configuration#getObjectWrapper()}</b>
+ */
+ protected ObjectWrapper getObjectWrapper() {
+ return configuration.getObjectWrapper();
+ }
+
+ /**
+ * The default writer writes directly to the response writer.
+ */
+ protected Writer getWriter() throws IOException {
+ return PortletActionContext.getRenderResponse().getWriter();
+ }
+
+ /**
+ * Build the instance of the ScopesHashModel, including JspTagLib support
+ * <p/>Objects added to the model are <p/>
+ * <ul>
+ * <li>Application - servlet context attributes hash model
+ * <li>JspTaglibs - jsp tag lib factory model
+ * <li>Request - request attributes hash model
+ * <li>Session - session attributes hash model
+ * <li>request - the HttpServletRequst object for direct access
+ * <li>response - the HttpServletResponse object for direct access
+ * <li>stack - the OgnLValueStack instance for direct access
+ * <li>ognl - the instance of the OgnlTool
+ * <li>action - the action itself
+ * <li>exception - optional : the JSP or Servlet exception as per the
+ * servlet spec (for JSP Exception pages)
+ * <li>struts - instance of the StrutsUtil class
+ * </ul>
+ */
+ protected TemplateModel createModel() throws TemplateModelException {
+ ServletContext servletContext = ServletActionContext
+ .getServletContext();
+ HttpServletRequest request = ServletActionContext.getRequest();
+ HttpServletResponse response = ServletActionContext.getResponse();
+ ValueStack stack = ServletActionContext.getContext()
+ .getValueStack();
+ return freemarkerManager.buildTemplateModel(stack,
+ invocation.getAction(), servletContext, request, response,
+ wrapper);
+ }
+
+ /**
+ * Returns the locale used for the
+ * {@link Configuration#getTemplate(String, Locale)}call. The base
+ * implementation simply returns the locale setting of the configuration.
+ * Override this method to provide different behaviour,
+ */
+ protected Locale deduceLocale() {
+ return configuration.getLocale();
+ }
+
+ /**
+ * the default implementation of postTemplateProcess applies the contentType
+ * parameter
+ */
+ protected void postTemplateProcess(Template template, TemplateModel data)
+ throws IOException {
+ }
+
+ /**
+ * Called before the execution is passed to template.process(). This is a
+ * generic hook you might use in subclasses to perform a specific action
+ * before the template is processed. By default does nothing. A typical
+ * action to perform here is to inject application-specific objects into the
+ * model root
+ *
+ * @return true to process the template, false to suppress template
+ * processing.
+ */
+ protected boolean preTemplateProcess(Template template, TemplateModel model)
+ throws IOException {
+ Object attrContentType = template.getCustomAttribute("content_type");
+
+ if (attrContentType != null) {
+ ServletActionContext.getResponse().setContentType(
+ attrContentType.toString());
+ } else {
+ String contentType = getContentType();
+
+ if (contentType == null) {
+ contentType = "text/html";
+ }
+
+ String encoding = template.getEncoding();
+
+ if (encoding != null) {
+ contentType = contentType + "; charset=" + encoding;
+ }
+
+ ServletActionContext.getResponse().setContentType(contentType);
+ }
+
+ return true;
+ }
+}
+
diff --git a/plugins/struts2-portlet-plugin/src/main/resources/LICENSE.txt b/plugins/struts2-portlet-plugin/src/main/resources/LICENSE.txt
new file mode 100644
index 0000000..dd5b3a5
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/resources/LICENSE.txt
@@ -0,0 +1,174 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
diff --git a/plugins/struts2-portlet-plugin/src/main/resources/NOTICE.txt b/plugins/struts2-portlet-plugin/src/main/resources/NOTICE.txt
new file mode 100644
index 0000000..bfba90c
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/resources/NOTICE.txt
@@ -0,0 +1,5 @@
+Apache Struts
+Copyright 2000-2011 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
\ No newline at end of file
diff --git a/plugins/struts2-portlet-plugin/src/main/resources/struts-plugin.xml b/plugins/struts2-portlet-plugin/src/main/resources/struts-plugin.xml
new file mode 100644
index 0000000..29b99bc
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/main/resources/struts-plugin.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+-->
+<!DOCTYPE struts PUBLIC
+ "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
+ "http://struts.apache.org/dtds/struts-2.0.dtd">
+
+<struts>
+
+ <bean type="org.apache.struts2.components.UrlRenderer" name="portlet" class="org.apache.struts2.components.PortletUrlRenderer"/>
+
+ <constant name="struts.urlRenderer" value="portlet" />
+ <constant name="struts.portlet.useDispatcherServlet" value="false" />
+ <constant name="struts.portlet.dispatcherServletName" value="Struts2PortletDispatcherServlet" />
+ <constant name="struts.objectFactory.spring.autoWire.alwaysRespect" value="false" />
+
+ <package name="struts-portlet-default" extends="struts-default">
+
+ <result-types>
+ <result-type name="dispatcher" class="org.apache.struts2.portlet.result.PortletResult" default="true"/>
+ <result-type name="redirect" class="org.apache.struts2.portlet.result.PortletResult"/>
+ <result-type name="redirectAction" class="org.apache.struts2.portlet.result.PortletActionRedirectResult"/>
+ <result-type name="freemarker" class="org.apache.struts2.views.freemarker.PortletFreemarkerResult"/>
+ <result-type name="velocity" class="org.apache.struts2.portlet.result.PortletVelocityResult"/>
+ </result-types>
+
+ <interceptors>
+ <interceptor name="portletAware" class="org.apache.struts2.portlet.interceptor.PortletAwareInterceptor"/>
+ <interceptor name="portletState" class="org.apache.struts2.portlet.interceptor.PortletStateInterceptor"/>
+ <!-- Default stack for operating in portlet environment -->
+ <interceptor-stack name="portletDefaultStack">
+ <interceptor-ref name="portletState"/>
+ <interceptor-ref name="portletAware"/>
+ <interceptor-ref name="defaultStack"/>
+ </interceptor-stack>
+ <!-- Extension of the default portlet stack which also includes the token interceptor -->
+ <interceptor-stack name="portletDefaultStackWithToken">
+ <interceptor-ref name="portletState"/>
+ <interceptor-ref name="portletAware"/>
+ <interceptor-ref name="token"/>
+ <interceptor-ref name="defaultStack"/>
+ </interceptor-stack>
+
+ </interceptors>
+
+ <default-interceptor-ref name="portletDefaultStack"/>
+
+ <action name="renderDirect" class="org.apache.struts2.portlet.dispatcher.DirectRenderFromEventAction">
+ <result name="success">${location}</result>
+ </action>
+ <action name="freemarkerDirect" class="org.apache.struts2.portlet.dispatcher.DirectRenderFromEventAction">
+ <result type="freemarker" name="success">${location}</result>
+ </action>
+ <action name="velocityDirect" class="org.apache.struts2.portlet.dispatcher.DirectRenderFromEventAction">
+ <result type="velocity" name="success">${location}</result>
+ </action>
+ </package>
+</struts>
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/components/PortletUrlRendererTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/components/PortletUrlRendererTest.java
new file mode 100644
index 0000000..a18dc15
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/components/PortletUrlRendererTest.java
@@ -0,0 +1,237 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.components;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.portlet.PortletMode;
+import javax.portlet.PortletURL;
+
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.StrutsTestCase;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.apache.struts2.portlet.servlet.PortletServletRequest;
+import org.apache.struts2.portlet.servlet.PortletServletResponse;
+import org.easymock.EasyMock;
+import org.springframework.mock.web.portlet.MockPortalContext;
+import org.springframework.mock.web.portlet.MockPortletContext;
+import org.springframework.mock.web.portlet.MockPortletURL;
+import org.springframework.mock.web.portlet.MockRenderRequest;
+import org.springframework.mock.web.portlet.MockRenderResponse;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.mock.MockActionInvocation;
+import com.opensymphony.xwork2.mock.MockActionProxy;
+import com.opensymphony.xwork2.util.ValueStack;
+
+public class PortletUrlRendererTest extends StrutsTestCase {
+
+ PortletUrlRenderer renderer;
+ MockPortletURL renderUrl;
+ MockPortletURL actionUrl;
+ MockRenderRequest request;
+ MockRenderResponse response;
+ MockPortletContext context;
+ ActionContext ctx;
+ ValueStack stack;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ renderer = new PortletUrlRenderer();
+ context = new MockPortletContext();
+ renderUrl = new MockPortletURL(
+ new MockPortalContext(), "render");
+ actionUrl = new MockPortletURL(
+ new MockPortalContext(), "action");
+ request = new MockRenderRequest();
+ response = new MockRenderResponse() {
+ @Override
+ public PortletURL createActionURL() {
+ return actionUrl;
+ }
+ @Override
+ public PortletURL createRenderURL() {
+ return renderUrl;
+ }
+ };
+
+ ctx = ActionContext.getContext();
+ ctx.put(PortletActionConstants.PHASE,
+ PortletActionConstants.RENDER_PHASE);
+ ctx.put(PortletActionConstants.REQUEST, request);
+ ctx.put(PortletActionConstants.RESPONSE, response);
+ ctx.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, context);
+
+ Map<PortletMode, String> modeMap = new HashMap<PortletMode, String>();
+ modeMap.put(PortletMode.VIEW, "/view");
+ ctx.put(PortletActionConstants.MODE_NAMESPACE_MAP, modeMap);
+ stack = ctx.getValueStack();
+ }
+
+ /**
+ * Ensure that the namespace of the current executing action is used when no
+ * namespace is specified. (WW-1875)
+ */
+ public void testShouldIncludeCurrentNamespaceIfNoNamespaceSpecifiedForRenderUrl()
+ throws Exception {
+
+ URL url = new URL(stack, new PortletServletRequest(request, null),
+ new PortletServletResponse(response));
+
+ MockActionInvocation ai = new MockActionInvocation();
+ MockActionProxy ap = new MockActionProxy();
+ ap.setActionName("testAction");
+ ap.setNamespace("/current_namespace");
+ ai.setProxy(ap);
+ ai.setStack(stack);
+ ai.setAction(new Object());
+ ctx.setActionInvocation(ai);
+
+ StringWriter renderOutput = new StringWriter();
+ renderer.renderUrl(renderOutput, url.getUrlProvider());
+
+ String action = renderUrl
+ .getParameter(PortletActionConstants.ACTION_PARAM);
+ assertEquals("/view/current_namespace/testAction", action);
+ }
+
+ /**
+ * Ensure that the namespace of the current executing action is used when no
+ * namespace is specified. (WW-1875)
+ */
+ public void testShouldIncludeCurrentNamespaceIfNoNamespaceSpecifiedForRenderFormUrl()
+ throws Exception {
+
+ Form form = new Form(stack, new PortletServletRequest(request, null),
+ new PortletServletResponse(response));
+
+ MockActionInvocation ai = new MockActionInvocation();
+ MockActionProxy ap = new MockActionProxy();
+ ap.setActionName("testAction");
+ ap.setNamespace("/current_namespace");
+ ai.setProxy(ap);
+ ai.setStack(stack);
+ ai.setAction(new Object());
+ ctx.setActionInvocation(ai);
+
+ renderer.renderFormUrl(form);
+
+ String action = actionUrl
+ .getParameter(PortletActionConstants.ACTION_PARAM);
+ assertEquals("/view/current_namespace/testAction", action);
+ }
+
+ public void testShouldEvaluateActionAsOGNLExpression() throws Exception {
+
+ TestObject obj = new TestObject();
+ obj.someProperty = "EvaluatedProperty";
+ stack.push(obj);
+ MockActionInvocation ai = new MockActionInvocation();
+ MockActionProxy ap = new MockActionProxy();
+ ap.setActionName("testAction");
+ ap.setNamespace("");
+ ai.setProxy(ap);
+ ai.setStack(stack);
+ ctx.setActionInvocation(ai);
+
+ URL url = new URL(stack, new PortletServletRequest(request, null),
+ new PortletServletResponse(response));
+ url.setAction("%{someProperty}");
+
+ StringWriter renderOutput = new StringWriter();
+ renderer.renderUrl(renderOutput, url.getUrlProvider());
+
+ String action = renderUrl
+ .getParameter(PortletActionConstants.ACTION_PARAM);
+ assertEquals("/view/EvaluatedProperty", action);
+
+ }
+
+ public void testShouldEvaluateAnchorAsOGNLExpression() throws Exception {
+
+ TestObject obj = new TestObject();
+ obj.someProperty = "EvaluatedProperty";
+ stack.push(obj);
+ MockActionInvocation ai = new MockActionInvocation();
+ MockActionProxy ap = new MockActionProxy();
+ ap.setActionName("testAction");
+ ap.setNamespace("");
+ ai.setProxy(ap);
+ ai.setStack(stack);
+ ctx.setActionInvocation(ai);
+
+ URL url = new URL(stack, new PortletServletRequest(request, null),
+ new PortletServletResponse(response));
+ url.setAnchor("%{someProperty}");
+
+ StringWriter renderOutput = new StringWriter();
+ renderer.renderUrl(renderOutput, url.getUrlProvider());
+ assertTrue(renderOutput.toString().indexOf("#EvaluatedProperty") != -1);
+
+ }
+
+ public void testShouldPassThroughRenderUrlToServletUrlRendererIfNotPortletRequest() throws Exception {
+ UrlRenderer mockRenderer = EasyMock.createMock(UrlRenderer.class);
+ ActionContext.getContext().put(StrutsStatics.STRUTS_PORTLET_CONTEXT, null);
+ renderer.setServletRenderer(mockRenderer);
+ URL url = new URL(stack, new PortletServletRequest(request, null), new PortletServletResponse(response));
+ StringWriter renderOutput = new StringWriter();
+ mockRenderer.renderUrl(renderOutput, url.getUrlProvider());
+ EasyMock.replay(mockRenderer);
+ renderer.renderUrl(renderOutput, url.getUrlProvider());
+ EasyMock.verify(mockRenderer);
+ }
+
+ public void testShouldPassThroughRenderFormUrlToServletUrlRendererIfNotPortletRequest() throws Exception {
+ UrlRenderer mockRenderer = EasyMock.createMock(UrlRenderer.class);
+ ActionContext.getContext().put(StrutsStatics.STRUTS_PORTLET_CONTEXT, null);
+ renderer.setServletRenderer(mockRenderer);
+ Form form = new Form(stack, new PortletServletRequest(request, null), new PortletServletResponse(response));
+ mockRenderer.renderFormUrl(form);
+ EasyMock.replay(mockRenderer);
+ renderer.renderFormUrl(form);
+ EasyMock.verify(mockRenderer);
+ }
+
+ public void testShouldPassThroughRenderUrlToServletUrlRendererWhenPortletUrlTypeIsNone() throws Exception {
+ UrlRenderer mockRenderer = EasyMock.createMock(UrlRenderer.class);
+ renderer.setServletRenderer(mockRenderer);
+ URL url = new URL(stack, new PortletServletRequest(request, null), new PortletServletResponse(response));
+ url.setPortletUrlType("none");
+ StringWriter renderOutput = new StringWriter();
+ mockRenderer.renderUrl(renderOutput, url.getUrlProvider());
+ EasyMock.replay(mockRenderer);
+ renderer.renderUrl(renderOutput, url.getUrlProvider());
+ EasyMock.verify(mockRenderer);
+ }
+
+ private final static class TestObject {
+ public String someProperty;
+ }
+
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/PortletApplicationMapTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/PortletApplicationMapTest.java
new file mode 100644
index 0000000..2e7a320
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/PortletApplicationMapTest.java
@@ -0,0 +1,170 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.portlet.PortletContext;
+
+import org.jmock.Mock;
+import org.jmock.MockObjectTestCase;
+import org.jmock.core.Constraint;
+
+/**
+ */
+public class PortletApplicationMapTest extends MockObjectTestCase {
+
+ Mock mockPortletContext;
+
+ PortletContext portletContext;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mockPortletContext = mock(PortletContext.class);
+ portletContext = (PortletContext) mockPortletContext.proxy();
+ }
+
+ public void testGetFromAttributes() {
+ mockPortletContext.stubs().method("getAttribute").with(eq("dummyKey"))
+ .will(returnValue("dummyValue"));
+
+ PortletApplicationMap map = new PortletApplicationMap(
+ (PortletContext) mockPortletContext.proxy());
+
+ assertEquals("dummyValue", map.get("dummyKey"));
+ }
+
+ public void testGetFromInitParameters() {
+ mockPortletContext.stubs().method("getAttribute").with(eq("dummyKey"));
+ mockPortletContext.stubs().method("getInitParameter").with(
+ eq("dummyKey")).will(returnValue("dummyValue"));
+
+ PortletApplicationMap map = new PortletApplicationMap(
+ (PortletContext) mockPortletContext.proxy());
+
+ assertEquals("dummyValue", map.get("dummyKey"));
+ }
+
+ public void testPut() {
+ mockPortletContext.expects(once()).method("setAttribute").with(
+ new Constraint[] { eq("dummyKey"), eq("dummyValue") });
+ mockPortletContext.expects(once()).method("getAttribute").with(
+ eq("dummyKey")).will(returnValue("dummyValue"));
+ PortletApplicationMap map = new PortletApplicationMap(portletContext);
+ Object val = map.put("dummyKey", "dummyValue");
+ assertEquals("dummyValue", val);
+ }
+
+ public void testRemove() {
+ mockPortletContext.expects(once()).method("getAttribute").with(
+ eq("dummyKey")).will(returnValue("dummyValue"));
+ mockPortletContext.expects(once()).method("removeAttribute").with(
+ eq("dummyKey"));
+ PortletApplicationMap map = new PortletApplicationMap(portletContext);
+ Object val = map.remove("dummyKey");
+ assertEquals("dummyValue", val);
+ }
+
+ public void testEntrySet() {
+
+ Enumeration names = new Enumeration() {
+
+ List keys = Arrays.asList(new Object[] { "key1", "key2" });
+
+ Iterator it = keys.iterator();
+
+ public boolean hasMoreElements() {
+ return it.hasNext();
+ }
+
+ public Object nextElement() {
+ return it.next();
+ }
+
+ };
+ Enumeration initParamNames = new Enumeration() {
+
+ List keys = Arrays.asList(new Object[] { "key3" });
+
+ Iterator it = keys.iterator();
+
+ public boolean hasMoreElements() {
+ return it.hasNext();
+ }
+
+ public Object nextElement() {
+ return it.next();
+ }
+
+ };
+
+ mockPortletContext.stubs().method("getAttributeNames").will(
+ returnValue(names));
+ mockPortletContext.stubs().method("getInitParameterNames").will(
+ returnValue(initParamNames));
+ mockPortletContext.stubs().method("getAttribute").with(eq("key1"))
+ .will(returnValue("value1"));
+ mockPortletContext.stubs().method("getAttribute").with(eq("key2"))
+ .will(returnValue("value2"));
+ mockPortletContext.stubs().method("getInitParameter").with(eq("key3"))
+ .will(returnValue("value3"));
+
+ PortletApplicationMap map = new PortletApplicationMap(portletContext);
+ Set entries = map.entrySet();
+
+ assertEquals(3, entries.size());
+ Iterator it = entries.iterator();
+ Map.Entry entry = (Map.Entry) it.next();
+ assertEquals("key2", entry.getKey());
+ assertEquals("value2", entry.getValue());
+ entry = (Map.Entry) it.next();
+ assertEquals("key1", entry.getKey());
+ assertEquals("value1", entry.getValue());
+ entry = (Map.Entry) it.next();
+ assertEquals("key3", entry.getKey());
+ assertEquals("value3", entry.getValue());
+
+ }
+
+ public void testClear() {
+
+ mockPortletContext.expects(once()).method("removeAttribute").with(eq("key1"));
+ mockPortletContext.expects(once()).method("removeAttribute").with(eq("key2"));
+
+ ArrayList dummy = new ArrayList();
+ dummy.add("key1");
+ dummy.add("key2");
+
+ mockPortletContext.expects(once()).method("getAttributeNames").will(
+ returnValue(Collections.enumeration(dummy)));
+
+ PortletApplicationMap map = new PortletApplicationMap(portletContext);
+ map.clear();
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/PortletRequestMapTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/PortletRequestMapTest.java
new file mode 100644
index 0000000..3c72543
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/PortletRequestMapTest.java
@@ -0,0 +1,108 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.portlet.PortletRequest;
+
+import org.jmock.MockObjectTestCase;
+import org.springframework.mock.web.portlet.MockPortletRequest;
+
+
+/**
+ * PortletRequestMapTest. Insert description.
+ *
+ */
+public class PortletRequestMapTest extends MockObjectTestCase {
+
+ public void testGet() {
+ PortletRequest request = new MockPortletRequest();
+ request.setAttribute("testAttribute", "testValue");
+
+ PortletRequestMap map = new PortletRequestMap(request);
+ String value = (String)map.get("testAttribute");
+ assertEquals("testValue", value);
+ }
+
+ public void testPut() {
+ PortletRequest request = new MockPortletRequest();
+ PortletRequestMap map = new PortletRequestMap(request);
+ Object obj = map.put("testAttribute", "testValue1");
+
+ assertEquals(obj, "testValue1");
+ assertEquals("testValue1", request.getAttribute("testAttribute"));
+ }
+
+ public void testClear() {
+ MockPortletRequest request = new MockPortletRequest();
+ request.setAttribute("testAttribute1", "testValue1");
+ request.setAttribute("testAttribute2", "testValue2");
+
+
+ PortletRequestMap map = new PortletRequestMap(request);
+ map.clear();
+
+ assertFalse(request.getAttributeNames().hasMoreElements());
+ }
+
+ public void testRemove() {
+ MockPortletRequest request = new MockPortletRequest();
+ request.setAttribute("testAttribute1", "testValue1");
+
+ PortletRequestMap map = new PortletRequestMap(request);
+ assertEquals("testValue1", map.remove("testAttribute1"));
+ assertNull(request.getAttribute("testAttribute1"));
+ }
+
+ public void testEntrySet() {
+ MockPortletRequest request = new MockPortletRequest();
+ request.setAttribute("testAttribute1", "testValue1");
+ request.setAttribute("testAttribute2", "testValue2");
+
+ PortletRequestMap map = new PortletRequestMap(request);
+ Set entries = map.entrySet();
+
+ assertEquals(2, entries.size());
+ Iterator it = entries.iterator();
+ Map.Entry entry = (Map.Entry)it.next();
+ checkEntry(entry);
+ entry = (Map.Entry)it.next();
+ checkEntry(entry);
+
+ }
+
+ private void checkEntry(Map.Entry entry) {
+ if(entry.getKey().equals("testAttribute1")) {
+ assertEquals("testValue1", entry.getValue());
+ }
+ else if(entry.getKey().equals("testAttribute2")) {
+ assertEquals("testValue2", entry.getValue());
+ }
+ else {
+ fail("Unexpected entry in etry set: " + entry);
+ }
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/PortletSessionMapTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/PortletSessionMapTest.java
new file mode 100644
index 0000000..2c88256
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/PortletSessionMapTest.java
@@ -0,0 +1,125 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.portlet.PortletSession;
+
+import junit.framework.TestCase;
+
+import org.springframework.mock.web.portlet.MockPortletRequest;
+
+
+/**
+ * PortletSessionMapTest. Insert description.
+ *
+ */
+public class PortletSessionMapTest extends TestCase {
+
+ public void testPut() {
+
+ MockPortletRequest request = new MockPortletRequest();
+
+ PortletSessionMap map = new PortletSessionMap(request);
+ assertEquals("testValue1", map.put("testAttribute1", "testValue1"));
+ assertEquals("testValue2", map.put("testAttribute2", "testValue2"));
+
+ PortletSession session = request.getPortletSession();
+ // Assert that the values has been propagated to the session
+ assertEquals("testValue1", session.getAttribute("testAttribute1"));
+ assertEquals("testValue2", session.getAttribute("testAttribute2"));
+ }
+
+ public void testGet() {
+ MockPortletRequest request = new MockPortletRequest();
+ PortletSession session = request.getPortletSession();
+ session.setAttribute("testAttribute1", "testValue1");
+ session.setAttribute("testAttribute2", "testValue2");
+ PortletSessionMap map = new PortletSessionMap(request);
+ Object val1 = map.get("testAttribute1");
+ Object val2 = map.get("testAttribute2");
+ // Assert that the values from the session is in the map
+ assertEquals("testValue1", val1);
+ assertEquals("testValue2", val2);
+ }
+
+ public void testClear() {
+ MockPortletRequest req = new MockPortletRequest();
+ PortletSession session = req.getPortletSession();
+ session.setAttribute("testAttribute1", "testValue1");
+ session.setAttribute("testAttribute2", "testValue2");
+
+ PortletSessionMap map = new PortletSessionMap(req);
+ map.clear();
+
+ // Assert that there are no elements in the portlet session
+ assertFalse(req.getPortletSession().getAttributeNames().hasMoreElements());
+ }
+
+ public void testRemove() {
+ MockPortletRequest request = new MockPortletRequest();
+ PortletSession session = request.getPortletSession();
+ session.setAttribute("testAttribute1", "testValue1");
+
+ PortletSessionMap map = new PortletSessionMap(request);
+ Object ret = map.remove("testAttribute1");
+ // Assert that the element that was removed was returned and the key is no longer in the
+ // portlet session
+ assertEquals("testValue1", ret);
+ assertNull(session.getAttribute("testAttribute1"));
+ }
+
+ public void testEntrySet() {
+ MockPortletRequest request = new MockPortletRequest();
+ PortletSession session = request.getPortletSession();
+ session.setAttribute("testAttribute1", "testValue1");
+ session.setAttribute("testAttribute2", "testValue2");
+
+ PortletSessionMap map = new PortletSessionMap(request);
+ Set entries = map.entrySet();
+
+ assertEquals(2, entries.size());
+ Iterator it = entries.iterator();
+ Map.Entry entry = (Map.Entry)it.next();
+ checkEntry(entry);
+ entry = (Map.Entry)it.next();
+ checkEntry(entry);
+
+ }
+
+ private void checkEntry(Map.Entry entry) {
+ if(entry.getKey().equals("testAttribute1")) {
+ assertEquals("testValue1", entry.getValue());
+ }
+ else if(entry.getKey().equals("testAttribute2")) {
+ assertEquals("testValue2", entry.getValue());
+ }
+ else {
+ fail("Unexpected entry in etry set: " + entry);
+ }
+ }
+
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/context/PortletActionContextTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/context/PortletActionContextTest.java
new file mode 100644
index 0000000..25c9553
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/context/PortletActionContextTest.java
@@ -0,0 +1,184 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.context;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletConfig;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+
+import junit.textui.TestRunner;
+
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.jmock.Mock;
+import org.jmock.MockObjectTestCase;
+
+import com.opensymphony.xwork2.ActionContext;
+
+/**
+ */
+public class PortletActionContextTest extends MockObjectTestCase {
+
+ Mock mockRenderRequest;
+ Mock mockRenderResponse;
+ Mock mockPortletConfig;
+ Mock mockActionRequest;
+ Mock mockActionResponse;
+
+ RenderRequest renderRequest;
+ RenderResponse renderResponse;
+
+ ActionRequest actionRequest;
+ ActionResponse actionResponse;
+
+ PortletConfig portletConfig;
+
+ Map context = new HashMap();
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mockRenderRequest = mock(RenderRequest.class);
+ mockRenderResponse = mock(RenderResponse.class);
+ mockActionRequest = mock(ActionRequest.class);
+ mockActionResponse = mock(ActionResponse.class);
+ mockPortletConfig = mock(PortletConfig.class);
+
+ renderRequest = (RenderRequest)mockRenderRequest.proxy();
+ renderResponse = (RenderResponse)mockRenderResponse.proxy();
+ actionRequest = (ActionRequest)mockActionRequest.proxy();
+ actionResponse = (ActionResponse)mockActionResponse.proxy();
+ portletConfig = (PortletConfig)mockPortletConfig.proxy();
+
+
+ ActionContext.setContext(new ActionContext(context));
+ }
+
+ public void testGetPhase() {
+ context.put(PortletActionConstants.PHASE, PortletActionConstants.RENDER_PHASE);
+
+ assertEquals(PortletActionConstants.RENDER_PHASE, PortletActionContext.getPhase());
+ }
+
+ public void testIsRender() {
+ context.put(PortletActionConstants.PHASE, PortletActionConstants.RENDER_PHASE);
+
+ assertTrue(PortletActionContext.isRender());
+ assertFalse(PortletActionContext.isEvent());
+ }
+
+ public void testIsEvent() {
+ context.put(PortletActionConstants.PHASE, PortletActionConstants.EVENT_PHASE);
+
+ assertTrue(PortletActionContext.isEvent());
+ assertFalse(PortletActionContext.isRender());
+ }
+
+ public void testGetPortletConfig() {
+ context.put(PortletActionConstants.PORTLET_CONFIG, portletConfig);
+ assertSame(portletConfig, PortletActionContext.getPortletConfig());
+ }
+
+ public void testGetRenderRequestAndResponse() {
+ context.put(PortletActionConstants.REQUEST, renderRequest);
+ context.put(PortletActionConstants.RESPONSE, renderResponse);
+ context.put(PortletActionConstants.PHASE, PortletActionConstants.RENDER_PHASE);
+ assertSame(renderRequest, PortletActionContext.getRenderRequest());
+ assertSame(renderResponse, PortletActionContext.getRenderResponse());
+ assertSame(renderRequest, PortletActionContext.getRequest());
+ assertSame(renderResponse, PortletActionContext.getResponse());
+ }
+
+ public void testGetRenderRequestAndResponseInEventPhase() {
+ context.put(PortletActionConstants.REQUEST, renderRequest);
+ context.put(PortletActionConstants.RESPONSE, renderResponse);
+ context.put(PortletActionConstants.PHASE, PortletActionConstants.EVENT_PHASE);
+ try {
+ PortletActionContext.getRenderRequest();
+ fail("Should throw IllegalStateException!");
+ }
+ catch(IllegalStateException e) {
+ assertTrue(true);
+ }
+ try {
+ PortletActionContext.getRenderResponse();
+ fail("Should throw IllegalStateException!");
+ }
+ catch(IllegalStateException e) {
+ assertTrue(true);
+ }
+ }
+
+ public void testGetActionRequestAndResponse() {
+ context.put(PortletActionConstants.REQUEST, actionRequest);
+ context.put(PortletActionConstants.RESPONSE, actionResponse);
+ context.put(PortletActionConstants.PHASE, PortletActionConstants.EVENT_PHASE);
+ assertSame(actionRequest, PortletActionContext.getActionRequest());
+ assertSame(actionResponse, PortletActionContext.getActionResponse());
+ assertSame(actionRequest, PortletActionContext.getRequest());
+ assertSame(actionResponse, PortletActionContext.getResponse());
+ }
+
+ public void testGetActionRequestAndResponseInRenderPhase() {
+ context.put(PortletActionConstants.REQUEST, actionRequest);
+ context.put(PortletActionConstants.RESPONSE, actionResponse);
+ context.put(PortletActionConstants.PHASE, PortletActionConstants.RENDER_PHASE);
+ try {
+ PortletActionContext.getActionRequest();
+ fail("Should throw IllegalStateException!");
+ }
+ catch(IllegalStateException e) {
+ assertTrue(true);
+ }
+ try {
+ PortletActionContext.getActionResponse();
+ fail("Should throw IllegalStateException!");
+ }
+ catch(IllegalStateException e) {
+ assertTrue(true);
+ }
+ }
+
+ public void testGetNamespace() {
+ context.put(PortletActionConstants.PORTLET_NAMESPACE, "testNamespace");
+ assertEquals("testNamespace", PortletActionContext.getPortletNamespace());
+ }
+
+ public void testGetDefaultActionForMode() {
+ ActionMapping mapping = new ActionMapping();
+ context.put(PortletActionConstants.DEFAULT_ACTION_FOR_MODE, mapping);
+ assertEquals(mapping, PortletActionContext.getDefaultActionForMode());
+ }
+
+ public void tearDown() throws Exception {
+ ActionContext.setContext(null);
+ super.tearDown();
+ }
+
+ public static void main(String[] args) {
+ TestRunner.run(PortletActionContextTest.class);
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/dispatcher/Jsr168DispatcherTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/dispatcher/Jsr168DispatcherTest.java
new file mode 100644
index 0000000..2359573
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/dispatcher/Jsr168DispatcherTest.java
@@ -0,0 +1,299 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.dispatcher;
+
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.ActionProxyFactory;
+import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.easymock.EasyMock;
+import org.jmock.Mock;
+import org.jmock.cglib.MockObjectTestCase;
+import org.jmock.core.Constraint;
+import org.springframework.mock.web.portlet.MockPortletConfig;
+import org.springframework.mock.web.portlet.MockPortletContext;
+
+import javax.portlet.*;
+import java.util.*;
+
+/**
+ * Jsr168DispatcherTest. Insert description.
+ *
+ */
+public class Jsr168DispatcherTest extends MockObjectTestCase implements PortletActionConstants {
+
+ private final String MULTIPART_REQUEST = "-----------------------------4827543632391\r\n"
+ + "Content-Disposition: form-data; name=\"upload\"; filename=\"test.txt\"\r\n"
+ + "Content-Type: text/plain\r\n"
+ + "\r\n"
+ + "This is a test file\r\n"
+ + "-----------------------------4827543632391\r\n"
+ + "Content-Disposition: form-data; name=\"caption\"\r\n"
+ + "\r\n"
+ + "TestCaption\r\n" + "-----------------------------4827543632391--";
+
+ Jsr168Dispatcher dispatcher = null;
+
+ Mock mockConfig = null;
+
+ Mock mockCtx = null;
+
+ Mock mockRequest = null;
+
+ Mock mockSession = null;
+
+ Mock mockActionFactory = null;
+
+ Mock mockActionProxy = null;
+
+ Mock mockAction = null;
+
+ Mock mockInvocation = null;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ dispatcher = new Jsr168Dispatcher();
+ }
+
+ private void initPortletConfig(final Map<String, String> initParams, final Map<String, Object> attributes) {
+ mockConfig = mock(PortletConfig.class);
+ mockCtx = mock(PortletContext.class);
+ mockConfig.stubs().method(ANYTHING);
+ mockCtx.stubs().method(ANYTHING);
+ setupStub(initParams, mockConfig, "getInitParameter");
+ mockCtx.stubs().method("getAttributeNames").will(returnValue(Collections.enumeration(attributes.keySet())));
+ setupStub(attributes, mockCtx, "getAttribute");
+ mockConfig.stubs().method("getPortletContext").will(returnValue(mockCtx.proxy()));
+ mockCtx.stubs().method("getInitParameterNames").will(returnValue(Collections.enumeration(initParams.keySet())));
+ setupStub(initParams, mockCtx, "getInitParameter");
+ mockConfig.stubs().method("getInitParameterNames").will(
+ returnValue(Collections.enumeration(initParams.keySet())));
+ setupStub(initParams, mockConfig, "getInitParameter");
+ mockConfig.stubs().method("getResourceBundle").will(returnValue(new ListResourceBundle() {
+ protected Object[][] getContents() {
+ return new String[][] { { "javax.portlet.title", "MyTitle" } };
+ }
+ }));
+ }
+
+ private void setupActionFactory(String namespace, String actionName, String result, ValueStack stack) {
+ if (mockActionFactory == null) {
+ mockActionFactory = mock(ActionProxyFactory.class);
+ }
+ mockAction = mock(Action.class);
+ mockActionProxy = mock(ActionProxy.class);
+ mockInvocation = mock(ActionInvocation.class);
+
+ mockActionFactory.expects(once()).method("createActionProxy").with(
+ new Constraint[] { eq(namespace), eq(actionName), NULL, isA(Map.class) }).will(
+ returnValue(mockActionProxy.proxy()));
+ mockActionProxy.stubs().method("getAction").will(returnValue(mockAction.proxy()));
+ mockActionProxy.expects(once()).method("execute").will(returnValue(result));
+ mockActionProxy.expects(once()).method("getInvocation").will(returnValue(mockInvocation.proxy()));
+ mockInvocation.stubs().method("getStack").will(returnValue(stack));
+
+ }
+
+ public void testParseConfigWithBang() {
+ MockPortletContext portletContext = new MockPortletContext();
+ MockPortletConfig portletConfig = new MockPortletConfig(portletContext);
+
+ portletConfig.addInitParameter("viewNamespace", "/view");
+ portletConfig.addInitParameter("defaultViewAction", "index!input");
+
+ Map<PortletMode, ActionMapping> actionMap = new HashMap<PortletMode, ActionMapping>();
+
+ dispatcher.parseModeConfig(actionMap, portletConfig, PortletMode.VIEW, "viewNamespace", "defaultViewAction");
+
+ ActionMapping mapping = actionMap.get(PortletMode.VIEW);
+ assertEquals("index", mapping.getName());
+ assertEquals("/view", mapping.getNamespace());
+ assertEquals("input", mapping.getMethod());
+ }
+
+ public void testRender_ok() throws Exception {
+ final Mock mockResponse = mock(RenderResponse.class);
+ mockResponse.stubs().method(ANYTHING);
+
+ PortletMode mode = PortletMode.VIEW;
+
+ Map<String, String[]> requestParams = new HashMap<String, String[]>();
+ requestParams.put(PortletActionConstants.ACTION_PARAM, new String[] { "/view/testAction" });
+ requestParams.put(EVENT_ACTION, new String[] { "true" });
+ requestParams.put(PortletActionConstants.MODE_PARAM, new String[] { mode.toString() });
+
+ Map<String, Object> sessionMap = new HashMap<String, Object>();
+
+ Map<String, String> initParams = new HashMap<String, String>();
+ initParams.put("viewNamespace", "/view");
+ initParams.put(StrutsConstants.STRUTS_ALWAYS_SELECT_FULL_NAMESPACE, "true");
+
+ initPortletConfig(initParams, new HashMap<String, Object>());
+ initRequest(requestParams, new HashMap<String, Object>(), sessionMap, new HashMap<String, String[]>(), PortletMode.VIEW, WindowState.NORMAL,
+ false, null);
+ setupActionFactory("/view", "testAction", "success", EasyMock.createNiceMock(ValueStack.class));
+
+ mockInvocation.expects(once()).method("getStack").will(returnValue(null));
+ // mockSession.expects(once()).method("setAttribute").with(new
+ // Constraint[]{eq(PortletActionConstants.LAST_MODE),
+ // eq(PortletMode.VIEW)});
+ dispatcher.setActionProxyFactory((ActionProxyFactory) mockActionFactory.proxy());
+ dispatcher.init((PortletConfig) mockConfig.proxy());
+ dispatcher.render((RenderRequest) mockRequest.proxy(), (RenderResponse) mockResponse.proxy());
+ }
+
+ public void testProcessAction_ok() throws Exception {
+ final Mock mockResponse = mock(ActionResponse.class);
+
+ PortletMode mode = PortletMode.VIEW;
+ Map<String, String> initParams = new HashMap<String, String>();
+ initParams.put("viewNamespace", "/view");
+
+ Map<String, String[]> requestParams = new HashMap<String, String[]>();
+ requestParams.put(PortletActionConstants.ACTION_PARAM, new String[] { "/view/testAction" });
+ requestParams.put(PortletActionConstants.MODE_PARAM, new String[] { mode.toString() });
+
+ initParams.put(StrutsConstants.STRUTS_ALWAYS_SELECT_FULL_NAMESPACE, "true");
+ initPortletConfig(initParams, new HashMap<String, Object>());
+ initRequest(requestParams, new HashMap<String, Object>(), new HashMap<String, Object>(), new HashMap<String, String[]>(), PortletMode.VIEW, WindowState.NORMAL,
+ true, null);
+ setupActionFactory("/view", "testAction", "success", EasyMock.createNiceMock(ValueStack.class));
+ // mockSession.expects(once()).method("setAttribute").with(new
+ // Constraint[]{eq(PortletActionConstants.LAST_MODE),
+ // eq(PortletMode.VIEW)});
+ dispatcher.setActionProxyFactory((ActionProxyFactory) mockActionFactory.proxy());
+ dispatcher.init((PortletConfig) mockConfig.proxy());
+ dispatcher.processAction((ActionRequest) mockRequest.proxy(), (ActionResponse) mockResponse.proxy());
+ }
+
+ /**
+ * Initialize the mock request (and as a result, the mock session)
+ *
+ * @param requestParams
+ * The request parameters
+ * @param requestAttributes
+ * The request attributes
+ * @param sessionParams
+ * The session attributes
+ * @param renderParams
+ * The render parameters. Will only be set if
+ * <code>isEvent</code> is <code>true</code>
+ * @param mode
+ * The portlet mode
+ * @param state
+ * The portlet window state
+ * @param isEvent
+ * <code>true</code> when the request is an ActionRequest.
+ * @param locale
+ * The locale. If <code>null</code>, the request will return
+ * <code>Locale.getDefault()</code>
+ */
+ private void initRequest(Map<String, String[]> requestParams, Map<String, Object> requestAttributes, Map<String, Object> sessionParams, Map<String, String[]> renderParams,
+ PortletMode mode, WindowState state, boolean isEvent, Locale locale) {
+ mockRequest = isEvent ? mock(ActionRequest.class) : mock(RenderRequest.class);
+ mockSession = mock(PortletSession.class);
+ mockSession.stubs().method(ANYTHING);
+ mockRequest.stubs().method(ANYTHING);
+ setupStub(sessionParams, mockSession, "getAttribute");
+ mockSession.stubs().method("getAttributeNames").will(
+ returnValue(Collections.enumeration(sessionParams.keySet())));
+ setupParamStub(requestParams, mockRequest, "getParameter");
+ setupStub(requestAttributes, mockRequest, "getAttribute");
+ mockRequest.stubs().method("getAttributeNames").will(
+ returnValue(Collections.enumeration(requestAttributes.keySet())));
+ mockRequest.stubs().method("getParameterMap").will(returnValue(requestParams));
+ mockRequest.stubs().method("getParameterNames").will(
+ returnValue(Collections.enumeration(requestParams.keySet())));
+ mockRequest.stubs().method("getPortletSession").will(returnValue(mockSession.proxy()));
+ if (locale != null) {
+ mockRequest.stubs().method("getLocale").will(returnValue(locale));
+ } else {
+ mockRequest.stubs().method("getLocale").will(returnValue(Locale.getDefault()));
+ }
+ mockRequest.stubs().method("getPortletMode").will(returnValue(mode));
+ mockRequest.stubs().method("getWindowState").will(returnValue(state));
+ }
+
+ private void setupParamStub(Map<String, String[]> requestParams, Mock mockRequest, String method) {
+ Map<String, String> newMap = new HashMap<String, String>();
+ for ( String key : requestParams.keySet() ) {
+ String[] val = requestParams.get(key);
+ newMap.put(key, val[0]);
+ }
+ setupStub(newMap, mockRequest, method);
+
+ }
+
+ /**
+ * Set up stubs for the mock.
+ *
+ * @param map
+ * The map containing the <code>key</code> and
+ * <code>values</code>. The key is the expected parameter to
+ * <code>method</code>, and value is the value that should be
+ * returned from the stub.
+ * @param mock
+ * The mock to initialize.
+ * @param method
+ * The name of the method to stub.
+ */
+ private void setupStub(Map map, Mock mock, String method) {
+ for ( Object key : map.keySet() ) {
+ Object val = map.get(key);
+ mock.stubs().method(method).with(eq(key)).will(returnValue(val));
+ }
+ }
+
+ public void testModeChangeUsingPortletWidgets() throws Exception {
+ final Mock mockResponse = mock(RenderResponse.class);
+ mockResponse.stubs().method(ANYTHING);
+ PortletMode mode = PortletMode.EDIT;
+
+ Map<String, String[]> requestParams = new HashMap<String, String[]>();
+ requestParams.put(PortletActionConstants.ACTION_PARAM, new String[] { "/view/testAction" });
+ requestParams.put(EVENT_ACTION, new String[] { "false" });
+ requestParams.put(PortletActionConstants.MODE_PARAM, new String[] { PortletMode.VIEW.toString() });
+
+ Map<String, Object> sessionMap = new HashMap<String, Object>();
+
+ Map<String, String> initParams = new HashMap<String, String>();
+ initParams.put("viewNamespace", "/view");
+ initParams.put("editNamespace", "/edit");
+
+ initPortletConfig(initParams, new HashMap<String, Object>());
+ initRequest(requestParams, new HashMap<String, Object>(), sessionMap, new HashMap<String, String[]>(), mode, WindowState.NORMAL, false, null);
+ setupActionFactory("/edit", "default", "success", EasyMock.createNiceMock(ValueStack.class));
+
+ mockInvocation.expects(once()).method("getStack").will(returnValue(null));
+ // mockSession.expects(once()).method("setAttribute").with(new
+ // Constraint[]{eq(PortletActionConstants.LAST_MODE),
+ // eq(PortletMode.VIEW)});
+ dispatcher.setActionProxyFactory((ActionProxyFactory) mockActionFactory.proxy());
+ dispatcher.init((PortletConfig) mockConfig.proxy());
+ dispatcher.render((RenderRequest) mockRequest.proxy(), (RenderResponse) mockResponse.proxy());
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptorTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptorTest.java
new file mode 100644
index 0000000..f059922
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptorTest.java
@@ -0,0 +1,99 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletRequest;
+
+import junit.framework.TestCase;
+
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.easymock.EasyMock;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+
+public class PortletAwareInterceptorTest extends TestCase implements PortletActionConstants {
+
+ private PortletAwareInterceptor interceptor;
+ private TestAction action;
+ private PortletRequest portletRequest;
+ private PortletConfig portletConfig;
+ private Map<String, Object> contextMap;
+ private ActionInvocation invocation;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ interceptor = new PortletAwareInterceptor();
+ action = new TestAction();
+ portletRequest = EasyMock.createNiceMock(PortletRequest.class);
+ portletConfig = EasyMock.createNiceMock(PortletConfig.class);
+ contextMap = new HashMap<String, Object>();
+ invocation = EasyMock.createNiceMock(ActionInvocation.class);
+ EasyMock.expect(invocation.getAction()).andReturn(action);
+ EasyMock.expect(invocation.getInvocationContext()).andReturn(new ActionContext(contextMap));
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testPortletRequestIsSet() throws Exception {
+ contextMap.put(REQUEST, portletRequest);
+ EasyMock.replay(invocation);
+ interceptor.intercept(invocation);
+ assertEquals(portletRequest, action.getPortletRequest());
+ }
+
+ public void testPortletConfigIsSet() throws Exception {
+ contextMap.put(PORTLET_CONFIG, portletConfig);
+ EasyMock.replay(invocation);
+ interceptor.intercept(invocation);
+ assertEquals(portletConfig, action.getPortletConfig());
+ }
+
+ public static class TestAction implements PortletRequestAware, PortletConfigAware {
+
+ private PortletRequest request;
+ private PortletConfig config;
+
+ public void setPortletRequest(PortletRequest request) {
+ this.request = request;
+ }
+
+ public void setPortletConfig(PortletConfig portletConfig) {
+ this.config = portletConfig;
+ }
+
+ public PortletConfig getPortletConfig() {
+ return config;
+ }
+
+ public PortletRequest getPortletRequest() {
+ return request;
+ }
+
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptorTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptorTest.java
new file mode 100644
index 0000000..c584f8a
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptorTest.java
@@ -0,0 +1,160 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.interceptor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.portlet.ActionResponse;
+import javax.portlet.RenderRequest;
+
+import junit.framework.TestCase;
+
+import org.apache.struts2.StrutsTestCase;
+import org.apache.struts2.dispatcher.DefaultActionSupport;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.dispatcher.DirectRenderFromEventAction;
+import org.easymock.EasyMock;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ValueStackFactory;
+
+public class PortletStateInterceptorTest extends StrutsTestCase implements PortletActionConstants {
+
+ private PortletStateInterceptor interceptor;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ interceptor = new PortletStateInterceptor();
+ }
+
+ public void testCopyValueStackFromEventToRenderPhase() throws Exception {
+ ActionResponse actionResponse = EasyMock.createNiceMock(ActionResponse.class);
+ ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
+
+ Map<String, Object> ctxMap = new HashMap<String, Object>();
+ ctxMap.put(PHASE, EVENT_PHASE);
+ ctxMap.put(RESPONSE, actionResponse);
+ Map<String, Object> session = new HashMap<String, Object>();
+
+ ActionContext ctx = new ActionContext(ctxMap);
+ ctx.setSession(session);
+ EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);
+ actionResponse.setRenderParameter(EVENT_ACTION, "true");
+
+ ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
+ EasyMock.expect(invocation.getStack()).andStubReturn(stack);
+
+ EasyMock.replay(actionResponse);
+ EasyMock.replay(invocation);
+
+ interceptor.intercept(invocation);
+
+ EasyMock.verify(actionResponse);
+ EasyMock.verify(invocation);
+
+ assertSame(stack, session.get(STACK_FROM_EVENT_PHASE));
+
+ }
+
+ public void testDoNotRestoreValueStackInRenderPhaseWhenProperPrg() throws Exception {
+ RenderRequest renderRequest = EasyMock.createNiceMock(RenderRequest.class);
+ ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
+
+
+ ValueStack eventPhaseStack = container.getInstance(ValueStackFactory.class).createValueStack();
+ eventPhaseStack.set("testKey", "testValue");
+
+ ValueStack currentStack = container.getInstance(ValueStackFactory.class).createValueStack();
+ currentStack.set("anotherTestKey", "anotherTestValue");
+
+ Map<String, Object> ctxMap = new HashMap<String, Object>();
+ Map<String, Object> session = new HashMap<String, Object>();
+
+ session.put(STACK_FROM_EVENT_PHASE, eventPhaseStack);
+
+ ctxMap.put(PHASE, RENDER_PHASE);
+ ctxMap.put(REQUEST, renderRequest);
+
+ ActionContext ctx = new ActionContext(ctxMap);
+ ctx.setSession(session);
+
+ EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);
+ EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);
+ EasyMock.expect(invocation.getAction()).andStubReturn(new DefaultActionSupport());
+ EasyMock.expect(renderRequest.getParameter(EVENT_ACTION)).andStubReturn("true");
+
+ EasyMock.replay(renderRequest);
+ EasyMock.replay(invocation);
+
+ interceptor.intercept(invocation);
+
+ ValueStack resultingStack = invocation.getStack();
+
+ assertNull(resultingStack.findValue("testKey"));
+ assertEquals("anotherTestValue", resultingStack.findValue("anotherTestKey"));
+
+
+ }
+
+ public void testRestoreValueStackInRenderPhaseWhenNotProperPrg() throws Exception {
+ RenderRequest renderRequest = EasyMock.createNiceMock(RenderRequest.class);
+ ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class);
+
+ ValueStack eventPhaseStack = container.getInstance(ValueStackFactory.class).createValueStack();
+ eventPhaseStack.set("testKey", "testValue");
+
+ ValueStack currentStack = container.getInstance(ValueStackFactory.class).createValueStack();
+ currentStack.set("anotherTestKey", "anotherTestValue");
+
+ EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);
+
+ Map<String, Object> ctxMap = new HashMap<String, Object>();
+ Map<String, Object> session = new HashMap<String, Object>();
+
+ session.put(STACK_FROM_EVENT_PHASE, eventPhaseStack);
+
+ ctxMap.put(PHASE, RENDER_PHASE);
+ ctxMap.put(REQUEST, renderRequest);
+
+ ActionContext ctx = new ActionContext(ctxMap);
+ ctx.setSession(session);
+
+ EasyMock.expect(invocation.getInvocationContext()).andStubReturn(ctx);
+ EasyMock.expect(invocation.getStack()).andStubReturn(currentStack);
+ EasyMock.expect(invocation.getAction()).andStubReturn(new DirectRenderFromEventAction());
+ EasyMock.expect(renderRequest.getParameter(EVENT_ACTION)).andStubReturn("true");
+
+ EasyMock.replay(renderRequest);
+ EasyMock.replay(invocation);
+
+ interceptor.intercept(invocation);
+
+ ValueStack resultingStack = invocation.getStack();
+ assertEquals("testValue", resultingStack.findValue("testKey"));
+ assertEquals("anotherTestValue", resultingStack.findValue("anotherTestKey"));
+
+
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java
new file mode 100644
index 0000000..25fff49
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/result/PortletResultTest.java
@@ -0,0 +1,246 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.result;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletMode;
+import javax.portlet.PortletRequestDispatcher;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+
+import junit.textui.TestRunner;
+
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.jmock.Mock;
+import org.jmock.cglib.MockObjectTestCase;
+import org.jmock.core.Constraint;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.mock.MockActionProxy;
+import com.opensymphony.xwork2.mock.MockActionInvocation;
+
+/**
+ * PortletResultTest. Insert description.
+ *
+ */
+public class PortletResultTest extends MockObjectTestCase implements PortletActionConstants {
+
+ Mock mockInvocation = null;
+ Mock mockConfig = null;
+ Mock mockCtx = null;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mockInvocation = mock(ActionInvocation.class);
+ mockCtx = mock(PortletContext.class);
+
+ Map paramMap = new HashMap();
+ Map sessionMap = new HashMap();
+
+ Map context = new HashMap();
+ context.put(ActionContext.SESSION, sessionMap);
+ context.put(ActionContext.PARAMETERS, paramMap);
+ context.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, mockCtx.proxy());
+
+ ActionContext.setContext(new ActionContext(context));
+
+ mockInvocation.stubs().method("getInvocationContext").will(returnValue(ActionContext.getContext()));
+
+ }
+
+ public void testDoExecute_render() {
+ Mock mockRequest = mock(RenderRequest.class);
+ Mock mockResponse = mock(RenderResponse.class);
+ Mock mockRd = mock(PortletRequestDispatcher.class);
+
+ RenderRequest req = (RenderRequest)mockRequest.proxy();
+ RenderResponse res = (RenderResponse)mockResponse.proxy();
+ PortletRequestDispatcher rd = (PortletRequestDispatcher)mockRd.proxy();
+ PortletContext ctx = (PortletContext)mockCtx.proxy();
+ ActionInvocation inv = (ActionInvocation)mockInvocation.proxy();
+
+ Constraint[] params = new Constraint[]{same(req), same(res)};
+ mockRd.expects(once()).method("include").with(params);
+ mockCtx.expects(once()).method("getRequestDispatcher").with(eq("/WEB-INF/pages/testPage.jsp")).will(returnValue(rd));
+ mockResponse.expects(once()).method("setContentType").with(eq("text/html"));
+
+ mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
+
+ ActionContext ctxMap = ActionContext.getContext();
+ ctxMap.put(PortletActionConstants.RESPONSE, res);
+ ctxMap.put(PortletActionConstants.REQUEST, req);
+ ctxMap.put(StrutsStatics.SERVLET_CONTEXT, ctx);
+ ctxMap.put(PortletActionConstants.PHASE, PortletActionConstants.RENDER_PHASE);
+
+ PortletResult result = new PortletResult();
+ try {
+ result.doExecute("/WEB-INF/pages/testPage.jsp", inv);
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ fail("Error occured!");
+ }
+
+ }
+
+ public void testDoExecute_event_locationIsAction() {
+
+ Mock mockRequest = mock(ActionRequest.class);
+ Mock mockResponse = mock(ActionResponse.class);
+
+ Constraint[] params = new Constraint[]{eq(PortletActionConstants.ACTION_PARAM), eq("testView")};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ params = new Constraint[]{eq(PortletActionConstants.MODE_PARAM), eq(PortletMode.VIEW.toString())};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
+ ActionContext ctx = ActionContext.getContext();
+
+ ctx.put(PortletActionConstants.REQUEST, mockRequest.proxy());
+ ctx.put(PortletActionConstants.RESPONSE, mockResponse.proxy());
+ ctx.put(PortletActionConstants.PHASE, PortletActionConstants.EVENT_PHASE);
+
+ PortletResult result = new PortletResult();
+ try {
+ result.doExecute("testView.action", (ActionInvocation)mockInvocation.proxy());
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ fail("Error occured!");
+ }
+
+ }
+
+ public void testDoExecute_event_locationIsJsp() {
+ Mock mockRequest = mock(ActionRequest.class);
+ Mock mockResponse = mock(ActionResponse.class);
+ Mock mockProxy = mock(ActionProxy.class);
+
+ Constraint[] params = new Constraint[]{eq(PortletActionConstants.ACTION_PARAM), eq("renderDirect")};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ params = new Constraint[]{eq(PortletActionConstants.MODE_PARAM), eq(PortletMode.VIEW.toString())};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
+ mockProxy.stubs().method("getNamespace").will(returnValue(""));
+
+ mockInvocation.stubs().method("getProxy").will(returnValue(mockProxy.proxy()));
+
+ ActionContext ctx = ActionContext.getContext();
+
+ Map session = new HashMap();
+
+ ctx.put(PortletActionConstants.REQUEST, mockRequest.proxy());
+ ctx.put(PortletActionConstants.RESPONSE, mockResponse.proxy());
+ ctx.put(PortletActionConstants.PHASE, PortletActionConstants.EVENT_PHASE);
+ ctx.put(ActionContext.SESSION, session);
+
+ PortletResult result = new PortletResult();
+ try {
+ result.doExecute("/WEB-INF/pages/testJsp.jsp", (ActionInvocation)mockInvocation.proxy());
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ fail("Error occured!");
+ }
+ assertEquals("/WEB-INF/pages/testJsp.jsp", session.get(RENDER_DIRECT_LOCATION));
+ }
+
+ public void testDoExecute_event_locationHasQueryParams() {
+ Mock mockRequest = mock(ActionRequest.class);
+ Mock mockResponse = mock(ActionResponse.class);
+
+ Constraint[] params = new Constraint[]{eq(PortletActionConstants.ACTION_PARAM), eq("testView")};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ params = new Constraint[]{eq("testParam1"), eq("testValue1")};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ params = new Constraint[]{eq("testParam2"), eq("testValue2")};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ params = new Constraint[]{eq(PortletActionConstants.MODE_PARAM), eq(PortletMode.VIEW.toString())};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
+
+ ActionContext ctx = ActionContext.getContext();
+
+ ctx.put(PortletActionConstants.REQUEST, mockRequest.proxy());
+ ctx.put(PortletActionConstants.RESPONSE, mockResponse.proxy());
+ ctx.put(PortletActionConstants.PHASE, PortletActionConstants.EVENT_PHASE);
+
+ PortletResult result = new PortletResult();
+ try {
+ result.doExecute("testView.action?testParam1=testValue1&testParam2=testValue2", (ActionInvocation)mockInvocation.proxy());
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ fail("Error occured!");
+ }
+ }
+
+ public void testTitleAndContentType() throws Exception {
+ Mock mockRequest = mock(RenderRequest.class);
+ Mock mockResponse = mock(RenderResponse.class);
+ Mock mockRd = mock(PortletRequestDispatcher.class);
+
+ RenderRequest req = (RenderRequest)mockRequest.proxy();
+ RenderResponse res = (RenderResponse)mockResponse.proxy();
+ PortletRequestDispatcher rd = (PortletRequestDispatcher)mockRd.proxy();
+ PortletContext ctx = (PortletContext)mockCtx.proxy();
+
+ Constraint[] params = new Constraint[]{same(req), same(res)};
+ mockRd.expects(once()).method("include").with(params);
+ mockCtx.expects(once()).method("getRequestDispatcher").with(eq("/WEB-INF/pages/testPage.jsp")).will(returnValue(rd));
+
+ mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
+
+ ActionContext ctxMap = ActionContext.getContext();
+ ctxMap.put(PortletActionConstants.RESPONSE, res);
+ ctxMap.put(PortletActionConstants.REQUEST, req);
+ ctxMap.put(StrutsStatics.SERVLET_CONTEXT, ctx);
+ ctxMap.put(PortletActionConstants.PHASE, PortletActionConstants.RENDER_PHASE);
+
+ mockResponse.expects(atLeastOnce()).method("setTitle").with(eq("testTitle"));
+ mockResponse.expects(atLeastOnce()).method("setContentType").with(eq("testContentType"));
+
+ PortletResult result = new PortletResult();
+ result.setTitle("testTitle");
+ result.setContentType("testContentType");
+ result.doExecute("/WEB-INF/pages/testPage.jsp", (ActionInvocation)mockInvocation.proxy());
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ ActionContext.setContext(null);
+ }
+
+ public static void main(String[] args) {
+ TestRunner.run(PortletResultTest.class);
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/servlet/PortletServletRequestTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/servlet/PortletServletRequestTest.java
new file mode 100644
index 0000000..9b26b86
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/servlet/PortletServletRequestTest.java
@@ -0,0 +1,49 @@
+package org.apache.struts2.portlet.servlet;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsTestCase;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.springframework.mock.web.portlet.MockPortletContext;
+import org.springframework.mock.web.portlet.MockPortletRequest;
+
+import com.opensymphony.xwork2.ActionContext;
+
+public class PortletServletRequestTest extends StrutsTestCase {
+
+ private MockPortletRequest portletRequest;
+ private MockPortletContext portletContext;
+ private PortletServletRequest request;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ portletRequest = new MockPortletRequest();
+ portletContext = new MockPortletContext();
+ request = new PortletServletRequest(portletRequest, portletContext);
+ }
+
+ public void testGetServletPathShouldHandleDefaultActionExtension() throws Exception {
+ portletRequest.setParameter(PortletActionConstants.ACTION_PARAM, "actionName");
+ request.setExtension("action");
+ assertEquals("actionName.action", request.getServletPath());
+ }
+
+ public void testGetServletPathShouldHandleCustomActionExtension() throws Exception {
+ portletRequest.setParameter(PortletActionConstants.ACTION_PARAM, "actionName");
+ request.setExtension("custom");
+ assertEquals("actionName.custom", request.getServletPath());
+ }
+
+ public void testGetServletPathShouldHandleNoExtension() throws Exception {
+ portletRequest.setParameter(PortletActionConstants.ACTION_PARAM, "actionName");
+ request.setExtension("");
+ assertEquals("actionName", request.getServletPath());
+ }
+
+ public void testGetServletPathShouldHandleMultipleExtensionsByUsingTheFirst() throws Exception {
+ portletRequest.setParameter(PortletActionConstants.ACTION_PARAM, "actionName");
+ request.setExtension("action,,");
+ assertEquals("actionName.action", request.getServletPath());
+ }
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/util/PortletUrlHelperTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/util/PortletUrlHelperTest.java
new file mode 100644
index 0000000..0a5266a
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/portlet/util/PortletUrlHelperTest.java
@@ -0,0 +1,160 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.portlet.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.portlet.PortletMode;
+import javax.portlet.PortletURL;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+import javax.portlet.WindowState;
+
+import junit.framework.TestCase;
+
+import org.apache.struts2.portlet.context.PortletActionContext;
+import org.easymock.MockControl;
+
+import com.opensymphony.xwork2.ActionContext;
+
+/**
+ */
+public class PortletUrlHelperTest extends TestCase {
+
+ RenderResponse renderResponse;
+
+ RenderRequest renderRequest;
+
+ PortletURL url;
+
+ MockControl renderResponseControl;
+
+ MockControl renderRequestControl;
+
+ MockControl portletUrlControl;
+
+ public void setUp() throws Exception {
+ super.setUp();
+
+ renderRequestControl = MockControl.createControl(RenderRequest.class);
+ renderResponseControl = MockControl.createControl(RenderResponse.class);
+ portletUrlControl = MockControl.createControl(PortletURL.class);
+
+ renderRequest = (RenderRequest) renderRequestControl.getMock();
+ renderResponse = (RenderResponse) renderResponseControl.getMock();
+ url = (PortletURL) portletUrlControl.getMock();
+
+ renderRequestControl.expectAndDefaultReturn(renderRequest
+ .getPortletMode(), PortletMode.VIEW);
+ renderRequestControl.expectAndDefaultReturn(renderRequest
+ .getWindowState(), WindowState.NORMAL);
+
+ Map modeNamespaceMap = new HashMap();
+ modeNamespaceMap.put("view", "/view");
+ modeNamespaceMap.put("edit", "/edit");
+ modeNamespaceMap.put("help", "/help");
+
+ Map context = new HashMap();
+ context.put(PortletActionContext.REQUEST, renderRequest);
+ context.put(PortletActionContext.RESPONSE, renderResponse);
+ context.put(PortletActionContext.PHASE,
+ PortletActionContext.RENDER_PHASE);
+ context.put(PortletActionContext.MODE_NAMESPACE_MAP, modeNamespaceMap);
+
+ ActionContext.setContext(new ActionContext(context));
+
+ }
+
+ public void testCreateRenderUrlWithNoModeOrState() throws Exception {
+ renderResponseControl.expectAndReturn(renderResponse.createRenderURL(),
+ url);
+
+ url.setPortletMode(PortletMode.VIEW);
+ url.setWindowState(WindowState.NORMAL);
+ url.setParameters(null);
+ portletUrlControl.setMatcher(MockControl.ALWAYS_MATCHER);
+ renderRequestControl.replay();
+ renderResponseControl.replay();
+ portletUrlControl.replay();
+ PortletUrlHelper.buildUrl("testAction", null, null,
+ new HashMap(), null, null, null);
+ portletUrlControl.verify();
+ renderRequestControl.verify();
+ renderResponseControl.verify();
+ }
+
+ public void testCreateRenderUrlWithDifferentPortletMode() throws Exception {
+ renderResponseControl.expectAndReturn(renderResponse.createRenderURL(),
+ url);
+
+ url.setPortletMode(PortletMode.EDIT);
+ url.setWindowState(WindowState.NORMAL);
+ url.setParameters(null);
+ portletUrlControl.setMatcher(MockControl.ALWAYS_MATCHER);
+ renderRequestControl.replay();
+ renderResponseControl.replay();
+ portletUrlControl.replay();
+ PortletUrlHelper.buildUrl("testAction", null, null,
+ new HashMap(), null, "edit", null);
+ portletUrlControl.verify();
+ renderRequestControl.verify();
+ renderResponseControl.verify();
+ }
+
+ public void testCreateRenderUrlWithDifferentWindowState() throws Exception {
+ renderResponseControl.expectAndReturn(renderResponse.createRenderURL(),
+ url);
+
+ url.setPortletMode(PortletMode.VIEW);
+ url.setWindowState(WindowState.MAXIMIZED);
+ url.setParameters(null);
+ portletUrlControl.setMatcher(MockControl.ALWAYS_MATCHER);
+ renderRequestControl.replay();
+ renderResponseControl.replay();
+ portletUrlControl.replay();
+ PortletUrlHelper.buildUrl("testAction", null, null,
+ new HashMap(), null, null, "maximized");
+ portletUrlControl.verify();
+ renderRequestControl.verify();
+ renderResponseControl.verify();
+ }
+
+ public void testCreateActionUrl() throws Exception {
+ renderResponseControl.expectAndReturn(renderResponse.createActionURL(),
+ url);
+
+ url.setPortletMode(PortletMode.VIEW);
+ url.setWindowState(WindowState.NORMAL);
+ url.setParameters(null);
+ portletUrlControl.setMatcher(MockControl.ALWAYS_MATCHER);
+ renderRequestControl.replay();
+ renderResponseControl.replay();
+ portletUrlControl.replay();
+ PortletUrlHelper.buildUrl("testAction", null, null,
+ new HashMap(), "action", null, null);
+ portletUrlControl.verify();
+ renderRequestControl.verify();
+ renderResponseControl.verify();
+ }
+
+}
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/views/freemarker/PortletFreemarkerResultTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/views/freemarker/PortletFreemarkerResultTest.java
new file mode 100644
index 0000000..8ae72a6
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/views/freemarker/PortletFreemarkerResultTest.java
@@ -0,0 +1,105 @@
+/*
+ * $Id: $
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.views.freemarker;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletMode;
+
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.jmock.Mock;
+import org.jmock.cglib.MockObjectTestCase;
+import org.jmock.core.Constraint;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+
+public class PortletFreemarkerResultTest extends MockObjectTestCase implements PortletActionConstants {
+
+ Mock mockInvocation = null;
+ Mock mockConfig = null;
+ Mock mockCtx = null;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mockInvocation = mock(ActionInvocation.class);
+ mockCtx = mock(PortletContext.class);
+
+ Map paramMap = new HashMap();
+ Map sessionMap = new HashMap();
+
+ Map context = new HashMap();
+ context.put(ActionContext.SESSION, sessionMap);
+ context.put(ActionContext.PARAMETERS, paramMap);
+ context.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, mockCtx.proxy());
+
+ ActionContext.setContext(new ActionContext(context));
+
+ mockInvocation.stubs().method("getInvocationContext").will(returnValue(ActionContext.getContext()));
+
+ }
+
+ public void testDoExecute_event_locationIsJsp() {
+ Mock mockRequest = mock(ActionRequest.class);
+ Mock mockResponse = mock(ActionResponse.class);
+ Mock mockProxy = mock(ActionProxy.class);
+
+ Constraint[] params = new Constraint[]{eq(PortletActionConstants.ACTION_PARAM), eq("freemarkerDirect")};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ params = new Constraint[]{eq(PortletActionConstants.MODE_PARAM), eq(PortletMode.VIEW.toString())};
+ mockResponse.expects(once()).method("setRenderParameter").with(params);
+ mockRequest.stubs().method("getPortletMode").will(returnValue(PortletMode.VIEW));
+ mockProxy.stubs().method("getNamespace").will(returnValue(""));
+
+ mockInvocation.stubs().method("getProxy").will(returnValue(mockProxy.proxy()));
+
+ ActionContext ctx = ActionContext.getContext();
+
+ Map session = new HashMap();
+
+ ctx.put(PortletActionConstants.REQUEST, mockRequest.proxy());
+ ctx.put(PortletActionConstants.RESPONSE, mockResponse.proxy());
+ ctx.put(PortletActionConstants.PHASE, PortletActionConstants.EVENT_PHASE);
+ ctx.put(ActionContext.SESSION, session);
+
+ PortletFreemarkerResult result = new PortletFreemarkerResult();
+ try {
+ result.doExecute("/WEB-INF/pages/testJsp.ftl", (ActionInvocation)mockInvocation.proxy());
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ fail("Error occured!");
+ }
+ assertEquals("/WEB-INF/pages/testJsp.ftl", session.get(RENDER_DIRECT_LOCATION));
+ }
+
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ ActionContext.setContext(null);
+ }
+}
\ No newline at end of file
diff --git a/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/views/jsp/PortletUrlTagTest.java b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/views/jsp/PortletUrlTagTest.java
new file mode 100644
index 0000000..6487bcc
--- /dev/null
+++ b/plugins/struts2-portlet-plugin/src/test/java/org/apache/struts2/views/jsp/PortletUrlTagTest.java
@@ -0,0 +1,399 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.struts2.views.jsp;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.portlet.PortletContext;
+import javax.portlet.PortletMode;
+import javax.portlet.PortletModeException;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletURL;
+import javax.portlet.WindowState;
+import javax.portlet.WindowStateException;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.StrutsTestCase;
+import org.apache.struts2.portlet.PortletActionConstants;
+import org.apache.struts2.portlet.servlet.PortletServletRequest;
+import org.apache.struts2.portlet.util.PortletUrlHelper;
+import org.springframework.mock.web.portlet.MockPortalContext;
+import org.springframework.mock.web.portlet.MockPortletContext;
+import org.springframework.mock.web.portlet.MockPortletURL;
+import org.springframework.mock.web.portlet.MockRenderRequest;
+import org.springframework.mock.web.portlet.MockRenderResponse;
+
+import com.mockobjects.servlet.MockJspWriter;
+import com.mockobjects.servlet.MockPageContext;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.mock.MockActionInvocation;
+import com.opensymphony.xwork2.mock.MockActionProxy;
+import com.opensymphony.xwork2.util.ValueStack;
+
+/**
+ */
+@SuppressWarnings("unchecked")
+public class PortletUrlTagTest extends StrutsTestCase {
+
+ private URLTag tag = new URLTag();
+
+ private ValueStack stack = null;
+
+ private ActionContext context = null;
+
+ private MockRenderRequest renderRequest;
+
+ private MockPortletUrl renderUrl;
+
+ private MockPortletUrl actionUrl;
+
+ private MockRenderResponse renderResponse;
+
+ private MockPageContext pageContext;
+
+ private MockActionInvocation actionInvocation;
+
+ private MockActionProxy actionProxy;
+
+ private MockJspWriter jspWriter;
+
+ private MockPortletContext portletContext;
+
+ public void setUp() throws Exception {
+ super.setUp();
+
+ context = ActionContext.getContext();
+ stack = context.getValueStack();
+
+ portletContext = new MockPortletContext();
+ renderRequest = new MockRenderRequest();
+ renderRequest.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
+ renderUrl = new MockPortletUrl("render");
+ actionUrl = new MockPortletUrl("action");
+ renderResponse = new MockRenderResponse() {
+ @Override
+ public PortletURL createRenderURL() {
+ return renderUrl;
+ }
+
+ @Override
+ public PortletURL createActionURL() {
+ return actionUrl;
+ }
+ };
+
+ Map modeMap = new HashMap();
+ modeMap.put(PortletMode.VIEW, "/view");
+ modeMap.put(PortletMode.HELP, "/help");
+ modeMap.put(PortletMode.EDIT, "/edit");
+
+ context.put(PortletActionConstants.REQUEST, renderRequest);
+ context.put(PortletActionConstants.RESPONSE, renderResponse);
+ context.put(PortletActionConstants.PHASE, PortletActionConstants.RENDER_PHASE);
+ context.put(PortletActionConstants.MODE_NAMESPACE_MAP, modeMap);
+ context.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, portletContext);
+
+ actionInvocation = new MockActionInvocation();
+ actionProxy = new MockActionProxy();
+
+ actionInvocation.setAction(new Object());
+ actionInvocation.setProxy(actionProxy);
+ actionInvocation.setStack(stack);
+
+ context.setActionInvocation(actionInvocation);
+
+ pageContext = new MockPageContext();
+ pageContext.setRequest(new PortletServletRequest(renderRequest, null));
+ jspWriter = new MockJspWriter();
+ pageContext.setJspWriter(jspWriter);
+
+ tag.setPageContext(pageContext);
+
+ }
+
+ public void testEnsureParamsAreStringArrays() {
+ Map params = new HashMap();
+ params.put("param1", "Test1");
+ params.put("param2", new String[] { "Test2" });
+
+ Map result = PortletUrlHelper.ensureParamsAreStringArrays(params);
+ assertEquals(2, result.size());
+ assertTrue(result.get("param1") instanceof String[]);
+ }
+
+ public void testSetWindowState() throws Exception {
+
+ tag.setAction("testAction");
+ tag.setWindowState("maximized");
+ tag.doStartTag();
+ tag.doEndTag();
+
+ assertEquals("/view/testAction", renderUrl.getParameter(PortletActionConstants.ACTION_PARAM));
+ assertEquals(PortletMode.VIEW.toString(), renderUrl.getParameter(PortletActionConstants.MODE_PARAM));
+ assertEquals(PortletMode.VIEW, renderUrl.getPortletMode());
+ assertEquals(WindowState.MAXIMIZED, renderUrl.getWindowState());
+
+ }
+
+ public void testSetPortletMode() throws Exception {
+
+ tag.setAction("testAction");
+ tag.setPortletMode("help");
+ tag.doStartTag();
+ tag.doEndTag();
+
+ assertEquals("/help/testAction", renderUrl.getParameter(PortletActionConstants.ACTION_PARAM));
+ assertEquals(PortletMode.HELP.toString(), renderUrl.getParameter(PortletActionConstants.MODE_PARAM));
+ assertEquals(PortletMode.HELP, renderUrl.getPortletMode());
+ assertEquals(WindowState.NORMAL, renderUrl.getWindowState());
+ }
+
+ public void testUrlWithQueryParams() throws Exception {
+
+ tag.setAction("testAction?testParam1=testValue1");
+ tag.doStartTag();
+ tag.doEndTag();
+
+ assertEquals("/view/testAction", renderUrl.getParameter(PortletActionConstants.ACTION_PARAM));
+ assertEquals("testValue1", renderUrl.getParameter("testParam1"));
+ assertEquals(PortletMode.VIEW.toString(), renderUrl.getParameter(PortletActionConstants.MODE_PARAM));
+ assertEquals(PortletMode.VIEW, renderUrl.getPortletMode());
+ assertEquals(WindowState.NORMAL, renderUrl.getWindowState());
+ }
+
+ public void testActionUrl() throws Exception {
+
+ tag.setAction("testAction");
+ tag.setPortletUrlType("action");
+ tag.doStartTag();
+ tag.doEndTag();
+
+ assertEquals("/view/testAction", actionUrl.getParameter(PortletActionConstants.ACTION_PARAM));
+ assertEquals(PortletMode.VIEW, actionUrl.getPortletMode());
+ assertEquals(WindowState.NORMAL, actionUrl.getWindowState());
+ }
+
+ public void testResourceUrl() throws Exception {
+ renderRequest.setContextPath("/myPortlet");
+ jspWriter.setExpectedData("/myPortlet/image.gif");
+ tag.setValue("image.gif");
+ tag.doStartTag();
+ tag.doEndTag();
+ jspWriter.verify();
+ }
+
+ public void testResourceUrlWithNestedParam() throws Exception {
+ renderRequest.setContextPath("/myPortlet");
+ jspWriter.setExpectedData("/myPortlet/image.gif?testParam1=testValue1");
+
+ ParamTag paramTag = new ParamTag();
+ paramTag.setPageContext(pageContext);
+ paramTag.setParent(tag);
+ paramTag.setName("testParam1");
+ paramTag.setValue("'testValue1'");
+ tag.setValue("image.gif");
+ tag.doStartTag();
+ paramTag.doStartTag();
+ paramTag.doEndTag();
+ tag.doEndTag();
+ jspWriter.verify();
+ }
+
+ public void testResourceUrlWithTwoNestedParam() throws Exception {
+ renderRequest.setContextPath("/myPortlet");
+ jspWriter.setExpectedData("/myPortlet/image.gif?testParam1=testValue1&testParam2=testValue2");
+
+ ParamTag paramTag = new ParamTag();
+ paramTag.setPageContext(pageContext);
+ paramTag.setParent(tag);
+ paramTag.setName("testParam1");
+ paramTag.setValue("'testValue1'");
+ ParamTag paramTag2 = new ParamTag();
+ paramTag2.setPageContext(pageContext);
+ paramTag2.setParent(tag);
+ paramTag2.setName("testParam2");
+ paramTag2.setValue("'testValue2'");
+ tag.setValue("image.gif");
+ tag.doStartTag();
+ paramTag.doStartTag();
+ paramTag.doEndTag();
+ paramTag2.doStartTag();
+ paramTag2.doEndTag();
+ tag.doEndTag();
+ jspWriter.verify();
+ }
+
+ public void testResourceUrlWithNestedParamThatIsNotString() throws Exception {
+ renderRequest.setContextPath("/myPortlet");
+ jspWriter.setExpectedData("/myPortlet/image.gif?id=5");
+
+ ParamTag paramTag = new ParamTag();
+ paramTag.setPageContext(pageContext);
+ paramTag.setParent(tag);
+ paramTag.setName("id");
+ paramTag.setValue("5");
+
+ tag.setValue("image.gif");
+ tag.doStartTag();
+ paramTag.doStartTag();
+ paramTag.doEndTag();
+ tag.doEndTag();
+ jspWriter.verify();
+ }
+
+ public void testResourceUrlWithNestedOgnlExpressionParamThatIsNotString() throws Exception {
+ renderRequest.setContextPath("/myPortlet");
+ jspWriter.setExpectedData("/myPortlet/image.gif?id=5");
+
+ Object o = new Object() {
+ public Integer getId() {
+ return 5;
+ }
+ };
+ tag.getStack().push(o);
+
+ ParamTag paramTag = new ParamTag();
+ paramTag.setPageContext(pageContext);
+ paramTag.setParent(tag);
+ paramTag.setName("id");
+ paramTag.setValue("id");
+
+ tag.setValue("image.gif");
+ tag.doStartTag();
+ paramTag.doStartTag();
+ paramTag.doEndTag();
+ tag.doEndTag();
+ jspWriter.verify();
+ }
+
+ public void testUrlWithMethod() throws Exception {
+ tag.setAction("testAction");
+ tag.setMethod("input");
+ tag.doStartTag();
+ tag.doEndTag();
+
+ assertEquals("/view/testAction!input", renderUrl.getParameter(PortletActionConstants.ACTION_PARAM));
+ assertEquals(PortletMode.VIEW.toString(), renderUrl.getParameter(PortletActionConstants.MODE_PARAM));
+ assertEquals(PortletMode.VIEW, renderUrl.getPortletMode());
+ assertEquals(WindowState.NORMAL, renderUrl.getWindowState());
+ }
+
+ public void testUrlWithNoActionOrMethod() throws Exception {
+ actionProxy.setActionName("currentExecutingAction");
+ actionProxy.setNamespace("/currentNamespace");
+ tag.doStartTag();
+ tag.doEndTag();
+
+ assertEquals("/view/currentNamespace/currentExecutingAction", renderUrl
+ .getParameter(PortletActionConstants.ACTION_PARAM));
+ assertEquals(PortletMode.VIEW.toString(), renderUrl.getParameter(PortletActionConstants.MODE_PARAM));
+ assertEquals(PortletMode.VIEW, renderUrl.getPortletMode());
+ assertEquals(WindowState.NORMAL, renderUrl.getWindowState());
+ }
+
+ public void testUrlShouldNotIncludeParamsFromHttpQueryString() throws Exception {
+
+ PortletServletRequestWithQueryString req = new PortletServletRequestWithQueryString(renderRequest, null);
+ req.setQueryString("thisParamShouldNotBeIncluded=thisValueShouldNotBeIncluded");
+ pageContext.setRequest(req);
+ tag.setAction("testAction?testParam1=testValue1");
+ tag.doStartTag();
+ tag.doEndTag();
+
+ assertEquals("/view/testAction", renderUrl.getParameter(PortletActionConstants.ACTION_PARAM));
+ assertEquals("testValue1", renderUrl.getParameter("testParam1"));
+ assertNull(renderUrl.getParameter("thisParamShouldNotBeIncluded"));
+ assertEquals(PortletMode.VIEW.toString(), renderUrl.getParameter(PortletActionConstants.MODE_PARAM));
+ assertEquals(PortletMode.VIEW, renderUrl.getPortletMode());
+ assertEquals(WindowState.NORMAL, renderUrl.getWindowState());
+ }
+
+ public void testUrlShouldIgnoreIncludeParams() throws Exception {
+ PortletServletRequestWithQueryString req = new PortletServletRequestWithQueryString(renderRequest, null);
+ req.setQueryString("thisParamShouldNotBeIncluded=thisValueShouldNotBeIncluded");
+ pageContext.setRequest(req);
+ tag.setAction("testAction?testParam1=testValue1");
+ tag.setIncludeParams("GET");
+ tag.doStartTag();
+ tag.doEndTag();
+
+ assertEquals("/view/testAction", renderUrl.getParameter(PortletActionConstants.ACTION_PARAM));
+ assertEquals("testValue1", renderUrl.getParameter("testParam1"));
+ assertNull(renderUrl.getParameter("thisParamShouldNotBeIncluded"));
+ assertEquals(PortletMode.VIEW.toString(), renderUrl.getParameter(PortletActionConstants.MODE_PARAM));
+ assertEquals(PortletMode.VIEW, renderUrl.getPortletMode());
+ assertEquals(WindowState.NORMAL, renderUrl.getWindowState());
+ }
+
+ private static class PortletServletRequestWithQueryString extends PortletServletRequest {
+
+ private String queryString;
+
+ public PortletServletRequestWithQueryString(PortletRequest portletRequest, PortletContext portletContext) {
+ super(portletRequest, portletContext);
+ }
+
+ public void setQueryString(String queryString) {
+ this.queryString = queryString;
+ }
+
+ @Override
+ public String getQueryString() {
+ return queryString;
+ }
+
+ }
+
+ private static class MockPortletUrl extends MockPortletURL {
+
+ private PortletMode portletMode;
+
+ private WindowState windowState;
+
+ public MockPortletUrl(String urlType) {
+ super(new MockPortalContext(), urlType);
+ }
+
+ @Override
+ public void setPortletMode(PortletMode portletMode) throws PortletModeException {
+ super.setPortletMode(portletMode);
+ this.portletMode = portletMode;
+ }
+
+ public PortletMode getPortletMode() {
+ return portletMode;
+ }
+
+ @Override
+ public void setWindowState(WindowState windowState) throws WindowStateException {
+ super.setWindowState(windowState);
+ this.windowState = windowState;
+ }
+
+ public WindowState getWindowState() {
+ return windowState;
+ }
+
+ }
+}