diff --git a/.gitignore b/.gitignore index 313053b7b..e95318aaa 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ # Scripts *.sh +# jenv +.java-version + # Maven core/target xwork-core/target diff --git a/apps/blank/pom.xml b/apps/blank/pom.xml index 5d036ba0b..a8d1cf8cd 100644 --- a/apps/blank/pom.xml +++ b/apps/blank/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-apps - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-blank diff --git a/apps/jboss-blank/pom.xml b/apps/jboss-blank/pom.xml index a31f04ed3..e3da1b52f 100644 --- a/apps/jboss-blank/pom.xml +++ b/apps/jboss-blank/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-apps - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-jboss-blank diff --git a/apps/mailreader/pom.xml b/apps/mailreader/pom.xml index d0366ed43..e239bdb92 100644 --- a/apps/mailreader/pom.xml +++ b/apps/mailreader/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-apps - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-mailreader diff --git a/apps/pom.xml b/apps/pom.xml index c0741ea72..ae0c0179c 100644 --- a/apps/pom.xml +++ b/apps/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-parent - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-apps pom @@ -40,120 +40,6 @@ rest-showcase - - - hostedqa - - - com.hostedqa - hostedqa-remote-ant - 1.7 - test - - - - - codehaus - codehaus - http://repository.codehaus.org - - - maven-hostedqa - maven-hostedqa - - true - always - ignore - - - true - - http://maven.hostedqa.com - - - - - - - src/main/java - - **/*.properties - **/*.xml - - - - - - maven-antrun-plugin - org.apache.maven.plugins - - - package - - run - - - - - - - - - - - - - - com.hostedqa - hostedqa-remote-ant - 1.7 - - - - - - - - release - - - release - - - - - - true - org.codehaus.mojo - rat-maven-plugin - - - verify - - check - - - false - - - rat.analysis.license.ApacheSoftwareLicense20 - - - - pom.xml - - - src/** - - - - - - - - - - @@ -196,6 +82,14 @@ WEB-INF/classes/LICENSE.txt,WEB-INF/classes/NOTICE.txt + + maven-site-plugin + + true + true + false + + ${project.artifactId} @@ -203,7 +97,6 @@ - org.apache.struts struts2-core diff --git a/apps/portlet/pom.xml b/apps/portlet/pom.xml index cc311695d..38c45d20f 100644 --- a/apps/portlet/pom.xml +++ b/apps/portlet/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-apps - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-portlet @@ -62,6 +62,7 @@ org.mortbay.jetty maven-jetty-plugin + 6.1.26 ${project.build.directory}/pluto-resources/web.xml src/main/webapp/WEB-INF/jetty-pluto-web-default.xml @@ -79,7 +80,7 @@ com.bekk.boss maven-jetty-pluto-embedded - 1.0 + 1.0.1 diff --git a/apps/rest-showcase/pom.xml b/apps/rest-showcase/pom.xml index 36296b5ce..dae55d40f 100644 --- a/apps/rest-showcase/pom.xml +++ b/apps/rest-showcase/pom.xml @@ -26,12 +26,12 @@ org.apache.struts struts2-apps - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-rest-showcase war - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT Struts 2 Rest Showcase Webapp Struts 2 Rest Showcase Example diff --git a/apps/showcase/pom.xml b/apps/showcase/pom.xml index 4c504977c..f5b744925 100644 --- a/apps/showcase/pom.xml +++ b/apps/showcase/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-apps - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-showcase diff --git a/archetypes/pom.xml b/archetypes/pom.xml index b82c19eba..feccd5268 100644 --- a/archetypes/pom.xml +++ b/archetypes/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-parent - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-archetypes @@ -46,4 +46,18 @@ UTF-8 + + + + + maven-site-plugin + + true + true + false + + + + + diff --git a/archetypes/struts2-archetype-angularjs/pom.xml b/archetypes/struts2-archetype-angularjs/pom.xml index affeda82f..ff146a152 100644 --- a/archetypes/struts2-archetype-angularjs/pom.xml +++ b/archetypes/struts2-archetype-angularjs/pom.xml @@ -2,7 +2,7 @@ org.apache.struts struts2-archetypes - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT 4.0.0 diff --git a/archetypes/struts2-archetype-blank/pom.xml b/archetypes/struts2-archetype-blank/pom.xml index ec2397bb8..473faf749 100644 --- a/archetypes/struts2-archetype-blank/pom.xml +++ b/archetypes/struts2-archetype-blank/pom.xml @@ -3,7 +3,7 @@ org.apache.struts struts2-archetypes - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT 4.0.0 diff --git a/archetypes/struts2-archetype-convention/pom.xml b/archetypes/struts2-archetype-convention/pom.xml index 9d9375620..7706e2551 100644 --- a/archetypes/struts2-archetype-convention/pom.xml +++ b/archetypes/struts2-archetype-convention/pom.xml @@ -2,7 +2,7 @@ org.apache.struts struts2-archetypes - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT 4.0.0 diff --git a/archetypes/struts2-archetype-dbportlet/pom.xml b/archetypes/struts2-archetype-dbportlet/pom.xml index 447b398ef..f0d08f0db 100644 --- a/archetypes/struts2-archetype-dbportlet/pom.xml +++ b/archetypes/struts2-archetype-dbportlet/pom.xml @@ -2,7 +2,7 @@ org.apache.struts struts2-archetypes - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT 4.0.0 diff --git a/archetypes/struts2-archetype-plugin/pom.xml b/archetypes/struts2-archetype-plugin/pom.xml index dda4a482c..d46db4983 100644 --- a/archetypes/struts2-archetype-plugin/pom.xml +++ b/archetypes/struts2-archetype-plugin/pom.xml @@ -2,7 +2,7 @@ org.apache.struts struts2-archetypes - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT 4.0.0 diff --git a/archetypes/struts2-archetype-portlet/pom.xml b/archetypes/struts2-archetype-portlet/pom.xml index e1e20cddc..50e4fe6c3 100644 --- a/archetypes/struts2-archetype-portlet/pom.xml +++ b/archetypes/struts2-archetype-portlet/pom.xml @@ -2,7 +2,7 @@ org.apache.struts struts2-archetypes - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT 4.0.0 diff --git a/archetypes/struts2-archetype-starter/pom.xml b/archetypes/struts2-archetype-starter/pom.xml index b19b5185a..44688f21c 100644 --- a/archetypes/struts2-archetype-starter/pom.xml +++ b/archetypes/struts2-archetype-starter/pom.xml @@ -4,7 +4,7 @@ org.apache.struts struts2-archetypes - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT 4.0.0 diff --git a/assembly/pom.xml b/assembly/pom.xml index 9dd80d249..75a1a135f 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -3,7 +3,7 @@ org.apache.struts struts2-parent - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-assembly @@ -16,6 +16,8 @@ maven-site-plugin + true + true false @@ -100,7 +102,7 @@ - + diff --git a/bom/pom.xml b/bom/pom.xml index 753b19cce..ccd3e5757 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -6,10 +6,11 @@ org.apache.struts struts-master 9 + ../struts-master struts2-bom - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT pom Struts 2 Bill of Materials @@ -24,9 +25,22 @@ - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT + + + + maven-site-plugin + + true + true + false + + + + + diff --git a/bundles/admin/pom.xml b/bundles/admin/pom.xml index ff70599eb..e4d4f3ee3 100644 --- a/bundles/admin/pom.xml +++ b/bundles/admin/pom.xml @@ -4,7 +4,7 @@ org.apache.struts struts2-osgi-bundles - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-osgi-admin-bundle diff --git a/bundles/demo/pom.xml b/bundles/demo/pom.xml index 397e3d15a..cdbe7626c 100644 --- a/bundles/demo/pom.xml +++ b/bundles/demo/pom.xml @@ -4,7 +4,7 @@ org.apache.struts struts2-osgi-bundles - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-osgi-demo-bundle diff --git a/bundles/pom.xml b/bundles/pom.xml index 672c46b70..803015ba5 100755 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-parent - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-osgi-bundles diff --git a/core/pom.xml b/core/pom.xml index 8b74f1df9..c310c6abc 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-parent - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-core jar @@ -76,28 +76,6 @@ - - - - org.codehaus.mojo - rat-maven-plugin - - - pom.xml - src/** - - - src/test/resources/org/apache/struts2/views/jsp/ui/* - src/main/resources/org/apache/struts2/static/domTT.js - src/test/resources/org/apache/struts2/interceptor/validation/* - src/site/resources/tags/** - src/main/resources/*LICENSE.txt - - - - - - alljars diff --git a/core/src/main/java/org/apache/struts2/interceptor/CookieInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/CookieInterceptor.java index ca195faa3..06c4c30ed 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/CookieInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/CookieInterceptor.java @@ -25,6 +25,7 @@ import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; +import com.opensymphony.xwork2.security.AcceptedPatternsChecker; import com.opensymphony.xwork2.security.ExcludedPatternsChecker; import com.opensymphony.xwork2.util.TextParseUtil; import com.opensymphony.xwork2.util.ValueStack; @@ -37,7 +38,6 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import java.util.regex.Pattern; /** * @@ -174,16 +174,20 @@ public class CookieInterceptor extends AbstractInterceptor { private Set cookiesNameSet = Collections.emptySet(); private Set cookiesValueSet = Collections.emptySet(); - // Allowed names of cookies - private Pattern acceptedPattern = Pattern.compile(ACCEPTED_PATTERN, Pattern.CASE_INSENSITIVE); - private ExcludedPatternsChecker excludedPatternsChecker; + private AcceptedPatternsChecker acceptedPatternsChecker; @Inject public void setExcludedPatternsChecker(ExcludedPatternsChecker excludedPatternsChecker) { this.excludedPatternsChecker = excludedPatternsChecker; } + @Inject + public void setAcceptedPatternsChecker(AcceptedPatternsChecker acceptedPatternsChecker) { + this.acceptedPatternsChecker = acceptedPatternsChecker; + this.acceptedPatternsChecker.setAcceptedPatterns(ACCEPTED_PATTERN); + } + /** * Set the cookiesName which if matched will allow the cookie * to be injected into action, could be comma-separated string. @@ -208,12 +212,13 @@ public class CookieInterceptor extends AbstractInterceptor { } /** - * Set the acceptCookieNames pattern of allowed names of cookies to protect against remote command execution vulnerability + * Set the acceptCookieNames pattern of allowed names of cookies + * to protect against remote command execution vulnerability. * - * @param pattern used to check cookie name against + * @param commaDelimitedPattern is used to check cookie name against, can set of comma delimited patterns */ - public void setAcceptCookieNames(String pattern) { - acceptedPattern = Pattern.compile(pattern); + public void setAcceptCookieNames(String commaDelimitedPattern) { + acceptedPatternsChecker.setAcceptedPatterns(commaDelimitedPattern); } public String intercept(ActionInvocation invocation) throws Exception { @@ -280,17 +285,17 @@ public class CookieInterceptor extends AbstractInterceptor { * @return true|false */ protected boolean isAccepted(String name) { - boolean matches = acceptedPattern.matcher(name).matches(); - if (matches) { + AcceptedPatternsChecker.IsAccepted accepted = acceptedPatternsChecker.isAccepted(name); + if (accepted.isAccepted()) { if (LOG.isTraceEnabled()) { - LOG.trace("Cookie [#0] matches acceptedPattern [#1]", name, ACCEPTED_PATTERN); - } - } else { - if (LOG.isTraceEnabled()) { - LOG.trace("Cookie [#0] doesn't match acceptedPattern [#1]", name, ACCEPTED_PATTERN); + LOG.trace("Cookie [#0] matches acceptedPattern [#1]", name, accepted.getAcceptedPattern()); } + return true; } - return matches; + if (LOG.isTraceEnabled()) { + LOG.trace("Cookie [#0] doesn't match acceptedPattern [#1]", name, accepted.getAcceptedPattern()); + } + return false; } /** diff --git a/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractUITagBeanInfo.java b/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractUITagBeanInfo.java index 87e410515..21c5d0029 100644 --- a/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractUITagBeanInfo.java +++ b/core/src/main/java/org/apache/struts2/views/jsp/ui/AbstractUITagBeanInfo.java @@ -45,15 +45,20 @@ public class AbstractUITagBeanInfo extends SimpleBeanInfo { List descriptors = new ArrayList(); // Add the tricky one first - Method setter = AbstractUITag.class.getMethod("setCssClass", String.class); - descriptors.add(new PropertyDescriptor("class", null, setter)); - descriptors.add(new PropertyDescriptor("cssClass", null, setter)); + Method classSetter = AbstractUITag.class.getMethod("setCssClass", String.class); + Method styleSetter = AbstractUITag.class.getMethod("setCssStyle", String.class); + + descriptors.add(new PropertyDescriptor("class", null, classSetter)); + descriptors.add(new PropertyDescriptor("cssClass", null, classSetter)); + + descriptors.add(new PropertyDescriptor("style", null, styleSetter)); + descriptors.add(new PropertyDescriptor("cssStyle", null, styleSetter)); for (Field field : AbstractUITag.class.getDeclaredFields()) { String fieldName = field.getName(); if (!"dynamicAttributes".equals(fieldName)) { String setterName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); - setter = AbstractUITag.class.getMethod(setterName, String.class); + Method setter = AbstractUITag.class.getMethod(setterName, String.class); descriptors.add(new PropertyDescriptor(fieldName, null, setter)); } } diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index 43f69ed42..c6eec3496 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -52,7 +52,7 @@ ognl.TypeConverter, com.opensymphony.xwork2.ActionContext" /> - + diff --git a/core/src/main/resources/template/simple/datetext.ftl b/core/src/main/resources/template/simple/datetext.ftl new file mode 100644 index 000000000..6d8396c9b --- /dev/null +++ b/core/src/main/resources/template/simple/datetext.ftl @@ -0,0 +1,21 @@ +<#-- +/* + * 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. + */ +--> +<#lt/>
Tag
<s:datetext/>
works only with the JavaTemplates Plugin!
<#rt/> diff --git a/core/src/main/resources/template/xhtml/datetext.ftl b/core/src/main/resources/template/xhtml/datetext.ftl new file mode 100644 index 000000000..d15b24a8c --- /dev/null +++ b/core/src/main/resources/template/xhtml/datetext.ftl @@ -0,0 +1,23 @@ +<#-- +/* + * 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. + */ +--> +<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlheader.ftl" /> +<#include "/${parameters.templateDir}/simple/datetext.ftl" /> +<#include "/${parameters.templateDir}/${parameters.expandTheme}/controlfooter.ftl" /> diff --git a/core/src/site/site.xml b/core/src/site/site.xml index 8944a2125..07a667ec7 100644 --- a/core/src/site/site.xml +++ b/core/src/site/site.xml @@ -1,8 +1,6 @@ + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + Apache Software Foundation http://www.apache.org/images/asf-logo.gif @@ -29,19 +32,26 @@ Apache Struts - http://struts.apache.org/images/struts.gif + http://struts.apache.org/img/struts-logo.svg http://struts.apache.org/ + + - - + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ diff --git a/core/src/test/java/org/apache/struts2/interceptor/CookieInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CookieInterceptorTest.java index a531a69d7..c73038224 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/CookieInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/CookieInterceptorTest.java @@ -27,6 +27,7 @@ import java.util.Map; import javax.servlet.http.Cookie; +import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker; import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker; import com.opensymphony.xwork2.mock.MockActionInvocation; import org.easymock.MockControl; @@ -44,11 +45,11 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { public void testIntercepDefault() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(new Cookie[] { + request.setCookies( new Cookie("cookie1", "cookie1value"), new Cookie("cookie2", "cookie2value"), new Cookie("cookie3", "cookie3value") - }); + ); ServletActionContext.setRequest(request); MockActionWithCookieAware action = new MockActionWithCookieAware(); @@ -67,6 +68,7 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { // by default the interceptor doesn't accept any cookies CookieInterceptor interceptor = new CookieInterceptor(); interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker()); + interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker()); interceptor.intercept(invocation); @@ -81,11 +83,11 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { public void testInterceptAll1() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(new Cookie[] { + request.setCookies( new Cookie("cookie1", "cookie1value"), new Cookie("cookie2", "cookie2value"), new Cookie("cookie3", "cookie3value") - }); + ); ServletActionContext.setRequest(request); MockActionWithCookieAware action = new MockActionWithCookieAware(); @@ -103,6 +105,7 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { CookieInterceptor interceptor = new CookieInterceptor(); interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker()); + interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker()); interceptor.setCookiesName("*"); interceptor.setCookiesValue("*"); interceptor.intercept(invocation); @@ -123,11 +126,11 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { public void testInterceptAll2() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(new Cookie[] { + request.setCookies( new Cookie("cookie1", "cookie1value"), new Cookie("cookie2", "cookie2value"), new Cookie("cookie3", "cookie3value") - }); + ); ServletActionContext.setRequest(request); MockActionWithCookieAware action = new MockActionWithCookieAware(); @@ -145,6 +148,7 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { CookieInterceptor interceptor = new CookieInterceptor(); interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker()); + interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker()); interceptor.setCookiesName("cookie1, cookie2, cookie3"); interceptor.setCookiesValue("cookie1value, cookie2value, cookie3value"); interceptor.intercept(invocation); @@ -164,11 +168,11 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { public void testInterceptSelectedCookiesNameOnly1() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(new Cookie[] { + request.setCookies( new Cookie("cookie1", "cookie1value"), new Cookie("cookie2", "cookie2value"), new Cookie("cookie3", "cookie3value") - }); + ); ServletActionContext.setRequest(request); MockActionWithCookieAware action = new MockActionWithCookieAware(); @@ -186,6 +190,7 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { CookieInterceptor interceptor = new CookieInterceptor(); interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker()); + interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker()); interceptor.setCookiesName("cookie1, cookie3"); interceptor.setCookiesValue("cookie1value, cookie2value, cookie3value"); interceptor.intercept(invocation); @@ -205,11 +210,11 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { public void testInterceptSelectedCookiesNameOnly2() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(new Cookie[] { + request.setCookies( new Cookie("cookie1", "cookie1value"), new Cookie("cookie2", "cookie2value"), new Cookie("cookie3", "cookie3value") - }); + ); ServletActionContext.setRequest(request); MockActionWithCookieAware action = new MockActionWithCookieAware(); @@ -227,6 +232,7 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { CookieInterceptor interceptor = new CookieInterceptor(); interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker()); + interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker()); interceptor.setCookiesName("cookie1, cookie3"); interceptor.setCookiesValue("*"); interceptor.intercept(invocation); @@ -246,11 +252,11 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { public void testInterceptSelectedCookiesNameOnly3() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(new Cookie[] { + request.setCookies( new Cookie("cookie1", "cookie1value"), new Cookie("cookie2", "cookie2value"), new Cookie("cookie3", "cookie3value") - }); + ); ServletActionContext.setRequest(request); MockActionWithCookieAware action = new MockActionWithCookieAware(); @@ -268,6 +274,7 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { CookieInterceptor interceptor = new CookieInterceptor(); interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker()); + interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker()); interceptor.setCookiesName("cookie1, cookie3"); interceptor.setCookiesValue(""); interceptor.intercept(invocation); @@ -288,11 +295,11 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { public void testInterceptSelectedCookiesNameAndValue() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(new Cookie[] { + request.setCookies( new Cookie("cookie1", "cookie1value"), new Cookie("cookie2", "cookie2value"), new Cookie("cookie3", "cookie3value") - }); + ); ServletActionContext.setRequest(request); MockActionWithCookieAware action = new MockActionWithCookieAware(); @@ -310,6 +317,7 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { CookieInterceptor interceptor = new CookieInterceptor(); interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker()); + interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker()); interceptor.setCookiesName("cookie1, cookie3"); interceptor.setCookiesValue("cookie1value"); interceptor.intercept(invocation); @@ -371,6 +379,7 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { } }; interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker()); + interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker()); interceptor.setCookiesName("*"); MockActionInvocation invocation = new MockActionInvocation(); @@ -431,6 +440,7 @@ public class CookieInterceptorTest extends StrutsInternalTestCase { } }; interceptor.setExcludedPatternsChecker(new DefaultExcludedPatternsChecker()); + interceptor.setAcceptedPatternsChecker(new DefaultAcceptedPatternsChecker()); interceptor.setCookiesName("*"); MockActionInvocation invocation = new MockActionInvocation(); diff --git a/core/src/test/java/org/apache/struts2/util/SecurityMemberAccessInServletsTest.java b/core/src/test/java/org/apache/struts2/util/SecurityMemberAccessInServletsTest.java new file mode 100644 index 000000000..3a8526875 --- /dev/null +++ b/core/src/test/java/org/apache/struts2/util/SecurityMemberAccessInServletsTest.java @@ -0,0 +1,81 @@ +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.util; + +import com.opensymphony.xwork2.ognl.SecurityMemberAccess; +import org.apache.struts2.StrutsInternalTestCase; +import org.apache.struts2.TestAction; + +import javax.servlet.jsp.tagext.TagSupport; +import java.lang.reflect.Member; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +public class SecurityMemberAccessInServletsTest extends StrutsInternalTestCase { + + private Map context; + + @Override + public void setUp() throws Exception { + context = new HashMap(); + } + + public void testJavaxServletPackageAccess() throws Exception { + // given + SecurityMemberAccess sma = new SecurityMemberAccess(false); + + Set excluded = new HashSet(); + excluded.add(Pattern.compile("^(?!javax\\.servlet\\..+)(javax\\..+)")); + sma.setExcludedPackageNamePatterns(excluded); + + String propertyName = "value"; + Member member = TagSupport.class.getMethod("doStartTag"); + + // when + boolean actual = sma.isAccessible(context, new TestAction(), member, propertyName); + + // then + assertTrue("javax.servlet package isn't accessible!", actual); + } + + public void testJavaxServletPackageExclusion() throws Exception { + // given + SecurityMemberAccess sma = new SecurityMemberAccess(false); + + Set excluded = new HashSet(); + excluded.add(Pattern.compile("^javax\\..+")); + sma.setExcludedPackageNamePatterns(excluded); + + String propertyName = "value"; + Member member = TagSupport.class.getMethod("doStartTag"); + + // when + boolean actual = sma.isAccessible(context, new TestAction(), member, propertyName); + + // then + assertFalse("javax.servlet package is accessible!", actual); + } + +} diff --git a/core/src/test/java/org/apache/struts2/views/jsp/TextTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/TextTagTest.java index dcc6c6535..56e86f74c 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/TextTagTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/TextTagTest.java @@ -115,7 +115,9 @@ public class TextTagTest extends AbstractTagTest { params.add(param2); params.add(param3); - String expected = MessageFormat.format(pattern, params.toArray()); + MessageFormat format = new MessageFormat(pattern, ActionContext.getContext().getLocale()); + String expected = format.format(params.toArray()); + tag.setName(key); tag.doStartTag(); ((Text) tag.component).addParameter(param1); diff --git a/plugins/cdi/pom.xml b/plugins/cdi/pom.xml index db60c1667..0de0e32d4 100644 --- a/plugins/cdi/pom.xml +++ b/plugins/cdi/pom.xml @@ -25,7 +25,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-cdi-plugin diff --git a/plugins/cdi/src/main/java/org/apache/struts2/cdi/CdiObjectFactory.java b/plugins/cdi/src/main/java/org/apache/struts2/cdi/CdiObjectFactory.java index 13c4965ec..13ae6bee7 100644 --- a/plugins/cdi/src/main/java/org/apache/struts2/cdi/CdiObjectFactory.java +++ b/plugins/cdi/src/main/java/org/apache/struts2/cdi/CdiObjectFactory.java @@ -70,7 +70,6 @@ public class CdiObjectFactory extends ObjectFactory { } protected BeanManager beanManager; - protected CreationalContext ctx; Map, InjectionTarget> injectionTargetCache = new ConcurrentHashMap, InjectionTarget>(); @@ -79,7 +78,6 @@ public class CdiObjectFactory extends ObjectFactory { LOG.info("Initializing Struts2 CDI integration..."); this.beanManager = findBeanManager(); if (beanManager != null) { - this.ctx = buildNonContextualCreationalContext(beanManager); LOG.info("Struts2 CDI integration initialized."); } else { LOG.error("Struts2 CDI integration could not be initialized."); @@ -152,13 +150,16 @@ public class CdiObjectFactory extends ObjectFactory { } @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({ "rawtypes", "unchecked" }) public Object buildBean(String className, Map extraContext, boolean injectInternal) throws Exception { Class clazz = getClassInstance(className); InjectionTarget injectionTarget = getInjectionTarget(clazz); + // a separate CreationalContext is required for every bean + final CreationalContext ctx = buildNonContextualCreationalContext(beanManager); + Object o = injectionTarget.produce(ctx); injectionTarget.inject(o, ctx); injectionTarget.postConstruct(o); diff --git a/plugins/cdi/src/site/site.xml b/plugins/cdi/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/cdi/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/codebehind/pom.xml b/plugins/codebehind/pom.xml index 824c1a140..61cad58c6 100644 --- a/plugins/codebehind/pom.xml +++ b/plugins/codebehind/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-codebehind-plugin diff --git a/plugins/codebehind/src/site/site.xml b/plugins/codebehind/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/codebehind/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/config-browser/pom.xml b/plugins/config-browser/pom.xml index 6489fc16f..0a36c52ea 100644 --- a/plugins/config-browser/pom.xml +++ b/plugins/config-browser/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-config-browser-plugin diff --git a/plugins/config-browser/src/site/site.xml b/plugins/config-browser/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/config-browser/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/convention/pom.xml b/plugins/convention/pom.xml index 1961e91f6..a8a8a4e58 100644 --- a/plugins/convention/pom.xml +++ b/plugins/convention/pom.xml @@ -3,7 +3,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-convention-plugin diff --git a/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java b/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java index f6a43a88c..d1ad0c028 100644 --- a/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java +++ b/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java @@ -38,7 +38,8 @@ import com.opensymphony.xwork2.util.TextParseUtil; import com.opensymphony.xwork2.util.WildcardHelper; import com.opensymphony.xwork2.util.classloader.ReloadingClassLoader; import com.opensymphony.xwork2.util.finder.ClassFinder; -import com.opensymphony.xwork2.util.finder.ClassFinder.ClassInfo; +import com.opensymphony.xwork2.util.finder.ClassFinderFactory; +import com.opensymphony.xwork2.util.finder.DefaultClassFinder; import com.opensymphony.xwork2.util.finder.ClassLoaderInterface; import com.opensymphony.xwork2.util.finder.ClassLoaderInterfaceDelegate; import com.opensymphony.xwork2.util.finder.Test; @@ -93,7 +94,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { private String[] actionPackages; private String[] excludePackages; private String[] packageLocators; - private String[] includeJars = new String[] { ".*?\\.jar(!/|/)?" }; + private String[] includeJars; private String packageLocatorsBasePackage; private boolean disableActionScanning = false; private boolean disablePackageLocatorsScanning = false; @@ -113,6 +114,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { private boolean eagerLoading = false; private FileManager fileManager; + private ClassFinderFactory classFinderFactory; /** * Constructs actions based on a list of packages. @@ -303,6 +305,11 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { this.fileManager = fileManagerFactory.getFileManager(); } + @Inject(required = false) + public void setClassFinderFactory(ClassFinderFactory classFinderFactory) { + this.classFinderFactory = classFinderFactory; + } + protected void initReloadClassLoader() { //when the configuration is reloaded, a new classloader will be setup if (isReloadEnabled() && reloadingClassLoader == null) @@ -387,7 +394,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { // specified by the user Test classPackageTest = getClassPackageTest(); List urls = readUrls(); - ClassFinder finder = new ClassFinder(getClassLoaderInterface(), urls, EXTRACT_BASE_INTERFACES, fileProtocols, classPackageTest); + ClassFinder finder = buildClassFinder(classPackageTest, urls); Test test = getActionClassTest(); classes.addAll(finder.findClasses(test)); @@ -400,6 +407,16 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { return classes; } + protected ClassFinder buildClassFinder(Test classPackageTest, List urls) { + if (classFinderFactory != null) { + LOG.trace("Using ClassFinderFactory to create instance of ClassFinder!"); + return classFinderFactory.buildClassFinder(getClassLoaderInterface(), urls, EXTRACT_BASE_INTERFACES, fileProtocols, classPackageTest); + } else { + LOG.trace("ClassFinderFactory not defined, fallback to default ClassFinder implementation"); + return new DefaultClassFinder(getClassLoaderInterface(), urls, EXTRACT_BASE_INTERFACES, fileProtocols, classPackageTest); + } + } + private List readUrls() throws IOException { List resourceUrls = new ArrayList(); // Usually the "classes" dir. @@ -458,35 +475,43 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { urlSet = urlSet.excludePaths(System.getProperty("sun.boot.class.path", "")); urlSet = urlSet.exclude(".*/JavaVM.framework/.*"); - List rawIncludedUrls = urlSet.getUrls(); - Set includeUrls = new HashSet(); - boolean[] patternUsed = new boolean[includeJars.length]; + if (includeJars == null) { + urlSet = urlSet.exclude(".*?\\.jar(!/|/)?"); + } else { + LOG.debug("jar urls regexes were specified: #0", Arrays.asList(includeJars)); - for (URL url : rawIncludedUrls) { - if (fileProtocols.contains(url.getProtocol())) { - //it is a jar file, make sure it macthes at least a url regex - for (int i = 0; i < includeJars.length; i++) { - String includeJar = includeJars[i]; - if (Pattern.matches(includeJar, url.toExternalForm())) { - includeUrls.add(url); - patternUsed[i] = true; - break; + List rawIncludedUrls = urlSet.getUrls(); + Set includeUrls = new HashSet(); + boolean[] patternUsed = new boolean[includeJars.length]; + + for (URL url : rawIncludedUrls) { + if (fileProtocols.contains(url.getProtocol())) { + //it is a jar file, make sure it macthes at least a url regex + for (int i = 0; i < includeJars.length; i++) { + String includeJar = includeJars[i]; + if (Pattern.matches(includeJar, url.toExternalForm())) { + includeUrls.add(url); + patternUsed[i] = true; + break; + } + } + } else { + LOG.debug("It is not a jar [#0]", url); + includeUrls.add(url); + } + } + + if (LOG.isWarnEnabled()) { + for (int i = 0; i < patternUsed.length; i++) { + if (!patternUsed[i]) { + LOG.warn("The includeJars pattern [#0] did not match any jars in the classpath", includeJars[i]); } } - } else { - //it is not a jar - includeUrls.add(url); } + return new UrlSet(includeUrls); } - if (LOG.isWarnEnabled()) { - for (int i = 0; i < patternUsed.length; i++) { - if (!patternUsed[i]) { - LOG.warn("The includeJars pattern [#0] did not match any jars in the classpath", includeJars[i]); - } - } - } - return new UrlSet(includeUrls); + return urlSet; } /** @@ -496,7 +521,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { * goal is to avoid loading the class if we don't have to, the (actionSuffix * || implements Action) test will have to remain until later. See * {@link #getActionClassTest()} for the test performed on the loaded - * {@link ClassInfo} structure. + * {@link ClassFinder.ClassInfo} structure. * * @param className the name of the class to test * @return true if the specified class should be included in the @@ -588,7 +613,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { /** * Construct a {@link Test} Object that determines if a specified class - * should be included in the package scan based on the full {@link ClassInfo} + * should be included in the package scan based on the full {@link ClassFinder.ClassInfo} * of the class. At this point, the class has been loaded, so it's ok to * perform tests such as checking annotations or looking at interfaces or * super-classes of the specified class. @@ -1121,4 +1146,5 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { } else return false; } + } diff --git a/plugins/convention/src/site/site.xml b/plugins/convention/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/convention/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/dojo/pom.xml b/plugins/dojo/pom.xml index 3cb068771..c3a48ef14 100644 --- a/plugins/dojo/pom.xml +++ b/plugins/dojo/pom.xml @@ -25,7 +25,7 @@ struts2-plugins org.apache.struts - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT 4.0.0 diff --git a/plugins/dojo/src/site/site.xml b/plugins/dojo/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/dojo/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/dwr/pom.xml b/plugins/dwr/pom.xml index cb399e2bc..446db222c 100644 --- a/plugins/dwr/pom.xml +++ b/plugins/dwr/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-dwr-plugin diff --git a/plugins/dwr/src/site/site.xml b/plugins/dwr/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/dwr/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/embeddedjsp/pom.xml b/plugins/embeddedjsp/pom.xml index b77292070..541db6c6b 100644 --- a/plugins/embeddedjsp/pom.xml +++ b/plugins/embeddedjsp/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-embeddedjsp-plugin diff --git a/plugins/embeddedjsp/src/site/site.xml b/plugins/embeddedjsp/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/embeddedjsp/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/gxp/pom.xml b/plugins/gxp/pom.xml index 89f4f9e42..e2ca54e8b 100644 --- a/plugins/gxp/pom.xml +++ b/plugins/gxp/pom.xml @@ -3,7 +3,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-gxp-plugin diff --git a/plugins/gxp/src/site/site.xml b/plugins/gxp/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/gxp/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/jasperreports/pom.xml b/plugins/jasperreports/pom.xml index 15bea415f..5bcf4701e 100644 --- a/plugins/jasperreports/pom.xml +++ b/plugins/jasperreports/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-jasperreports-plugin diff --git a/plugins/jasperreports/src/site/site.xml b/plugins/jasperreports/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/jasperreports/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/java8-support/README.adoc b/plugins/java8-support/README.adoc new file mode 100644 index 000000000..beae27c45 --- /dev/null +++ b/plugins/java8-support/README.adoc @@ -0,0 +1,11 @@ += Struts 2 Java 8 Support plugin + +This plugin aims to add support for Java 8 specific features. +As Struts 2 core targets Java 6/7 and some features won't work and they have to be adjusted. +Below is a list of features supported by this plugin + +== Supported Java 8 features +- Lambada expressions in actions when using them with the Convention plugin + +== Installation +Just drop this plugin into `WEB-INF/lib` folder or add it as Maven dependency diff --git a/plugins/java8-support/pom.xml b/plugins/java8-support/pom.xml new file mode 100644 index 000000000..c99b3686a --- /dev/null +++ b/plugins/java8-support/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + + org.apache.struts + struts2-plugins + 2.3.22-SNAPSHOT + + + struts2-java8-support-plugin + jar + Struts 2 Java 8 support plugin + + + + + org.apache.felix + maven-bundle-plugin + true + + + org.apache.struts2.osgi.StrutsActivator + META-INF + + + + + + + + + + org.apache.struts.xwork + xwork-core + + + asm + asm + + + asm + asm-commons + + + + + org.ow2.asm + asm + + + org.ow2.asm + asm-commons + + + + + UTF-8 + + diff --git a/plugins/java8-support/src/main/java/org/apache/struts2/convention/Java8ClassFinder.java b/plugins/java8-support/src/main/java/org/apache/struts2/convention/Java8ClassFinder.java new file mode 100644 index 000000000..2d49e18bb --- /dev/null +++ b/plugins/java8-support/src/main/java/org/apache/struts2/convention/Java8ClassFinder.java @@ -0,0 +1,585 @@ +/* + * Copyright 2002-2003,2009 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.convention; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.FileManager; +import com.opensymphony.xwork2.FileManagerFactory; +import com.opensymphony.xwork2.XWorkException; +import com.opensymphony.xwork2.util.finder.ClassFinder; +import com.opensymphony.xwork2.util.finder.ClassLoaderInterface; +import com.opensymphony.xwork2.util.finder.Test; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.commons.lang3.StringUtils; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +/** + * Copy of {@link com.opensymphony.xwork2.util.finder.DefaultClassFinder} with proper support for Java8 + */ +public class Java8ClassFinder implements ClassFinder { + + private static final Logger LOG = LoggerFactory.getLogger(Java8ClassFinder.class); + + private final Map> annotated = new HashMap>(); + private final Map classInfos = new LinkedHashMap(); + + private final List classesNotLoaded = new ArrayList(); + + private boolean extractBaseInterfaces; + private ClassLoaderInterface classLoaderInterface; + private FileManager fileManager; + + public Java8ClassFinder(ClassLoaderInterface classLoaderInterface, Collection urls, boolean extractBaseInterfaces, Set protocols, Test classNameFilter) { + this.classLoaderInterface = classLoaderInterface; + this.extractBaseInterfaces = extractBaseInterfaces; + this.fileManager = ActionContext.getContext().getInstance(FileManagerFactory.class).getFileManager(); + + List classNames = new ArrayList(); + for (URL location : urls) { + try { + if (protocols.contains(location.getProtocol())) { + classNames.addAll(jar(location)); + } else if ("file".equals(location.getProtocol())) { + try { + // See if it's actually a jar + URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/"); + JarURLConnection juc = (JarURLConnection) jarUrl.openConnection(); + juc.getJarFile(); + classNames.addAll(jar(jarUrl)); + } catch (IOException e) { + classNames.addAll(file(location)); + } + } + } catch (Exception e) { + if (LOG.isErrorEnabled()) + LOG.error("Unable to read URL [#0]", e, location.toExternalForm()); + } + } + + for (String className : classNames) { + try { + if (classNameFilter.test(className)) + readClassDef(className); + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Unable to read class [#0]", e, className); + } + } + } + + public Java8ClassFinder(Class... classes){ + this(Arrays.asList(classes)); + } + + public Java8ClassFinder(List classes){ + this.classLoaderInterface = null; + List infos = new ArrayList(); + List packages = new ArrayList(); + for (Class clazz : classes) { + + Package aPackage = clazz.getPackage(); + if (aPackage != null && !packages.contains(aPackage)){ + infos.add(new PackageInfo(aPackage)); + packages.add(aPackage); + } + + ClassInfo classInfo = new ClassInfo(clazz, this); + infos.add(classInfo); + classInfos.put(classInfo.getName(), classInfo); + for (Method method : clazz.getDeclaredMethods()) { + infos.add(new MethodInfo(classInfo, method)); + } + + for (Constructor constructor : clazz.getConstructors()) { + infos.add(new MethodInfo(classInfo, constructor)); + } + + for (Field field : clazz.getDeclaredFields()) { + infos.add(new FieldInfo(classInfo, field)); + } + } + + for (Info info : infos) { + for (AnnotationInfo annotation : info.getAnnotations()) { + List annotationInfos = getAnnotationInfos(annotation.getName()); + annotationInfos.add(info); + } + } + } + + public ClassLoaderInterface getClassLoaderInterface() { + return classLoaderInterface; + } + + public boolean isAnnotationPresent(Class annotation) { + List infos = annotated.get(annotation.getName()); + return infos != null && !infos.isEmpty(); + } + + public List getClassesNotLoaded() { + return Collections.unmodifiableList(classesNotLoaded); + } + + public List findAnnotatedPackages(Class annotation) { + classesNotLoaded.clear(); + List packages = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof PackageInfo) { + PackageInfo packageInfo = (PackageInfo) info; + try { + Package pkg = packageInfo.get(); + // double check via proper reflection + if (pkg.isAnnotationPresent(annotation)) { + packages.add(pkg); + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(packageInfo.getName()); + } + } + } + return packages; + } + + public List findAnnotatedClasses(Class annotation) { + classesNotLoaded.clear(); + List classes = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof ClassInfo) { + ClassInfo classInfo = (ClassInfo) info; + try { + Class clazz = classInfo.get(); + // double check via proper reflection + if (clazz.isAnnotationPresent(annotation)) { + classes.add(clazz); + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + } + return classes; + } + + public List findAnnotatedMethods(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List methods = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof MethodInfo && !"".equals(info.getName())) { + MethodInfo methodInfo = (MethodInfo) info; + ClassInfo classInfo = methodInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(annotation)) { + methods.add(method); + } + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + } + return methods; + } + + public List findAnnotatedConstructors(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List constructors = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof MethodInfo && "".equals(info.getName())) { + MethodInfo methodInfo = (MethodInfo) info; + ClassInfo classInfo = methodInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Constructor constructor : clazz.getConstructors()) { + if (constructor.isAnnotationPresent(annotation)) { + constructors.add(constructor); + } + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + } + return constructors; + } + + public List findAnnotatedFields(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List fields = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof FieldInfo) { + FieldInfo fieldInfo = (FieldInfo) info; + ClassInfo classInfo = fieldInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Field field : clazz.getDeclaredFields()) { + if (field.isAnnotationPresent(annotation)) { + fields.add(field); + } + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + } + return fields; + } + + public List findClassesInPackage(String packageName, boolean recursive) { + classesNotLoaded.clear(); + List classes = new ArrayList(); + for (ClassInfo classInfo : classInfos.values()) { + try { + if (recursive && classInfo.getPackageName().startsWith(packageName)){ + classes.add(classInfo.get()); + } else if (classInfo.getPackageName().equals(packageName)){ + classes.add(classInfo.get()); + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + return classes; + } + + public List findClasses(Test test) { + classesNotLoaded.clear(); + List classes = new ArrayList(); + for (ClassInfo classInfo : classInfos.values()) { + try { + if (test.test(classInfo)) { + classes.add(classInfo.get()); + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + return classes; + } + + public List findClasses() { + classesNotLoaded.clear(); + List classes = new ArrayList(); + for (ClassInfo classInfo : classInfos.values()) { + try { + classes.add(classInfo.get()); + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + return classes; + } + + private static List getURLs(ClassLoaderInterface classLoader, String[] dirNames) { + List urls = new ArrayList(); + for (String dirName : dirNames) { + try { + Enumeration classLoaderURLs = classLoader.getResources(dirName); + while (classLoaderURLs.hasMoreElements()) { + URL url = classLoaderURLs.nextElement(); + urls.add(url); + } + } catch (IOException ioe) { + if (LOG.isErrorEnabled()) + LOG.error("Could not read driectory [#0]", ioe, dirName); + } + } + + return urls; + } + + private List file(URL location) { + List classNames = new ArrayList(); + File dir = new File(URLDecoder.decode(location.getPath())); + if ("META-INF".equals(dir.getName())) { + dir = dir.getParentFile(); // Scrape "META-INF" off + } + if (dir.isDirectory()) { + scanDir(dir, classNames, ""); + } + return classNames; + } + + private void scanDir(File dir, List classNames, String packageName) { + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + scanDir(file, classNames, packageName + file.getName() + "."); + } else if (file.getName().endsWith(".class")) { + String name = file.getName(); + name = name.replaceFirst(".class$", ""); + // Classes packaged in an exploded .war (e.g. in a VFS file system) should not + // have WEB-INF.classes in their package name. + classNames.add(StringUtils.removeStart(packageName, "WEB-INF.classes.") + name); + } + } + } + + private List jar(URL location) throws IOException { + URL url = fileManager.normalizeToFileProtocol(location); + if (url != null) { + InputStream in = url.openStream(); + try { + JarInputStream jarStream = new JarInputStream(in); + return jar(jarStream); + } finally { + in.close(); + } + } else if (LOG.isDebugEnabled()) + LOG.debug("Unable to read [#0]", location.toExternalForm()); + + return Collections.emptyList(); + } + + private List jar(JarInputStream jarStream) throws IOException { + List classNames = new ArrayList(); + + JarEntry entry; + while ((entry = jarStream.getNextJarEntry()) != null) { + if (entry.isDirectory() || !entry.getName().endsWith(".class")) { + continue; + } + String className = entry.getName(); + className = className.replaceFirst(".class$", ""); + + //war files are treated as .jar files, so takeout WEB-INF/classes + className = StringUtils.removeStart(className, "WEB-INF/classes/"); + + className = className.replace('/', '.'); + classNames.add(className); + } + + return classNames; + } + + private List getAnnotationInfos(String name) { + List infos = annotated.get(name); + if (infos == null) { + infos = new ArrayList(); + annotated.put(name, infos); + } + return infos; + } + + private void readClassDef(String className) { + if (!className.endsWith(".class")) { + className = className.replace('.', '/') + ".class"; + } + try { + URL resource = classLoaderInterface.getResource(className); + if (resource != null) { + InputStream in = resource.openStream(); + try { + ClassReader classReader = new ClassReader(in); + classReader.accept(new InfoBuildingClassVisitor(this), ClassReader.SKIP_DEBUG); + } finally { + in.close(); + } + } else { + throw new XWorkException("Could not load " + className); + } + } catch (IOException e) { + throw new XWorkException("Could not load " + className, e); + } + + } + + public class InfoBuildingClassVisitor extends ClassVisitor { + private Info info; + private ClassFinder classFinder; + + public InfoBuildingClassVisitor(ClassFinder classFinder) { + super(Opcodes.ASM5); + this.classFinder = classFinder; + } + + public InfoBuildingClassVisitor(Info info, ClassFinder classFinder) { + this(classFinder); + this.info = info; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if (name.endsWith("package-info")) { + info = new PackageInfo(javaName(name), classFinder); + } else { + ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName), classFinder); + + for (String interfce : interfaces) { + classInfo.getInterfaces().add(javaName(interfce)); + } + info = classInfo; + classInfos.put(classInfo.getName(), classInfo); + + if (extractBaseInterfaces) + extractSuperInterfaces(classInfo); + } + } + + private void extractSuperInterfaces(ClassInfo classInfo) { + String superType = classInfo.getSuperType(); + + if (superType != null) { + ClassInfo base = classInfos.get(superType); + + if (base == null) { + //try to load base + String resource = superType.replace('.', '/') + ".class"; + readClassDef(resource); + base = classInfos.get(superType); + } + + if (base != null) { + List interfaces = classInfo.getSuperInterfaces(); + interfaces.addAll(base.getSuperInterfaces()); + interfaces.addAll(base.getInterfaces()); + } + } + } + + private String javaName(String name) { + return (name == null)? null:name.replace('/', '.'); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + AnnotationInfo annotationInfo = new AnnotationInfo(desc); + info.getAnnotations().add(annotationInfo); + getAnnotationInfos(annotationInfo.getName()).add(info); + return null; + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + ClassInfo classInfo = ((ClassInfo) info); + FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc); + classInfo.getFields().add(fieldInfo); + return null; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + ClassInfo classInfo = ((ClassInfo) info); + MethodInfo methodInfo = new MethodInfo(classInfo, name, desc); + classInfo.getMethods().add(methodInfo); + return new InfoBuildingMethodVisitor(methodInfo); + } + } + + public class InfoBuildingMethodVisitor extends MethodVisitor { + private Info info; + + public InfoBuildingMethodVisitor() { + super(Opcodes.ASM5); + } + + public InfoBuildingMethodVisitor(Info info) { + this(); + this.info = info; + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + AnnotationInfo annotationInfo = new AnnotationInfo(desc); + info.getAnnotations().add(annotationInfo); + getAnnotationInfos(annotationInfo.getName()).add(info); + return null; + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) { + MethodInfo methodInfo = ((MethodInfo) info); + List annotationInfos = methodInfo.getParameterAnnotations(param); + AnnotationInfo annotationInfo = new AnnotationInfo(desc); + annotationInfos.add(annotationInfo); + return null; + } + } + + private static final class DefaultClassnameFilterImpl implements Test { + public boolean test(String className) { + return true; + } + } +} + diff --git a/plugins/java8-support/src/main/java/org/apache/struts2/convention/Java8ClassFinderFactory.java b/plugins/java8-support/src/main/java/org/apache/struts2/convention/Java8ClassFinderFactory.java new file mode 100644 index 000000000..3ab5f9eb2 --- /dev/null +++ b/plugins/java8-support/src/main/java/org/apache/struts2/convention/Java8ClassFinderFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2003,2009 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.struts2.convention; + +import com.opensymphony.xwork2.util.finder.ClassFinder; +import com.opensymphony.xwork2.util.finder.ClassFinderFactory; +import com.opensymphony.xwork2.util.finder.ClassLoaderInterface; +import com.opensymphony.xwork2.util.finder.Test; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +import java.net.URL; +import java.util.Collection; +import java.util.Set; + +public class Java8ClassFinderFactory implements ClassFinderFactory { + + private static final Logger LOG = LoggerFactory.getLogger(Java8ClassFinderFactory.class); + + public Java8ClassFinderFactory() { + try { + LOG.trace("Checking if ASM5 is on the classpath...."); + Class.forName("org.objectweb.asm.MethodVisitor"); + LOG.trace("Proper version of ASM5 is in use!"); + } catch (ClassNotFoundException e) { + LOG.warn("ASM5 is missing or older version is used! If you use Maven, please exclude asm.jar and asm-commons.jar version 3 from xwork!"); + } + } + + public ClassFinder buildClassFinder(ClassLoaderInterface classLoaderInterface, Collection urls, boolean extractBaseInterfaces, Set protocols, Test classNameFilter) { + LOG.debug("Creating new instance of Java8ClassFinder"); + return new Java8ClassFinder(classLoaderInterface, urls, extractBaseInterfaces, protocols, classNameFilter); + } + +} diff --git a/plugins/java8-support/src/main/resources/LICENSE.txt b/plugins/java8-support/src/main/resources/LICENSE.txt new file mode 100644 index 000000000..dd5b3a58a --- /dev/null +++ b/plugins/java8-support/src/main/resources/LICENSE.txt @@ -0,0 +1,174 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/plugins/java8-support/src/main/resources/NOTICE.txt b/plugins/java8-support/src/main/resources/NOTICE.txt new file mode 100644 index 000000000..bfba90c29 --- /dev/null +++ b/plugins/java8-support/src/main/resources/NOTICE.txt @@ -0,0 +1,5 @@ +Apache Struts +Copyright 2000-2011 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). \ No newline at end of file diff --git a/src/site/xdoc/jxr.xml b/plugins/java8-support/src/main/resources/struts-plugin.xml old mode 100755 new mode 100644 similarity index 65% rename from src/site/xdoc/jxr.xml rename to plugins/java8-support/src/main/resources/struts-plugin.xml index f7f1024ce..c765cf817 --- a/src/site/xdoc/jxr.xml +++ b/plugins/java8-support/src/main/resources/struts-plugin.xml @@ -1,4 +1,4 @@ - + - - - - Source Xref report - + - + -
+ - - -
- - -
+ diff --git a/plugins/java8-support/src/site/site.xml b/plugins/java8-support/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/java8-support/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/javatemplates/pom.xml b/plugins/javatemplates/pom.xml index 30a2b264b..5b0cb57a2 100644 --- a/plugins/javatemplates/pom.xml +++ b/plugins/javatemplates/pom.xml @@ -25,7 +25,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-javatemplates-plugin diff --git a/plugins/javatemplates/src/site/site.xml b/plugins/javatemplates/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/javatemplates/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/jfreechart/pom.xml b/plugins/jfreechart/pom.xml index 8e6597a99..04dcef190 100644 --- a/plugins/jfreechart/pom.xml +++ b/plugins/jfreechart/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-jfreechart-plugin diff --git a/plugins/jfreechart/src/site/site.xml b/plugins/jfreechart/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/jfreechart/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/jsf/pom.xml b/plugins/jsf/pom.xml index c2fb01f5a..616c39b74 100644 --- a/plugins/jsf/pom.xml +++ b/plugins/jsf/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-jsf-plugin diff --git a/plugins/jsf/src/site/site.xml b/plugins/jsf/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/jsf/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/json/pom.xml b/plugins/json/pom.xml index c7fdd9b63..4050b4423 100644 --- a/plugins/json/pom.xml +++ b/plugins/json/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-json-plugin diff --git a/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java b/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java index 69158bc01..f802af9eb 100644 --- a/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java +++ b/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java @@ -87,8 +87,8 @@ public class JSONInterceptor extends AbstractInterceptor { } Object rootObject = null; + final ValueStack stack = invocation.getStack(); if (this.root != null) { - ValueStack stack = invocation.getStack(); rootObject = stack.findValue(this.root); if (rootObject == null) { @@ -100,6 +100,26 @@ public class JSONInterceptor extends AbstractInterceptor { // load JSON object Object obj = JSONUtil.deserialize(request.getReader()); + // JSON array (this.root cannot be null in this case) + if(obj instanceof List && this.root != null) { + String mapKey = this.root; + rootObject = null; + + if(this.root.indexOf('.') != -1) { + mapKey = this.root.substring(this.root.lastIndexOf('.') + 1); + + rootObject = stack.findValue(this.root.substring(0, this.root.lastIndexOf('.'))); + if (rootObject == null) { + throw new RuntimeException("JSON array: Invalid root expression: '" + this.root + "'."); + } + } + + // create a map with a list inside + Map m = new HashMap(); + m.put(mapKey, new ArrayList((List) obj)); + obj = m; + } + if (obj instanceof Map) { Map json = (Map) obj; diff --git a/plugins/json/src/site/site.xml b/plugins/json/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/json/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/json/src/test/java/org/apache/struts2/json/AnotherBean.java b/plugins/json/src/test/java/org/apache/struts2/json/AnotherBean.java new file mode 100644 index 000000000..81ba27d10 --- /dev/null +++ b/plugins/json/src/test/java/org/apache/struts2/json/AnotherBean.java @@ -0,0 +1,32 @@ +package org.apache.struts2.json; + +import java.util.ArrayList; +import java.util.List; + +public class AnotherBean { + private List beans; + + private AnotherBean yetAnotherBean; + + public List getBeans() { + if (this.beans == null) { + this.beans = new ArrayList(); + } + return this.beans; + } + + public void setBeans(List beans) { + this.beans = beans; + } + + public AnotherBean getYetAnotherBean() { + if(this.yetAnotherBean == null) { + this.yetAnotherBean = new AnotherBean(); + } + return yetAnotherBean; + } + + public void setYetAnotherBean(AnotherBean yetAnotherBean) { + this.yetAnotherBean = yetAnotherBean; + } +} diff --git a/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java b/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java index 7836f849a..7bf53d328 100644 --- a/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java +++ b/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java @@ -459,7 +459,59 @@ public class JSONInterceptorTest extends StrutsTestCase { assertEquals(bean2.getDoubleField(), 10.1); assertEquals(bean2.getByteField(), 3); } + + public void testJSONArray() throws Exception { + setRequestContent("json-12.txt"); + this.request.addHeader("content-type", "application/json"); + // interceptor + JSONInterceptor interceptor = new JSONInterceptor(); + interceptor.setRoot("beans"); + TestAction5 action = new TestAction5(); + + this.invocation.setAction(action); + this.invocation.getStack().push(action); + + interceptor.intercept(this.invocation); + + List beans = action.getBeans(); + + assertNotNull(beans); + assertEquals(1, beans.size()); + assertTrue(beans.get(0).isBooleanField()); + assertEquals(beans.get(0).getStringField(), "test"); + assertEquals(beans.get(0).getIntField(), 10); + assertEquals(beans.get(0).getCharField(), 's'); + assertEquals(beans.get(0).getDoubleField(), 10.1); + assertEquals(beans.get(0).getByteField(), 3); + } + + public void testJSONArray2() throws Exception { + setRequestContent("json-12.txt"); + this.request.addHeader("content-type", "application/json"); + + // interceptor + JSONInterceptor interceptor = new JSONInterceptor(); + interceptor.setRoot("anotherBean.yetAnotherBean.beans"); + TestAction5 action = new TestAction5(); + + this.invocation.setAction(action); + this.invocation.getStack().push(action); + + interceptor.intercept(this.invocation); + + List beans = action.getAnotherBean().getYetAnotherBean().getBeans(); + + assertNotNull(beans); + assertEquals(1, beans.size()); + assertTrue(beans.get(0).isBooleanField()); + assertEquals(beans.get(0).getStringField(), "test"); + assertEquals(beans.get(0).getIntField(), 10); + assertEquals(beans.get(0).getCharField(), 's'); + assertEquals(beans.get(0).getDoubleField(), 10.1); + assertEquals(beans.get(0).getByteField(), 3); + } + @Override protected void setUp() throws Exception { super.setUp(); diff --git a/plugins/json/src/test/java/org/apache/struts2/json/TestAction5.java b/plugins/json/src/test/java/org/apache/struts2/json/TestAction5.java new file mode 100644 index 000000000..9c3321a2d --- /dev/null +++ b/plugins/json/src/test/java/org/apache/struts2/json/TestAction5.java @@ -0,0 +1,52 @@ +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.json; + +import java.util.ArrayList; +import java.util.List; + +public class TestAction5 { + private List beans; + + private AnotherBean anotherBean; + + public List getBeans() { + if (this.beans == null) { + this.beans = new ArrayList(); + } + return this.beans; + } + + public void setBeans(List beans) { + this.beans = beans; + } + + public AnotherBean getAnotherBean() { + if(this.anotherBean == null) { + this.anotherBean = new AnotherBean(); + } + return anotherBean; + } + + public void setAnotherBean(AnotherBean anotherBean) { + this.anotherBean = anotherBean; + } +} diff --git a/plugins/json/src/test/resources/org/apache/struts2/json/json-12.txt b/plugins/json/src/test/resources/org/apache/struts2/json/json-12.txt new file mode 100644 index 000000000..abd0c7180 --- /dev/null +++ b/plugins/json/src/test/resources/org/apache/struts2/json/json-12.txt @@ -0,0 +1,8 @@ +[{ + "booleanField": true, + "stringField" : "test", + "intField" : 10, + "charField": "s", + "doubleField": 10.1, + "byteField": 3 +}] diff --git a/plugins/junit/pom.xml b/plugins/junit/pom.xml index fed1b51ec..0a22bbfe7 100644 --- a/plugins/junit/pom.xml +++ b/plugins/junit/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-junit-plugin diff --git a/plugins/junit/src/site/site.xml b/plugins/junit/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/junit/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/osgi/pom.xml b/plugins/osgi/pom.xml index de33bc079..6695e118e 100644 --- a/plugins/osgi/pom.xml +++ b/plugins/osgi/pom.xml @@ -4,7 +4,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-osgi-plugin diff --git a/plugins/osgi/src/site/site.xml b/plugins/osgi/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/osgi/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/oval/pom.xml b/plugins/oval/pom.xml index cad0ada7e..7c3ad347c 100644 --- a/plugins/oval/pom.xml +++ b/plugins/oval/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-oval-plugin diff --git a/plugins/oval/src/site/site.xml b/plugins/oval/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/oval/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/pell-multipart/pom.xml b/plugins/pell-multipart/pom.xml index 0378e3b7d..57bd06e53 100644 --- a/plugins/pell-multipart/pom.xml +++ b/plugins/pell-multipart/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-pell-multipart-plugin diff --git a/plugins/pell-multipart/src/site/site.xml b/plugins/pell-multipart/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/pell-multipart/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/plexus/pom.xml b/plugins/plexus/pom.xml index f7f838f4a..bf5b7d1c6 100644 --- a/plugins/plexus/pom.xml +++ b/plugins/plexus/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-plexus-plugin diff --git a/plugins/plexus/src/site/site.xml b/plugins/plexus/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/plexus/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/pom.xml b/plugins/pom.xml index 276dd1dd0..2debf2694 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-parent - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-plugins @@ -34,33 +34,34 @@ Struts Plugins + cdi codebehind - convention config-browser - javatemplates + convention + dojo + dwr + embeddedjsp + gxp jasperreports + java8-support + javatemplates jfreechart jsf + json + junit + osgi + oval pell-multipart plexus + portlet + portlet-tiles + rest sitegraph sitemesh spring struts1 - tiles - dojo - rest - portlet - portlet-tiles - junit testng - dwr - oval - osgi - json - embeddedjsp - gxp - cdi + tiles tiles3 diff --git a/plugins/portlet-tiles/pom.xml b/plugins/portlet-tiles/pom.xml index d05c7bfa9..146d577ba 100644 --- a/plugins/portlet-tiles/pom.xml +++ b/plugins/portlet-tiles/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-portlet-tiles-plugin diff --git a/plugins/portlet-tiles/src/site/site.xml b/plugins/portlet-tiles/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/portlet-tiles/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/portlet/pom.xml b/plugins/portlet/pom.xml index e8d0f9a4d..88d06cbf7 100644 --- a/plugins/portlet/pom.xml +++ b/plugins/portlet/pom.xml @@ -3,7 +3,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-portlet-plugin diff --git a/plugins/portlet/src/site/site.xml b/plugins/portlet/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/portlet/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/rest/pom.xml b/plugins/rest/pom.xml index 4644252f8..7b183b81b 100644 --- a/plugins/rest/pom.xml +++ b/plugins/rest/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-rest-plugin diff --git a/plugins/rest/src/site/site.xml b/plugins/rest/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/rest/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/sitegraph/pom.xml b/plugins/sitegraph/pom.xml index 884002a3b..01156123c 100644 --- a/plugins/sitegraph/pom.xml +++ b/plugins/sitegraph/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-sitegraph-plugin diff --git a/plugins/sitegraph/src/site/site.xml b/plugins/sitegraph/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/sitegraph/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/sitemesh/pom.xml b/plugins/sitemesh/pom.xml index 9ed7b5361..753ed3f09 100644 --- a/plugins/sitemesh/pom.xml +++ b/plugins/sitemesh/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-sitemesh-plugin diff --git a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/StrutsSiteMeshFactory.java b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/StrutsSiteMeshFactory.java index 458cbf7d4..96405fb37 100644 --- a/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/StrutsSiteMeshFactory.java +++ b/plugins/sitemesh/src/main/java/org/apache/struts2/sitemesh/StrutsSiteMeshFactory.java @@ -1,14 +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.struts2.sitemesh; import com.opensymphony.module.sitemesh.Config; import com.opensymphony.module.sitemesh.factory.DefaultFactory; +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; import org.apache.commons.lang3.ObjectUtils; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsStatics; - public class StrutsSiteMeshFactory extends DefaultFactory { + private static final Logger LOG = LoggerFactory.getLogger(StrutsSiteMeshFactory.class); + public StrutsSiteMeshFactory(Config config) { super(config); } @@ -22,7 +44,14 @@ public class StrutsSiteMeshFactory extends DefaultFactory { } private boolean isInsideActionTag() { + if(ActionContext.getContext() == null) { + if (LOG.isTraceEnabled()) { + LOG.trace("ActionContext is null! Not a user request?"); + } + return false; + } Object attribute = ServletActionContext.getRequest().getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION); return (Boolean) ObjectUtils.defaultIfNull(attribute, Boolean.FALSE); } + } diff --git a/plugins/sitemesh/src/site/site.xml b/plugins/sitemesh/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/sitemesh/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/spring/pom.xml b/plugins/spring/pom.xml index 12359c755..f2b1aff27 100644 --- a/plugins/spring/pom.xml +++ b/plugins/spring/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-spring-plugin diff --git a/plugins/spring/src/site/site.xml b/plugins/spring/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/spring/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/src/site/site.xml b/plugins/src/site/site.xml new file mode 100644 index 000000000..e03d26d5c --- /dev/null +++ b/plugins/src/site/site.xml @@ -0,0 +1,58 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/struts1/pom.xml b/plugins/struts1/pom.xml index 9948a8315..c732a76c3 100644 --- a/plugins/struts1/pom.xml +++ b/plugins/struts1/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-struts1-plugin diff --git a/plugins/struts1/src/site/site.xml b/plugins/struts1/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/struts1/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/testng/pom.xml b/plugins/testng/pom.xml index 753116cad..bb68d5510 100644 --- a/plugins/testng/pom.xml +++ b/plugins/testng/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-testng-plugin diff --git a/plugins/testng/src/site/site.xml b/plugins/testng/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/testng/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/tiles/pom.xml b/plugins/tiles/pom.xml index 6e5613e7d..0cd82837f 100644 --- a/plugins/tiles/pom.xml +++ b/plugins/tiles/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-tiles-plugin diff --git a/plugins/tiles/src/site/site.xml b/plugins/tiles/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/tiles/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/plugins/tiles3/pom.xml b/plugins/tiles3/pom.xml index 75feaaa33..209304f2f 100644 --- a/plugins/tiles3/pom.xml +++ b/plugins/tiles3/pom.xml @@ -26,7 +26,7 @@ org.apache.struts struts2-plugins - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT struts2-tiles3-plugin diff --git a/plugins/tiles3/src/site/site.xml b/plugins/tiles3/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/plugins/tiles3/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/pom.xml b/pom.xml index 39b9c8e84..94f9fba78 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ 4.0.0 struts2-parent - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT pom Struts 2 http://struts.apache.org/ @@ -31,8 +31,8 @@ scm:git:git://git.apache.org/struts.git scm:git:https://git-wip-us.apache.org/repos/asf/struts.git http://git.apache.org/struts.git - HEAD - + HEAD + JIRA @@ -87,7 +87,8 @@ ${project.version} 3.0.5.RELEASE 3.0.6 - 5.0.2 + 3.3 + 5.0.2 2.0.6 @@ -130,12 +131,12 @@ org.apache.maven.plugins maven-site-plugin - 3.2 + 3.3 org.apache.maven.plugins maven-project-info-reports-plugin - 2.4 + 2.6 org.codehaus.mojo @@ -286,17 +287,17 @@ org.apache.maven.plugins maven-site-plugin - 3.1 + 3.3 ${siteDeploy.url} false - - org.apache.maven.doxia - doxia-module-markdown - 1.3 - + + org.apache.maven.doxia + doxia-module-markdown + 1.3 + @@ -310,20 +311,37 @@ org.apache.maven.plugins maven-project-info-reports-plugin + 2.7 - @@ -496,6 +514,11 @@ struts2-osgi-demo-bundle ${project.version} + + org.apache.struts + struts2-java8-support-plugin + ${project.version} + org.freemarker @@ -567,11 +590,21 @@ org.ow2.asm asm - ${asm.version} + ${asm5.version} org.ow2.asm asm-commons + ${asm5.version} + + + asm + asm + ${asm.version} + + + asm + asm-commons ${asm.version} diff --git a/src/site/markdown/plugins.md b/src/site/markdown/plugins.md deleted file mode 100644 index 332a102ab..000000000 --- a/src/site/markdown/plugins.md +++ /dev/null @@ -1,37 +0,0 @@ -# Apache Struts Plugins - -Apache Struts provides a plugin mechanism which allows to extend the framework -easily. A few selected plugins are bundled with Struts and are maintained by the -Struts team. These plugins are linked below. You can find community build plugins -linked on the [Struts 2 Plugin Wiki](https://cwiki.apache.org/S2PLUGINS/home.html). - -## API References - - * [CDI](struts2-plugins/struts2-cdi-browser-plugin/apidocs/index.html) - * [Codebehind](struts2-plugins/struts2-codebehind-browser-plugin/apidocs/index.html) - * [Config Browser](struts2-plugins/struts2-config-browser-plugin/apidocs/index.html) - * [Convention](struts2-plugins/struts2-convention-plugin/apidocs/index.html) - * [Dojo](struts2-plugins/struts2-dojo-plugin/apidocs/index.html) - * [DWR](struts2-plugins/struts2-dwr-plugin/apidocs/index.html) - * [Embedded JSP](struts2-plugins/struts2-embeddedjsp-plugin/apidocs/index.html) - * [GXP](struts2-plugins/struts2-gxp-plugin/apidocs/index.html) - * [JasperReports](struts2-plugins/struts2-jasperreports-plugin/apidocs/index.html) - * [JavaTemplates](struts2-plugins/struts2-javatemplates-plugin/apidocs/index.html) - * [JFreeChart](struts2-plugins/struts2-jfreechart-plugin/apidocs/index.html) - * [JavaServer Faces](struts2-plugins/struts2-jsf-plugin/apidocs/index.html) - * [JSON](struts2-plugins/struts2-json-plugin/apidocs/index.html) - * [JUnit](struts2-plugins/struts2-junit-plugin/apidocs/index.html) - * [OSGi](struts2-plugins/struts2-osgi-plugin/apidocs/index.html) - * [OVAL](struts2-plugins/struts2-oval-plugin/apidocs/index.html) - * [Pell Multipart](struts2-plugins/struts2-pell-multipart-plugin/apidocs/index.html) - * [Plexus](struts2-plugins/struts2-plexus-plugin/apidocs/index.html) - * [Portlet](struts2-plugins/struts2-portlet-plugin/apidocs/index.html) - * [Portlet Tiles](struts2-plugins/struts2-portlet-tiles-plugin/apidocs/index.html) - * [Rest](struts2-plugins/struts2-rest-plugin/apidocs/index.html) - * [Sitegraph](struts2-plugins/struts2-sitegraph-plugin/apidocs/index.html) - * [Sitemesh](struts2-plugins/struts2-sitemesh-plugin/apidocs/index.html) - * [Spring](struts2-plugins/struts2-spring-plugin/apidocs/index.html) - * [Struts 1](struts2-plugins/struts2-struts1-plugin/apidocs/index.html) - * [TestNG](struts2-plugins/struts2-testng-plugin/apidocs/index.html) - * [Tiles](struts2-plugins/struts2-tiles-plugin/apidocs/index.html) - * [Tiles 3](struts2-plugins/struts2-tiles3-plugin/apidocs/index.html) diff --git a/src/site/resources/archetype-catalog.xml b/src/site/resources/archetype-catalog.xml deleted file mode 100644 index fab8fde9a..000000000 --- a/src/site/resources/archetype-catalog.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - org.apache.struts - struts2-archetype-blank - 2.3.16.2 - http://repo1.maven.org/maven2/ - Struts 2 Archetypes - Blank - - - org.apache.struts - struts2-archetype-convention - 2.3.16.2 - http://repo1.maven.org/maven2/ - Struts 2 Archetypes - Blank Convention - - - org.apache.struts - struts2-archetype-dbportlet - 2.3.16.2 - http://repo1.maven.org/maven2/ - Struts 2 Archetypes - Database Portlet - - - org.apache.struts - struts2-archetype-plugin - 2.3.16.2 - http://repo1.maven.org/maven2/ - Struts 2 Archetypes - Plugin - - - org.apache.struts - struts2-archetype-portlet - 2.3.16.2 - http://repo1.maven.org/maven2/ - Struts 2 Archetypes - Portlet - - - org.apache.struts - struts2-archetype-starter - 2.3.16.2 - http://repo1.maven.org/maven2/ - Struts 2 Archetypes - Starter - - - diff --git a/src/site/resources/css/site.css b/src/site/resources/css/site.css deleted file mode 100644 index 356442cb1..000000000 --- a/src/site/resources/css/site.css +++ /dev/null @@ -1,71 +0,0 @@ -/* - * $Id: $ - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License - */ - -a.externalLink, a.externalLink:link, a.externalLink:visited, a.externalLink:active, a.externalLink:hover { - background:none; - padding-right:0; -} - -img.poweredBy { - margin-left:17px; -} - -.hero-unit h1 { - font-size: 40px; - margin-top: 20px; -} - -.hero-unit { - padding: 20px; -} - -.right { - text-align: right; -} - -.section img { - margin: 20px; -} - -a:visited { - color: #00438a; -} - -a.btn-primary:visited, a.btn-info:visited { - color: #fff; -} - -.right { - float: right; -} - -#bannerRight img { margin-right: 80px; } - -.breadcrumb { padding-right: 130px; } - -#bodyColumn .row { - padding:20px; -} - -.huge { - font-size: 24px; - line-height: 52px; -} \ No newline at end of file diff --git a/src/site/resources/images/download.gif b/src/site/resources/images/download.gif deleted file mode 100644 index c67d21b9b..000000000 Binary files a/src/site/resources/images/download.gif and /dev/null differ diff --git a/src/site/resources/images/help.gif b/src/site/resources/images/help.gif deleted file mode 100644 index 728f83773..000000000 Binary files a/src/site/resources/images/help.gif and /dev/null differ diff --git a/src/site/resources/images/plugins.gif b/src/site/resources/images/plugins.gif deleted file mode 100644 index b1a8c1338..000000000 Binary files a/src/site/resources/images/plugins.gif and /dev/null differ diff --git a/src/site/resources/images/struts2-arch.png b/src/site/resources/images/struts2-arch.png deleted file mode 100644 index a9979bf29..000000000 Binary files a/src/site/resources/images/struts2-arch.png and /dev/null differ diff --git a/src/site/resources/images/struts2-merger.png b/src/site/resources/images/struts2-merger.png deleted file mode 100644 index 0891bd57a..000000000 Binary files a/src/site/resources/images/struts2-merger.png and /dev/null differ diff --git a/src/site/resources/images/struts2-merger2.png b/src/site/resources/images/struts2-merger2.png deleted file mode 100644 index f11c9f039..000000000 Binary files a/src/site/resources/images/struts2-merger2.png and /dev/null differ diff --git a/src/site/resources/images/struts2.png b/src/site/resources/images/struts2.png deleted file mode 100644 index 1ca27b193..000000000 Binary files a/src/site/resources/images/struts2.png and /dev/null differ diff --git a/src/site/site.xml b/src/site/site.xml index 85fb5a38f..4b8a308a4 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -1,8 +1,6 @@ - - - - - Welcome - - - - -
- - -
-
- Struts 2 and WebWork merger -
-

- Apache Struts 2 is an elegant, extensible framework for creating enterprise-ready Java web applications. - The framework is designed to streamline the full development cycle, - from building, to deploying, to maintaining applications over time. -

- -

- Apache Struts 2 was originally known as WebWork 2. - After working independently for several years, - the WebWork and Struts communities joined forces to create Struts2. - This new version of Struts is simpler to use and - closer to how Struts was always meant to be. -

-
- -
- -
- - - - - - - - -
- Build! -
    -
  • - Easy startup - - Jumpstart new projects with our bootstrap tutorial and template application or Maven - archetype. -
  • -
  • - Improved Design - - Code clean against HTTP-independant framework interfaces. -
  • -
  • - Enhanced Tags - - Code less with stylesheet-driven form tags that provide their own markup. -
  • -
  • - Stateful Checkboxes - - Avoid special handling with smart checkboxes that know when they are toggled. -
  • -
  • - Flexible Cancel Buttons - - Go directly to a different action on cancel. -
  • -
  • - First-class AJAX support - - Add interactivity and flexibility with AJAX tags that look and feel just like standard - Struts tags. -
  • -
  • - Easy Spring integration - - Inject dependencies into Actions using Spring without glue code or red tape. (Plexus - support also available.) -
  • -
  • - Enhanced Results - - Do more with speciality results for JasperReports, JFreeChart, Action chaining, and - file downloading. -
  • -
  • - POJO forms - - No more ActionForms! Use any JavaBean to capture form input or - put properties directly on an Action class. Use both binary and String properties! -
  • -
  • - POJO Actions - - Use any class as an Action class -- even the - interface is optional! -
  • - -
-
- Deploy! -
    -
  • - Easy plugins
    - Add framework extensions by dropping in a JAR. - No manual configuration required! Bundled plugins add support for JavaServer Faces, - JasperReports, JFreeChart, Tiles, and more ... -
  • -
  • - Integrated profiling
    - Peek inside Struts2 - to find where the cycles are going! -
  • -
  • - Precise Error Reporting
    - Flip directly to the location and line of an error. -
  • - -
- -
- Maintain! -
    -
  • - Easy-to-test Actions
    - Test Struts2 - Actions directly, - without resorting to mock HTTP objects. -
  • -
  • - Intelligent Defaults
    - Skip obvious and redundant settings. Most framework configuration - elements have a default value that we can set and forget. Say it once! -
  • -
  • - Easy-to-customize controller
    - Customize the request handling - per action, if desired. Struts2 - only does what you want it to do! -
  • -
  • - Integrating Debugging
    - Research problem reports with built-in - debugging tools. -
  • -
  • - Easy-to-tweak tags
    - Customize tag markup by editing a FreeMarker - template. No need to grok the taglib API! - JSP, FreeMarker, and Velocity tags are fully supported. -
  • -
-
- - -

- To download the framework, visit - - Apache Struts Distributions. - - For more about Apache Struts 2, visit - - Getting Started. - - For more about framework extensions, visit the - - Struts 2 Plugin Registry. - - For help with migrating, visit our - - Migration Guide. - -

-
-
- -
- -

- Apache Struts 2 requires: -

- -
    -
  • Servlet API 2.4
  • -
  • JSP API 2.0
  • -
  • Java 5
  • -
- -

- For a full list of requirements, including dependencies used by optional plugins, - see Project Dependencies -

- -
- -
-

Apache Struts is distributed under the Apache License, Version 2.0

-
- -
diff --git a/xwork-core/pom.xml b/xwork-core/pom.xml index 8f7a02492..b6424145c 100644 --- a/xwork-core/pom.xml +++ b/xwork-core/pom.xml @@ -5,7 +5,7 @@ org.apache.struts struts2-parent - 2.3.21-SNAPSHOT + 2.3.22-SNAPSHOT org.apache.struts.xwork @@ -126,11 +126,11 @@ ognl
- org.ow2.asm + asm asm - org.ow2.asm + asm asm-commons diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java b/xwork-core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java index dd44b140e..1bf7ccf4c 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java @@ -421,23 +421,38 @@ public class DefaultActionInvocation implements ActionInvocation { Object methodResult; try { methodResult = ognlUtil.getValue(methodName + "()", getStack().getContext(), action); - } catch (OgnlException e) { - // hmm -- OK, try doXxx instead - try { - String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1) + "()"; - methodResult = ognlUtil.getValue(altMethodName, ActionContext.getContext().getContextMap(), action); - } catch (OgnlException e1) { - // well, give the unknown handler a shot - if (unknownHandlerManager.hasUnknownHandlers()) { - try { - methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName); - } catch (NoSuchMethodException e2) { - // throw the original one - throw e; + } catch (MethodFailedException e) { + // if reason is missing method, try find version with "do" prefix + if (e.getReason() instanceof NoSuchMethodException) { + try { + String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1) + "()"; + methodResult = ognlUtil.getValue(altMethodName, getStack().getContext(), action); + } catch (MethodFailedException e1) { + // if still method doesn't exist, try checking UnknownHandlers + if (e1.getReason() instanceof NoSuchMethodException) { + if (unknownHandlerManager.hasUnknownHandlers()) { + try { + methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName); + } catch (NoSuchMethodException e2) { + // throw the original one + throw e; + } + } else { + // throw the original one + throw e; + } + // throw the original exception as UnknownHandlers weren't able to handle invocation as well + if (methodResult == null) { + throw e; + } + } else { + // exception isn't related to missing action method, throw it + throw e1; } - } else { - throw e; } + } else { + // exception isn't related to missing action method, throw it + throw e; } } return saveResult(actionConfig, methodResult); diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/UnknownHandler.java b/xwork-core/src/main/java/com/opensymphony/xwork2/UnknownHandler.java index 8e9303a81..faabfc08c 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/UnknownHandler.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/UnknownHandler.java @@ -54,7 +54,10 @@ public interface UnknownHandler { * @param action The action object * @param methodName The method name to call * @return The result returned from invoking the action method, can return null - * @throws NoSuchMethodException If the method cannot be found (deprecated) - should return nunll instead + * @deprecated @throws NoSuchMethodException If the method cannot be found should return null instead, + * don't throw exception as other UnknownHandles won't be invoked + * 'throws NoSuchMethodException' signature will be removed with next + * major release */ public Object handleUnknownActionMethod(Object action, String methodName) throws NoSuchMethodException; } diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java b/xwork-core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java index d26d09457..8317feb1a 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java @@ -362,7 +362,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor { } boolean result = true; for (Object obj : values) { - if (isExcluded(obj.toString())) { + if (isExcluded(String.valueOf(obj))) { result = false; } } diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java b/xwork-core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java index 90b1a543e..7fa70f7c8 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java @@ -63,6 +63,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS Map overrides; transient OgnlUtil ognlUtil; transient SecurityMemberAccess securityMemberAccess; + private transient XWorkConverter converter; private boolean devMode; private boolean logMissingProperties; @@ -330,7 +331,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS Object ret = findInContext(expr); if (ret == null) { if (shouldLogMissingPropertyWarning(e)) { - LOG.warn("Could not find property [" + ((NoSuchPropertyException) e).getName() + "]"); + LOG.warn("Could not find property [#0]!", e, expr); } if (throwExceptionOnFailure) { throw new XWorkException(e); @@ -351,6 +352,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS value = getValue(expr, asType); if (value == null) { value = findInContext(expr); + return converter.convertValue(getContext(), value, asType); } } finally { context.remove(THROW_EXCEPTION_ON_FAILURE); @@ -473,4 +475,8 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS securityMemberAccess.setExcludeProperties(excludeProperties); } + @Inject + public void setXWorkConverter(final XWorkConverter converter) { + this.converter = converter; + } } diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java b/xwork-core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java index a172237f4..78882458f 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java @@ -20,7 +20,6 @@ import com.opensymphony.xwork2.util.logging.LoggerFactory; import ognl.DefaultMemberAccess; import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.Map; @@ -60,16 +59,35 @@ public class SecurityMemberAccess extends DefaultMemberAccess { return true; } - if (isPackageExcluded(target.getClass().getPackage(), member.getDeclaringClass().getPackage())) { + Class targetClass = target.getClass(); + Class memberClass = member.getDeclaringClass(); + + if (Modifier.isStatic(member.getModifiers()) && allowStaticMethodAccess) { + if (LOG.isWarnEnabled()) { + LOG.warn("Support for accessing static methods is deprecated! Please refactor your application!"); + } + if (!isClassExcluded(member.getDeclaringClass())) { + targetClass = member.getDeclaringClass(); + } + } + + if (isPackageExcluded(targetClass.getPackage(), memberClass.getPackage())) { if (LOG.isWarnEnabled()) { LOG.warn("Package of target [#0] or package of member [#1] are excluded!", target, member); } return false; } - if (isClassExcluded(target.getClass(), member.getDeclaringClass())) { + if (isClassExcluded(targetClass)) { if (LOG.isWarnEnabled()) { - LOG.warn("Target class [#0] or declaring class of member type [#1] are excluded!", target, member); + LOG.warn("Target class [#0] is excluded!", target); + } + return false; + } + + if (isClassExcluded(memberClass)) { + if (LOG.isWarnEnabled()) { + LOG.warn("Declaring class of member type [#0] is excluded!", member); } return false; } @@ -118,12 +136,12 @@ public class SecurityMemberAccess extends DefaultMemberAccess { return false; } - protected boolean isClassExcluded(Class targetClass, Class declaringClass) { - if (targetClass == Object.class || declaringClass == Object.class) { + protected boolean isClassExcluded(Class clazz) { + if (clazz == Object.class) { return true; } for (Class excludedClass : excludedClasses) { - if (targetClass.isAssignableFrom(excludedClass) || declaringClass.isAssignableFrom(excludedClass)) { + if (clazz.isAssignableFrom(excludedClass)) { return true; } } diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/util/LocalizedTextUtil.java b/xwork-core/src/main/java/com/opensymphony/xwork2/util/LocalizedTextUtil.java index b7e147f22..1e51dedfd 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/util/LocalizedTextUtil.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/util/LocalizedTextUtil.java @@ -88,6 +88,8 @@ public class LocalizedTextUtil { private static final Logger LOG = LoggerFactory.getLogger(LocalizedTextUtil.class); + private static final String TOMCAT_RESOURCE_ENTRIES_FIELD = "resourceEntries"; + private static final ConcurrentMap> classLoaderMap = new ConcurrentHashMap>(); private static boolean reloadBundles = false; @@ -839,15 +841,28 @@ public class LocalizedTextUtil { try { if ("org.apache.catalina.loader.WebappClassLoader".equals(cl.getName())) { - clearMap(cl, loader, "resourceEntries"); + clearMap(cl, loader, TOMCAT_RESOURCE_ENTRIES_FIELD); } else { if (LOG.isDebugEnabled()) { LOG.debug("class loader " + cl.getName() + " is not tomcat loader."); } } + } catch (NoSuchFieldException nsfe) { + if ("org.apache.catalina.loader.WebappClassLoaderBase".equals(cl.getSuperclass().getName())) { + if (LOG.isDebugEnabled()) { + LOG.debug("Base class #0 doesn't contain '#1' field, trying with parent!", nsfe, cl.getName(), TOMCAT_RESOURCE_ENTRIES_FIELD); + } + try { + clearMap(cl.getSuperclass(), loader, TOMCAT_RESOURCE_ENTRIES_FIELD); + } catch (Exception e) { + if (LOG.isWarnEnabled()) { + LOG.warn("Couldn't clear tomcat cache using #0", e, cl.getSuperclass().getName()); + } + } + } } catch (Exception e) { if (LOG.isWarnEnabled()) { - LOG.warn("couldn't clear tomcat cache", e); + LOG.warn("Couldn't clear tomcat cache", e, cl.getName()); } } } diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinder.java b/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinder.java index f337eb974..cf8fb9b56 100644 --- a/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinder.java +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinder.java @@ -1,57 +1,12 @@ -/* - * Copyright 2002-2003,2009 The Apache Software Foundation. - * - * Licensed 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 com.opensymphony.xwork2.util.finder; -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.FileManager; -import com.opensymphony.xwork2.FileManagerFactory; -import com.opensymphony.xwork2.XWorkException; -import com.opensymphony.xwork2.util.logging.Logger; -import com.opensymphony.xwork2.util.logging.LoggerFactory; -import org.apache.commons.lang3.StringUtils; -import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Opcodes; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.net.JarURLConnection; -import java.net.URL; -import java.net.URLDecoder; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarInputStream; /** * ClassFinder searches the classpath of the specified ClassLoaderInterface for @@ -64,104 +19,10 @@ import java.util.jar.JarInputStream; * The getClassesNotLoaded() method can be used immediately after any find* * method to get a list of classes which matched the find requirements (i.e. * contained the annotation), but were unable to be loaded. - * - * @author David Blevins - * @version $Rev$ $Date$ */ -public class ClassFinder { - private static final Logger LOG = LoggerFactory.getLogger(ClassFinder.class); +public interface ClassFinder { - private final Map> annotated = new HashMap>(); - private final Map classInfos = new LinkedHashMap(); - - private final List classesNotLoaded = new ArrayList(); - - private boolean extractBaseInterfaces; - private ClassLoaderInterface classLoaderInterface; - private FileManager fileManager; - - public ClassFinder(ClassLoaderInterface classLoaderInterface, Collection urls, boolean extractBaseInterfaces, Set protocols, Test classNameFilter) { - this.classLoaderInterface = classLoaderInterface; - this.extractBaseInterfaces = extractBaseInterfaces; - this.fileManager = ActionContext.getContext().getInstance(FileManagerFactory.class).getFileManager(); - - List classNames = new ArrayList(); - for (URL location : urls) { - try { - if (protocols.contains(location.getProtocol())) { - classNames.addAll(jar(location)); - } else if ("file".equals(location.getProtocol())) { - try { - // See if it's actually a jar - URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/"); - JarURLConnection juc = (JarURLConnection) jarUrl.openConnection(); - juc.getJarFile(); - classNames.addAll(jar(jarUrl)); - } catch (IOException e) { - classNames.addAll(file(location)); - } - } - } catch (Exception e) { - if (LOG.isErrorEnabled()) - LOG.error("Unable to read URL [#0]", e, location.toExternalForm()); - } - } - - for (String className : classNames) { - try { - if (classNameFilter.test(className)) - readClassDef(className); - } catch (Throwable e) { - if (LOG.isErrorEnabled()) - LOG.error("Unable to read class [#0]", e, className); - } - } - } - - public ClassFinder(Class... classes){ - this(Arrays.asList(classes)); - } - - public ClassFinder(List classes){ - this.classLoaderInterface = null; - List infos = new ArrayList(); - List packages = new ArrayList(); - for (Class clazz : classes) { - - Package aPackage = clazz.getPackage(); - if (aPackage != null && !packages.contains(aPackage)){ - infos.add(new PackageInfo(aPackage)); - packages.add(aPackage); - } - - ClassInfo classInfo = new ClassInfo(clazz); - infos.add(classInfo); - classInfos.put(classInfo.getName(), classInfo); - for (Method method : clazz.getDeclaredMethods()) { - infos.add(new MethodInfo(classInfo, method)); - } - - for (Constructor constructor : clazz.getConstructors()) { - infos.add(new MethodInfo(classInfo, constructor)); - } - - for (Field field : clazz.getDeclaredFields()) { - infos.add(new FieldInfo(classInfo, field)); - } - } - - for (Info info : infos) { - for (AnnotationInfo annotation : info.getAnnotations()) { - List annotationInfos = getAnnotationInfos(annotation.getName()); - annotationInfos.add(info); - } - } - } - - public boolean isAnnotationPresent(Class annotation) { - List infos = annotated.get(annotation.getName()); - return infos != null && !infos.isEmpty(); - } + boolean isAnnotationPresent(Class annotation); /** * Returns a list of classes that could not be loaded in last invoked findAnnotated* method. @@ -175,278 +36,57 @@ public class ClassFinder { * This method is not thread safe. * @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call. */ - public List getClassesNotLoaded() { - return Collections.unmodifiableList(classesNotLoaded); + List getClassesNotLoaded(); + + List findAnnotatedPackages(Class annotation); + + List findAnnotatedClasses(Class annotation); + + List findAnnotatedMethods(Class annotation); + + List findAnnotatedConstructors(Class annotation); + + List findAnnotatedFields(Class annotation); + + List findClassesInPackage(String packageName, boolean recursive); + + List findClasses(Test test); + + List findClasses(); + + ClassLoaderInterface getClassLoaderInterface(); + + public static interface Info { + String getName(); + + List getAnnotations(); } - public List findAnnotatedPackages(Class annotation) { - classesNotLoaded.clear(); - List packages = new ArrayList(); - List infos = getAnnotationInfos(annotation.getName()); - for (Info info : infos) { - if (info instanceof PackageInfo) { - PackageInfo packageInfo = (PackageInfo) info; - try { - Package pkg = packageInfo.get(); - // double check via proper reflection - if (pkg.isAnnotationPresent(annotation)) { - packages.add(pkg); - } - } catch (ClassNotFoundException e) { - classesNotLoaded.add(packageInfo.getName()); - } - } - } - return packages; - } + public class AnnotationInfo extends Annotatable implements Info { + private final String name; - public List findAnnotatedClasses(Class annotation) { - classesNotLoaded.clear(); - List classes = new ArrayList(); - List infos = getAnnotationInfos(annotation.getName()); - for (Info info : infos) { - if (info instanceof ClassInfo) { - ClassInfo classInfo = (ClassInfo) info; - try { - Class clazz = classInfo.get(); - // double check via proper reflection - if (clazz.isAnnotationPresent(annotation)) { - classes.add(clazz); - } - } catch (Throwable e) { - if (LOG.isErrorEnabled()) - LOG.error("Error loading class [#0]", e, classInfo.getName()); - classesNotLoaded.add(classInfo.getName()); - } - } - } - return classes; - } - - public List findAnnotatedMethods(Class annotation) { - classesNotLoaded.clear(); - List seen = new ArrayList(); - List methods = new ArrayList(); - List infos = getAnnotationInfos(annotation.getName()); - for (Info info : infos) { - if (info instanceof MethodInfo && !"".equals(info.getName())) { - MethodInfo methodInfo = (MethodInfo) info; - ClassInfo classInfo = methodInfo.getDeclaringClass(); - - if (seen.contains(classInfo)) continue; - - seen.add(classInfo); - - try { - Class clazz = classInfo.get(); - for (Method method : clazz.getDeclaredMethods()) { - if (method.isAnnotationPresent(annotation)) { - methods.add(method); - } - } - } catch (Throwable e) { - if (LOG.isErrorEnabled()) - LOG.error("Error loading class [#0]", e, classInfo.getName()); - classesNotLoaded.add(classInfo.getName()); - } - } - } - return methods; - } - - public List findAnnotatedConstructors(Class annotation) { - classesNotLoaded.clear(); - List seen = new ArrayList(); - List constructors = new ArrayList(); - List infos = getAnnotationInfos(annotation.getName()); - for (Info info : infos) { - if (info instanceof MethodInfo && "".equals(info.getName())) { - MethodInfo methodInfo = (MethodInfo) info; - ClassInfo classInfo = methodInfo.getDeclaringClass(); - - if (seen.contains(classInfo)) continue; - - seen.add(classInfo); - - try { - Class clazz = classInfo.get(); - for (Constructor constructor : clazz.getConstructors()) { - if (constructor.isAnnotationPresent(annotation)) { - constructors.add(constructor); - } - } - } catch (Throwable e) { - if (LOG.isErrorEnabled()) - LOG.error("Error loading class [#0]", e, classInfo.getName()); - classesNotLoaded.add(classInfo.getName()); - } - } - } - return constructors; - } - - public List findAnnotatedFields(Class annotation) { - classesNotLoaded.clear(); - List seen = new ArrayList(); - List fields = new ArrayList(); - List infos = getAnnotationInfos(annotation.getName()); - for (Info info : infos) { - if (info instanceof FieldInfo) { - FieldInfo fieldInfo = (FieldInfo) info; - ClassInfo classInfo = fieldInfo.getDeclaringClass(); - - if (seen.contains(classInfo)) continue; - - seen.add(classInfo); - - try { - Class clazz = classInfo.get(); - for (Field field : clazz.getDeclaredFields()) { - if (field.isAnnotationPresent(annotation)) { - fields.add(field); - } - } - } catch (Throwable e) { - if (LOG.isErrorEnabled()) - LOG.error("Error loading class [#0]", e, classInfo.getName()); - classesNotLoaded.add(classInfo.getName()); - } - } - } - return fields; - } - - public List findClassesInPackage(String packageName, boolean recursive) { - classesNotLoaded.clear(); - List classes = new ArrayList(); - for (ClassInfo classInfo : classInfos.values()) { - try { - if (recursive && classInfo.getPackageName().startsWith(packageName)){ - classes.add(classInfo.get()); - } else if (classInfo.getPackageName().equals(packageName)){ - classes.add(classInfo.get()); - } - } catch (Throwable e) { - if (LOG.isErrorEnabled()) - LOG.error("Error loading class [#0]", e, classInfo.getName()); - classesNotLoaded.add(classInfo.getName()); - } - } - return classes; - } - - public List findClasses(Test test) { - classesNotLoaded.clear(); - List classes = new ArrayList(); - for (ClassInfo classInfo : classInfos.values()) { - try { - if (test.test(classInfo)) { - classes.add(classInfo.get()); - } - } catch (Throwable e) { - if (LOG.isErrorEnabled()) - LOG.error("Error loading class [#0]", e, classInfo.getName()); - classesNotLoaded.add(classInfo.getName()); - } - } - return classes; - } - - public List findClasses() { - classesNotLoaded.clear(); - List classes = new ArrayList(); - for (ClassInfo classInfo : classInfos.values()) { - try { - classes.add(classInfo.get()); - } catch (Throwable e) { - if (LOG.isErrorEnabled()) - LOG.error("Error loading class [#0]", e, classInfo.getName()); - classesNotLoaded.add(classInfo.getName()); - } - } - return classes; - } - - private static List getURLs(ClassLoaderInterface classLoader, String[] dirNames) { - List urls = new ArrayList(); - for (String dirName : dirNames) { - try { - Enumeration classLoaderURLs = classLoader.getResources(dirName); - while (classLoaderURLs.hasMoreElements()) { - URL url = classLoaderURLs.nextElement(); - urls.add(url); - } - } catch (IOException ioe) { - if (LOG.isErrorEnabled()) - LOG.error("Could not read driectory [#0]", ioe, dirName); - } + public AnnotationInfo(Annotation annotation){ + this(annotation.getClass().getName()); } - return urls; - } - - private List file(URL location) { - List classNames = new ArrayList(); - File dir = new File(URLDecoder.decode(location.getPath())); - if ("META-INF".equals(dir.getName())) { - dir = dir.getParentFile(); // Scrape "META-INF" off - } - if (dir.isDirectory()) { - scanDir(dir, classNames, ""); - } - return classNames; - } - - private void scanDir(File dir, List classNames, String packageName) { - File[] files = dir.listFiles(); - for (File file : files) { - if (file.isDirectory()) { - scanDir(file, classNames, packageName + file.getName() + "."); - } else if (file.getName().endsWith(".class")) { - String name = file.getName(); - name = name.replaceFirst(".class$", ""); - // Classes packaged in an exploded .war (e.g. in a VFS file system) should not - // have WEB-INF.classes in their package name. - classNames.add(StringUtils.removeStart(packageName, "WEB-INF.classes.") + name); - } - } - } - - private List jar(URL location) throws IOException { - URL url = fileManager.normalizeToFileProtocol(location); - if (url != null) { - InputStream in = url.openStream(); - try { - JarInputStream jarStream = new JarInputStream(in); - return jar(jarStream); - } finally { - in.close(); - } - } else if (LOG.isDebugEnabled()) - LOG.debug("Unable to read [#0]", location.toExternalForm()); - - return Collections.emptyList(); - } - - private List jar(JarInputStream jarStream) throws IOException { - List classNames = new ArrayList(); - - JarEntry entry; - while ((entry = jarStream.getNextJarEntry()) != null) { - if (entry.isDirectory() || !entry.getName().endsWith(".class")) { - continue; - } - String className = entry.getName(); - className = className.replaceFirst(".class$", ""); - - //war files are treated as .jar files, so takeout WEB-INF/classes - className = StringUtils.removeStart(className, "WEB-INF/classes/"); - - className = className.replace('/', '.'); - classNames.add(className); + public AnnotationInfo(Class annotation) { + this.name = annotation.getName().intern(); } - return classNames; + public AnnotationInfo(String name) { + name = name.replaceAll("^L|;$", ""); + name = name.replace('/', '.'); + this.name = name.intern(); + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } } public class Annotatable { @@ -467,12 +107,6 @@ public class ClassFinder { } - public static interface Info { - String getName(); - - List getAnnotations(); - } - public class PackageInfo extends Annotatable implements Info { private final String name; private final ClassInfo info; @@ -485,8 +119,8 @@ public class ClassFinder { this.info = null; } - public PackageInfo(String name) { - info = new ClassInfo(name, null); + public PackageInfo(String name, ClassFinder classFinder) { + info = new ClassInfo(name, null, classFinder); this.name = name; this.pkg = null; } @@ -509,19 +143,22 @@ public class ClassFinder { private final List superInterfaces = new ArrayList(); private final List fields = new ArrayList(); private Class clazz; + private ClassFinder classFinder; private ClassNotFoundException notFound; - public ClassInfo(Class clazz) { + public ClassInfo(Class clazz, ClassFinder classFinder) { super(clazz); this.clazz = clazz; + this.classFinder = classFinder; this.name = clazz.getName(); Class superclass = clazz.getSuperclass(); this.superType = superclass != null ? superclass.getName(): null; } - public ClassInfo(String name, String superType) { + public ClassInfo(String name, String superType, ClassFinder classFinder) { this.name = name; this.superType = superType; + this.classFinder = classFinder; } public String getPackageName(){ @@ -560,10 +197,10 @@ public class ClassFinder { if (clazz != null) return clazz; if (notFound != null) throw notFound; try { - this.clazz = classLoaderInterface.loadClass(name); + this.clazz = classFinder.getClassLoaderInterface().loadClass(name); return clazz; } catch (ClassNotFoundException notFound) { - classesNotLoaded.add(name); + classFinder.getClassesNotLoaded().add(name); this.notFound = notFound; throw notFound; } @@ -669,179 +306,4 @@ public class ClassFinder { } } - public class AnnotationInfo extends Annotatable implements Info { - private final String name; - - public AnnotationInfo(Annotation annotation){ - this(annotation.getClass().getName()); - } - - public AnnotationInfo(Class annotation) { - this.name = annotation.getName().intern(); - } - - public AnnotationInfo(String name) { - name = name.replaceAll("^L|;$", ""); - name = name.replace('/', '.'); - this.name = name.intern(); - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return name; - } - } - - private List getAnnotationInfos(String name) { - List infos = annotated.get(name); - if (infos == null) { - infos = new ArrayList(); - annotated.put(name, infos); - } - return infos; - } - - private void readClassDef(String className) { - if (!className.endsWith(".class")) { - className = className.replace('.', '/') + ".class"; - } - try { - URL resource = classLoaderInterface.getResource(className); - if (resource != null) { - InputStream in = resource.openStream(); - try { - ClassReader classReader = new ClassReader(in); - classReader.accept(new InfoBuildingClassVisitor(), ClassReader.SKIP_DEBUG); - } finally { - in.close(); - } - } else { - throw new XWorkException("Could not load " + className); - } - } catch (IOException e) { - throw new XWorkException("Could not load " + className, e); - } - - } - - public class InfoBuildingClassVisitor extends ClassVisitor { - private Info info; - - public InfoBuildingClassVisitor() { - super(Opcodes.ASM5); - } - - public InfoBuildingClassVisitor(Info info) { - this(); - this.info = info; - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - if (name.endsWith("package-info")) { - info = new PackageInfo(javaName(name)); - } else { - ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName)); - - for (String interfce : interfaces) { - classInfo.getInterfaces().add(javaName(interfce)); - } - info = classInfo; - classInfos.put(classInfo.getName(), classInfo); - - if (extractBaseInterfaces) - extractSuperInterfaces(classInfo); - } - } - - private void extractSuperInterfaces(ClassInfo classInfo) { - String superType = classInfo.getSuperType(); - - if (superType != null) { - ClassInfo base = classInfos.get(superType); - - if (base == null) { - //try to load base - String resource = superType.replace('.', '/') + ".class"; - readClassDef(resource); - base = classInfos.get(superType); - } - - if (base != null) { - List interfaces = classInfo.getSuperInterfaces(); - interfaces.addAll(base.getSuperInterfaces()); - interfaces.addAll(base.getInterfaces()); - } - } - } - - private String javaName(String name) { - return (name == null)? null:name.replace('/', '.'); - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - AnnotationInfo annotationInfo = new AnnotationInfo(desc); - info.getAnnotations().add(annotationInfo); - getAnnotationInfos(annotationInfo.getName()).add(info); - return null; - } - - @Override - public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { - ClassInfo classInfo = ((ClassInfo) info); - FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc); - classInfo.getFields().add(fieldInfo); - return null; - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - ClassInfo classInfo = ((ClassInfo) info); - MethodInfo methodInfo = new MethodInfo(classInfo, name, desc); - classInfo.getMethods().add(methodInfo); - return new InfoBuildingMethodVisitor(methodInfo); - } - } - - public class InfoBuildingMethodVisitor extends MethodVisitor { - private Info info; - - public InfoBuildingMethodVisitor() { - super(Opcodes.ASM5); - } - - public InfoBuildingMethodVisitor(Info info) { - this(); - this.info = info; - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - AnnotationInfo annotationInfo = new AnnotationInfo(desc); - info.getAnnotations().add(annotationInfo); - getAnnotationInfos(annotationInfo.getName()).add(info); - return null; - } - - @Override - public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) { - MethodInfo methodInfo = ((MethodInfo) info); - List annotationInfos = methodInfo.getParameterAnnotations(param); - AnnotationInfo annotationInfo = new AnnotationInfo(desc); - annotationInfos.add(annotationInfo); - return null; - } - } - - private static final class DefaultClassnameFilterImpl implements Test { - public boolean test(String className) { - return true; - } - } } - diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinderFactory.java b/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinderFactory.java new file mode 100644 index 000000000..7998c3cfd --- /dev/null +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinderFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-2003,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.finder; + +import java.net.URL; +import java.util.Collection; +import java.util.Set; + +/** + * Allows create different ClassFinders which should help support different Java versions + */ +public interface ClassFinderFactory { + + ClassFinder buildClassFinder(ClassLoaderInterface classLoaderInterface, Collection urls, boolean extractBaseInterfaces, Set protocols, Test classNameFilter); + +} diff --git a/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/DefaultClassFinder.java b/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/DefaultClassFinder.java new file mode 100644 index 000000000..a80849bdd --- /dev/null +++ b/xwork-core/src/main/java/com/opensymphony/xwork2/util/finder/DefaultClassFinder.java @@ -0,0 +1,582 @@ +/* + * Copyright 2002-2003,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.finder; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.FileManager; +import com.opensymphony.xwork2.FileManagerFactory; +import com.opensymphony.xwork2.XWorkException; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.commons.lang3.StringUtils; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.commons.EmptyVisitor; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +public class DefaultClassFinder implements ClassFinder { + private static final Logger LOG = LoggerFactory.getLogger(DefaultClassFinder.class); + + private final Map> annotated = new HashMap>(); + private final Map classInfos = new LinkedHashMap(); + + private final List classesNotLoaded = new ArrayList(); + + private boolean extractBaseInterfaces; + private ClassLoaderInterface classLoaderInterface; + private FileManager fileManager; + + public DefaultClassFinder(ClassLoaderInterface classLoaderInterface, Collection urls, boolean extractBaseInterfaces, Set protocols, Test classNameFilter) { + this.classLoaderInterface = classLoaderInterface; + this.extractBaseInterfaces = extractBaseInterfaces; + this.fileManager = ActionContext.getContext().getInstance(FileManagerFactory.class).getFileManager(); + + List classNames = new ArrayList(); + for (URL location : urls) { + try { + if (protocols.contains(location.getProtocol())) { + classNames.addAll(jar(location)); + } else if ("file".equals(location.getProtocol())) { + try { + // See if it's actually a jar + URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/"); + JarURLConnection juc = (JarURLConnection) jarUrl.openConnection(); + juc.getJarFile(); + classNames.addAll(jar(jarUrl)); + } catch (IOException e) { + classNames.addAll(file(location)); + } + } + } catch (Exception e) { + if (LOG.isErrorEnabled()) + LOG.error("Unable to read URL [#0]", e, location.toExternalForm()); + } + } + + for (String className : classNames) { + try { + if (classNameFilter.test(className)) + readClassDef(className); + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Unable to read class [#0]", e, className); + } + } + } + + public DefaultClassFinder(Class... classes){ + this(Arrays.asList(classes)); + } + + public DefaultClassFinder(List classes){ + this.classLoaderInterface = null; + List infos = new ArrayList(); + List packages = new ArrayList(); + for (Class clazz : classes) { + + Package aPackage = clazz.getPackage(); + if (aPackage != null && !packages.contains(aPackage)){ + infos.add(new PackageInfo(aPackage)); + packages.add(aPackage); + } + + ClassInfo classInfo = new ClassInfo(clazz, this); + infos.add(classInfo); + classInfos.put(classInfo.getName(), classInfo); + for (Method method : clazz.getDeclaredMethods()) { + infos.add(new MethodInfo(classInfo, method)); + } + + for (Constructor constructor : clazz.getConstructors()) { + infos.add(new MethodInfo(classInfo, constructor)); + } + + for (Field field : clazz.getDeclaredFields()) { + infos.add(new FieldInfo(classInfo, field)); + } + } + + for (Info info : infos) { + for (AnnotationInfo annotation : info.getAnnotations()) { + List annotationInfos = getAnnotationInfos(annotation.getName()); + annotationInfos.add(info); + } + } + } + + public ClassLoaderInterface getClassLoaderInterface() { + return classLoaderInterface; + } + + public boolean isAnnotationPresent(Class annotation) { + List infos = annotated.get(annotation.getName()); + return infos != null && !infos.isEmpty(); + } + + public List getClassesNotLoaded() { + return Collections.unmodifiableList(classesNotLoaded); + } + + public List findAnnotatedPackages(Class annotation) { + classesNotLoaded.clear(); + List packages = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof PackageInfo) { + PackageInfo packageInfo = (PackageInfo) info; + try { + Package pkg = packageInfo.get(); + // double check via proper reflection + if (pkg.isAnnotationPresent(annotation)) { + packages.add(pkg); + } + } catch (ClassNotFoundException e) { + classesNotLoaded.add(packageInfo.getName()); + } + } + } + return packages; + } + + public List findAnnotatedClasses(Class annotation) { + classesNotLoaded.clear(); + List classes = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof ClassInfo) { + ClassInfo classInfo = (ClassInfo) info; + try { + Class clazz = classInfo.get(); + // double check via proper reflection + if (clazz.isAnnotationPresent(annotation)) { + classes.add(clazz); + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + } + return classes; + } + + public List findAnnotatedMethods(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List methods = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof MethodInfo && !"".equals(info.getName())) { + MethodInfo methodInfo = (MethodInfo) info; + ClassInfo classInfo = methodInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(annotation)) { + methods.add(method); + } + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + } + return methods; + } + + public List findAnnotatedConstructors(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List constructors = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof MethodInfo && "".equals(info.getName())) { + MethodInfo methodInfo = (MethodInfo) info; + ClassInfo classInfo = methodInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Constructor constructor : clazz.getConstructors()) { + if (constructor.isAnnotationPresent(annotation)) { + constructors.add(constructor); + } + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + } + return constructors; + } + + public List findAnnotatedFields(Class annotation) { + classesNotLoaded.clear(); + List seen = new ArrayList(); + List fields = new ArrayList(); + List infos = getAnnotationInfos(annotation.getName()); + for (Info info : infos) { + if (info instanceof FieldInfo) { + FieldInfo fieldInfo = (FieldInfo) info; + ClassInfo classInfo = fieldInfo.getDeclaringClass(); + + if (seen.contains(classInfo)) continue; + + seen.add(classInfo); + + try { + Class clazz = classInfo.get(); + for (Field field : clazz.getDeclaredFields()) { + if (field.isAnnotationPresent(annotation)) { + fields.add(field); + } + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + } + return fields; + } + + public List findClassesInPackage(String packageName, boolean recursive) { + classesNotLoaded.clear(); + List classes = new ArrayList(); + for (ClassInfo classInfo : classInfos.values()) { + try { + if (recursive && classInfo.getPackageName().startsWith(packageName)){ + classes.add(classInfo.get()); + } else if (classInfo.getPackageName().equals(packageName)){ + classes.add(classInfo.get()); + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + return classes; + } + + public List findClasses(Test test) { + classesNotLoaded.clear(); + List classes = new ArrayList(); + for (ClassInfo classInfo : classInfos.values()) { + try { + if (test.test(classInfo)) { + classes.add(classInfo.get()); + } + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + return classes; + } + + public List findClasses() { + classesNotLoaded.clear(); + List classes = new ArrayList(); + for (ClassInfo classInfo : classInfos.values()) { + try { + classes.add(classInfo.get()); + } catch (Throwable e) { + if (LOG.isErrorEnabled()) + LOG.error("Error loading class [#0]", e, classInfo.getName()); + classesNotLoaded.add(classInfo.getName()); + } + } + return classes; + } + + private static List getURLs(ClassLoaderInterface classLoader, String[] dirNames) { + List urls = new ArrayList(); + for (String dirName : dirNames) { + try { + Enumeration classLoaderURLs = classLoader.getResources(dirName); + while (classLoaderURLs.hasMoreElements()) { + URL url = classLoaderURLs.nextElement(); + urls.add(url); + } + } catch (IOException ioe) { + if (LOG.isErrorEnabled()) + LOG.error("Could not read driectory [#0]", ioe, dirName); + } + } + + return urls; + } + + private List file(URL location) { + List classNames = new ArrayList(); + File dir = new File(URLDecoder.decode(location.getPath())); + if ("META-INF".equals(dir.getName())) { + dir = dir.getParentFile(); // Scrape "META-INF" off + } + if (dir.isDirectory()) { + scanDir(dir, classNames, ""); + } + return classNames; + } + + private void scanDir(File dir, List classNames, String packageName) { + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + scanDir(file, classNames, packageName + file.getName() + "."); + } else if (file.getName().endsWith(".class")) { + String name = file.getName(); + name = name.replaceFirst(".class$", ""); + // Classes packaged in an exploded .war (e.g. in a VFS file system) should not + // have WEB-INF.classes in their package name. + classNames.add(StringUtils.removeStart(packageName, "WEB-INF.classes.") + name); + } + } + } + + private List jar(URL location) throws IOException { + URL url = fileManager.normalizeToFileProtocol(location); + if (url != null) { + InputStream in = url.openStream(); + try { + JarInputStream jarStream = new JarInputStream(in); + return jar(jarStream); + } finally { + in.close(); + } + } else if (LOG.isDebugEnabled()) + LOG.debug("Unable to read [#0]", location.toExternalForm()); + + return Collections.emptyList(); + } + + private List jar(JarInputStream jarStream) throws IOException { + List classNames = new ArrayList(); + + JarEntry entry; + while ((entry = jarStream.getNextJarEntry()) != null) { + if (entry.isDirectory() || !entry.getName().endsWith(".class")) { + continue; + } + String className = entry.getName(); + className = className.replaceFirst(".class$", ""); + + //war files are treated as .jar files, so takeout WEB-INF/classes + className = StringUtils.removeStart(className, "WEB-INF/classes/"); + + className = className.replace('/', '.'); + classNames.add(className); + } + + return classNames; + } + + public class PackageInfo extends Annotatable implements Info { + private final String name; + private final ClassInfo info; + private final Package pkg; + + public PackageInfo(Package pkg){ + super(pkg); + this.pkg = pkg; + this.name = pkg.getName(); + this.info = null; + } + + public PackageInfo(String name, ClassFinder classFinder) { + info = new ClassInfo(name, null, classFinder); + this.name = name; + this.pkg = null; + } + + public String getName() { + return name; + } + + public Package get() throws ClassNotFoundException { + return (pkg != null)?pkg:info.get().getPackage(); + } + } + + private List getAnnotationInfos(String name) { + List infos = annotated.get(name); + if (infos == null) { + infos = new ArrayList(); + annotated.put(name, infos); + } + return infos; + } + + private void readClassDef(String className) { + if (!className.endsWith(".class")) { + className = className.replace('.', '/') + ".class"; + } + try { + URL resource = classLoaderInterface.getResource(className); + if (resource != null) { + InputStream in = resource.openStream(); + try { + ClassReader classReader = new ClassReader(in); + classReader.accept(new InfoBuildingVisitor(this), ClassReader.SKIP_DEBUG); + } finally { + in.close(); + } + } else { + throw new XWorkException("Could not load " + className); + } + } catch (IOException e) { + throw new XWorkException("Could not load " + className, e); + } + + } + + public class InfoBuildingVisitor extends EmptyVisitor { + private Info info; + private ClassFinder classFinder; + + public InfoBuildingVisitor(ClassFinder classFinder) { + this.classFinder = classFinder; + } + + public InfoBuildingVisitor(Info info, ClassFinder classFinder) { + this.info = info; + this.classFinder = classFinder; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if (name.endsWith("package-info")) { + info = new PackageInfo(javaName(name), classFinder); + } else { + ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName), classFinder); + + for (String interfce : interfaces) { + classInfo.getInterfaces().add(javaName(interfce)); + } + info = classInfo; + classInfos.put(classInfo.getName(), classInfo); + + if (extractBaseInterfaces) + extractSuperInterfaces(classInfo); + } + } + + private void extractSuperInterfaces(ClassInfo classInfo) { + String superType = classInfo.getSuperType(); + + if (superType != null) { + ClassInfo base = classInfos.get(superType); + + if (base == null) { + //try to load base + String resource = superType.replace('.', '/') + ".class"; + readClassDef(resource); + base = classInfos.get(superType); + } + + if (base != null) { + List interfaces = classInfo.getSuperInterfaces(); + interfaces.addAll(base.getSuperInterfaces()); + interfaces.addAll(base.getInterfaces()); + } + } + } + + private String javaName(String name) { + return (name == null)? null:name.replace('/', '.'); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + AnnotationInfo annotationInfo = new AnnotationInfo(desc); + info.getAnnotations().add(annotationInfo); + getAnnotationInfos(annotationInfo.getName()).add(info); + return new InfoBuildingVisitor(annotationInfo, classFinder); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + ClassInfo classInfo = ((ClassInfo) info); + FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc); + classInfo.getFields().add(fieldInfo); + return new InfoBuildingVisitor(fieldInfo, classFinder); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + ClassInfo classInfo = ((ClassInfo) info); + MethodInfo methodInfo = new MethodInfo(classInfo, name, desc); + classInfo.getMethods().add(methodInfo); + return new InfoBuildingVisitor(methodInfo, classFinder); + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) { + MethodInfo methodInfo = ((MethodInfo) info); + List annotationInfos = methodInfo.getParameterAnnotations(param); + AnnotationInfo annotationInfo = new AnnotationInfo(desc); + annotationInfos.add(annotationInfo); + return new InfoBuildingVisitor(annotationInfo, classFinder); + } + } + + private static final class DefaultClassnameFilterImpl implements Test { + public boolean test(String className) { + return true; + } + } +} + diff --git a/xwork-core/src/site/site.xml b/xwork-core/src/site/site.xml new file mode 100644 index 000000000..07a667ec7 --- /dev/null +++ b/xwork-core/src/site/site.xml @@ -0,0 +1,57 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 1.3.1 + + + Apache Software Foundation + http://www.apache.org/images/asf-logo.gif + http://www.apache.org/ + + + Apache Struts + http://struts.apache.org/img/struts-logo.svg + http://struts.apache.org/ + + + + + + + + + + + + +
+
+ Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts + project logos are trademarks of The Apache Software Foundation. +
+
+ + + diff --git a/xwork-core/src/test/java/com/opensymphony/xwork2/DefaultActionInvocationTest.java b/xwork-core/src/test/java/com/opensymphony/xwork2/DefaultActionInvocationTest.java index e0aa8ba7e..42915381c 100644 --- a/xwork-core/src/test/java/com/opensymphony/xwork2/DefaultActionInvocationTest.java +++ b/xwork-core/src/test/java/com/opensymphony/xwork2/DefaultActionInvocationTest.java @@ -1,14 +1,12 @@ package com.opensymphony.xwork2; -import com.mockobjects.dynamic.Mock; import com.opensymphony.xwork2.config.entities.InterceptorMapping; import com.opensymphony.xwork2.mock.MockActionProxy; import com.opensymphony.xwork2.mock.MockContainer; import com.opensymphony.xwork2.mock.MockInterceptor; import com.opensymphony.xwork2.ognl.OgnlUtil; +import com.opensymphony.xwork2.util.ValueStack; import com.opensymphony.xwork2.util.ValueStackFactory; -import org.easymock.EasyMock; -import org.easymock.IMocksControl; import java.util.ArrayList; import java.util.HashMap; @@ -81,6 +79,267 @@ public class DefaultActionInvocationTest extends XWorkTestCase { assertEquals(mockContainer, deserializable.container); } + public void testInvokingExistingExecuteMethod() throws Exception { + // given + DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap(), false) { + public ValueStack getStack() { + return new StubValueStack(); + } + }; + + SimpleAction action = new SimpleAction() { + @Override + public String execute() throws Exception { + return SUCCESS; + } + }; + MockActionProxy proxy = new MockActionProxy(); + proxy.setMethod("execute"); + + dai.proxy = proxy; + dai.ognlUtil = new OgnlUtil(); + + // when + String result = dai.invokeAction(action, null); + + // then + assertEquals("success", result); + } + + public void testInvokingExistingDoInputMethod() throws Exception { + // given + DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap(), false) { + public ValueStack getStack() { + return new StubValueStack(); + } + }; + + SimpleAction action = new SimpleAction(); + MockActionProxy proxy = new MockActionProxy(); + proxy.setMethod("with"); + + dai.proxy = proxy; + dai.ognlUtil = new OgnlUtil(); + + // when + String result = dai.invokeAction(action, null); + + // then + assertEquals("with", result); + } + + public void testInvokingMissingMethod() throws Exception { + // given + DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap(), false) { + public ValueStack getStack() { + return new StubValueStack(); + } + }; + + SimpleAction action = new SimpleAction() { + @Override + public String execute() throws Exception { + return ERROR; + } + }; + MockActionProxy proxy = new MockActionProxy(); + proxy.setMethod("notExists"); + + UnknownHandlerManager uhm = new DefaultUnknownHandlerManager() { + @Override + public boolean hasUnknownHandlers() { + return false; + } + }; + + dai.proxy = proxy; + dai.ognlUtil = new OgnlUtil(); + dai.unknownHandlerManager = uhm; + + // when + Throwable actual = null; + try { + dai.invokeAction(action, null); + } catch (Exception e) { + actual = e; + } + + // then + assertNotNull(actual); + assertTrue(actual instanceof NoSuchMethodException); + } + + public void testInvokingExistingMethodThatThrowsException() throws Exception { + // given + DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap(), false) { + public ValueStack getStack() { + return new StubValueStack(); + } + }; + + SimpleAction action = new SimpleAction() { + @Override + public String execute() throws Exception { + throw new IllegalArgumentException(); + } + }; + MockActionProxy proxy = new MockActionProxy(); + proxy.setMethod("execute"); + + dai.proxy = proxy; + dai.ognlUtil = new OgnlUtil(); + + // when + Throwable actual = null; + try { + dai.invokeAction(action, null); + } catch (Exception e) { + actual = e; + } + + // then + assertNotNull(actual); + assertTrue(actual instanceof IllegalArgumentException); + } + + public void testInvokingExistingDoMethodThatThrowsException() throws Exception { + // given + DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap(), false) { + public ValueStack getStack() { + return new StubValueStack(); + } + }; + + UnknownHandlerManager uhm = new DefaultUnknownHandlerManager() { + @Override + public boolean hasUnknownHandlers() { + return false; + } + }; + + SimpleAction action = new SimpleAction() { + @Override + public String doWith() throws Exception { + throw new IllegalArgumentException(); + } + }; + MockActionProxy proxy = new MockActionProxy(); + proxy.setMethod("with"); + + dai.proxy = proxy; + dai.ognlUtil = new OgnlUtil(); + dai.unknownHandlerManager = uhm; + + // when + // when + Throwable actual = null; + try { + dai.invokeAction(action, null); + } catch (Exception e) { + actual = e; + } + + // then + assertNotNull(actual); + assertTrue(actual instanceof IllegalArgumentException); + } + + @Deprecated + public void testUnknownHandlerManagerThatThrowsException() throws Exception { + // given + DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap(), false) { + public ValueStack getStack() { + return new StubValueStack(); + } + }; + + UnknownHandlerManager uhm = new DefaultUnknownHandlerManager() { + @Override + public boolean hasUnknownHandlers() { + return true; + } + + @Override + public Object handleUnknownMethod(Object action, String methodName) throws NoSuchMethodException { + throw new NoSuchMethodException(); + } + }; + + SimpleAction action = new SimpleAction() { + @Override + public String doWith() throws Exception { + throw new IllegalArgumentException(); + } + }; + MockActionProxy proxy = new MockActionProxy(); + proxy.setMethod("notExists"); + + dai.proxy = proxy; + dai.ognlUtil = new OgnlUtil(); + dai.unknownHandlerManager = uhm; + + // when + // when + Throwable actual = null; + try { + dai.invokeAction(action, null); + } catch (Exception e) { + actual = e; + } + + // then + assertNotNull(actual); + assertTrue(actual instanceof NoSuchMethodException); + } + + @Deprecated + public void testUnknownHandlerManagerThatReturnsNull() throws Exception { + // given + DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap(), false) { + public ValueStack getStack() { + return new StubValueStack(); + } + }; + + UnknownHandlerManager uhm = new DefaultUnknownHandlerManager() { + @Override + public boolean hasUnknownHandlers() { + return true; + } + + @Override + public Object handleUnknownMethod(Object action, String methodName) throws NoSuchMethodException { + return null; + } + }; + + SimpleAction action = new SimpleAction() { + @Override + public String doWith() throws Exception { + throw new IllegalArgumentException(); + } + }; + MockActionProxy proxy = new MockActionProxy(); + proxy.setMethod("notExists"); + + dai.proxy = proxy; + dai.ognlUtil = new OgnlUtil(); + dai.unknownHandlerManager = uhm; + + // when + // when + Throwable actual = null; + try { + dai.invokeAction(action, null); + } catch (Exception e) { + actual = e; + } + + // then + assertNotNull(actual); + assertTrue(actual instanceof NoSuchMethodException); + } + } class DefaultActionInvocationTester extends DefaultActionInvocation { diff --git a/xwork-core/src/test/java/com/opensymphony/xwork2/SimpleAction.java b/xwork-core/src/test/java/com/opensymphony/xwork2/SimpleAction.java index d22d231c1..6a180a1e8 100644 --- a/xwork-core/src/test/java/com/opensymphony/xwork2/SimpleAction.java +++ b/xwork-core/src/test/java/com/opensymphony/xwork2/SimpleAction.java @@ -249,6 +249,9 @@ public class SimpleAction extends ActionSupport { return INPUT; } + public String doWith() throws Exception { + return "with"; + } public long getLongFoo() { return longFoo; diff --git a/xwork-core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java b/xwork-core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java index e0e949cec..8c7c3ae44 100644 --- a/xwork-core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java +++ b/xwork-core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java @@ -93,6 +93,26 @@ public class OgnlValueStackTest extends XWorkTestCase { assertEquals("1, 2", vs.findValue("childAges", String.class)); } + public void testValuesFromContextAreConverted() { + final OgnlValueStack vs = createValueStack(); + + final String propertyName = "dogName"; + final String propertyValue = "Rover"; + vs.getContext().put(propertyName, new String[]{propertyValue}); + + assertEquals(propertyValue, vs.findValue(propertyName, String.class)); + } + + public void testNullValueFromContextGetsConverted() { + final OgnlValueStack vs = createValueStack(); + + final String propertyName = "dogName"; + final String propertyValue = null; + vs.getContext().put(propertyName, propertyValue); + + assertEquals(propertyValue, vs.findValue(propertyName, String.class)); + } + public void testFailOnException() { OgnlValueStack vs = createValueStack(); diff --git a/xwork-core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java b/xwork-core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java index 61a91a057..69dceca28 100644 --- a/xwork-core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java +++ b/xwork-core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java @@ -3,6 +3,7 @@ package com.opensymphony.xwork2.ognl; import junit.framework.TestCase; import java.lang.reflect.Member; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -202,6 +203,58 @@ public class SecurityMemberAccessTest extends TestCase { assertTrue("Access to enums is blocked!", actual); } + public void testAccessStatic() throws Exception { + // given + SecurityMemberAccess sma = new SecurityMemberAccess(true); + sma.setExcludedClasses(new HashSet>(Arrays.>asList(Class.class))); + + // when + Member method = StaticTester.class.getMethod("sayHello"); + boolean actual = sma.isAccessible(context, Class.class, method, null); + + // then + assertTrue("Access to static is blocked!", actual); + } + + public void testBlockStaticAccess() throws Exception { + // given + SecurityMemberAccess sma = new SecurityMemberAccess(false); + sma.setExcludedClasses(new HashSet>(Arrays.>asList(Class.class))); + + // when + Member method = StaticTester.class.getMethod("sayHello"); + boolean actual = sma.isAccessible(context, Class.class, method, null); + + // then + assertFalse("Access to static isn't blocked!", actual); + } + + public void testBlockStaticAccessIfClassIsExcluded() throws Exception { + // given + SecurityMemberAccess sma = new SecurityMemberAccess(false); + sma.setExcludedClasses(new HashSet>(Arrays.>asList(Class.class))); + + // when + Member method = Class.class.getMethod("getClassLoader"); + boolean actual = sma.isAccessible(context, Class.class, method, null); + + // then + assertFalse("Access to static method of excluded class isn't blocked!", actual); + } + + public void testAllowStaticAccessIfClassIsNotExcluded() throws Exception { + // given + SecurityMemberAccess sma = new SecurityMemberAccess(false); + sma.setExcludedClasses(new HashSet>(Arrays.>asList(ClassLoader.class))); + + // when + Member method = Class.class.getMethod("getClassLoader"); + boolean actual = sma.isAccessible(context, Class.class, method, null); + + // then + assertTrue("Invalid test! Access to static method of excluded class is blocked!", actual); + } + } class FooBar implements FooBarInterface { @@ -249,4 +302,12 @@ interface FooBarInterface extends FooInterface, BarInterface { enum MyValues { ONE, TWO, THREE -} \ No newline at end of file +} + +class StaticTester { + + public static String sayHello() { + return "Hello"; + } + +}