You are viewing a plain text version of this content. The canonical link for it is here.
Posted to pluto-scm@portals.apache.org by as...@apache.org on 2019/04/09 21:00:08 UTC
[portals-pluto] branch master updated: PLUTO-772 Implement MVCBean
portlets
This is an automated email from the ASF dual-hosted git repository.
asfgriff pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/portals-pluto.git
The following commit(s) were added to refs/heads/master by this push:
new 03b0ef0 PLUTO-772 Implement MVCBean portlets
03b0ef0 is described below
commit 03b0ef0982d6473962cc17b6f3775af1b922810a
Author: Neil Griffin <ne...@gmail.com>
AuthorDate: Sat Apr 6 16:02:46 2019 -0400
PLUTO-772 Implement MVCBean portlets
---
README | 23 +
.../apache/pluto/maven/InstallationDependency.java | 114 +++--
.../src/main/resources/versions.properties | 19 +-
.../pluto/container/driver/PortletServlet3.java | 17 +-
pluto-container/pom.xml | 28 ++
.../bean/mvc/AfterControllerEventImpl.java | 32 ++
.../bean/mvc/AfterProcessViewEventImpl.java | 31 ++
.../bean/mvc/BaseControllerEventImpl.java | 43 ++
.../bean/mvc/BaseProcessViewEventImpl.java | 42 ++
.../apache/pluto/container/bean/mvc/BeanUtil.java | 51 +++
.../bean/mvc/BeanValidationInterceptor.java | 155 +++++++
.../bean/mvc/BeanValidationInterceptorBinding.java | 36 ++
.../mvc/BeanValidationMessageInterpolator.java | 34 ++
.../container/bean/mvc/BeanValidationProducer.java | 105 +++++
.../bean/mvc/BeanValidationValidator.java | 34 ++
.../bean/mvc/BeforeControllerEventImpl.java | 32 ++
.../bean/mvc/BeforeProcessViewEventImpl.java | 31 ++
.../pluto/container/bean/mvc/BindingErrorImpl.java | 38 ++
.../container/bean/mvc/BindingResultImpl.java | 123 +++++
.../container/bean/mvc/ConfigurationImpl.java | 132 ++++++
.../container/bean/mvc/ControllerInterceptor.java | 295 ++++++++++++
.../bean/mvc/ControllerInterceptorBinding.java | 36 ++
.../bean/mvc/ControllerRedirectEventImpl.java | 42 ++
.../apache/pluto/container/bean/mvc/CsrfImpl.java | 46 ++
.../bean/mvc/CsrfValidationInterceptor.java | 134 ++++++
.../bean/mvc/CsrfValidationInterceptorBinding.java | 36 ++
.../bean/mvc/DescendingPriorityComparator.java | 61 +++
.../pluto/container/bean/mvc/EncodersImpl.java | 38 ++
.../container/bean/mvc/LocaleResolverChain.java | 51 +++
.../bean/mvc/LocaleResolverContextImpl.java | 79 ++++
.../container/bean/mvc/LocaleResolverImpl.java | 50 ++
.../bean/mvc/MessageInterpolatorContextImpl.java | 49 ++
.../pluto/container/bean/mvc/ModelsImpl.java | 74 +++
.../container/bean/mvc/ModifiedAnnotatedType.java | 90 ++++
.../pluto/container/bean/mvc/ModifiedField.java | 88 ++++
.../pluto/container/bean/mvc/ModifiedMethod.java | 103 +++++
.../container/bean/mvc/MutableBindingResult.java | 35 ++
.../pluto/container/bean/mvc/MvcContextImpl.java | 146 ++++++
.../pluto/container/bean/mvc/MvcExtension.java | 220 +++++++++
.../container/bean/mvc/MvcRuntimeDelegate.java | 72 +++
.../bean/mvc/ParamConverterProviderImpl.java | 184 ++++++++
.../bean/mvc/ParamConverterProviderManager.java | 64 +++
.../bean/mvc/ParamConverterProviders.java | 34 ++
.../bean/mvc/ParamConverterProvidersProducer.java | 57 +++
.../pluto/container/bean/mvc/ParamErrorImpl.java | 44 ++
.../pluto/container/bean/mvc/PortletParam.java | 34 ++
.../container/bean/mvc/PortletParamProducer.java | 507 +++++++++++++++++++++
.../pluto/container/bean/mvc/RequestImpl.java | 62 +++
.../pluto/container/bean/mvc/ResourceInfoImpl.java | 46 ++
.../container/bean/mvc/ServletContextProducer.java | 63 +++
.../pluto/container/bean/mvc/SpringCsrfToken.java | 66 +++
.../pluto/container/bean/mvc/UriInfoImpl.java | 127 ++++++
.../container/bean/mvc/ValidationErrorImpl.java | 39 ++
.../container/bean/mvc/ViewEngineContextImpl.java | 145 ++++++
.../bean/mvc/ViewEngineContextProducer.java | 39 ++
.../container/bean/mvc/ViewEngineJspImpl.java | 71 +++
.../pluto/container/bean/mvc/ViewEngines.java | 34 ++
.../container/bean/mvc/ViewEnginesProducer.java | 51 +++
.../pluto/container/bean/mvc/ViewRenderer.java | 38 ++
.../container/bean/mvc/ViewRendererMvcImpl.java | 126 +++++
.../bean/processor/AnnotatedConfigBean.java | 15 +
.../bean/processor/MethodAnnotationRecognizer.java | 6 +-
.../bean/processor/MethodDescription.java | 20 +-
.../processor/PortletAnnotationRecognizer.java | 125 ++++-
.../bean/processor/PortletCDIExtension.java | 6 +
.../container/bean/processor/PortletInvoker.java | 45 +-
.../processor/PortletSessionScopedBeanMap.java | 19 +
.../bean/processor/RedirectScopedBeanHolder.java | 176 +++++++
.../bean/processor/RedirectScopedConfig.java | 146 ++++++
.../bean/processor/RedirectScopedContext.java | 87 ++++
.../container/bean/processor/SignatureVariant.java | 34 +-
.../om/portlet/impl/ConfigurationHolder.java | 15 +-
.../services/javax.ws.rs.ext.RuntimeDelegate | 1 +
.../processor/fixtures/RedirectScopedClass.java | 35 ++
.../bean/processor/tests/RedirectScopedTest.java | 96 ++++
.../container/PortletAsyncContextImpl.java | 4 +
pom.xml | 102 +++--
portlet-tck_3.0/V2AddlRequestTests/pom.xml | 1 -
portlet-tck_3.0/V3HeaderPortletTests/pom.xml | 1 -
79 files changed, 5467 insertions(+), 163 deletions(-)
diff --git a/README b/README
index 67cc429..2b76575 100644
--- a/README
+++ b/README
@@ -63,3 +63,26 @@ http://localhost:8080/pluto/portal
Use the credentials pluto/pluto to log on to the application.
+Third-Party Licensing
+=====================
+
+The Apache Pluto software is compiled against version 2.1.1 of the JAX-RS API
+which is made available under the Eclipse Public License, version 2.0 (EPL 2.0).
+
+In addition, the software is compiled against version 1.3.1 of the Common
+Annotations for the Java(TM) Platform API (Commons-Annotations API) which is
+made available under the Common Development and Distribution License (CDDL).
+
+According to the [ASF 3RD PARTY LICENSE POLICY]
+(https://apache.org/legal/resolved.html#weak-copyleft-licenses), third-party
+software that is licensed under the EPL 2.0 or CDDL licenses may be included
+in binary form. To that end, the binary version of the JAX-RS API dependency
+is found in the following path of the Pluto+Tomcat bundle:
+
+ <tomcat-home>/lib/javax.ws.rs-api-2.1.1.jar
+
+Apache Tomcat includes its own version of the Commons-Annotations API (which is
+licensed under the Apache License, Version 2.0) in the following-path of the
+Pluto-Tomcat bundle:
+
+ <tomcat-home>/lib/annotations-api.jar
diff --git a/maven-pluto-plugin/src/main/java/org/apache/pluto/maven/InstallationDependency.java b/maven-pluto-plugin/src/main/java/org/apache/pluto/maven/InstallationDependency.java
index 93f23a6..84e80da 100644
--- a/maven-pluto-plugin/src/main/java/org/apache/pluto/maven/InstallationDependency.java
+++ b/maven-pluto-plugin/src/main/java/org/apache/pluto/maven/InstallationDependency.java
@@ -23,10 +23,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Properties;
-//import sun.security.provider.SHA;
-
-//import com.sun.corba.se.impl.protocol.SharedCDRClientRequestDispatcherImpl;
-
/**
*
*/
@@ -47,15 +43,47 @@ class InstallationDependency {
}
public static final InstallationDependency PORTLET_API =
- new InstallationDependency("javax.portlet", "portlet-api",
- VERSION_PROPERTIES.getProperty("portals.portlet-api.version"));
+ new InstallationDependency("javax.portlet", "portlet-api",
+ VERSION_PROPERTIES.getProperty("portals.portlet-api.version"));
+
+ public static final InstallationDependency COMMONS_LANG3 =
+ new InstallationDependency("org.apache.commons", "commons-lang3",
+ VERSION_PROPERTIES.getProperty("commons-lang.version"));
+
+ public static final InstallationDependency CLASSMATE =
+ new InstallationDependency("com.fasterxml", "classmate",
+ VERSION_PROPERTIES.getProperty("classmate.version"));
+
+ public static final InstallationDependency HIBERNATE_VALIDATOR =
+ new InstallationDependency("org.hibernate.validator", "hibernate-validator",
+ VERSION_PROPERTIES.getProperty("hibernate-validator.version"));
+
+ public static final InstallationDependency HIBERNATE_VALIDATOR_CDI =
+ new InstallationDependency("org.hibernate.validator", "hibernate-validator-cdi",
+ VERSION_PROPERTIES.getProperty("hibernate-validator.version"));
+
+ public static final InstallationDependency JAXRS_API =
+ new InstallationDependency("javax.ws.rs", "javax.ws.rs-api",
+ VERSION_PROPERTIES.getProperty("jaxrs-api.version"));
+
+ public static final InstallationDependency JBOSS_LOGGING =
+ new InstallationDependency("org.jboss.logging", "jboss-logging",
+ VERSION_PROPERTIES.getProperty("jboss-logging.version"));
+
+ public static final InstallationDependency MVC_API =
+ new InstallationDependency("javax.mvc", "javax.mvc-api",
+ VERSION_PROPERTIES.getProperty("mvc-api.version"));
+
+ public static final InstallationDependency VALIDATION_API =
+ new InstallationDependency("javax.validation", "validation-api",
+ VERSION_PROPERTIES.getProperty("validation-api.version"));
public static final InstallationDependency CDI_IMPL =
- new InstallationDependency("org.jboss.weld.servlet", "weld-servlet",
+ new InstallationDependency("org.jboss.weld.servlet", "weld-servlet",
VERSION_PROPERTIES.getProperty("cdi.version"));
public static final InstallationDependency ANNOTATION_DETECTOR =
- new InstallationDependency("eu.infomas", "annotation-detector",
+ new InstallationDependency("eu.infomas", "annotation-detector",
VERSION_PROPERTIES.getProperty("annotation-detector.version"));
public static final InstallationDependency SLF4J_API =
@@ -67,10 +95,10 @@ class InstallationDependency {
VERSION_PROPERTIES.getProperty("slf4j.version"));
public static final InstallationDependency CONTAINER_API =
- new InstallationDependency("org.apache.portals.pluto", "pluto-container-api",
+ new InstallationDependency("org.apache.portals.pluto", "pluto-container-api",
VERSION_PROPERTIES.getProperty("pluto.version"));
- public static final InstallationDependency CONTAINER =
+ public static final InstallationDependency CONTAINER =
new InstallationDependency("org.apache.portals.pluto", "pluto-container",
VERSION_PROPERTIES.getProperty("pluto.version"));
@@ -79,75 +107,35 @@ class InstallationDependency {
VERSION_PROPERTIES.getProperty("pluto.version"));
public static final InstallationDependency CONTAINER_DRIVER_API =
- new InstallationDependency("org.apache.portals.pluto", "pluto-container-driver-api",
+ new InstallationDependency("org.apache.portals.pluto", "pluto-container-driver-api",
VERSION_PROPERTIES.getProperty("pluto.version"));
- public static final InstallationDependency PORTAL =
- new InstallationDependency("org.apache.portals.pluto", "pluto-portal",
+ public static final InstallationDependency PORTAL =
+ new InstallationDependency("org.apache.portals.pluto", "pluto-portal",
VERSION_PROPERTIES.getProperty("pluto.version"), "war");
- public static final InstallationDependency TESTSUITE =
+ public static final InstallationDependency TESTSUITE =
new InstallationDependency("org.apache.portals.pluto", "pluto-testsuite",
VERSION_PROPERTIES.getProperty("pluto.version"), "war");
- public static final InstallationDependency CASTOR =
- new InstallationDependency("org.codehaus.castor", "castor",
- VERSION_PROPERTIES.getProperty("castor.version"));
-
- public static final InstallationDependency COMMONS_LOGGING_API =
- new InstallationDependency("commons-logging", "commons-logging-api",
- VERSION_PROPERTIES.getProperty("commons-logging.version"));
-
- public static final InstallationDependency XERCES =
- new InstallationDependency("xerces", "xercesImpl",
- VERSION_PROPERTIES.getProperty("xercesImpl.version"));
-
- public static final InstallationDependency XML_PARSER_APIS =
- new InstallationDependency("xerces", "xmlParserAPIs",
- VERSION_PROPERTIES.getProperty("xmlParserAPIs.version"));
-
-
- public static final InstallationDependency JAXB_API =
- new InstallationDependency("javax.xml.bind","jaxb-api",
- VERSION_PROPERTIES.getProperty("jaxb.version"));
- public static final InstallationDependency JAXB_IMPL =
- new InstallationDependency("com.sun.xml.bind","jaxb-impl",
- VERSION_PROPERTIES.getProperty("jaxb-impl.version"));
-
- public static final InstallationDependency STAX_API =
- new InstallationDependency("stax","stax-api",
- VERSION_PROPERTIES.getProperty("stax.api.version"));
- public static final InstallationDependency STAX_IMPL =
- new InstallationDependency("stax","stax",
- VERSION_PROPERTIES.getProperty("stax.impl.version"));
-
- public static final InstallationDependency XALAN =
- new InstallationDependency("xalan","xalan",
- VERSION_PROPERTIES.getProperty("xalan.version"));
- public static final InstallationDependency ACTIVATION =
- new InstallationDependency("javax.activation","activation",
- VERSION_PROPERTIES.getProperty("activation.version"));
-
public static final InstallationDependency CCPP_API =
new InstallationDependency("javax.ccpp","ccpp",
VERSION_PROPERTIES.getProperty("ccpp-api.version"));
-// public static final InstallationDependency CCPP_IMPL=
-// new InstallationDependency("com.sun.ccpp","ccpp-ri",
-// VERSION_PROPERTIES.getProperty("ccpp-ri.version"));
-// public static final InstallationDependency RDF_FILTER =
-// new InstallationDependency("com.megginson.sax.rdf.rdffilter","rdffilter",
-// VERSION_PROPERTIES.getProperty("rdffilter.version"));
-// public static final InstallationDependency JENA =
-// new InstallationDependency("com.hp.hpl.jena","jena",
-// VERSION_PROPERTIES.getProperty("jena.version"));
-
-
+
private static final List<InstallationDependency> ENDORSED = new ArrayList<InstallationDependency>();
private static final List<InstallationDependency> SHARED = new ArrayList<InstallationDependency>();
static {
SHARED.add(PORTLET_API);
+ SHARED.add(COMMONS_LANG3);
+ SHARED.add(CLASSMATE);
+ SHARED.add(HIBERNATE_VALIDATOR);
+ SHARED.add(HIBERNATE_VALIDATOR_CDI);
+ SHARED.add(JAXRS_API);
+ SHARED.add(JBOSS_LOGGING);
+ SHARED.add(MVC_API);
+ SHARED.add(VALIDATION_API);
SHARED.add(CONTAINER);
SHARED.add(CONTAINER_API);
SHARED.add(CONTAINER_DRIVER_API);
diff --git a/maven-pluto-plugin/src/main/resources/versions.properties b/maven-pluto-plugin/src/main/resources/versions.properties
index f9a9083..2a64c3a 100644
--- a/maven-pluto-plugin/src/main/resources/versions.properties
+++ b/maven-pluto-plugin/src/main/resources/versions.properties
@@ -28,16 +28,9 @@ annotation-detector.version=${annotation-detector.version}
slf4j.version=${slf4j.version}
jstl.version=${jstl.version}
taglibs.standard.version=${taglibs.standard.version}
-xerces.version=${xerces.version}
-xercesImpl.version=${xerces.version}
-xmlParserAPIs.version=${xerces.version}
-castor.version=${castor.version}
commons-digester.version=${commons-digester.version}
commons-cli.version=${commons-cli.version}
-commons-httpclient.version=${commons-httpclient.version}
-commons-beanutils.version=${commons-beanutils.version}
commons-logging.version=${commons-logging.version}
-log4j.version=${log4j.version}
springframework.version=${springframework.version}
plexus.version=${plexus.version}
maven.version=${maven.version}
@@ -49,11 +42,13 @@ pluto.version=${pom.version}
#pluto.version=${pom.parent.version}
jaxb.version=${jaxb.version}
jaxb-impl.version=${jaxb-impl.version}
-xalan.version=${xalan.version}
-activation.version=${activation.version}
stax.api.version=${stax.api.version}
stax.impl.version=${stax.impl.version}
ccpp-api.version=${ccpp-api.version}
-ccpp-ri.version=${ccpp-ri.version}
-rdffilter.version=${rdffilter.version}
-jena.version=${jena.version}
+commons-lang.version=${commons-lang.version}
+classmate.version=${classmate.version}
+jaxrs-api.version=${jaxrs-api.version}
+jboss-logging.version=${jboss-logging.version}
+hibernate-validator.version=${hibernate-validator.version}
+mvc-api.version=${mvc-api.version}
+validation-api.version=${validation-api.version}
diff --git a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServlet3.java b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServlet3.java
index aeeea83..3da4fdf 100644
--- a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServlet3.java
+++ b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServlet3.java
@@ -70,6 +70,7 @@ import org.apache.pluto.container.bean.processor.PortletInvoker;
import org.apache.pluto.container.bean.processor.PortletRequestScopedBeanHolder;
import org.apache.pluto.container.bean.processor.PortletSessionBeanHolder;
import org.apache.pluto.container.bean.processor.PortletStateScopedBeanHolder;
+import org.apache.pluto.container.bean.processor.RedirectScopedBeanHolder;
import org.apache.pluto.container.impl.HttpServletPortletRequestWrapper;
import org.apache.pluto.container.om.portlet.impl.ConfigurationHolder;
import org.slf4j.Logger;
@@ -187,6 +188,7 @@ public class PortletServlet3 extends HttpServlet {
Bean<?> bean = beanmgr.resolve(beans);
acb = (AnnotatedConfigBean) beanmgr.getReference(bean, bean.getBeanClass(), beanmgr.createCreationalContext(bean));
LOG.debug("ACB instance: " + acb + ", RS config: " + ((acb==null) ? "null" : acb.getSessionScopedConfig()));
+ acb.getRedirectScopedConfig().activate(beanmgr);
acb.getSessionScopedConfig().activate(beanmgr);
acb.getStateScopedConfig().activate(beanmgr);
} catch (Throwable t) {
@@ -580,7 +582,7 @@ public class PortletServlet3 extends HttpServlet {
request.removeAttribute(PortletInvokerService.PORTLET_RESPONSE);
request.removeAttribute(PortletInvokerService.FILTER_MANAGER);
- afterInvoke(portletResponse);
+ afterInvoke(portletRequest, portletResponse);
} else {
LOG.debug("Async started, not releasing resources. executing req body: " + requestContext.isExecutingRequestBody());
@@ -630,6 +632,9 @@ public class PortletServlet3 extends HttpServlet {
// Set the portlet session bean holder for the thread & session
PortletSessionBeanHolder.setBeanHolder(req, acb.getSessionScopedConfig());
+ // Set the redirect scoped bean holder
+ RedirectScopedBeanHolder.setBeanHolder(req);
+
// Set the render state scoped bean holder
PortletStateScopedBeanHolder.setBeanHolder(req, acb.getStateScopedConfig());
@@ -652,7 +657,7 @@ public class PortletServlet3 extends HttpServlet {
* must be called after all method invocations have taken place, even if an
* exception occurs.
*/
- private void afterInvoke(PortletResponse resp) {
+ private void afterInvoke(PortletRequest portletRequest, PortletResponse portletResponse) {
if (acb != null) {
@@ -662,13 +667,17 @@ public class PortletServlet3 extends HttpServlet {
// Remove the portlet session bean holder for the thread
PortletSessionBeanHolder.removeBeanHolder();
+ // Remove the redirect bean holder for the thread
+ RedirectScopedBeanHolder.removeBeanHolder(
+ (portletRequest instanceof RenderRequest) && !(portletRequest instanceof HeaderRequest));
+
// Remove the render state bean holder. pass response if we're
// dealing with a StateAwareResponse. The response is used for state
// storage.
StateAwareResponse sar = null;
- if (resp instanceof StateAwareResponse) {
- sar = (StateAwareResponse) resp;
+ if (portletResponse instanceof StateAwareResponse) {
+ sar = (StateAwareResponse) portletResponse;
}
PortletStateScopedBeanHolder.removeBeanHolder(sar);
diff --git a/pluto-container/pom.xml b/pluto-container/pom.xml
index aedaecd..d7e28df 100644
--- a/pluto-container/pom.xml
+++ b/pluto-container/pom.xml
@@ -34,6 +34,10 @@
<!-- Other Support Libraries =========================================== -->
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
@@ -47,6 +51,12 @@
<!-- Specification Libraries =========================================== -->
<dependency>
+ <groupId>javax.annotation</groupId>
+ <artifactId>javax.annotation-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
<groupId>javax.portlet</groupId>
<artifactId>portlet-api</artifactId>
<version>${project.version}</version>
@@ -54,6 +64,24 @@
</dependency>
<dependency>
+ <groupId>javax.mvc</groupId>
+ <artifactId>javax.mvc-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<scope>provided</scope>
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/AfterControllerEventImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/AfterControllerEventImpl.java
new file mode 100644
index 0000000..a7ab68d
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/AfterControllerEventImpl.java
@@ -0,0 +1,32 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.event.AfterControllerEvent;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.UriInfo;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class AfterControllerEventImpl extends BaseControllerEventImpl implements AfterControllerEvent {
+
+ public AfterControllerEventImpl(ResourceInfo resourceInfo, UriInfo uriInfo) {
+ super(resourceInfo, uriInfo);
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/AfterProcessViewEventImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/AfterProcessViewEventImpl.java
new file mode 100644
index 0000000..c1bdc1d
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/AfterProcessViewEventImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.engine.ViewEngine;
+import javax.mvc.event.AfterProcessViewEvent;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class AfterProcessViewEventImpl extends BaseProcessViewEventImpl implements AfterProcessViewEvent {
+
+ public AfterProcessViewEventImpl(String view, Class<? extends ViewEngine> viewEngine) {
+ super(view, viewEngine);
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BaseControllerEventImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BaseControllerEventImpl.java
new file mode 100644
index 0000000..ee77c1a
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BaseControllerEventImpl.java
@@ -0,0 +1,43 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.UriInfo;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class BaseControllerEventImpl {
+
+ private ResourceInfo resourceInfo;
+ private UriInfo uriInfo;
+
+ public BaseControllerEventImpl(ResourceInfo resourceInfo, UriInfo uriInfo) {
+ this.resourceInfo = resourceInfo;
+ this.uriInfo = uriInfo;
+ }
+
+ public ResourceInfo getResourceInfo() {
+ return resourceInfo;
+ }
+
+ public UriInfo getUriInfo() {
+ return uriInfo;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BaseProcessViewEventImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BaseProcessViewEventImpl.java
new file mode 100644
index 0000000..25defb2
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BaseProcessViewEventImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.engine.ViewEngine;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class BaseProcessViewEventImpl {
+
+ private Class<? extends ViewEngine> viewEngine;
+ private String view;
+
+ public BaseProcessViewEventImpl(String view, Class<? extends ViewEngine> viewEngine) {
+ this.view = view;
+ this.viewEngine = viewEngine;
+ }
+
+ public Class<? extends ViewEngine> getEngine() {
+ return viewEngine;
+ }
+
+ public String getView() {
+ return view;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanUtil.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanUtil.java
new file mode 100644
index 0000000..8fc3fda
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanUtil.java
@@ -0,0 +1,51 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.enterprise.inject.Vetoed;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Vetoed
+public class BeanUtil {
+
+ public static <T> List<T> getBeanInstances(BeanManager beanManager, Class<T> type, Annotation... qualifiers) {
+
+ List<T> beanInstances = new ArrayList<>();
+
+ if (qualifiers == null) {
+ qualifiers = new Annotation[] {};
+ }
+
+ Set<Bean<?>> beans = beanManager.getBeans(type, qualifiers);
+
+ for (Bean<?> bean : beans) {
+ beanInstances.add((T) beanManager.getReference(bean, type, beanManager.createCreationalContext(bean)));
+ }
+
+ return beanInstances;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationInterceptor.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationInterceptor.java
new file mode 100644
index 0000000..1b1ac3f
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationInterceptor.java
@@ -0,0 +1,155 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+import javax.mvc.MvcContext;
+import javax.validation.ConstraintViolation;
+import javax.validation.ElementKind;
+import javax.validation.MessageInterpolator;
+import javax.validation.Path;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.QueryParam;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Interceptor
+@BeanValidationInterceptorBinding
+@Priority(Interceptor.Priority.LIBRARY_BEFORE)
+public class BeanValidationInterceptor implements Serializable {
+
+ private static final long serialVersionUID = 2378576156374329311L;
+
+ @BeanValidationMessageInterpolator
+ @Inject
+ private MessageInterpolator messageInterpolator;
+
+ @Inject
+ private MutableBindingResult mutableBindingResult;
+
+ @Inject
+ private MvcContext mvcContext;
+
+ @Inject
+ private ValidatorFactory validatorFactory;
+
+ @AroundInvoke
+ public Object validateMethodInvocation(InvocationContext invocationContext) throws Exception {
+
+ Validator validator = validatorFactory.getValidator();
+
+ if (validator == null) {
+ return invocationContext.proceed();
+ }
+
+ Set<ConstraintViolation<Object>> constraintViolations = validator.validate(invocationContext.getTarget());
+
+ for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
+
+ Path propertyPath = constraintViolation.getPropertyPath();
+
+ Path.Node lastPathNode = null;
+
+ for (Path.Node pathNode : propertyPath) {
+ lastPathNode = pathNode;
+ }
+
+ if ((lastPathNode != null) && (lastPathNode.getKind() == ElementKind.PROPERTY)) {
+
+ Object leafBean = constraintViolation.getLeafBean();
+ Class<?> leafBeanClass = leafBean.getClass();
+
+ BeanInfo beanInfo = Introspector.getBeanInfo(leafBean.getClass());
+
+ PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
+
+ for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+ String propertyDescriptorName = propertyDescriptor.getName();
+
+ if (propertyDescriptorName.equals("targetClass")) {
+ Method readMethod = propertyDescriptor.getReadMethod();
+ leafBeanClass = (Class<?>) readMethod.invoke(leafBean);
+ }
+ }
+
+ Field leafBeanField = leafBeanClass.getDeclaredField(lastPathNode.getName());
+
+ String paramName = null;
+
+ Annotation[] annotations = leafBeanField.getAnnotations();
+
+ for (Annotation annotation : annotations) {
+
+ if (annotation instanceof CookieParam) {
+ CookieParam cookieParam = (CookieParam) annotation;
+ paramName = cookieParam.value();
+
+ break;
+ }
+ else if (annotation instanceof FormParam) {
+ FormParam formParam = (FormParam) annotation;
+ paramName = formParam.value();
+
+ break;
+ }
+ else if (annotation instanceof HeaderParam) {
+ HeaderParam headerParam = (HeaderParam) annotation;
+ paramName = headerParam.value();
+
+ break;
+ }
+ else if (annotation instanceof QueryParam) {
+ QueryParam queryParam = (QueryParam) annotation;
+ paramName = queryParam.value();
+ }
+ }
+
+ String interpolatedMessage = constraintViolation.getMessage();
+
+ if (messageInterpolator != null) {
+ interpolatedMessage = messageInterpolator.interpolate(constraintViolation.getMessageTemplate(),
+ new MessageInterpolatorContextImpl(constraintViolation), mvcContext.getLocale());
+ }
+
+ mutableBindingResult.addValidationError(new ValidationErrorImpl(paramName, interpolatedMessage,
+ constraintViolation));
+ }
+
+ }
+
+ return invocationContext.proceed();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationInterceptorBinding.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationInterceptorBinding.java
new file mode 100644
index 0000000..eeea311
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationInterceptorBinding.java
@@ -0,0 +1,36 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.interceptor.InterceptorBinding;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Inherited
+@InterceptorBinding
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface BeanValidationInterceptorBinding {
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationMessageInterpolator.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationMessageInterpolator.java
new file mode 100644
index 0000000..a800e89
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationMessageInterpolator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
+public @interface BeanValidationMessageInterpolator {
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationProducer.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationProducer.java
new file mode 100644
index 0000000..82726b8
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationProducer.java
@@ -0,0 +1,105 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.Iterator;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Dependent;
+import javax.enterprise.inject.Instance;
+import javax.enterprise.inject.Produces;
+import javax.inject.Inject;
+import javax.validation.MessageInterpolator;
+import javax.validation.NoProviderFoundException;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+@ApplicationScoped
+public class BeanValidationProducer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BeanValidationProducer.class);
+
+ private MessageInterpolator messageInterpolator;
+ private Validator validator;
+
+ @Inject
+ private Instance<ValidatorFactory> validatorFactoryInstance;
+
+ @BeanValidationMessageInterpolator
+ @Dependent
+ @Produces
+ public MessageInterpolator getMessageInterpolator() {
+ return messageInterpolator;
+ }
+
+ @BeanValidationValidator
+ @Dependent
+ @Produces
+ public Validator getValidator() {
+ return validator;
+ }
+
+ @PostConstruct
+ public void postConstruct() {
+ ValidatorFactory validatorFactory = null;
+
+ Iterator<ValidatorFactory> iterator = validatorFactoryInstance.iterator();
+
+ if (iterator.hasNext()) {
+ validatorFactory = iterator.next();
+ }
+
+ if (validatorFactory == null) {
+
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("ValidatorFactory was not injected -- if using Hibernate " + "Validator, please include the " +
+ "hibernate-validator-cdi dependency.");
+ }
+
+ try {
+ validatorFactory = Validation.buildDefaultValidatorFactory();
+ }
+ catch (NoProviderFoundException npfe) {
+ LOG.error(npfe.getMessage(), npfe);
+ }
+ }
+
+ if (validatorFactory != null) {
+ messageInterpolator = validatorFactory.getMessageInterpolator();
+
+ if ((messageInterpolator == null) && LOG.isWarnEnabled()) {
+ LOG.warn("Bean validation MessageInterpolator not available");
+ }
+
+ validator = validatorFactory.getValidator();
+
+ if ((validator == null) && LOG.isWarnEnabled()) {
+ LOG.warn("Bean validation validator not available");
+ }
+ }
+ }
+
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationValidator.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationValidator.java
new file mode 100644
index 0000000..78e8ada
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeanValidationValidator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
+public @interface BeanValidationValidator {
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeforeControllerEventImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeforeControllerEventImpl.java
new file mode 100644
index 0000000..7c261f4
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeforeControllerEventImpl.java
@@ -0,0 +1,32 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.event.BeforeControllerEvent;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.UriInfo;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class BeforeControllerEventImpl extends BaseControllerEventImpl implements BeforeControllerEvent {
+
+ public BeforeControllerEventImpl(ResourceInfo resourceInfo, UriInfo uriInfo) {
+ super(resourceInfo, uriInfo);
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeforeProcessViewEventImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeforeProcessViewEventImpl.java
new file mode 100644
index 0000000..cfa6ccc
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BeforeProcessViewEventImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.engine.ViewEngine;
+import javax.mvc.event.BeforeProcessViewEvent;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class BeforeProcessViewEventImpl extends BaseProcessViewEventImpl implements BeforeProcessViewEvent {
+
+ public BeforeProcessViewEventImpl(String view, Class<? extends ViewEngine> viewEngine) {
+ super(view, viewEngine);
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BindingErrorImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BindingErrorImpl.java
new file mode 100644
index 0000000..6163ce3
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BindingErrorImpl.java
@@ -0,0 +1,38 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.binding.BindingError;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class BindingErrorImpl extends ParamErrorImpl implements BindingError {
+
+ private String submittedValue;
+
+ public BindingErrorImpl(String paramName, String message, String submittedValue) {
+ super(paramName, message);
+ this.submittedValue = submittedValue;
+ }
+
+ @Override
+ public String getSubmittedValue() {
+ return submittedValue;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BindingResultImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BindingResultImpl.java
new file mode 100644
index 0000000..582956c
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/BindingResultImpl.java
@@ -0,0 +1,123 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.mvc.RedirectScoped;
+import javax.mvc.binding.BindingError;
+import javax.mvc.binding.ParamError;
+import javax.mvc.binding.ValidationError;
+
+
+/**
+ * @author Neil Griffin
+ */
+@RedirectScoped
+public class BindingResultImpl implements MutableBindingResult, Serializable {
+
+ private static final long serialVersionUID = 2217732469752342741L;
+
+ private Set<BindingError> bindingErrors = new LinkedHashSet<>();
+ private boolean consulted = false;
+ private Set<ValidationError> validationErrors = new LinkedHashSet<>();
+
+ @Override
+ public void addBindingError(BindingError bindingError) {
+ bindingErrors.add(bindingError);
+ }
+
+ @Override
+ public void addValidationError(ValidationError validationError) {
+ validationErrors.add(validationError);
+ }
+
+ public void clear() {
+ bindingErrors.clear();
+ validationErrors.clear();
+ consulted = false;
+ }
+
+ @Override
+ public Set<ParamError> getAllErrors() {
+
+ consulted = true;
+
+ Set<ParamError> allErrors = new LinkedHashSet<>();
+ allErrors.addAll(bindingErrors);
+ allErrors.addAll(validationErrors);
+
+ return allErrors;
+ }
+
+ @Override
+ public List<String> getAllMessages() {
+
+ consulted = true;
+
+ List<String> allMessages = new ArrayList<>();
+
+ for (BindingError bindingError : bindingErrors) {
+ allMessages.add(bindingError.getMessage());
+ }
+
+ for (ValidationError validationError : validationErrors) {
+ allMessages.add(validationError.getMessage());
+ }
+
+ return allMessages;
+ }
+
+ @Override
+ public Set<ParamError> getErrors(String paramName) {
+ consulted = true;
+
+ Set<ParamError> errors = new LinkedHashSet<>();
+
+ for (BindingError bindingError : bindingErrors) {
+
+ if (Objects.equals(bindingError.getParamName(), paramName)) {
+ errors.add(bindingError);
+ }
+ }
+
+ for (ValidationError validationError : validationErrors) {
+
+ if (Objects.equals(validationError.getParamName(), paramName)) {
+ errors.add(validationError);
+ }
+ }
+
+ return errors;
+ }
+
+ public boolean isConsulted() {
+ return consulted;
+ }
+
+ @Override
+ public boolean isFailed() {
+ consulted = true;
+
+ return !bindingErrors.isEmpty() || !validationErrors.isEmpty();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ConfigurationImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ConfigurationImpl.java
new file mode 100644
index 0000000..e8b0e31
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ConfigurationImpl.java
@@ -0,0 +1,132 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletContext;
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Feature;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ConfigurationImpl implements Configuration {
+
+ public static final String DEFAULT_VIEW_EXTENSION = "org.apache.pluto.defaultViewExtension";
+
+ private Map<String, Object> properties;
+
+ @Inject
+ private PortletConfig portletConfig;
+
+ @Inject
+ private PortletContext portletContext;
+
+ @Override
+ public Set<Class<?>> getClasses() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<Class<?>, Integer> getContracts(Class<?> componentClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<Object> getInstances() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+
+ if (properties == null) {
+
+ properties = new HashMap<>();
+
+ Enumeration<String> initParameterNames = portletConfig.getInitParameterNames();
+
+ while (initParameterNames.hasMoreElements()) {
+ String initParameterName = initParameterNames.nextElement();
+ properties.put(initParameterName, portletConfig.getInitParameter(initParameterName));
+ }
+
+ initParameterNames = portletContext.getInitParameterNames();
+
+ while (initParameterNames.hasMoreElements()) {
+ String initParameterName = initParameterNames.nextElement();
+ properties.put(initParameterName, portletContext.getInitParameter(initParameterName));
+ }
+
+ if (!properties.containsKey(DEFAULT_VIEW_EXTENSION)) {
+ properties.put(DEFAULT_VIEW_EXTENSION, "jsp");
+ }
+ }
+
+ return properties;
+ }
+
+ @Override
+ public Object getProperty(String name) {
+
+ Map<String, Object> properties = getProperties();
+
+ return properties.get(name);
+ }
+
+ @Override
+ public Collection<String> getPropertyNames() {
+
+ Map<String, Object> properties = getProperties();
+
+ return properties.keySet();
+ }
+
+ @Override
+ public RuntimeType getRuntimeType() {
+ return RuntimeType.SERVER;
+ }
+
+ @Override
+ public boolean isEnabled(Feature feature) {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(Class<? extends Feature> featureClass) {
+ return false;
+ }
+
+ @Override
+ public boolean isRegistered(Object component) {
+ return false;
+ }
+
+ @Override
+ public boolean isRegistered(Class<?> componentClass) {
+ return false;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ControllerInterceptor.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ControllerInterceptor.java
new file mode 100644
index 0000000..cd2bebc
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ControllerInterceptor.java
@@ -0,0 +1,295 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.annotation.Priority;
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+import javax.mvc.View;
+import javax.mvc.event.MvcEvent;
+import javax.portlet.ActionResponse;
+import javax.portlet.BaseURL;
+import javax.portlet.MimeResponse;
+import javax.portlet.MutableRenderParameters;
+import javax.portlet.MutableResourceParameters;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletSession;
+import javax.portlet.ResourceResponse;
+import javax.portlet.annotations.ActionMethod;
+import javax.portlet.annotations.DestroyMethod;
+import javax.portlet.annotations.EventMethod;
+import javax.portlet.annotations.InitMethod;
+import javax.portlet.annotations.RenderMethod;
+import javax.portlet.filter.RenderURLWrapper;
+import javax.portlet.filter.ResourceURLWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+
+import org.apache.pluto.container.bean.processor.MethodType;
+import org.apache.pluto.container.bean.processor.PortletInvoker;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Interceptor
+@ControllerInterceptorBinding
+@Priority(Interceptor.Priority.LIBRARY_BEFORE)
+public class ControllerInterceptor implements Serializable {
+
+ private static final long serialVersionUID = 1573287230987622411L;
+
+ private static final Logger LOG = LoggerFactory.getLogger(ControllerInterceptor.class);
+
+ @Inject
+ private ActionResponse actionResponse;
+
+ @Inject
+ private Event<MvcEvent> mvcEvent;
+
+ @Inject
+ private MimeResponse mimeResponse;
+
+ @Inject
+ private PortletConfig portletConfig;
+
+ @Inject
+ private PortletRequest portletRequest;
+
+ @Inject
+ private ResourceResponse resourceResponse;
+
+ @Inject
+ private ViewRenderer viewRenderer;
+
+ @AroundInvoke
+ public Object processView(InvocationContext invocationContext) throws Exception {
+
+ PortletRequest portletRequest = this.portletRequest;
+ MimeResponse mimeResponse = this.mimeResponse;
+
+ // Set the viewName request attribute before invoking the controller
+ // @RenderMethod or @ServeResourceMethod. This makes it possible for
+ // the developer to determine the viewName that may have been set by an
+ // @ActionMethod in the ACTION_PHASE (or ResourceURL redirect) by
+ // calling the ViewEngineContext.getView() method.
+
+ String viewNameFromSession = null;
+
+ Method invocationContextMethod = invocationContext.getMethod();
+
+ MethodType methodType = _getMethodType(invocationContextMethod);
+
+ if ((methodType == MethodType.RENDER) || (methodType == MethodType.RESOURCE)) {
+
+ Object[] parameters = invocationContext.getParameters();
+
+ if ((parameters != null) && (parameters.length > 1)) {
+
+ portletRequest = (PortletRequest) parameters[0];
+ mimeResponse = (MimeResponse) parameters[1];
+ }
+
+ if (portletRequest.getParameter(ViewRenderer.REDIRECTED_VIEW) != null) {
+
+ PortletSession portletSession = portletRequest.getPortletSession(true);
+
+ viewNameFromSession = (String) portletSession.getAttribute(ViewRenderer.VIEW_NAME);
+
+ portletSession.removeAttribute(ViewRenderer.VIEW_NAME);
+
+ if (viewNameFromSession != null) {
+ portletRequest.setAttribute(ViewRenderer.VIEW_NAME, viewNameFromSession);
+ }
+ }
+ }
+
+ Object target = invocationContext.getTarget();
+
+ mvcEvent.fire(new BeforeControllerEventImpl(new ResourceInfoImpl(target.getClass(), invocationContextMethod),
+ new UriInfoImpl()));
+
+ Object result = invocationContext.proceed();
+
+ BaseURL redirectURL = null;
+
+ boolean renderView = true;
+
+ String viewName = null;
+
+ if (_isNull(result)) {
+ View view = invocationContextMethod.getAnnotation(View.class);
+
+ if (view != null) {
+ viewName = view.value();
+ }
+ }
+ else {
+ viewName = result.toString();
+ }
+
+ if (!_isNull(viewName) && ((methodType == MethodType.ACTION) || (methodType == MethodType.RESOURCE))) {
+
+ PortletSession portletSession = portletRequest.getPortletSession(true);
+
+ if (viewName.startsWith(ViewRenderer.REDIRECT_PREFIX)) {
+ viewName = viewName.substring(ViewRenderer.REDIRECT_PREFIX.length());
+
+ if (methodType == MethodType.ACTION) {
+ redirectURL = new ActionRedirectURL(actionResponse);
+ }
+ else {
+ redirectURL = new ResourceRedirectURL(resourceResponse);
+ }
+ }
+
+ portletSession.setAttribute(ViewRenderer.VIEW_NAME, viewName);
+
+ if (methodType == MethodType.ACTION) {
+
+ if (redirectURL == null) {
+ MutableRenderParameters mutableRenderParameters = actionResponse.getRenderParameters();
+
+ mutableRenderParameters.setValue(ViewRenderer.REDIRECTED_VIEW, Boolean.TRUE.toString());
+ }
+ else {
+
+ try {
+ actionResponse.sendRedirect(redirectURL.toString());
+ }
+ catch (IOException ioe) {
+ LOG.error(ioe.getMessage(), ioe);
+ }
+ }
+ }
+ else {
+
+ if (redirectURL != null) {
+ resourceResponse.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+ resourceResponse.addProperty(HttpHeaders.LOCATION, redirectURL.toString());
+
+ renderView = false;
+ }
+ }
+ }
+
+ if (renderView) {
+
+ if (_isNull(viewName)) {
+ viewName = viewNameFromSession;
+ }
+
+ if (!_isNull(viewName)) {
+ portletRequest.setAttribute(ViewRenderer.VIEW_NAME, viewName);
+ }
+ }
+
+ target = invocationContext.getTarget();
+
+ mvcEvent.fire(new AfterControllerEventImpl(new ResourceInfoImpl(target.getClass(), invocationContextMethod),
+ new UriInfoImpl()));
+
+ if (redirectURL != null) {
+
+ try {
+ URI location = new URI(redirectURL.toString());
+
+ mvcEvent.fire(new ControllerRedirectEventImpl(
+ new ResourceInfoImpl(target.getClass(), invocationContextMethod), new UriInfoImpl(), location));
+ }
+ catch (URISyntaxException urise) {
+ LOG.error(urise.getMessage(), urise);
+ }
+ }
+
+ if ((viewName != null) && (portletRequest.getAttribute(PortletInvoker.LAST_METHOD) != null)) {
+ portletRequest.removeAttribute(PortletInvoker.LAST_METHOD);
+ viewRenderer.render(portletRequest, mimeResponse, portletConfig);
+ }
+
+ return null;
+ }
+
+ private MethodType _getMethodType(Method invocationContextMethod) {
+
+ if (invocationContextMethod.isAnnotationPresent(ActionMethod.class)) {
+ return MethodType.ACTION;
+ }
+
+ if (invocationContextMethod.isAnnotationPresent(DestroyMethod.class)) {
+ return MethodType.DESTROY;
+ }
+
+ if (invocationContextMethod.isAnnotationPresent(EventMethod.class)) {
+ return MethodType.EVENT;
+ }
+
+ if (invocationContextMethod.isAnnotationPresent(InitMethod.class)) {
+ return MethodType.INIT;
+ }
+
+ if (invocationContextMethod.isAnnotationPresent(RenderMethod.class)) {
+ return MethodType.RENDER;
+ }
+
+ return MethodType.RESOURCE;
+ }
+
+ private boolean _isNull(Object viewName) {
+ return (viewName == null) || viewName.toString().trim().toLowerCase().equals("null");
+ }
+
+ private static class ActionRedirectURL extends RenderURLWrapper {
+
+ public ActionRedirectURL(ActionResponse actionResponse) {
+ super(actionResponse.createRedirectURL(MimeResponse.Copy.ALL));
+
+ MutableRenderParameters mutableRenderParameters = getRenderParameters();
+
+ mutableRenderParameters.setValue(ViewRenderer.REDIRECTED_VIEW, Boolean.TRUE.toString());
+ }
+
+ }
+
+ private static class ResourceRedirectURL extends ResourceURLWrapper {
+
+ public ResourceRedirectURL(ResourceResponse resourceResponse) {
+ super(resourceResponse.createResourceURL());
+
+ MutableResourceParameters mutableResourceParameters = getResourceParameters();
+
+ mutableResourceParameters.setValue(ViewRenderer.REDIRECTED_VIEW, Boolean.TRUE.toString());
+
+ setResourceID(null);
+ }
+
+ }
+
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ControllerInterceptorBinding.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ControllerInterceptorBinding.java
new file mode 100644
index 0000000..30a7723
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ControllerInterceptorBinding.java
@@ -0,0 +1,36 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.interceptor.InterceptorBinding;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Inherited
+@InterceptorBinding
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface ControllerInterceptorBinding {
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ControllerRedirectEventImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ControllerRedirectEventImpl.java
new file mode 100644
index 0000000..f9c1825
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ControllerRedirectEventImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.net.URI;
+
+import javax.mvc.event.ControllerRedirectEvent;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.UriInfo;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ControllerRedirectEventImpl extends BaseControllerEventImpl implements ControllerRedirectEvent {
+
+ private URI location;
+
+ public ControllerRedirectEventImpl(ResourceInfo resourceInfo, UriInfo uriInfo, URI location) {
+ super(resourceInfo, uriInfo);
+ this.location = location;
+ }
+
+ @Override
+ public URI getLocation() {
+ return location;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/CsrfImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/CsrfImpl.java
new file mode 100644
index 0000000..9366f67
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/CsrfImpl.java
@@ -0,0 +1,46 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.enterprise.inject.Vetoed;
+import javax.mvc.security.Csrf;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Vetoed
+public class CsrfImpl implements Csrf {
+
+ private String name;
+ private String token;
+
+ public CsrfImpl(String name, String token) {
+ this.name = name;
+ this.token = token;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getToken() {
+ return token;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/CsrfValidationInterceptor.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/CsrfValidationInterceptor.java
new file mode 100644
index 0000000..c9d5103
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/CsrfValidationInterceptor.java
@@ -0,0 +1,134 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+import javax.mvc.MvcContext;
+import javax.mvc.security.Csrf;
+import javax.mvc.security.CsrfProtected;
+import javax.portlet.ClientDataRequest;
+import javax.ws.rs.core.Configuration;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Interceptor
+@CsrfValidationInterceptorBinding
+@Priority(Interceptor.Priority.LIBRARY_BEFORE)
+public class CsrfValidationInterceptor implements Serializable {
+
+ private static final long serialVersionUID = 1348567603498123441L;
+
+ private static final Logger LOG = LoggerFactory.getLogger(CsrfValidationInterceptor.class);
+
+ @Inject
+ private Configuration configuration;
+
+ @Inject
+ private MvcContext mvcContext;
+
+ @AroundInvoke
+ public Object validateMethodInvocation(InvocationContext invocationContext) throws Exception {
+
+ Csrf.CsrfOptions csrfOptions = Csrf.CsrfOptions.EXPLICIT;
+
+ Object csrfProtection = configuration.getProperty(Csrf.CSRF_PROTECTION);
+
+ if (csrfProtection != null) {
+
+ if (csrfProtection instanceof Csrf.CsrfOptions) {
+ csrfOptions = (Csrf.CsrfOptions) csrfProtection;
+ }
+ else {
+
+ try {
+ csrfOptions = Csrf.CsrfOptions.valueOf(csrfProtection.toString());
+ }
+ catch (IllegalArgumentException e) {
+ LOG.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ if (csrfOptions == Csrf.CsrfOptions.OFF) {
+ return invocationContext.proceed();
+ }
+
+ Method targetMethod = invocationContext.getMethod();
+
+ if ((csrfOptions == Csrf.CsrfOptions.EXPLICIT) && !targetMethod.isAnnotationPresent(CsrfProtected.class)) {
+ return invocationContext.proceed();
+ }
+
+ boolean proceed = false;
+
+ Object[] parameters = invocationContext.getParameters();
+
+ if (parameters.length == 2) {
+
+ if (parameters[0] instanceof ClientDataRequest) {
+
+ ClientDataRequest clientDataRequest = (ClientDataRequest) parameters[0];
+
+ String method = clientDataRequest.getMethod();
+
+ if (method.equalsIgnoreCase("post")) {
+
+ Csrf csrf = mvcContext.getCsrf();
+
+ String csrfRequestParameterValue = clientDataRequest.getParameter(csrf.getName());
+
+ String csrfToken = csrf.getToken();
+
+ if ((csrfToken != null) && csrfToken.equalsIgnoreCase(csrfRequestParameterValue)) {
+ proceed = true;
+ }
+ else {
+ LOG.error("Invalid CSRF token");
+ }
+
+ }
+ else {
+ proceed = true;
+ }
+ }
+ else {
+ LOG.error("First parameter of method signature must be ActionRequest or ResourceRequest");
+ }
+ }
+ else {
+ LOG.error("Method signature must include ActionRequest,ActionResponse or ResourceRequest,ResourceResponse");
+ }
+
+ if (proceed) {
+ return invocationContext.proceed();
+ }
+
+ return null;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/CsrfValidationInterceptorBinding.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/CsrfValidationInterceptorBinding.java
new file mode 100644
index 0000000..0054264
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/CsrfValidationInterceptorBinding.java
@@ -0,0 +1,36 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.interceptor.InterceptorBinding;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Inherited
+@InterceptorBinding
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface CsrfValidationInterceptorBinding {
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/DescendingPriorityComparator.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/DescendingPriorityComparator.java
new file mode 100644
index 0000000..959b7c6
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/DescendingPriorityComparator.java
@@ -0,0 +1,61 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.Comparator;
+
+import javax.annotation.Priority;
+import javax.enterprise.inject.Vetoed;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Vetoed
+public class DescendingPriorityComparator<T> implements Comparator<Object> {
+
+ private int _defaultPriority;
+
+ public DescendingPriorityComparator() {
+ this(0);
+ }
+
+ public DescendingPriorityComparator(int defaultPriority) {
+ _defaultPriority = defaultPriority;
+ }
+
+ @Override
+ public int compare(Object object1, Object object2) {
+ Class<?> class1 = object1.getClass();
+ Priority priority1 = class1.getAnnotation(Priority.class);
+ Class<?> class2 = object2.getClass();
+ Priority priority2 = class2.getAnnotation(Priority.class);
+
+ if ((priority1 == null) && (priority2 == null)) {
+ return 0;
+ }
+ else if (priority1 == null) {
+ return Integer.compare(priority2.value(), _defaultPriority);
+ }
+ else if (priority2 == null) {
+ return Integer.compare(_defaultPriority, priority1.value());
+ }
+ else {
+ return Integer.compare(priority1.value(), priority2.value());
+ }
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/EncodersImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/EncodersImpl.java
new file mode 100644
index 0000000..85d5885
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/EncodersImpl.java
@@ -0,0 +1,38 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.security.Encoders;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class EncodersImpl implements Encoders {
+
+ @Override
+ public String html(String markup) {
+ return StringEscapeUtils.escapeHtml4(markup);
+ }
+
+ @Override
+ public String js(String code) {
+ return StringEscapeUtils.escapeEcmaScript(code);
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/LocaleResolverChain.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/LocaleResolverChain.java
new file mode 100644
index 0000000..8f6536b
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/LocaleResolverChain.java
@@ -0,0 +1,51 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.mvc.locale.LocaleResolver;
+
+
+/**
+ * @author Neil Griffin
+ */
+@ApplicationScoped
+public class LocaleResolverChain {
+
+ private static List<LocaleResolver> localeResolvers;
+
+ @Inject
+ private BeanManager _beanManager;
+
+ public List<LocaleResolver> getLocaleResolvers() {
+ return localeResolvers;
+ }
+
+ @PostConstruct
+ private void postConstruct() {
+
+ localeResolvers = BeanUtil.getBeanInstances(_beanManager, LocaleResolver.class);
+
+ Collections.sort(localeResolvers, new DescendingPriorityComparator(1000));
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/LocaleResolverContextImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/LocaleResolverContextImpl.java
new file mode 100644
index 0000000..e0ca250
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/LocaleResolverContextImpl.java
@@ -0,0 +1,79 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.mvc.locale.LocaleResolverContext;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.UriInfo;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class LocaleResolverContextImpl implements LocaleResolverContext {
+
+ private List<Locale> acceptableLanguages;
+ private Configuration configuration;
+ private Map<String, Cookie> cookies;
+ private Map<String, String> headers;
+ private UriInfo uriInfo;
+
+ public LocaleResolverContextImpl(List<Locale> acceptableLanguages, Configuration configuration,
+ Map<String, Cookie> cookies, Map<String, String> headers, UriInfo uriInfo) {
+ this.acceptableLanguages = acceptableLanguages;
+ this.configuration = configuration;
+ this.cookies = cookies;
+ this.headers = headers;
+ this.uriInfo = uriInfo;
+ }
+
+ @Override
+ public List<Locale> getAcceptableLanguages() {
+ return acceptableLanguages;
+ }
+
+ @Override
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ @Override
+ public Cookie getCookie(String name) {
+ return cookies.get(name);
+ }
+
+ @Override
+ public String getHeaderString(String name) {
+ return headers.get(name);
+ }
+
+ @Override
+ public Request getRequest() {
+ return new RequestImpl();
+ }
+
+ @Override
+ public UriInfo getUriInfo() {
+ return uriInfo;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/LocaleResolverImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/LocaleResolverImpl.java
new file mode 100644
index 0000000..81db78c
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/LocaleResolverImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+
+import javax.annotation.Priority;
+import javax.enterprise.context.ApplicationScoped;
+import javax.mvc.locale.LocaleResolver;
+import javax.mvc.locale.LocaleResolverContext;
+
+
+/**
+ * @author Neil Griffin
+ */
+@ApplicationScoped
+@Priority(0)
+public class LocaleResolverImpl implements LocaleResolver {
+
+ @Override
+ public Locale resolveLocale(LocaleResolverContext localeResolverContext) {
+
+ List<Locale> acceptableLanguages = localeResolverContext.getAcceptableLanguages();
+
+ for (Locale acceptableLanguage : acceptableLanguages) {
+
+ if (!Objects.equals(acceptableLanguage.getLanguage(), "*")) {
+ return acceptableLanguage;
+ }
+ }
+
+ return Locale.getDefault();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MessageInterpolatorContextImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MessageInterpolatorContextImpl.java
new file mode 100644
index 0000000..e6c5eac
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MessageInterpolatorContextImpl.java
@@ -0,0 +1,49 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.metadata.ConstraintDescriptor;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class MessageInterpolatorContextImpl implements MessageInterpolator.Context {
+
+ private ConstraintViolation<?> constraintViolation;
+
+ public MessageInterpolatorContextImpl(ConstraintViolation<?> constraintViolation) {
+ this.constraintViolation = constraintViolation;
+ }
+
+ @Override
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
+ return constraintViolation.getConstraintDescriptor();
+ }
+
+ @Override
+ public Object getValidatedValue() {
+ return constraintViolation.getInvalidValue();
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> aClass) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModelsImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModelsImpl.java
new file mode 100644
index 0000000..6b76587
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModelsImpl.java
@@ -0,0 +1,74 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Named;
+import javax.mvc.Models;
+import javax.mvc.RedirectScoped;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Named("models")
+@RedirectScoped
+public class ModelsImpl implements Models, Serializable {
+
+ private static final long serialVersionUID = 2433287856890024741L;
+
+ private final Map<String, Object> modelsMap = new LinkedHashMap<>();
+
+ @Override
+ public Map<String, Object> asMap() {
+ return Collections.unmodifiableMap(modelsMap);
+ }
+
+ public void clear() {
+ modelsMap.clear();
+ }
+
+ @Override
+ public Object get(String name) {
+ return get(name, Object.class);
+ }
+
+ @Override
+ public <T> T get(String name, Class<T> type) {
+ return type.cast(modelsMap.get(name));
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ Set<String> keySet = modelsMap.keySet();
+
+ return keySet.iterator();
+ }
+
+ @Override
+ public Models put(String name, Object model) {
+ modelsMap.put(name, model);
+
+ return this;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModifiedAnnotatedType.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModifiedAnnotatedType.java
new file mode 100644
index 0000000..cda0c53
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModifiedAnnotatedType.java
@@ -0,0 +1,90 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.AnnotatedConstructor;
+import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedType;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ModifiedAnnotatedType<X> implements AnnotatedType<X> {
+
+ private final AnnotatedType<X> annotatedType;
+ private final Set<AnnotatedField<? super X>> fields;
+ private final Set<AnnotatedMethod<? super X>> methods;
+
+ public ModifiedAnnotatedType(AnnotatedType<X> annotatedType, Set<AnnotatedField<? super X>> fields,
+ Set<AnnotatedMethod<? super X>> methods) {
+ this.annotatedType = annotatedType;
+ this.fields = fields;
+ this.methods = methods;
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+ return annotatedType.getAnnotation(annotationType);
+ }
+
+ @Override
+ public Set<Annotation> getAnnotations() {
+ return annotatedType.getAnnotations();
+ }
+
+ @Override
+ public Type getBaseType() {
+ return annotatedType.getBaseType();
+ }
+
+ @Override
+ public Set<AnnotatedConstructor<X>> getConstructors() {
+ return annotatedType.getConstructors();
+ }
+
+ @Override
+ public Set<AnnotatedField<? super X>> getFields() {
+ return fields;
+ }
+
+ @Override
+ public Class<X> getJavaClass() {
+ return annotatedType.getJavaClass();
+ }
+
+ @Override
+ public Set<AnnotatedMethod<? super X>> getMethods() {
+ return methods;
+ }
+
+ @Override
+ public Set<Type> getTypeClosure() {
+ return annotatedType.getTypeClosure();
+ }
+
+ @Override
+ public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ return annotatedType.isAnnotationPresent(annotationType);
+ }
+
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModifiedField.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModifiedField.java
new file mode 100644
index 0000000..b4294ae
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModifiedField.java
@@ -0,0 +1,88 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedType;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ModifiedField<X> implements AnnotatedField<X> {
+
+ private AnnotatedField annotatedField;
+ private Set<Annotation> annotations;
+
+ public ModifiedField(AnnotatedField<X> annotatedField, Set<Annotation> annotations) {
+ this.annotatedField = annotatedField;
+ this.annotations = annotations;
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+
+ for (Annotation annotation : annotations) {
+
+ if (annotationClass.isAssignableFrom(annotation.getClass())) {
+ return (T) annotation;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Set<Annotation> getAnnotations() {
+ return annotations;
+ }
+
+ @Override
+ public Type getBaseType() {
+ return annotatedField.getBaseType();
+ }
+
+ @Override
+ public AnnotatedType<X> getDeclaringType() {
+ return annotatedField.getDeclaringType();
+ }
+
+ @Override
+ public Field getJavaMember() {
+ return annotatedField.getJavaMember();
+ }
+
+ @Override
+ public Set<Type> getTypeClosure() {
+ return annotatedField.getTypeClosure();
+ }
+
+ @Override
+ public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
+ return getAnnotation(annotationClass) != null;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return annotatedField.isStatic();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModifiedMethod.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModifiedMethod.java
new file mode 100644
index 0000000..ab5913f
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ModifiedMethod.java
@@ -0,0 +1,103 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedParameter;
+import javax.enterprise.inject.spi.AnnotatedType;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ModifiedMethod<X> implements AnnotatedMethod<X> {
+
+ private AnnotatedMethod annotatedMethod;
+ private Set<Annotation> annotations;
+
+ public ModifiedMethod(AnnotatedMethod<X> annotatedMethod, Set<Annotation> annotations) {
+ this.annotatedMethod = annotatedMethod;
+ this.annotations = annotations;
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+
+ for (Annotation annotation : annotations) {
+ Class<? extends Annotation> curAnnotationType = annotation.annotationType();
+
+ if (curAnnotationType.equals(annotationType)) {
+ return annotationType.cast(annotation);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Set<Annotation> getAnnotations() {
+ return annotations;
+ }
+
+ @Override
+ public Type getBaseType() {
+ return annotatedMethod.getBaseType();
+ }
+
+ @Override
+ public AnnotatedType<X> getDeclaringType() {
+ return annotatedMethod.getDeclaringType();
+ }
+
+ @Override
+ public Method getJavaMember() {
+ return annotatedMethod.getJavaMember();
+ }
+
+ @Override
+ public List<AnnotatedParameter<X>> getParameters() {
+ return annotatedMethod.getParameters();
+ }
+
+ @Override
+ public Set<Type> getTypeClosure() {
+ return annotatedMethod.getTypeClosure();
+ }
+
+ @Override
+ public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+
+ Annotation annotation = getAnnotation(annotationType);
+
+ if (annotation == null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return annotatedMethod.isStatic();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MutableBindingResult.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MutableBindingResult.java
new file mode 100644
index 0000000..4adc6ef
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MutableBindingResult.java
@@ -0,0 +1,35 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.binding.BindingError;
+import javax.mvc.binding.BindingResult;
+import javax.mvc.binding.ValidationError;
+
+
+/**
+ * @author Neil Griffin
+ */
+public interface MutableBindingResult extends BindingResult {
+
+ public void addBindingError(BindingError bindingError);
+
+ public void addValidationError(ValidationError validationError);
+
+ public boolean isConsulted();
+
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MvcContextImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MvcContextImpl.java
new file mode 100644
index 0000000..f1bdac4
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MvcContextImpl.java
@@ -0,0 +1,146 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.mvc.MvcContext;
+import javax.mvc.locale.LocaleResolver;
+import javax.mvc.security.Csrf;
+import javax.mvc.security.Encoders;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletRequest;
+import javax.portlet.annotations.PortletRequestScoped;
+import javax.servlet.http.Cookie;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.UriBuilder;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Named("mvc")
+@PortletRequestScoped
+public class MvcContextImpl implements MvcContext {
+
+ @Inject
+ private Configuration configuration;
+
+ private Csrf csrf;
+
+ @Inject
+ private Encoders encoders;
+
+ private Locale locale;
+
+ @Inject
+ private LocaleResolverChain localeResolverChain;
+
+ @Inject
+ private PortletContext portletContext;
+
+ @Inject
+ private PortletRequest portletRequest;
+
+ @Override
+ public String getBasePath() {
+ return portletContext.getContextPath();
+ }
+
+ @Override
+ public Configuration getConfig() {
+ return configuration;
+ }
+
+ @Override
+ public Csrf getCsrf() {
+ return csrf;
+ }
+
+ @Override
+ public Encoders getEncoders() {
+ return encoders;
+ }
+
+ @Override
+ public Locale getLocale() {
+ return locale;
+ }
+
+ @PostConstruct
+ public void postConstruct() {
+
+ SpringCsrfToken springCsrfToken = new SpringCsrfToken(portletRequest.getAttribute(
+ "org.springframework.security.web.csrf.CsrfToken"));
+
+ csrf = new CsrfImpl(springCsrfToken.getName(), springCsrfToken.getValue());
+
+ List<LocaleResolver> localeResolvers = localeResolverChain.getLocaleResolvers();
+
+ Map<String, javax.ws.rs.core.Cookie> cookieMap = new HashMap<>();
+
+ Cookie[] cookies = portletRequest.getCookies();
+
+ for (Cookie cookie : cookies) {
+ cookieMap.put(cookie.getName(), new javax.ws.rs.core.Cookie(cookie.getName(), cookie.getValue()));
+ }
+
+ Map<String, String> headerMap = new HashMap<>();
+
+ Enumeration<String> propertyNames = portletRequest.getPropertyNames();
+
+ while (propertyNames.hasMoreElements()) {
+ String header = propertyNames.nextElement();
+ headerMap.put(header, portletRequest.getProperty(header));
+ }
+
+ LocaleResolverContextImpl localeResolverContext = new LocaleResolverContextImpl(Collections.list(
+ portletRequest.getLocales()), configuration, cookieMap, headerMap, new UriInfoImpl());
+
+ for (LocaleResolver localeResolver : localeResolvers) {
+ locale = localeResolver.resolveLocale(localeResolverContext);
+
+ if (locale != null) {
+ break;
+ }
+ }
+ }
+
+ @Override
+ public URI uri(String identifier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URI uri(String identifier, Map<String, Object> params) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public UriBuilder uriBuilder(String s) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MvcExtension.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MvcExtension.java
new file mode 100644
index 0000000..f862778
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MvcExtension.java
@@ -0,0 +1,220 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.Annotation;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Vetoed;
+import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.inject.Inject;
+import javax.mvc.Controller;
+import javax.portlet.annotations.ActionMethod;
+import javax.portlet.annotations.RenderMethod;
+import javax.portlet.annotations.ServeResourceMethod;
+import javax.validation.executable.ExecutableType;
+import javax.validation.executable.ValidateOnExecution;
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Vetoed
+public class MvcExtension implements Extension {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MvcExtension.class);
+
+ private static final Annotation BEAN_VALIDATION_INTERCEPTOR_BINDING = new BeanValidationInterceptorBinding() {
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return BeanValidationInterceptorBinding.class;
+ }
+ };
+
+ private static final Annotation CONTROLLER_INTERCEPTOR_BINDING = new ControllerInterceptorBinding() {
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return ControllerInterceptorBinding.class;
+ }
+ };
+
+ private static final Annotation CSRF_VALIDATION_INTERCEPTOR_BINDING = new CsrfValidationInterceptorBinding() {
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return CsrfValidationInterceptorBinding.class;
+ }
+ };
+
+ private static final Annotation INJECT = new Inject() {
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return Inject.class;
+ }
+
+ };
+
+ private static final Annotation PORTLET_PARAM = new PortletParam() {
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return PortletParam.class;
+ }
+
+ };
+
+ private static final Class<? extends Annotation>[] UNSUPPORTED_ANNOTATION_CLASSES = new Class[] {
+ MatrixParam.class, PathParam.class
+ };
+
+ public static <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> processAnnotatedType) {
+
+ AnnotatedType<T> annotatedType = processAnnotatedType.getAnnotatedType();
+
+ boolean modifiedMethods = false;
+
+ Set<AnnotatedMethod<? super T>> annotatedMethods = new LinkedHashSet<>();
+
+ boolean typeLevelController = annotatedType.isAnnotationPresent(Controller.class);
+
+ for (AnnotatedMethod<? super T> annotatedMethod : annotatedType.getMethods()) {
+
+ ActionMethod actionMethod = annotatedMethod.getAnnotation(ActionMethod.class);
+
+ RenderMethod renderMethod = annotatedMethod.getAnnotation(RenderMethod.class);
+
+ ServeResourceMethod serveResourceMethod = annotatedMethod.getAnnotation(ServeResourceMethod.class);
+
+ if (((actionMethod != null) || (renderMethod != null) || (serveResourceMethod != null)) &&
+ (typeLevelController || annotatedMethod.isAnnotationPresent(Controller.class))) {
+
+ Set<Annotation> methodAnnotations = new LinkedHashSet<>(annotatedMethod.getAnnotations());
+
+ methodAnnotations.add(CONTROLLER_INTERCEPTOR_BINDING);
+
+ modifiedMethods = true;
+
+ if ((actionMethod != null) || (serveResourceMethod != null)) {
+ methodAnnotations.add(CSRF_VALIDATION_INTERCEPTOR_BINDING);
+ }
+
+ ValidateOnExecution validateOnExecution = annotatedMethod.getAnnotation(ValidateOnExecution.class);
+
+ boolean addBeanValidationInterceptor = true;
+
+ if (validateOnExecution != null) {
+ ExecutableType[] executableTypes = validateOnExecution.type();
+
+ for (ExecutableType executableType : executableTypes) {
+
+ if (executableType == ExecutableType.NONE) {
+ addBeanValidationInterceptor = false;
+
+ break;
+ }
+ }
+ }
+
+ if (addBeanValidationInterceptor) {
+ methodAnnotations.add(BEAN_VALIDATION_INTERCEPTOR_BINDING);
+ }
+
+ annotatedMethod = new ModifiedMethod<>(annotatedMethod, methodAnnotations);
+ }
+
+ annotatedMethods.add(annotatedMethod);
+ }
+
+ boolean modifiedFieldAnnotations = false;
+
+ Set<AnnotatedField<? super T>> annotatedFields = new LinkedHashSet<>();
+
+ for (AnnotatedField<? super T> annotatedField : annotatedType.getFields()) {
+
+ Set<Annotation> fieldAnnotations = new LinkedHashSet<>(annotatedField.getAnnotations());
+
+ for (Class<? extends Annotation> unsupportedAnnotationClass : UNSUPPORTED_ANNOTATION_CLASSES) {
+
+ Annotation unsupportedAnnotation = annotatedField.getAnnotation(unsupportedAnnotationClass);
+
+ if (unsupportedAnnotation != null) {
+
+ fieldAnnotations.remove(unsupportedAnnotation);
+
+ modifiedFieldAnnotations = true;
+
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("Removed unsupported @" + unsupportedAnnotationClass.getSimpleName() +
+ " annotation from " + annotatedField);
+ }
+ }
+
+ }
+
+ BeanParam beanParam = annotatedField.getAnnotation(BeanParam.class);
+ CookieParam cookieParam = annotatedField.getAnnotation(CookieParam.class);
+ FormParam formParam = annotatedField.getAnnotation(FormParam.class);
+ HeaderParam headerParam = annotatedField.getAnnotation(HeaderParam.class);
+ QueryParam queryParam = annotatedField.getAnnotation(QueryParam.class);
+
+ if ((beanParam != null) || (cookieParam != null) || (formParam != null) || (headerParam != null) ||
+ (queryParam != null)) {
+
+ if (!annotatedField.isAnnotationPresent(Inject.class)) {
+ fieldAnnotations.add(INJECT);
+ modifiedFieldAnnotations = true;
+ }
+
+ if (beanParam == null) {
+ fieldAnnotations.add(PORTLET_PARAM);
+ modifiedFieldAnnotations = true;
+ }
+
+ }
+
+ if (modifiedFieldAnnotations) {
+ annotatedField = new ModifiedField(annotatedField, fieldAnnotations);
+ }
+
+ annotatedFields.add(annotatedField);
+ }
+
+ if (modifiedMethods || modifiedFieldAnnotations) {
+ processAnnotatedType.setAnnotatedType(new ModifiedAnnotatedType<>(annotatedType, annotatedFields,
+ annotatedMethods));
+ }
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MvcRuntimeDelegate.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MvcRuntimeDelegate.java
new file mode 100644
index 0000000..6242e8f
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/MvcRuntimeDelegate.java
@@ -0,0 +1,72 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.Variant;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class MvcRuntimeDelegate extends RuntimeDelegate {
+
+ @Override
+ public <T> T createEndpoint(Application application, Class<T> aClass) throws IllegalArgumentException,
+ UnsupportedOperationException {
+ return null;
+ }
+
+ @Override
+ public <T> HeaderDelegate<T> createHeaderDelegate(Class<T> aClass) throws IllegalArgumentException {
+ return new HeaderDelegate<T>() {
+ @Override
+ public T fromString(String s) {
+ return (T) s;
+ }
+
+ @Override
+ public String toString(T t) {
+ return t.toString();
+ }
+ };
+ }
+
+ @Override
+ public Link.Builder createLinkBuilder() {
+ return null;
+ }
+
+ @Override
+ public Response.ResponseBuilder createResponseBuilder() {
+ return null;
+ }
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return null;
+ }
+
+ @Override
+ public Variant.VariantListBuilder createVariantListBuilder() {
+ return null;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProviderImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProviderImpl.java
new file mode 100644
index 0000000..2fb5bcf
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProviderImpl.java
@@ -0,0 +1,184 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.function.Function;
+
+import javax.inject.Inject;
+import javax.mvc.MvcContext;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.ext.ParamConverter;
+import javax.ws.rs.ext.ParamConverterProvider;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ParamConverterProviderImpl implements ParamConverterProvider {
+
+ @Inject
+ private MvcContext mvcContext;
+
+ @Override
+ public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+
+ if (rawType == null) {
+ return null;
+ }
+
+ if (rawType.equals(Integer.class) || rawType.equals(Integer.TYPE) || rawType.equals(Long.class) ||
+ rawType.equals(Long.TYPE) || rawType.equals(Double.class) || rawType.equals(Double.TYPE) ||
+ rawType.equals(Float.class) || rawType.equals(Float.TYPE) || rawType.equals(Boolean.class) ||
+ rawType.equals(Boolean.TYPE)) {
+
+ return new ParamConverter<T>() {
+ @Override
+ public T fromString(String value) {
+
+ if (value == null) {
+ throw new IllegalArgumentException("Unable to convert a null parameter value");
+ }
+
+ if (rawType.equals(Integer.class) || rawType.equals(Integer.TYPE)) {
+ Function<Number, Integer> numberIntegerFunction = new Function<Number, Integer>() {
+ @Override
+ public Integer apply(Number number) {
+ return number.intValue();
+ }
+ };
+
+ return _getNumber(value, numberIntegerFunction);
+ }
+ else if (rawType.equals(Long.class) || rawType.equals(Long.TYPE)) {
+ Function<Number, Long> numberLongFunction = new Function<Number, Long>() {
+ @Override
+ public Long apply(Number number) {
+ return number.longValue();
+ }
+ };
+
+ return _getNumber(value, numberLongFunction);
+ }
+ else if (rawType.equals(Double.class) || rawType.equals(Double.TYPE)) {
+ Function<Number, Double> numberDoubleFunction = new Function<Number, Double>() {
+ @Override
+ public Double apply(Number number) {
+ return number.doubleValue();
+ }
+ };
+
+ return _getNumber(value, numberDoubleFunction);
+ }
+ else if (rawType.equals(Float.class) || rawType.equals(Float.TYPE)) {
+ Function<Number, Float> numberFloatFunction = new Function<Number, Float>() {
+ @Override
+ public Float apply(Number number) {
+ return number.floatValue();
+ }
+ };
+
+ return _getNumber(value, numberFloatFunction);
+ }
+ else if (rawType.equals(Boolean.class) || rawType.equals(Boolean.TYPE)) {
+
+ if (value == null) {
+ return (T) Boolean.FALSE;
+ }
+
+ value = value.trim();
+ value = value.toLowerCase();
+
+ if (Boolean.valueOf(value)) {
+ return (T) Boolean.TRUE;
+ }
+
+ return (T) Boolean.valueOf(value.equals("on"));
+ }
+
+ return null;
+ }
+
+ private T _getNumber(String value, Function getNumberFunction) {
+ NumberFormat numberFormat = NumberFormat.getInstance(mvcContext.getLocale());
+
+ try {
+ Number number = numberFormat.parse(value);
+
+ return (T) getNumberFunction.apply(number);
+ }
+ catch (ParseException pe) {
+ throw new IllegalArgumentException(pe);
+ }
+ }
+
+ @Override
+ public String toString(T value) {
+
+ if (value == null) {
+ return "";
+ }
+
+ return value.toString();
+ }
+ };
+ }
+ else {
+ return null;
+ }
+ }
+
+ private String _getParamName(Annotation[] annotations) {
+
+ for (Annotation annotation : annotations) {
+
+ Class<? extends Annotation> annotationClass = annotation.getClass();
+
+ if (CookieParam.class.isAssignableFrom(annotationClass)) {
+ CookieParam cookieParam = (CookieParam) annotation;
+
+ return cookieParam.value();
+ }
+
+ if (FormParam.class.isAssignableFrom(annotationClass)) {
+ FormParam formParam = (FormParam) annotation;
+
+ return formParam.value();
+ }
+
+ if (HeaderParam.class.isAssignableFrom(annotationClass)) {
+ HeaderParam headerParam = (HeaderParam) annotation;
+
+ return headerParam.value();
+ }
+
+ if (QueryParam.class.isAssignableFrom(annotationClass)) {
+ QueryParam queryParam = (QueryParam) annotation;
+
+ return queryParam.value();
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProviderManager.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProviderManager.java
new file mode 100644
index 0000000..f2a2931
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProviderManager.java
@@ -0,0 +1,64 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.ws.rs.ext.ParamConverter;
+import javax.ws.rs.ext.ParamConverterProvider;
+
+
+/**
+ * @author Neil Griffin
+ */
+@ApplicationScoped
+public class ParamConverterProviderManager {
+
+ private static List<ParamConverterProvider> paramConverterProviders;
+
+ @Inject
+ private BeanManager _beanManager;
+
+ public <T> ParamConverter getParamConverter(Class<?> rawType, Type baseType, Annotation[] annotations) {
+
+ for (ParamConverterProvider paramConverterProvider : paramConverterProviders) {
+ ParamConverter<T> paramConverter = paramConverterProvider.getConverter((Class<T>) rawType, baseType,
+ annotations);
+
+ if (paramConverter != null) {
+ return paramConverter;
+ }
+ }
+
+ return null;
+ }
+
+ @PostConstruct
+ public void postConstruct() {
+
+ paramConverterProviders = BeanUtil.getBeanInstances(_beanManager, ParamConverterProvider.class);
+
+ Collections.sort(paramConverterProviders, new DescendingPriorityComparator());
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProviders.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProviders.java
new file mode 100644
index 0000000..5b8f50c
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProviders.java
@@ -0,0 +1,34 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
+public @interface ParamConverterProviders {
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProvidersProducer.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProvidersProducer.java
new file mode 100644
index 0000000..52dbb23
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamConverterProvidersProducer.java
@@ -0,0 +1,57 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.ws.rs.ext.ParamConverterProvider;
+
+
+/**
+ * @author Neil Griffin
+ */
+@ApplicationScoped
+public class ParamConverterProvidersProducer {
+
+ @Inject
+ private BeanManager beanManager;
+
+ private List<ParamConverterProvider> paramConverterProviders;
+
+ @ApplicationScoped
+ @ParamConverterProviders
+ @Produces
+ public List<ParamConverterProvider> getParamConverterProviders() {
+ return paramConverterProviders;
+ }
+
+ @PostConstruct
+ public void postConstruct() {
+ paramConverterProviders = BeanUtil.getBeanInstances(beanManager, ParamConverterProvider.class);
+
+ paramConverterProviders.add(new ParamConverterProviderImpl());
+
+ Collections.sort(paramConverterProviders, new DescendingPriorityComparator());
+ }
+
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamErrorImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamErrorImpl.java
new file mode 100644
index 0000000..db6b9b9
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ParamErrorImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.binding.ParamError;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ParamErrorImpl implements ParamError {
+
+ private final String message;
+ private final String paramName;
+
+ public ParamErrorImpl(String paramName, String message) {
+ this.paramName = paramName;
+ this.message = message;
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public String getParamName() {
+ return paramName;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/PortletParam.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/PortletParam.java
new file mode 100644
index 0000000..c83697a
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/PortletParam.java
@@ -0,0 +1,34 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
+public @interface PortletParam {
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/PortletParamProducer.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/PortletParamProducer.java
new file mode 100644
index 0000000..cfbe42f
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/PortletParamProducer.java
@@ -0,0 +1,507 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.Annotated;
+import javax.enterprise.inject.spi.InjectionPoint;
+import javax.inject.Inject;
+import javax.portlet.ClientDataRequest;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletResponse;
+import javax.servlet.http.Cookie;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.ext.ParamConverter;
+import javax.ws.rs.ext.ParamConverterProvider;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Dependent
+public class PortletParamProducer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PortletParamProducer.class);
+
+ @Inject
+ private MutableBindingResult mutableBindingResult;
+
+ @Inject
+ @ParamConverterProviders
+ private List<ParamConverterProvider> paramConverterProviders;
+
+ @Dependent
+ @PortletParam
+ @Produces
+ public Boolean getBooleanParam(ClientDataRequest clientDataRequest, PortletResponse portletResponse,
+ InjectionPoint injectionPoint) {
+
+ String value = getStringParam(clientDataRequest, portletResponse, injectionPoint);
+
+ if (value == null) {
+ return null;
+ }
+
+ Annotated field = injectionPoint.getAnnotated();
+
+ Annotation[] fieldAnnotations = _getFieldAnnotations(field);
+
+ ParamConverter<Boolean> paramConverter = _getParamConverter(Boolean.class, field.getBaseType(),
+ fieldAnnotations);
+
+ if (paramConverter != null) {
+
+ try {
+ return paramConverter.fromString(value);
+ }
+ catch (IllegalArgumentException iae) {
+ _addBindingError(fieldAnnotations, iae.getMessage(), value);
+
+ return null;
+ }
+ }
+
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("Unable to find a ParamConverterProvider for type Boolean");
+ }
+
+ return null;
+ }
+
+ @Dependent
+ @PortletParam
+ @Produces
+ public Date getDateParam(ClientDataRequest clientDataRequest, PortletResponse portletResponse,
+ InjectionPoint injectionPoint) {
+
+ String value = getStringParam(clientDataRequest, portletResponse, injectionPoint);
+
+ if (value == null) {
+ return null;
+ }
+
+ Annotated field = injectionPoint.getAnnotated();
+
+ Annotation[] fieldAnnotations = _getFieldAnnotations(field);
+
+ ParamConverter<Date> paramConverter = _getParamConverter(Date.class, field.getBaseType(), fieldAnnotations);
+
+ if (paramConverter != null) {
+
+ try {
+ return paramConverter.fromString(value);
+ }
+ catch (IllegalArgumentException iae) {
+ _addBindingError(fieldAnnotations, iae.getMessage(), value);
+
+ return null;
+ }
+ }
+
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("Unable to find a ParamConverterProvider for type Date");
+ }
+
+ return null;
+ }
+
+ @Dependent
+ @PortletParam
+ @Produces
+ public Double getDoubleParam(ClientDataRequest clientDataRequest, PortletResponse portletResponse,
+ InjectionPoint injectionPoint) {
+
+ String value = getStringParam(clientDataRequest, portletResponse, injectionPoint);
+
+ if (value == null) {
+ return null;
+ }
+
+ Annotated field = injectionPoint.getAnnotated();
+
+ Annotation[] fieldAnnotations = _getFieldAnnotations(field);
+
+ ParamConverter<Double> paramConverter = _getParamConverter(Double.class, field.getBaseType(), fieldAnnotations);
+
+ if (paramConverter != null) {
+
+ try {
+ return paramConverter.fromString(value);
+ }
+ catch (IllegalArgumentException iae) {
+ _addBindingError(fieldAnnotations, iae.getMessage(), value);
+
+ return null;
+ }
+ }
+
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("Unable to find a ParamConverterProvider for type Double");
+ }
+
+ return null;
+ }
+
+ @Dependent
+ @PortletParam
+ @Produces
+ public Float getFloatParam(ClientDataRequest clientDataRequest, PortletResponse portletResponse,
+ InjectionPoint injectionPoint) {
+
+ String value = getStringParam(clientDataRequest, portletResponse, injectionPoint);
+
+ if (value == null) {
+ return null;
+ }
+
+ Annotated field = injectionPoint.getAnnotated();
+
+ Annotation[] fieldAnnotations = _getFieldAnnotations(field);
+
+ ParamConverter<Float> paramConverter = _getParamConverter(Float.class, field.getBaseType(), fieldAnnotations);
+
+ if (paramConverter != null) {
+
+ try {
+ return paramConverter.fromString(value);
+ }
+ catch (IllegalArgumentException iae) {
+ _addBindingError(fieldAnnotations, iae.getMessage(), value);
+
+ return null;
+ }
+ }
+
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("Unable to find a ParamConverterProvider for type Float");
+ }
+
+ return null;
+ }
+
+ @Dependent
+ @PortletParam
+ @Produces
+ public Integer getIntegerParam(ClientDataRequest clientDataRequest, PortletResponse portletResponse,
+ InjectionPoint injectionPoint) {
+
+ String value = getStringParam(clientDataRequest, portletResponse, injectionPoint);
+
+ if (value == null) {
+ return null;
+ }
+
+ Annotated field = injectionPoint.getAnnotated();
+
+ Annotation[] fieldAnnotations = _getFieldAnnotations(field);
+
+ ParamConverter<Integer> paramConverter = _getParamConverter(Integer.class, field.getBaseType(),
+ fieldAnnotations);
+
+ if (paramConverter != null) {
+
+ try {
+ return paramConverter.fromString(value);
+ }
+ catch (IllegalArgumentException iae) {
+ _addBindingError(fieldAnnotations, iae.getMessage(), value);
+
+ return null;
+ }
+ }
+
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("Unable to find a ParamConverterProvider for type Integer");
+ }
+
+ return null;
+ }
+
+ @Dependent
+ @PortletParam
+ @Produces
+ public Long getLongParam(ClientDataRequest clientDataRequest, PortletResponse portletResponse,
+ InjectionPoint injectionPoint) {
+
+ String value = getStringParam(clientDataRequest, portletResponse, injectionPoint);
+
+ if (value == null) {
+ return null;
+ }
+
+ Annotated field = injectionPoint.getAnnotated();
+
+ Annotation[] fieldAnnotations = _getFieldAnnotations(field);
+
+ ParamConverter<Long> paramConverter = _getParamConverter(Long.class, field.getBaseType(), fieldAnnotations);
+
+ if (paramConverter != null) {
+
+ try {
+ return paramConverter.fromString(value);
+ }
+ catch (IllegalArgumentException iae) {
+ _addBindingError(fieldAnnotations, iae.getMessage(), value);
+
+ return null;
+ }
+ }
+
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("Unable to find a ParamConverterProvider for type Long");
+ }
+
+ return Long.valueOf(value);
+ }
+
+ @Dependent
+ @PortletParam
+ @Produces
+ public String getStringParam(ClientDataRequest clientDataRequest, PortletResponse portletResponse,
+ InjectionPoint injectionPoint) {
+
+ Annotated field = injectionPoint.getAnnotated();
+
+ String defaultValue = null;
+
+ DefaultValue defaultValueAnnotation = field.getAnnotation(DefaultValue.class);
+
+ if (defaultValueAnnotation != null) {
+ defaultValue = defaultValueAnnotation.value();
+ }
+
+ CookieParam cookieParam = field.getAnnotation(CookieParam.class);
+
+ if (cookieParam != null) {
+ Cookie[] cookies = clientDataRequest.getCookies();
+
+ for (Cookie cookie : cookies) {
+
+ if (Objects.equals(cookieParam.value(), cookie.getName())) {
+ String cookieValue = cookie.getValue();
+
+ if (cookieValue == null) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Injecting the default value=" + defaultValue + " into a field annotated " +
+ "with @CookieParam(\"" + cookieParam.value() +
+ "\") because the cookie does not have a " + "value.");
+ }
+
+ return defaultValue;
+ }
+
+ return cookieValue;
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Injecting the default value=" + defaultValue +
+ " into a field annotated with @CookieParam(\"" + cookieParam.value() +
+ "\") because there is no cookie " + "with that name.");
+ }
+
+ return defaultValue;
+ }
+
+ FormParam formParam = field.getAnnotation(FormParam.class);
+
+ if (formParam != null) {
+ String lifecyclePhase = (String) clientDataRequest.getAttribute(PortletRequest.LIFECYCLE_PHASE);
+
+ String httpMethod = clientDataRequest.getMethod();
+
+ if ("post".equalsIgnoreCase(httpMethod) &&
+ (lifecyclePhase.equals(PortletRequest.ACTION_PHASE) ||
+ lifecyclePhase.equals(PortletRequest.RESOURCE_PHASE))) {
+
+ String parameterValue = clientDataRequest.getParameter(formParam.value());
+
+ if (parameterValue == null) {
+ parameterValue = clientDataRequest.getParameter(portletResponse.getNamespace() + formParam.value());
+ }
+
+ if (parameterValue == null) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Injecting the default value=" + defaultValue +
+ " into a field annotated with @FormParam(\"" + formParam.value() +
+ "\") because the request " + "parameter does not have a value.");
+ }
+
+ return defaultValue;
+ }
+
+ return parameterValue;
+ }
+
+ if (LOG.isDebugEnabled()) {
+
+ if (lifecyclePhase.equals(PortletRequest.RENDER_PHASE)) {
+ LOG.debug("Injection into a field annotated with " + "@FormParam(\"" + formParam.value() +
+ "\") is " + "invalid during the RENDER_PHASE.");
+ }
+ else {
+ LOG.debug("Injection into a field annotated with " + "@FormParam(\"" + formParam.value() +
+ "\") is " + "invalid during the " + lifecyclePhase + " (HTTP " + httpMethod + ") request.");
+ }
+ }
+
+ return null;
+ }
+
+ HeaderParam headerParam = field.getAnnotation(HeaderParam.class);
+
+ if (headerParam != null) {
+ Enumeration<String> propertyNames = clientDataRequest.getPropertyNames();
+
+ while (propertyNames.hasMoreElements()) {
+ String propertyName = propertyNames.nextElement();
+
+ if (Objects.equals(headerParam.value(), propertyName)) {
+ String headerValue = clientDataRequest.getProperty(propertyName);
+
+ if (headerValue == null) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Injecting the default value=" + defaultValue + " into a field annotated " +
+ "with @HeaderParam(\"" + headerParam.value() +
+ "\") because the header does not have a " + "value.");
+ }
+
+ return defaultValue;
+ }
+
+ return headerValue;
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Injecting the default value=" + defaultValue +
+ " into a field annotated with @HeaderParam(\"" + headerParam.value() +
+ "\") because there is no header with that name.");
+ }
+
+ return defaultValue;
+ }
+
+ QueryParam queryParam = field.getAnnotation(QueryParam.class);
+
+ if (queryParam != null) {
+ String parameterValue = clientDataRequest.getParameter(queryParam.value());
+
+ if (parameterValue == null) {
+ parameterValue = clientDataRequest.getParameter(portletResponse.getNamespace() + queryParam.value());
+ }
+
+ if (parameterValue == null) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Injecting the default value=" + defaultValue +
+ " into a field annotated with @QueryParam(\"" + queryParam.value() +
+ "\") because the request parameter does not have " + "a value.");
+ }
+
+ return defaultValue;
+ }
+
+ return parameterValue;
+ }
+
+ return defaultValue;
+ }
+
+ private void _addBindingError(Annotation[] fieldAnnotations, String message, String value) {
+
+ if (mutableBindingResult == null) {
+ LOG.error("Unable to add binding error");
+ }
+ else {
+ mutableBindingResult.addBindingError(new BindingErrorImpl(_getParamName(fieldAnnotations), message, value));
+ }
+ }
+
+ private Annotation[] _getFieldAnnotations(Annotated field) {
+ Set<Annotation> annotations = field.getAnnotations();
+
+ return annotations.toArray(new Annotation[annotations.size()]);
+ }
+
+ private <T> ParamConverter _getParamConverter(Class<?> rawType, Type baseType, Annotation[] annotations) {
+
+ for (ParamConverterProvider paramConverterProvider : paramConverterProviders) {
+
+ ParamConverter<T> paramConverter = paramConverterProvider.getConverter((Class<T>) rawType, baseType,
+ annotations);
+
+ if (paramConverter != null) {
+ return paramConverter;
+ }
+ }
+
+ return null;
+ }
+
+ private String _getParamName(Annotation[] annotations) {
+
+ for (Annotation annotation : annotations) {
+ Class<? extends Annotation> annotationClass = annotation.getClass();
+
+ if (CookieParam.class.isAssignableFrom(annotationClass)) {
+ CookieParam cookieParam = (CookieParam) annotation;
+
+ return cookieParam.value();
+ }
+
+ if (FormParam.class.isAssignableFrom(annotationClass)) {
+ FormParam formParam = (FormParam) annotation;
+
+ return formParam.value();
+ }
+
+ if (HeaderParam.class.isAssignableFrom(annotationClass)) {
+ HeaderParam headerParam = (HeaderParam) annotation;
+
+ return headerParam.value();
+ }
+
+ if (QueryParam.class.isAssignableFrom(annotationClass)) {
+ QueryParam queryParam = (QueryParam) annotation;
+
+ return queryParam.value();
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/RequestImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/RequestImpl.java
new file mode 100644
index 0000000..00c5925
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/RequestImpl.java
@@ -0,0 +1,62 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.Date;
+import java.util.List;
+
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Variant;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class RequestImpl implements Request {
+
+ @Override
+ public Response.ResponseBuilder evaluatePreconditions() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Response.ResponseBuilder evaluatePreconditions(EntityTag entityTag) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Response.ResponseBuilder evaluatePreconditions(Date date) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Response.ResponseBuilder evaluatePreconditions(Date date, EntityTag entityTag) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getMethod() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Variant selectVariant(List<Variant> list) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ResourceInfoImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ResourceInfoImpl.java
new file mode 100644
index 0000000..6a921d3
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ResourceInfoImpl.java
@@ -0,0 +1,46 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.reflect.Method;
+
+import javax.ws.rs.container.ResourceInfo;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ResourceInfoImpl implements ResourceInfo {
+
+ private Method resourceMethod;
+ private Class<?> resourceClass;
+
+ public ResourceInfoImpl(Class<?> resourceClass, Method resourceMethod) {
+ this.resourceClass = resourceClass;
+ this.resourceMethod = resourceMethod;
+ }
+
+ @Override
+ public Class<?> getResourceClass() {
+ return resourceClass;
+ }
+
+ @Override
+ public Method getResourceMethod() {
+ return resourceMethod;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ServletContextProducer.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ServletContextProducer.java
new file mode 100644
index 0000000..b36e20f
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ServletContextProducer.java
@@ -0,0 +1,63 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.Priority;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Initialized;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Alternative;
+import javax.enterprise.inject.Produces;
+import javax.interceptor.Interceptor;
+import javax.servlet.ServletContext;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Alternative
+@ApplicationScoped
+@Priority(Interceptor.Priority.APPLICATION + 10)
+public class ServletContextProducer {
+
+ private Set<ServletContext> servletContexts = new HashSet<>();
+
+ public void applicationScopedInitialized(
+ @Initialized(ApplicationScoped.class)
+ @Observes ServletContext servletContext) {
+ servletContexts.add(servletContext);
+ }
+
+ @Produces
+ public ServletContext getServletContext() {
+
+ Thread currentThread = Thread.currentThread();
+ ClassLoader contextClassLoader = currentThread.getContextClassLoader();
+
+ for (ServletContext servletContext : servletContexts) {
+
+ if (contextClassLoader.equals(servletContext.getClassLoader())) {
+ return servletContext;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/SpringCsrfToken.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/SpringCsrfToken.java
new file mode 100644
index 0000000..3bbe843
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/SpringCsrfToken.java
@@ -0,0 +1,66 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.reflect.Method;
+
+import javax.enterprise.inject.Vetoed;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Vetoed
+public class SpringCsrfToken {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SpringCsrfToken.class);
+
+ private String name;
+ private String value;
+
+ public SpringCsrfToken(Object springCsrfToken) {
+
+ if (springCsrfToken != null) {
+
+ Class<?> springCsrfTokenClass = springCsrfToken.getClass();
+
+ try {
+ Method getParameterNameMethod = springCsrfTokenClass.getMethod("getParameterName", new Class[] {});
+ getParameterNameMethod.setAccessible(true);
+ name = (String) getParameterNameMethod.invoke(springCsrfToken, new Object[] {});
+
+ Method getTokenMethod = springCsrfTokenClass.getMethod("getToken", null);
+ getTokenMethod.setAccessible(true);
+ value = (String) getTokenMethod.invoke(springCsrfToken, null);
+ }
+ catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/UriInfoImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/UriInfoImpl.java
new file mode 100644
index 0000000..00213f9
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/UriInfoImpl.java
@@ -0,0 +1,127 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class UriInfoImpl implements UriInfo {
+
+ @Override
+ public URI getAbsolutePath() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public UriBuilder getAbsolutePathBuilder() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URI getBaseUri() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public UriBuilder getBaseUriBuilder() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Object> getMatchedResources() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<String> getMatchedURIs() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<String> getMatchedURIs(boolean decode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getPath() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getPath(boolean decode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public MultivaluedMap<String, String> getPathParameters() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public MultivaluedMap<String, String> getPathParameters(boolean decode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<PathSegment> getPathSegments() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<PathSegment> getPathSegments(boolean decode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public MultivaluedMap<String, String> getQueryParameters() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public MultivaluedMap<String, String> getQueryParameters(boolean decode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URI getRequestUri() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public UriBuilder getRequestUriBuilder() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URI relativize(URI uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URI resolve(URI uri) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ValidationErrorImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ValidationErrorImpl.java
new file mode 100644
index 0000000..7bc957b
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ValidationErrorImpl.java
@@ -0,0 +1,39 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.binding.ValidationError;
+import javax.validation.ConstraintViolation;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ValidationErrorImpl extends ParamErrorImpl implements ValidationError {
+
+ private ConstraintViolation constraintViolation;
+
+ public ValidationErrorImpl(String paramName, String message, ConstraintViolation constraintViolation) {
+ super(paramName, message);
+ this.constraintViolation = constraintViolation;
+ }
+
+ @Override
+ public ConstraintViolation<?> getViolation() {
+ return constraintViolation;
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngineContextImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngineContextImpl.java
new file mode 100644
index 0000000..59a28ad
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngineContextImpl.java
@@ -0,0 +1,145 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Locale;
+
+import javax.mvc.Models;
+import javax.mvc.engine.ViewEngineContext;
+import javax.portlet.MimeResponse;
+import javax.portlet.PortletRequest;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ViewEngineContextImpl implements ViewEngineContext {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ViewEngineContextImpl.class);
+
+ private Configuration configuration;
+ private Locale locale;
+ private MediaType mediaType;
+ private MimeResponse mimeResponse;
+ private Models models;
+ private PortletRequest portletRequest;
+
+ public ViewEngineContextImpl(Configuration configuration, PortletRequest portletRequest, MimeResponse mimeResponse,
+ Models models, Locale locale) {
+ this.configuration = configuration;
+ this.portletRequest = portletRequest;
+ this.mimeResponse = mimeResponse;
+ this.models = models;
+ this.locale = locale;
+ }
+
+ @Override
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ @Override
+ public Locale getLocale() {
+ return locale;
+ }
+
+ @Override
+ public MediaType getMediaType() {
+
+ if (mediaType == null) {
+ String contentType = mimeResponse.getContentType();
+
+ if (contentType == null) {
+ mediaType = MediaType.TEXT_HTML_TYPE;
+ }
+ else {
+
+ String type = contentType;
+ String subtype = null;
+
+ int pos = contentType.indexOf("/");
+
+ if (pos > 0) {
+ type = contentType.substring(0, pos);
+ subtype = contentType.substring(pos + 1);
+ }
+
+ mediaType = new MediaType(type, subtype, mimeResponse.getCharacterEncoding());
+ }
+ }
+
+ return mediaType;
+ }
+
+ @Override
+ public Models getModels() {
+ return models;
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+
+ try {
+ return mimeResponse.getPortletOutputStream();
+ }
+ catch (IOException e) {
+ LOG.error(e.getMessage(), e);
+
+ return null;
+ }
+ }
+
+ @Override
+ public <T> T getRequest(Class<T> type) {
+ return (T) portletRequest;
+ }
+
+ @Override
+ public ResourceInfo getResourceInfo() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T getResponse(Class<T> type) {
+ return (T) mimeResponse;
+ }
+
+ @Override
+ public MultivaluedMap<String, Object> getResponseHeaders() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public UriInfo getUriInfo() {
+ return new UriInfoImpl();
+ }
+
+ @Override
+ public String getView() {
+ return (String) portletRequest.getAttribute(ViewRenderer.VIEW_NAME);
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngineContextProducer.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngineContextProducer.java
new file mode 100644
index 0000000..a11d15a
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngineContextProducer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.inject.Produces;
+import javax.mvc.engine.ViewEngineContext;
+import javax.portlet.MimeResponse;
+import javax.portlet.PortletRequest;
+import javax.ws.rs.core.Configuration;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ViewEngineContextProducer {
+
+ @Dependent
+ @Produces
+ ViewEngineContext getViewEngineContext(Configuration configuration, PortletRequest portletRequest,
+ MimeResponse mimeResponse, ModelsImpl modelsImpl) {
+ return new ViewEngineContextImpl(configuration, portletRequest, mimeResponse, modelsImpl,
+ portletRequest.getLocale());
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngineJspImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngineJspImpl.java
new file mode 100644
index 0000000..e76bfa7
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngineJspImpl.java
@@ -0,0 +1,71 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.mvc.engine.ViewEngine;
+import javax.mvc.engine.ViewEngineContext;
+import javax.mvc.engine.ViewEngineException;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletRequestDispatcher;
+import javax.portlet.PortletResponse;
+import javax.ws.rs.core.Configuration;
+
+
+/**
+ * @author Neil Griffin
+ */
+public class ViewEngineJspImpl implements ViewEngine {
+
+ private final Configuration configuration;
+ private final PortletContext portletContext;
+
+ public ViewEngineJspImpl(Configuration configuration, PortletContext portletContext) {
+
+ this.configuration = configuration;
+ this.portletContext = portletContext;
+ }
+
+ @Override
+ public void processView(ViewEngineContext viewEngineContext) throws ViewEngineException {
+
+ String view = viewEngineContext.getView();
+
+ String viewFolder = (String) configuration.getProperty(ViewEngine.VIEW_FOLDER);
+
+ if (viewFolder == null) {
+ viewFolder = ViewEngine.DEFAULT_VIEW_FOLDER;
+ }
+
+ String viewPath = viewFolder.concat(view);
+
+ PortletRequestDispatcher requestDispatcher = portletContext.getRequestDispatcher(viewPath);
+
+ try {
+ requestDispatcher.include(viewEngineContext.getRequest(PortletRequest.class),
+ viewEngineContext.getResponse(PortletResponse.class));
+ }
+ catch (Exception e) {
+ throw new ViewEngineException(e);
+ }
+ }
+
+ @Override
+ public boolean supports(String view) {
+ return (view != null) && (view.endsWith(".jsp") || view.endsWith(".jspx"));
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngines.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngines.java
new file mode 100644
index 0000000..6061aac
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEngines.java
@@ -0,0 +1,34 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+
+/**
+ * @author Neil Griffin
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
+public @interface ViewEngines {
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEnginesProducer.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEnginesProducer.java
new file mode 100644
index 0000000..ee0e6dd
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewEnginesProducer.java
@@ -0,0 +1,51 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.mvc.engine.ViewEngine;
+import javax.portlet.PortletContext;
+import javax.ws.rs.core.Configuration;
+
+
+/**
+ * @author Neil Griffin
+ */
+@ApplicationScoped
+public class ViewEnginesProducer {
+
+ @ApplicationScoped
+ @Produces
+ @ViewEngines
+ public List<ViewEngine> getViewEngines(BeanManager beanManager, PortletContext portletContext,
+ Configuration configuration) {
+
+ List<ViewEngine> viewEngines = BeanUtil.getBeanInstances(beanManager, ViewEngine.class);
+
+ viewEngines.add(new ViewEngineJspImpl(configuration, portletContext));
+
+ Collections.sort(viewEngines, new DescendingPriorityComparator(ViewEngine.PRIORITY_APPLICATION));
+
+ return viewEngines;
+ }
+
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewRenderer.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewRenderer.java
new file mode 100644
index 0000000..d5835ca
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewRenderer.java
@@ -0,0 +1,38 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import javax.portlet.MimeResponse;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletException;
+import javax.portlet.PortletRequest;
+
+
+/**
+ * @author Neil Griffin
+ */
+public interface ViewRenderer {
+
+ public static final String REDIRECT_PREFIX = "redirect:";
+
+ public static final String REDIRECTED_VIEW = "redirectedView";
+
+ public static final String VIEW_NAME = "viewName";
+
+ public void render(PortletRequest portletRequest, MimeResponse mimeResponse, PortletConfig portletConfig)
+ throws PortletException;
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewRendererMvcImpl.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewRendererMvcImpl.java
new file mode 100644
index 0000000..d01f741
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/mvc/ViewRendererMvcImpl.java
@@ -0,0 +1,126 @@
+/*
+ * 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.pluto.container.bean.mvc;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.mvc.Models;
+import javax.mvc.binding.ParamError;
+import javax.mvc.engine.ViewEngine;
+import javax.mvc.engine.ViewEngineException;
+import javax.portlet.MimeResponse;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletException;
+import javax.portlet.PortletRequest;
+import javax.ws.rs.core.Configuration;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * @author Neil Griffin
+ */
+@ApplicationScoped
+public class ViewRendererMvcImpl implements ViewRenderer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ViewRendererMvcImpl.class);
+
+ @Inject
+ private BeanManager beanManager;
+
+ @Inject
+ private Configuration configuration;
+
+ @Inject
+ private Models models;
+
+ @Inject
+ private MutableBindingResult mutableBindingResult;
+
+ @Inject
+ @ViewEngines
+ private List<ViewEngine> viewEngines;
+
+ @Override
+ public void render(PortletRequest portletRequest, MimeResponse mimeResponse, PortletConfig portletConfig)
+ throws PortletException {
+
+ Map<String, Object> modelMap = models.asMap();
+
+ for (Map.Entry<String, Object> entry : modelMap.entrySet()) {
+ portletRequest.setAttribute(entry.getKey(), entry.getValue());
+ }
+
+ String viewName = (String) portletRequest.getAttribute(VIEW_NAME);
+
+ if (viewName != null) {
+
+ if (!viewName.contains(".")) {
+ String defaultViewExtension = (String) configuration.getProperty(
+ ConfigurationImpl.DEFAULT_VIEW_EXTENSION);
+
+ viewName = viewName.concat(".").concat(defaultViewExtension);
+ }
+
+ ViewEngine supportingViewEngine = null;
+
+ for (ViewEngine viewEngine : viewEngines) {
+
+ if (viewEngine.supports(viewName)) {
+ supportingViewEngine = viewEngine;
+
+ break;
+ }
+ }
+
+ if (supportingViewEngine == null) {
+ throw new PortletException(new ViewEngineException("No ViewEngine found that supports " + viewName));
+ }
+
+ try {
+ beanManager.fireEvent(new BeforeProcessViewEventImpl(viewName, supportingViewEngine.getClass()));
+
+ supportingViewEngine.processView(new ViewEngineContextImpl(configuration, portletRequest, mimeResponse,
+ models, portletRequest.getLocale()));
+
+ beanManager.fireEvent(new AfterProcessViewEventImpl(viewName, supportingViewEngine.getClass()));
+ }
+ catch (ViewEngineException vee) {
+ throw new PortletException(vee);
+ }
+ }
+
+ if ((mutableBindingResult != null) && !mutableBindingResult.isConsulted()) {
+
+ Set<ParamError> allErrors = mutableBindingResult.getAllErrors();
+
+ for (ParamError paramError : allErrors) {
+
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("BindingResult error not processed for " + paramError.getParamName() + ": " +
+ paramError.getMessage());
+ }
+ }
+ }
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/AnnotatedConfigBean.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/AnnotatedConfigBean.java
index 4944dcd..dff76f4 100644
--- a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/AnnotatedConfigBean.java
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/AnnotatedConfigBean.java
@@ -32,6 +32,7 @@ public class AnnotatedConfigBean {
private AnnotatedMethodStore methodStore = null;
private ConfigSummary summary = null;
+ private RedirectScopedConfig redirectScopedConfig = null;
private PortletStateScopedConfig stateScopedConfig = null;
private PortletSessionScopedConfig sessionScopedConfig = null;
@@ -64,6 +65,20 @@ public class AnnotatedConfigBean {
}
/**
+ * @return the redirectScopedConfig
+ */
+ public RedirectScopedConfig getRedirectScopedConfig() {
+ return redirectScopedConfig;
+ }
+
+ /**
+ * @param redirectScopedConfig the redirectScopedConfig to set
+ */
+ public void setRedirectScopedConfig(RedirectScopedConfig redirectScopedConfig) {
+ this.redirectScopedConfig = redirectScopedConfig;
+ }
+
+ /**
* @return the stateScopedConfig
*/
public PortletStateScopedConfig getStateScopedConfig() {
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/MethodAnnotationRecognizer.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/MethodAnnotationRecognizer.java
index 985fede..af18dc9 100644
--- a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/MethodAnnotationRecognizer.java
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/MethodAnnotationRecognizer.java
@@ -59,7 +59,11 @@ public class MethodAnnotationRecognizer extends PortletAnnotationRecognizer impl
public MethodAnnotationRecognizer(AnnotatedMethodStore pms, ConfigSummary summary) {
- super(pms, summary);
+ this(pms, summary, true);
+ }
+
+ public MethodAnnotationRecognizer(AnnotatedMethodStore pms, ConfigSummary summary, boolean mvc) {
+ super(pms, summary, mvc);
ad = new AnnotationDetector(this);
}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/MethodDescription.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/MethodDescription.java
index f6c30a6..916cc62 100644
--- a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/MethodDescription.java
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/MethodDescription.java
@@ -110,15 +110,18 @@ public class MethodDescription {
// If true, multiple methods of this type are allowed
private boolean allowMultiple = false;
+
+ // If true, it is a method signature required for MVC
+ private boolean mvc = false;
// Message string used during method matching.
private final StringBuilder errtxt = new StringBuilder(128);
public MethodDescription(Class<?> rt, Class<?>[] at, Class<?>[] et, MethodType mt) {
- retType = rt;
- argTypes = at;
- expTypes = et;
- type = mt;
+ this.retType = rt;
+ this.argTypes = at;
+ this.expTypes = et;
+ this.type = mt;
}
/**
@@ -238,7 +241,14 @@ public class MethodDescription {
return ok;
}
-
+
+ public boolean isMvc() {
+ return mvc;
+ }
+
+ public void setMvc(boolean mvc) {
+ this.mvc = mvc;
+ }
/**
* Returns the expected method signature in string form, optionally
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletAnnotationRecognizer.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletAnnotationRecognizer.java
index eaba9a2..a56aab2 100644
--- a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletAnnotationRecognizer.java
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletAnnotationRecognizer.java
@@ -27,6 +27,7 @@ import static org.apache.pluto.container.bean.processor.MethodDescription.METH_R
import static org.apache.pluto.container.bean.processor.MethodDescription.METH_DES;
import static org.apache.pluto.container.bean.processor.MethodDescription.METH_INI;
+import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -41,8 +42,15 @@ import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.BeanManager;
+import javax.mvc.RedirectScoped;
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.PortletSession;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+import javax.portlet.ResourceRequest;
+import javax.portlet.ResourceResponse;
import javax.portlet.annotations.ActionMethod;
import javax.portlet.annotations.DestroyMethod;
import javax.portlet.annotations.EventMethod;
@@ -69,6 +77,7 @@ public class PortletAnnotationRecognizer extends AnnotationRecognizer {
new HashSet<Class<? extends Annotation>>();
static {
+ classAnnotations.add(RedirectScoped.class);
classAnnotations.add(RenderStateScoped.class);
classAnnotations.add(PortletSessionScoped.class);
classAnnotations.add(SessionScoped.class);
@@ -87,34 +96,52 @@ public class PortletAnnotationRecognizer extends AnnotationRecognizer {
// Add method definitions for init methods
- list = new ArrayList<MethodDescription>();
+ list = new ArrayList<>();
list.add(METH_INI);
descriptions.put(InitMethod.class, list);
// Add method definitions for destroy methods
- list = new ArrayList<MethodDescription>();
+ list = new ArrayList<>();
list.add(METH_DES);
descriptions.put(DestroyMethod.class, list);
// Add method definitions for action methods
- list = new ArrayList<MethodDescription>();
+ list = new ArrayList<>();
list.add(METH_ACT);
+
+ md = new MethodDescription(String.class,
+ new Class<?>[] {ActionRequest.class, ActionResponse.class},
+ new Class<?>[] {PortletException.class, java.io.IOException.class},
+ MethodType.ACTION);
+ md.setMvc(true);
+ md.setVariant(SignatureVariant.STRING_ACTIONREQ_ACTIONRESP);
+ list.add(md);
+
descriptions.put(ActionMethod.class, list);
// Add method definitions for event methods
- list = new ArrayList<MethodDescription>();
+ list = new ArrayList<>();
list.add(METH_EVT);
descriptions.put(EventMethod.class, list);
// Add method definitions for serve resource
- list = new ArrayList<MethodDescription>();
+ list = new ArrayList<>();
list.add(METH_RES);
-
+
+ md = new MethodDescription(String.class,
+ new Class<?>[] {ResourceRequest.class, ResourceResponse.class},
+ new Class<?>[] {PortletException.class, java.io.IOException.class},
+ MethodType.RESOURCE);
+ md.setAllowMultiple(true);
+ md.setMvc(true);
+ md.setVariant(SignatureVariant.STRING_RESOURCEREQ_RESOURCERESP);
+ list.add(md);
+
md = new MethodDescription(String.class,
new Class<?>[] {},
new Class<?>[] {PortletException.class, java.io.IOException.class},
@@ -135,16 +162,25 @@ public class PortletAnnotationRecognizer extends AnnotationRecognizer {
// Add method definitions for render
- list = new ArrayList<MethodDescription>();
+ list = new ArrayList<>();
list.add(METH_REN);
-
+
+ md = new MethodDescription(String.class,
+ new Class<?>[] {},
+ new Class<?>[] {PortletException.class, java.io.IOException.class},
+ MethodType.RENDER);
+ md.setAllowMultiple(true);
+ md.setVariant(SignatureVariant.STRING_VOID);
+ list.add(md);
+
md = new MethodDescription(String.class,
- new Class<?>[] {},
+ new Class<?>[] {RenderRequest.class, RenderResponse.class},
new Class<?>[] {PortletException.class, java.io.IOException.class},
MethodType.RENDER);
md.setAllowMultiple(true);
- md.setVariant(SignatureVariant.STRING_VOID);
+ md.setMvc(true);
+ md.setVariant(SignatureVariant.STRING_RENDERREQ_RENDERRESP);
list.add(md);
md = new MethodDescription(void.class,
@@ -159,7 +195,7 @@ public class PortletAnnotationRecognizer extends AnnotationRecognizer {
// Add method definitions for header methods
- list = new ArrayList<MethodDescription>();
+ list = new ArrayList<>();
list.add(METH_HDR);
md = new MethodDescription(String.class,
@@ -183,19 +219,50 @@ public class PortletAnnotationRecognizer extends AnnotationRecognizer {
}
private final AnnotatedMethodStore ams;
+ private final boolean mvc;
+ private final RedirectScopedConfig redirectScopedConfig = new RedirectScopedConfig();
private final PortletStateScopedConfig stateScopedConfig = new PortletStateScopedConfig();
private final PortletSessionScopedConfig sessionScopedConfig = new PortletSessionScopedConfig();
-
+
/**
- * @param cla
- * @param metha
+ * @param pms
+ * @param summary
*/
public PortletAnnotationRecognizer(AnnotatedMethodStore pms, ConfigSummary summary) {
- super(classAnnotations, descriptions, summary);
+ this(pms, summary, true);
+ }
+
+ /**
+ * @param pms
+ * @param summary
+ */
+ public PortletAnnotationRecognizer(AnnotatedMethodStore pms, ConfigSummary summary, boolean mvc) {
+ super(classAnnotations, filterDescriptions(descriptions, mvc), summary);
this.ams = pms;
+ this.mvc = mvc;
LOG.trace("Created the PortletAnnotationRecognizer.");
}
+ private static Map<Class<? extends Annotation>,List<MethodDescription>> filterDescriptions(Map<Class<? extends Annotation>, List<MethodDescription>> descriptions, boolean mvc) {
+ if (mvc) {
+ return descriptions;
+ }
+
+ Map<Class<? extends Annotation>, List<MethodDescription>> filteredDescriptions = new HashMap<>();
+
+ for (Map.Entry<Class<? extends Annotation>, List<MethodDescription>> entry : descriptions.entrySet()) {
+ List<MethodDescription> filteredMethodDescriptions = new ArrayList<>();
+ for (MethodDescription methodDescription: entry.getValue()) {
+ if (!methodDescription.isMvc()) {
+ filteredMethodDescriptions.add(methodDescription);
+ }
+ }
+ filteredDescriptions.put(entry.getKey(), filteredMethodDescriptions);
+ }
+
+ return filteredDescriptions;
+ }
+
/**
* Extracts data from the class annotation and stores it for later use.
* <p>
@@ -209,8 +276,24 @@ public class PortletAnnotationRecognizer extends AnnotationRecognizer {
protected AnnotatedType<?> handleClassAnnotation(Annotation anno, AnnotatedType<?> aType) throws InvalidAnnotationException {
String typeName = aType.getJavaClass().getCanonicalName();
Class<?> theClass = aType.getJavaClass();
-
- if (anno instanceof RenderStateScoped) {
+
+ if (anno instanceof RedirectScoped) {
+
+ // Verify the redirect scoped class, store the annotation &
+ // bean type with the corresponding bean holder.
+
+ if (!Serializable.class.isAssignableFrom(theClass)) {
+ StringBuilder txt = new StringBuilder(128);
+ txt.append("Annotation problem: An @RedirectScoped bean must implement java.io.Serializable.");
+ txt.append("Annotation: ").append(anno.annotationType().getSimpleName());
+ txt.append(", Class: ").append(typeName);
+ LOG.debug(txt.toString());
+ summary.addStateBeanErrorString(theClass, txt.toString());
+ } else {
+ redirectScopedConfig.addAnnotation(theClass, (RedirectScoped) anno);
+ }
+ }
+ else if (anno instanceof RenderStateScoped) {
// Verify the render state scoped class, store the annotation &
// bean type with the corresponding bean holder.
@@ -387,6 +470,7 @@ public class PortletAnnotationRecognizer extends AnnotationRecognizer {
protected void activateCustomScopes(BeanManager bm) {
// Activate the custom scoped beans
+ redirectScopedConfig.activate(bm);
stateScopedConfig.activate(bm);
sessionScopedConfig.activate(bm);
}
@@ -394,6 +478,13 @@ public class PortletAnnotationRecognizer extends AnnotationRecognizer {
/**
* @return the stateScopedConfig
*/
+ public RedirectScopedConfig getRedirectScopedConfig() {
+ return redirectScopedConfig;
+ }
+
+ /**
+ * @return the stateScopedConfig
+ */
public PortletStateScopedConfig getStateScopedConfig() {
return stateScopedConfig;
}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletCDIExtension.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletCDIExtension.java
index 46f8d95..f9d3c37 100644
--- a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletCDIExtension.java
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletCDIExtension.java
@@ -39,6 +39,7 @@ import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.portlet.PortletConfig;
+import org.apache.pluto.container.bean.mvc.MvcExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -72,6 +73,7 @@ public class PortletCDIExtension implements Extension {
*/
void processType(@Observes ProcessAnnotatedType<?> pat) throws InvalidAnnotationException {
par.checkAnnotatedType(pat);
+ MvcExtension.processAnnotatedType(pat);
}
@@ -154,6 +156,9 @@ public class PortletCDIExtension implements Extension {
* @param abd
*/
void addPortletCustomScopeContexts(@Observes AfterBeanDiscovery abd) {
+ RedirectScopedContext rsc = new RedirectScopedContext();
+ abd.addContext(rsc);
+
PortletSessionScopedContext pssc = new PortletSessionScopedContext();
abd.addContext(pssc);
@@ -191,6 +196,7 @@ public class PortletCDIExtension implements Extension {
LOG.trace("Got AnnotatedConfigBean instance.");
acb.setMethodStore(ams);
acb.setSummary(summary);
+ acb.setRedirectScopedConfig(par.getRedirectScopedConfig());
acb.setStateScopedConfig(par.getStateScopedConfig());
acb.setSessionScopedConfig(par.getSessionScopedConfig());
} catch (Exception e) {
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletInvoker.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletInvoker.java
index 6726440..e81dbac 100644
--- a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletInvoker.java
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletInvoker.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
+import javax.portlet.ActionParameters;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.EventPortlet;
@@ -60,6 +61,9 @@ import org.slf4j.LoggerFactory;
*
*/
public class PortletInvoker implements Portlet, ResourceServingPortlet, EventPortlet, HeaderPortlet {
+
+ public static final String LAST_METHOD = PortletInvoker.class.getName() + ".LAST_METHOD";
+
private static final Logger LOG = LoggerFactory.getLogger(PortletInvoker.class);
private static final boolean isDebug = LOG.isDebugEnabled();
@@ -252,7 +256,8 @@ public class PortletInvoker implements Portlet, ResourceServingPortlet, EventPor
meths = getMethods(mi);
}
- if (meths.size() == 0) {
+ int totalMethods = meths.size();
+ if (totalMethods == 0) {
// If a resource URL was activated, but no resource method could be
// found, add appropriate error string.
@@ -265,9 +270,14 @@ public class PortletInvoker implements Portlet, ResourceServingPortlet, EventPor
// now do the invocation
- for (AnnotatedMethod meth : meths) {
+ for (int i = 0; i < totalMethods; i++) {
+
+ if (i == (totalMethods - 1)) {
+ req.setAttribute(LAST_METHOD, Boolean.TRUE);
+ }
// Set the character encoding & content type, if available
+ AnnotatedMethod meth = meths.get(i);
ServeResourceMethod rm = (ServeResourceMethod) meth.getAnnotation();
if (rm != null) {
@@ -283,7 +293,10 @@ public class PortletInvoker implements Portlet, ResourceServingPortlet, EventPor
Object[] args = {};
Object result;
- if (meth.getDescription().getVariant() == SignatureVariant.VOID_RESOURCEREQ_RESOURCERESP) {
+ MethodDescription methDescription = meth.getDescription();
+ SignatureVariant signatureVariant = methDescription.getVariant();
+ if ((signatureVariant == SignatureVariant.STRING_RESOURCEREQ_RESOURCERESP) ||
+ (signatureVariant == SignatureVariant.VOID_RESOURCEREQ_RESOURCERESP)) {
args = new Object[] { req, resp };
}
@@ -308,7 +321,7 @@ public class PortletInvoker implements Portlet, ResourceServingPortlet, EventPor
// No async processing.
// If output is to be expected, write it to the writer
- if (meth.getDescription().getVariant() == SignatureVariant.STRING_VOID) {
+ if (signatureVariant == SignatureVariant.STRING_VOID) {
if (result != null) {
assert result instanceof String;
resp.getWriter().write((String) result);
@@ -361,7 +374,14 @@ public class PortletInvoker implements Portlet, ResourceServingPortlet, EventPor
@Override
public void processAction(ActionRequest req, ActionResponse resp) throws PortletException, IOException {
- String an = req.getActionParameters().getValue(ACTION_NAME);
+ ActionParameters actionParameters = req.getActionParameters();
+
+ String an = actionParameters.getValue(ACTION_NAME);
+
+ if (an == null) {
+ an = actionParameters.getValue(resp.getNamespace() + ACTION_NAME);
+ }
+
String id = (an != null) ? an : "";
MethodIdentifier mi = new MethodIdentifier(portletName, id, MethodType.ACTION);
@@ -409,10 +429,16 @@ public class PortletInvoker implements Portlet, ResourceServingPortlet, EventPor
return;
}
- for (AnnotatedMethod meth : meths) {
+ int totalMethods = meths.size();
+ for (int i = 0; i < totalMethods; i++) {
+
+ if (i == (totalMethods - 1)) {
+ req.setAttribute(LAST_METHOD, Boolean.TRUE);
+ }
// Set the content type, if available (determined by first render
// method)
+ AnnotatedMethod meth = meths.get(i);
RenderMethod rm = (RenderMethod) meth.getAnnotation();
if ((rm != null) && !rm.contentType().matches("(^$|\\*|\\*/\\*|text/\\*)")) {
@@ -423,7 +449,10 @@ public class PortletInvoker implements Portlet, ResourceServingPortlet, EventPor
Object[] args = {};
Object result;
- if (meth.getDescription().getVariant() == SignatureVariant.VOID_RENDERREQ_RENDERRESP) {
+ MethodDescription methodDescription = meth.getDescription();
+ SignatureVariant variant = methodDescription.getVariant();
+ if ((variant == SignatureVariant.DEFAULT.STRING_RENDERREQ_RENDERRESP) ||
+ (variant == SignatureVariant.VOID_RENDERREQ_RENDERRESP)) {
args = new Object[] { req, resp };
}
@@ -433,7 +462,7 @@ public class PortletInvoker implements Portlet, ResourceServingPortlet, EventPor
// If output is to be expected, write it to the writer
- if (meth.getDescription().getVariant() == SignatureVariant.STRING_VOID) {
+ if (variant == SignatureVariant.STRING_VOID) {
if (result != null) {
assert result instanceof String;
resp.getWriter().write((String) result);
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletSessionScopedBeanMap.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletSessionScopedBeanMap.java
index c049c88..851efd3 100644
--- a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletSessionScopedBeanMap.java
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/PortletSessionScopedBeanMap.java
@@ -200,4 +200,23 @@ public class PortletSessionScopedBeanMap implements HttpSessionBindingListener,
beans.clear();
}
}
+
+ public void removeByWindowId(String windowId) {
+
+ if (isTrace) {
+ LOG.trace("Removing beans associated with windowId=" + windowId);
+ }
+
+ synchronized(beans) {
+ for (String curWindowId : beans.keySet()) {
+ if (curWindowId.equals(windowId)) {
+ Map<Contextual<?>, BeanInstance<?>> beanMap =
+ beans.get(curWindowId);
+ for (Contextual<?> bean : beanMap.keySet()) {
+ remove(curWindowId, beanMap, bean);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/RedirectScopedBeanHolder.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/RedirectScopedBeanHolder.java
new file mode 100644
index 0000000..e679d0b
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/RedirectScopedBeanHolder.java
@@ -0,0 +1,176 @@
+/* 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.pluto.container.bean.processor;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.enterprise.context.spi.Contextual;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletSession;
+import java.io.Serializable;
+
+/**
+ * This is a container for RedirectScoped CDI beans.
+ *
+ * @author Scott Nicklous
+ * @author Neil Griffin
+ *
+ */
+public class RedirectScopedBeanHolder implements Serializable {
+ private static final Logger LOG = LoggerFactory
+ .getLogger(RedirectScopedBeanHolder.class);
+ private static final boolean isTrace = LOG.isTraceEnabled();
+
+ private static final long serialVersionUID = 3856727894564632122L;
+ private static final String ATTRIBNAME = "redirectScopedBeanHolder";
+
+ // The ThreadLocal manages the holders so that there is one holder per thread.
+ private static final ThreadLocal<RedirectScopedBeanHolder> holders = new ThreadLocal<>();
+
+ // ========== Instance vars
+
+ private final PortletSessionScopedBeanMap beans;
+ private final String windowId;
+
+ /**
+ * private constructor ... constructed once per thread
+ */
+ private RedirectScopedBeanHolder(PortletSessionScopedBeanMap beans, String windowId) {
+ this.beans = beans;
+ this.windowId = windowId;
+ }
+
+ /**
+ * Sets the redirect scope bean holder in a ThreadLocal object for the portlet session associated with the given request. If no bean holder
+ * exists in the session, a new one is created.
+ *
+ * @param req The portlet request.
+ * @return The redirect scope bean holder
+ */
+ public static void setBeanHolder(PortletRequest req) {
+
+ PortletSession ps = req.getPortletSession();
+ String windowId = req.getWindowID();
+
+ PortletSessionScopedBeanMap map = (PortletSessionScopedBeanMap) ps.getAttribute(ATTRIBNAME,
+ PortletSession.APPLICATION_SCOPE);
+
+ boolean createdMap = false;
+ if (map == null) {
+ map = new PortletSessionScopedBeanMap();
+ ps.setAttribute(ATTRIBNAME, map, PortletSession.APPLICATION_SCOPE);
+ createdMap = true;
+ }
+
+ RedirectScopedBeanHolder
+ holder = new RedirectScopedBeanHolder(map, windowId);
+ holders.set(holder);
+
+ if (isTrace) {
+ StringBuilder txt = new StringBuilder(80);
+ txt.append("Set redirect scope bean holder.");
+ txt.append(" ThreadId: ").append(Thread.currentThread().getId());
+ txt.append(", PortletSession: ").append(ps.getId());
+ txt.append(", WindowId: ").append(windowId);
+ txt.append(", Added new BeanMap to session: ").append(createdMap);
+ LOG.debug(txt.toString());
+ }
+ }
+
+ /**
+ * Removes the bean holder for the current session.
+ * @param destroyBeans Flag indicating whether or not the beans stored in the session should be destroyed.
+ */
+ public static void removeBeanHolder(boolean destroyBeans) {
+
+ if (destroyBeans) {
+ RedirectScopedBeanHolder holder = holders.get();
+ holder.removeBeans();
+ }
+ holders.remove();
+
+ if (isTrace) {
+ StringBuilder txt = new StringBuilder(80);
+ txt.append("Removed redirect scope bean holder.");
+ txt.append(" ThreadId=").append(Thread.currentThread().getId());
+ LOG.debug(txt.toString());
+ }
+ }
+
+ /**
+ * Returns the redirect scope bean holder that was set for the thread.
+ *
+ * @return
+ */
+ public static RedirectScopedBeanHolder getBeanHolder() {
+ return holders.get();
+ }
+
+ /**
+ * Removes the bean holder for the current thread and
+ * returns the removed instance to the caller.
+ *
+ * @return the removed bean holder
+ */
+ public static RedirectScopedBeanHolder deregister() {
+ RedirectScopedBeanHolder holder = holders.get();
+ holders.remove();
+ return holder;
+ }
+
+ /**
+ * Registers the provided bean holder for the current thread.
+ *
+ * @param holder the bean holder to register
+ */
+ public static void register(RedirectScopedBeanHolder holder) {
+ holders.set(holder);
+ }
+
+ /**
+ * Returns an instance for the contextual type, or null if none available.
+ *
+ * @param bean
+ * Contextual type (Bean) for which an instance is desired
+ * @return The instance, or null if none exists
+ */
+ public <T> T getBean(Contextual<T> bean) {
+ return beans.getBeanInstance(windowId, bean);
+ }
+
+ /**
+ * Adds a bean instance with associated creational context to the store for the given contextual object (bean).
+ *
+ * @param bean
+ * The bean type
+ * @param crco
+ * The creational context
+ * @param instance
+ * The bean instance
+ */
+ public <T> void putBeanInstance(Contextual<T> bean, CreationalContext<T> crco, T instance) {
+ beans.putBeanInstance(windowId, bean, crco, instance);
+ }
+
+ private void removeBeans() {
+ beans.removeByWindowId(windowId);
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/RedirectScopedConfig.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/RedirectScopedConfig.java
new file mode 100644
index 0000000..81bd0d6
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/RedirectScopedConfig.java
@@ -0,0 +1,146 @@
+/* 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.pluto.container.bean.processor;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.enterprise.context.spi.Contextual;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.mvc.RedirectScoped;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Configuration for RedirectScoped CDI beans.
+ *
+ * @author Scott Nicklous
+ * @author Neil Griffin
+ *
+ */
+public class RedirectScopedConfig implements Serializable {
+ private static final long serialVersionUID = 4387223464722335532L;
+ private final Logger LOG = LoggerFactory.getLogger(RedirectScopedConfig.class);
+ private final boolean isTrace = LOG.isTraceEnabled();
+
+
+ // Contains a sorted list of RedirectScoped annotated class names. The sorted list
+ // is used to generate indexes for the assigned render parameter names.
+ private final List<String> sortedAnnotatedClassNames = new ArrayList<>();
+
+ // Description for the RedirectScoped bean
+ private class PSSDescription implements Serializable {
+ private static final long serialVersionUID = 3289471768384711112L;
+ RedirectScoped pssAnno;
+ }
+
+ // Maps the bean contextual to the annotation. The bean contextual is obtained
+ // during the activation process after all beans have been discovered.
+ // Note that synchronization is not needed since the map is only changed during the
+ // bean scanning phase.
+ private final Map<Contextual<?>, PSSDescription> context2Anno = new HashMap<>();
+
+ // Maps the bean class to the corresponding annotation. The entries are set
+ // while the extension is processing annotated types.
+ // Note that synchronization is not needed since the map is only changed during the
+ // bean scanning phase.
+ private final Map<Class<?>, PSSDescription> class2Anno = new HashMap<>();
+
+ /**
+ * Called by the CDI extension during the scanning phase to add information about
+ * a <code>{@literal @}RedirectScoped</code> bean.
+ *
+ * @param beanClass The bean class
+ * @param anno The annotation
+ */
+ public void addAnnotation(Class<?> beanClass, RedirectScoped anno) {
+ PSSDescription desc = new PSSDescription();
+ desc.pssAnno = anno;
+ class2Anno.put(beanClass, desc);
+ }
+
+ /**
+ * Gets the concrete contextual for the bean and puts it into the context map.
+ * Called after bean discovery during the activation process.
+ * Finishes up the configuration process and provides a debug summary.
+ *
+ * @param bm The bean manager
+ */
+ public void activate(BeanManager bm) {
+
+ // The assigned render parameters are based on the alphabetic order of the PSS bean
+ // class names, so generate such a list.
+
+ for (Class<?> c : class2Anno.keySet()) {
+ sortedAnnotatedClassNames.add(c.getCanonicalName());
+ }
+ Collections.sort(sortedAnnotatedClassNames);
+
+ // Activate the beans
+
+ for (Class<?> cls : class2Anno.keySet()) {
+ Set<Bean<?>> beans = bm.getBeans(cls);
+ Bean<?> bean = bm.resolve(beans);
+ assert bean != null;
+ context2Anno.put(bean, class2Anno.get(cls));
+ }
+
+ // dump configuration data to trace
+
+ if (isTrace) {
+ StringBuilder txt = new StringBuilder(128);
+ txt.append("RedirectScopedBeanHolder configuration.");
+ txt.append(" Annotated Beans: ");
+ txt.append(getConfigAsString());
+ LOG.debug(txt.toString());
+ }
+ }
+
+ /**
+ * Returns the redirect scoped annotated classes.
+ * <p>
+ * Used for test / validation purposes.
+ *
+ * @return Set of annotated classes
+ */
+ public Set<Class<?>> getBeanClasses() {
+ return class2Anno.keySet();
+ }
+
+ /**
+ * Returns a redirect scoped bean summary for display
+ *
+ * @return The configuration summary string
+ *
+ */
+ public String getConfigAsString() {
+ StringBuilder txt = new StringBuilder(128);
+ for (Class<?> c : class2Anno.keySet()) {
+ txt.append("\n\tClass: ").append(c.getCanonicalName());
+ }
+ return txt.toString();
+ }
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/RedirectScopedContext.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/RedirectScopedContext.java
new file mode 100644
index 0000000..dcc60b3
--- /dev/null
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/RedirectScopedContext.java
@@ -0,0 +1,87 @@
+/* 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.pluto.container.bean.processor;
+
+import javax.enterprise.context.ContextNotActiveException;
+import javax.enterprise.context.spi.Context;
+import javax.enterprise.context.spi.Contextual;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.mvc.RedirectScoped;
+import java.lang.annotation.Annotation;
+
+/**
+ * This is the Context implementation for the RedirectScoped custom CDI scope.
+ *
+ * @author nick
+ * @author Neil Griffin
+ *
+ */
+public class RedirectScopedContext implements Context {
+
+ /* (non-Javadoc)
+ * @see javax.enterprise.context.spi.Context#get(javax.enterprise.context.spi.Contextual)
+ */
+ @Override
+ public <T> T get(Contextual<T> bean) {
+ RedirectScopedBeanHolder holder = RedirectScopedBeanHolder.getBeanHolder();
+ if (holder == null) {
+ throw new ContextNotActiveException("The redirect context is not active.");
+ }
+ return holder.getBean(bean);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.enterprise.context.spi.Context#get(javax.enterprise.context.spi.Contextual, javax.enterprise.context.spi.CreationalContext)
+ */
+ @Override
+ public <T> T get(Contextual<T> bean, CreationalContext<T> crco) {
+ RedirectScopedBeanHolder holder = RedirectScopedBeanHolder.getBeanHolder();
+
+ if (holder == null) {
+ throw new ContextNotActiveException("The redirect context is not active.");
+ }
+
+ T inst = holder.getBean(bean);
+ if (inst == null) {
+ inst = bean.create(crco);
+ holder.putBeanInstance(bean, crco, inst);
+ }
+
+ return inst;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.enterprise.context.spi.Context#getScope()
+ */
+ @Override
+ public Class<? extends Annotation> getScope() {
+ return RedirectScoped.class;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.enterprise.context.spi.Context#isActive()
+ */
+ @Override
+ public boolean isActive() {
+ RedirectScopedBeanHolder holder = RedirectScopedBeanHolder.getBeanHolder();
+ return (holder != null);
+ }
+
+}
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/SignatureVariant.java b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/SignatureVariant.java
index d37e076..4bc91ad 100644
--- a/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/SignatureVariant.java
+++ b/pluto-container/src/main/java/org/apache/pluto/container/bean/processor/SignatureVariant.java
@@ -31,7 +31,31 @@ public enum SignatureVariant {
* The default signature for the method.
*/
DEFAULT,
-
+
+ /**
+ * Signature variant for ActionMethod, with return value String and
+ * taking a ActionRequest and ActionResponse as arguments.
+ */
+ STRING_ACTIONREQ_ACTIONRESP,
+
+ /**
+ * Signature variant for RenderMethod, with return value String and
+ * taking a RenderRequest and RenderResponse as arguments.
+ */
+ STRING_RENDERREQ_RENDERRESP,
+
+ /**
+ * Signature variant for ResourceMethod, with return value String and
+ * taking a ResourceRequest and ResourceResponse as arguments.
+ */
+ STRING_RESOURCEREQ_RESOURCERESP,
+
+ /**
+ * Signature variant for render, header, and resource methods, with return
+ * value String and taking no arguments.
+ */
+ STRING_VOID,
+
/**
* Signature variant for HeaderMethod, with return value void and
* taking a HeaderRequest and HeaderResponse as arguments.
@@ -49,13 +73,7 @@ public enum SignatureVariant {
* taking a ResourceRequest and ResourceResponse as arguments.
*/
VOID_RESOURCEREQ_RESOURCERESP,
-
- /**
- * Signature variant for render, header, and resource methods, with return
- * value String and taking no arguments.
- */
- STRING_VOID,
-
+
/**
* Signature variant for render, header, and resource methods, with return
* value void and taking no arguments.
diff --git a/pluto-container/src/main/java/org/apache/pluto/container/om/portlet/impl/ConfigurationHolder.java b/pluto-container/src/main/java/org/apache/pluto/container/om/portlet/impl/ConfigurationHolder.java
index e345709..bd4d97e 100644
--- a/pluto-container/src/main/java/org/apache/pluto/container/om/portlet/impl/ConfigurationHolder.java
+++ b/pluto-container/src/main/java/org/apache/pluto/container/om/portlet/impl/ConfigurationHolder.java
@@ -338,13 +338,24 @@ public class ConfigurationHolder {
}
/**
+ * Scans list of files for method annotations that do not conform to MVC signatures.
+ * (unit test execution path)
+ *
+ * @param files the set of files to scan
+ */
+ public void scanMethodAnnotations(Set<File> files) {
+ scanMethodAnnotations(files, false);
+ }
+
+ /**
* Scans list of files for method annotations.
* (unit test execution path)
*
* @param files the set of files to scan
+ * @param mvc flag indicating whether or not MVC method signatures are permitted
*/
- public void scanMethodAnnotations(Set<File> files) {
- MethodAnnotationRecognizer mar = new MethodAnnotationRecognizer(methodStore, configSummary);
+ public void scanMethodAnnotations(Set<File> files, boolean mvc) {
+ MethodAnnotationRecognizer mar = new MethodAnnotationRecognizer(methodStore, configSummary, mvc);
mar.scanFiles(files);
LOG.debug("Scan complete: \n" + methodStore.getMethodsAsString());
}
diff --git a/pluto-container/src/main/resources/META-INF/services/javax.ws.rs.ext.RuntimeDelegate b/pluto-container/src/main/resources/META-INF/services/javax.ws.rs.ext.RuntimeDelegate
new file mode 100644
index 0000000..806cbbd
--- /dev/null
+++ b/pluto-container/src/main/resources/META-INF/services/javax.ws.rs.ext.RuntimeDelegate
@@ -0,0 +1 @@
+org.apache.pluto.container.bean.mvc.MvcRuntimeDelegate
\ No newline at end of file
diff --git a/pluto-container/src/test/java/org/apache/pluto/container/bean/processor/fixtures/RedirectScopedClass.java b/pluto-container/src/test/java/org/apache/pluto/container/bean/processor/fixtures/RedirectScopedClass.java
new file mode 100644
index 0000000..9a977e6
--- /dev/null
+++ b/pluto-container/src/test/java/org/apache/pluto/container/bean/processor/fixtures/RedirectScopedClass.java
@@ -0,0 +1,35 @@
+/* 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.pluto.container.bean.processor.fixtures;
+
+import javax.mvc.RedirectScoped;
+import java.io.Serializable;
+
+/**
+ * Verifies that an {@literal @}RedirectScoped bean is recognized.
+ *
+ * @author Scott Nicklous
+ * @author Neil Griffin
+ */
+@RedirectScoped
+public class RedirectScopedClass implements Serializable {
+
+ private static final long serialVersionUID = 3676449534190350039L;
+}
diff --git a/pluto-container/src/test/java/org/apache/pluto/container/bean/processor/tests/RedirectScopedTest.java b/pluto-container/src/test/java/org/apache/pluto/container/bean/processor/tests/RedirectScopedTest.java
new file mode 100644
index 0000000..11b9392
--- /dev/null
+++ b/pluto-container/src/test/java/org/apache/pluto/container/bean/processor/tests/RedirectScopedTest.java
@@ -0,0 +1,96 @@
+/* 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.pluto.container.bean.processor.tests;
+
+import org.apache.pluto.container.bean.processor.AnnotatedConfigBean;
+import org.apache.pluto.container.bean.processor.AnnotatedMethodStore;
+import org.apache.pluto.container.bean.processor.ConfigSummary;
+import org.apache.pluto.container.bean.processor.PortletCDIExtension;
+import org.apache.pluto.container.bean.processor.RedirectScopedConfig;
+import org.apache.pluto.container.bean.processor.fixtures.RedirectScopedClass;
+import org.jglue.cdiunit.AdditionalClasses;
+import org.jglue.cdiunit.AdditionalPackages;
+import org.jglue.cdiunit.CdiRunner;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test class for RedirectScoped beans
+ *
+ * @author Scott Nicklous
+ * @author Neil Griffin
+ *
+ */
+@RunWith(CdiRunner.class)
+@AdditionalClasses(PortletCDIExtension.class)
+@AdditionalPackages(RedirectScopedClass.class)
+public class RedirectScopedTest {
+
+ @Inject
+ AnnotatedConfigBean acb;
+
+ @Inject
+ BeanManager beanmgr;
+
+ private AnnotatedMethodStore annotatedMethodStore;
+ private ConfigSummary summary;
+ private RedirectScopedConfig redirectScopedConfig;
+
+ @Before
+ public void setUp() {
+ annotatedMethodStore = acb.getMethodStore();
+ summary = acb.getSummary();
+ redirectScopedConfig = acb.getRedirectScopedConfig();
+ redirectScopedConfig.activate(beanmgr);
+ }
+
+ @Test
+ public void injectBeanTest() {
+ assertNotNull(acb);
+ assertNotNull(annotatedMethodStore);
+ assertNotNull(summary);
+ assertNotNull(redirectScopedConfig);
+ }
+
+ @Test
+ public void annotatedClassPresent1() {
+ Set<Class<?>> classes = redirectScopedConfig.getBeanClasses();
+ assertNotNull(classes);
+ assertEquals(1, classes.size());
+ }
+
+ @Test
+ public void annotatedClassPresent2() {
+ Set<Class<?>> classes = redirectScopedConfig.getBeanClasses();
+ assertNotNull(classes);
+ assertTrue(classes.contains(RedirectScopedClass.class));
+ }
+}
diff --git a/pluto-portal-driver-impl/src/main/java/org/apache/pluto/driver/services/container/PortletAsyncContextImpl.java b/pluto-portal-driver-impl/src/main/java/org/apache/pluto/driver/services/container/PortletAsyncContextImpl.java
index 5ab4991..bd71ffe 100644
--- a/pluto-portal-driver-impl/src/main/java/org/apache/pluto/driver/services/container/PortletAsyncContextImpl.java
+++ b/pluto-portal-driver-impl/src/main/java/org/apache/pluto/driver/services/container/PortletAsyncContextImpl.java
@@ -43,6 +43,7 @@ import org.apache.pluto.container.bean.processor.PortletArtifactProducer;
import org.apache.pluto.container.bean.processor.PortletRequestScopedBeanHolder;
import org.apache.pluto.container.bean.processor.PortletSessionBeanHolder;
import org.apache.pluto.container.bean.processor.PortletStateScopedBeanHolder;
+import org.apache.pluto.container.bean.processor.RedirectScopedBeanHolder;
import org.apache.pluto.container.impl.HttpServletPortletRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -71,6 +72,7 @@ public class PortletAsyncContextImpl implements PortletAsyncManager, AsyncContex
private PortletSessionBeanHolder sessbh;
private PortletStateScopedBeanHolder statebh;
private PortletRequestScopedBeanHolder reqbh;
+ private RedirectScopedBeanHolder redbh;
private BeanManager beanmgr;
private Runnable pendingRunner;
private boolean doDeregister = true;
@@ -128,6 +130,7 @@ public class PortletAsyncContextImpl implements PortletAsyncManager, AsyncContex
PortletSessionBeanHolder.register(sessbh);
PortletStateScopedBeanHolder.register(statebh);
PortletRequestScopedBeanHolder.register(reqbh);
+ RedirectScopedBeanHolder.register(redbh);
PortletArtifactProducer.setPrecursors(resreq, prctx.getResponse(), prctx.getPortletConfig());
}
@@ -149,6 +152,7 @@ public class PortletAsyncContextImpl implements PortletAsyncManager, AsyncContex
public void deregisterContext(boolean isListener) {
if (!complete && (!isListener || doDeregister)) {
this.sessbh = PortletSessionBeanHolder.deregister();
+ this.redbh = RedirectScopedBeanHolder.deregister();
this.statebh = PortletStateScopedBeanHolder.deregister();
this.reqbh = PortletRequestScopedBeanHolder.deregister();
PortletArtifactProducer.remove();
diff --git a/pom.xml b/pom.xml
index ab93f30..5523865 100644
--- a/pom.xml
+++ b/pom.xml
@@ -291,24 +291,15 @@ generate mailto links. -->
<xmlunit.version>1.1</xmlunit.version>
<cdi.version>2.3.1.Final</cdi.version>
<annotation-detector.version>3.0.5</annotation-detector.version>
-
- <!-- The following properties are not directly used as maven
-dependencies, they're used by the maven pluto plugin for
-handling installation dependencies
-TODO: Check if we need all of them. -->
- <xerces.version>2.6.2</xerces.version>
- <xalan.version>2.7.0</xalan.version>
- <castor.version>1.1.1</castor.version>
- <commons-collections.version>3.2</commons-collections.version>
- <commons-httpclient.version>3.0</commons-httpclient.version>
- <commons-beanutils.version>1.7.0</commons-beanutils.version>
- <log4j.version>1.2.14</log4j.version>
- <activation.version>1.1</activation.version>
- <ccpp-ri.version>1.0</ccpp-ri.version>
- <rdffilter.version>1.0</rdffilter.version>
- <jena.version>1.4.0</jena.version>
-
- <!-- scope of certain dependencies is "compile" for deployment on tomcat,
+ <!-- MVC ================================================== -->
+ <classmate.version>1.3.4</classmate.version>
+ <hibernate-validator.version>6.0.14.Final</hibernate-validator.version>
+ <jaxrs-api.version>2.1.1</jaxrs-api.version>
+ <mvc-api.version>1.0-pfd</mvc-api.version>
+ <jboss-logging.version>3.3.2.Final</jboss-logging.version>
+ <validation-api.version>2.0.1.Final</validation-api.version>
+
+ <!-- scope of certain dependencies is "compile" for deployment on tomcat,
but "provided" for deployment on tomee. See tomee profile. -->
<dependency.scope>compile</dependency.scope>
@@ -349,15 +340,15 @@ TODO: Check if we need all of them. -->
<!-- Specification Libraries =========================================== -->
<dependency>
- <groupId>javax.inject</groupId>
- <artifactId>javax.inject</artifactId>
- <version>1</version>
+ <groupId>javax.annotation</groupId>
+ <artifactId>javax.annotation-api</artifactId>
+ <version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>javax.mvc</groupId>
- <artifactId>javax.mvc-api</artifactId>
- <version>1.0-pfd</version>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ <version>1</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -373,12 +364,6 @@ TODO: Check if we need all of them. -->
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>javax.ws.rs</groupId>
- <artifactId>javax.ws.rs-api</artifactId>
- <version>2.1.1</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>${tomcat.servlet-api.version}</version>
@@ -459,6 +444,51 @@ TODO: Check if we need all of them. -->
<version>${xmlunit.version}</version>
<scope>test</scope>
</dependency>
+ <!-- MVC ================================================== -->
+ <dependency>
+ <groupId>com.fasterxml</groupId>
+ <artifactId>classmate</artifactId>
+ <version>${classmate.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate.validator</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ <version>${hibernate-validator.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate.validator</groupId>
+ <artifactId>hibernate-validator-cdi</artifactId>
+ <version>${hibernate-validator.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss-logging.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.mvc</groupId>
+ <artifactId>javax.mvc-api</artifactId>
+ <version>${mvc-api.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <version>${validation-api.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ <version>${jaxrs-api.version}</version>
+ <scope>provided</scope>
+ </dependency>
<!-- Misc ================================================= -->
<dependency>
<groupId>ant</groupId>
@@ -521,17 +551,6 @@ TODO: Check if we need all of them. -->
<version>${taglibs.standard.version}</version>
</dependency>
<dependency>
- <groupId>org.hibernate.validator</groupId>
- <artifactId>hibernate-validator</artifactId>
- <version>6.0.14.Final</version>
- <exclusions>
- <exclusion>
- <groupId>javax.validation</groupId>
- <artifactId>validation-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-api</artifactId>
<version>3.11.0</version>
@@ -604,6 +623,7 @@ TODO: Check if we need all of them. -->
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang.version}</version>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-digester</groupId>
diff --git a/portlet-tck_3.0/V2AddlRequestTests/pom.xml b/portlet-tck_3.0/V2AddlRequestTests/pom.xml
index afdf932..21cea09 100644
--- a/portlet-tck_3.0/V2AddlRequestTests/pom.xml
+++ b/portlet-tck_3.0/V2AddlRequestTests/pom.xml
@@ -47,7 +47,6 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
- <version>3.0</version>
</dependency>
<!-- for tooling purposes -->
diff --git a/portlet-tck_3.0/V3HeaderPortletTests/pom.xml b/portlet-tck_3.0/V3HeaderPortletTests/pom.xml
index b1c6537..d957173 100644
--- a/portlet-tck_3.0/V3HeaderPortletTests/pom.xml
+++ b/portlet-tck_3.0/V3HeaderPortletTests/pom.xml
@@ -46,7 +46,6 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
- <version>3.0</version>
</dependency>
<!-- for tooling purposes -->