Author: mrdon Date: Sat Dec 30 21:02:02 2006 New Revision: 491386 URL: http://svn.apache.org/viewvc?view=rev&rev=491386 Log: Initial version of scope plugin that mimics JBoss Seam-style of scoped bijection
Added: struts/sandbox/trunk/struts2-scope-plugin/ struts/sandbox/trunk/struts2-scope-plugin/pom.xml struts/sandbox/trunk/struts2-scope-plugin/src/ struts/sandbox/trunk/struts2-scope-plugin/src/main/ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/In.java struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/Out.java struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeInterceptor.java struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeType.java struts/sandbox/trunk/struts2-scope-plugin/src/main/resources/ struts/sandbox/trunk/struts2-scope-plugin/src/main/resources/struts-plugin.xml Added: struts/sandbox/trunk/struts2-scope-plugin/pom.xml URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-scope-plugin/pom.xml?view=auto&rev=491386 ============================================================================== --- struts/sandbox/trunk/struts2-scope-plugin/pom.xml (added) +++ struts/sandbox/trunk/struts2-scope-plugin/pom.xml Sat Dec 30 21:02:02 2006 @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.struts</groupId> + <artifactId>struts2-plugins</artifactId> + <version>2.0.2-SNAPSHOT</version> + </parent> + <groupId>org.apache.struts</groupId> + <artifactId>struts2-scope-plugin</artifactId> + <packaging>jar</packaging> + <name>Struts 2 Scope Plugin</name> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/struts/sandbox/trunk/struts2-scope-plugin/</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/struts/sandbox/trunk/struts2-scope-plugin/</developerConnection> + <url>http://svn.apache.org/viewcvs.cgi/struts/sandbox/trunk/struts2-scope-plugin/</url> + </scm> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + <version>3.8.1</version> + </dependency> + </dependencies> +</project> Added: struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/In.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/In.java?view=auto&rev=491386 ============================================================================== --- struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/In.java (added) +++ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/In.java Sat Dec 30 21:02:02 2006 @@ -0,0 +1,23 @@ +package org.apache.struts2.interceptor.scope; + +public @interface In { + /** + * Specifies that a component should be instantiated if the context variable is null. + */ + boolean create() default false; + + /** + * Specifies that the injected value must not be null, by default. + */ + boolean required() default true; + + /** + * Explicitly specify the scope to search, instead of searching all scopes. + */ + ScopeType scope() default ScopeType.UNSPECIFIED; + + /** + * The context variable name. Defaults to the name of the annotated field or getter method. + */ + String value() default ""; +} Added: struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/Out.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/Out.java?view=auto&rev=491386 ============================================================================== --- struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/Out.java (added) +++ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/Out.java Sat Dec 30 21:02:02 2006 @@ -0,0 +1,19 @@ +package org.apache.struts2.interceptor.scope; + +public @interface Out { + + /** + * Specifies that the injected value must not be null, by default. + */ + boolean required() default true; + + /** + * Explicitly specify the scope to search, instead of searching all scopes. + */ + ScopeType scope() default ScopeType.ACTION_CONTEXT; + + /** + * The context variable name. Defaults to the name of the annotated field or getter method. + */ + String value() default ""; +} Added: struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeInterceptor.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeInterceptor.java?view=auto&rev=491386 ============================================================================== --- struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeInterceptor.java (added) +++ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeInterceptor.java Sat Dec 30 21:02:02 2006 @@ -0,0 +1,277 @@ +/* + * $Id: CreateSessionInterceptor.java 471756 2006-11-06 15:01:43Z husted $ + * + * 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.interceptor.scope; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.StrutsException; +import org.apache.struts2.dispatcher.ServletActionRedirectResult; +import org.apache.struts2.dispatcher.ServletRedirectResult; +import org.apache.struts2.interceptor.CreateSessionInterceptor; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.interceptor.AbstractInterceptor; +import com.opensymphony.xwork2.util.AnnotationUtils; + +/** + * <!-- START SNIPPET: description --> + * + * This interceptor creates the HttpSession. + * <p/> + * This is particular usefull when using the <@s.token> tag in freemarker templates. + * The tag <b>do</b> require that a HttpSession is already created since freemarker commits + * the response to the client immediately. + * + * <!-- END SNIPPET: description --> + * + * <p/> <u>Interceptor parameters:</u> + * + * + * <!-- START SNIPPET: extending --> + * + * <ul> + * <li>none</li> + * </ul> + * + * <!-- END SNIPPET: extending --> + * + * + * <!-- START SNIPPET: parameters --> + * + * <ul> + * + * <li>None</li> + * + * </ul> + * + * <!-- END SNIPPET: parameters --> + * + * <b>Example:</b> + * + * <pre> + * <!-- START SNIPPET: example --> + * + * <action name="someAction" class="com.examples.SomeAction"> + * <interceptor-ref name="create-session"/> + * <interceptor-ref name="defaultStack"/> + * <result name="input">input_with_token_tag.ftl</result> + * </action> + * + * <!-- END SNIPPET: example --> + * </pre> + * + * @version $Date: 2006-11-06 07:01:43 -0800 (Mon, 06 Nov 2006) $ $Id: CreateSessionInterceptor.java 471756 2006-11-06 15:01:43Z husted $ + */ +public class ScopeInterceptor extends AbstractInterceptor { + + private static final long serialVersionUID = -4590322556118858869L; + + private static final Log LOG = LogFactory.getLog(ScopeInterceptor.class); + private static final Map<Class,CachedMethods> cachedMethods = Collections.synchronizedMap(new HashMap<Class,CachedMethods>()); + + /* (non-Javadoc) + * @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation) + */ + public String intercept(ActionInvocation invocation) throws Exception { + Object action = invocation.getAction(); + + ActionContext ctx = invocation.getInvocationContext(); + Class cls = action.getClass(); + boolean flashScopeUsed = false; + List<Method> inMethods = findAnnotationedMethods(cls, false); + for (Method m : inMethods) { + In in = m.getAnnotation(In.class); + String propName = in.value(); + if (propName.length() == 0) { + propName = determinePropertyName(m.getName(), false); + } + + flashScopeUsed |= in.scope() == ScopeType.FLASH; + + Object obj = findObjectInScope(in.scope(), propName, ctx); + + if (in.required() && obj == null) { + throw new StrutsException("Scope object "+propName+" cannot be found in scope "+in.scope()); + } + m.invoke(action, obj); + } + + if (flashScopeUsed) { + ctx.getSession().remove(ScopeType.FLASH.toString()); + } + + String ret = invocation.invoke(); + + List<Method> outMethods = findAnnotationedMethods(cls, true); + for (Method m : outMethods) { + Out out = m.getAnnotation(Out.class); + String propName = out.value(); + if (propName.length() == 0) { + propName = determinePropertyName(m.getName(), true); + } + + Object obj = m.invoke(action); + + putObjectInScope(out.scope(), propName, ctx, obj); + } + return ret; + } + + + /** + * @param in + * @param propName + * @param ctx + */ + private Object findObjectInScope(ScopeType scopeType, String propName, ActionContext ctx) { + Object obj = null; + if (obj == null && (scopeType == ScopeType.ACTION_CONTEXT ||scopeType == ScopeType.UNSPECIFIED)) { + obj = ctx.get(propName); + } + if (obj == null && (scopeType == ScopeType.REQUEST || scopeType == ScopeType.UNSPECIFIED)) { + obj = ServletActionContext.getRequest().getAttribute(propName); + } + if (obj == null && (scopeType == ScopeType.FLASH || scopeType == ScopeType.UNSPECIFIED)) { + HttpSession session = ServletActionContext.getRequest().getSession(false); + if (session != null) { + Map flash = (Map)session.getAttribute(ScopeType.FLASH.toString()); + if (flash != null) { + obj = flash.get(propName); + } + } + } + if (obj == null && (scopeType == ScopeType.SESSION || scopeType == ScopeType.UNSPECIFIED)) { + obj = ctx.getSession().get(propName); + } + if (obj == null && (scopeType == ScopeType.APPLICATION || scopeType == ScopeType.UNSPECIFIED)) { + obj = ctx.getApplication().get(propName); + } + return obj; + } + + private void putObjectInScope(ScopeType scopeType, String propName, ActionContext ctx, Object obj) { + HttpSession session = null; + switch (scopeType) { + case ACTION_CONTEXT : ctx.put(propName, obj); + break; + case REQUEST : ServletActionContext.getRequest().setAttribute(propName, obj); + break; + case FLASH : session = ServletActionContext.getRequest().getSession(true); + Map<String,Object> flash = (Map<String,Object>) session.getAttribute(ScopeType.FLASH.toString()); + if (flash == null) { + flash = new HashMap<String,Object>(); + } + flash.put(propName, obj); + session.setAttribute(ScopeType.FLASH.toString(), flash); + break; + case SESSION : session = ServletActionContext.getRequest().getSession(true); + session.setAttribute(propName, obj); + break; + case APPLICATION : ctx.getApplication().put(propName, obj); + break; + } + } + + + private List<Method> findAnnotationedMethods(Class cls, boolean out) { + CachedMethods cache = cachedMethods.get(cls); + if (cache == null) { + cache = new CachedMethods(); + cachedMethods.put(cls, cache); + } + List<Method> methods = null; + if (out) { + methods = cache.getOutMethods(); + if (methods == null) { + methods = AnnotationUtils.findAnnotatedMethods(cls, Out.class); + cache.setOutMethods(methods); + } + } else { + methods = cache.getInMethods(); + if (methods == null) { + methods = AnnotationUtils.findAnnotatedMethods(cls, In.class); + cache.setInMethods(methods); + } + } + return methods; + } + + private String determinePropertyName(String methodName, boolean out) { + String name = methodName; + if (!out && methodName.startsWith("set")) { + name = name.substring("set".length()); + } else if (out) { + if (methodName.startsWith("get") || methodName.startsWith("has")) { + name = name.substring("get".length()); + } else if (methodName.startsWith("is")) { + name = name.substring("is".length()); + } + } + if (Character.isUpperCase(name.charAt(0))) { + if (name.length() == 1 || !Character.isUpperCase(name.charAt(1))) { + name = Character.toLowerCase(name.charAt(0)) + name.substring(1);return name; + } + } + return name; + } + + private static class CachedMethods { + private List<Method> inMethods; + private List<Method> outMethods; + /** + * @return the inMethods + */ + public List<Method> getInMethods() { + return inMethods; + } + /** + * @param inMethods the inMethods to set + */ + public void setInMethods(List<Method> inMethods) { + this.inMethods = inMethods; + } + /** + * @return the outMethods + */ + public List<Method> getOutMethods() { + return outMethods; + } + /** + * @param outMethods the outMethods to set + */ + public void setOutMethods(List<Method> outMethods) { + this.outMethods = outMethods; + } + } + +} Added: struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeType.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeType.java?view=auto&rev=491386 ============================================================================== --- struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeType.java (added) +++ struts/sandbox/trunk/struts2-scope-plugin/src/main/java/org/apache/struts2/interceptor/scope/ScopeType.java Sat Dec 30 21:02:02 2006 @@ -0,0 +1,10 @@ +package org.apache.struts2.interceptor.scope; + +public enum ScopeType { + APPLICATION, + SESSION, + REQUEST, + ACTION_CONTEXT, + FLASH, + UNSPECIFIED +} \ No newline at end of file Added: struts/sandbox/trunk/struts2-scope-plugin/src/main/resources/struts-plugin.xml URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-scope-plugin/src/main/resources/struts-plugin.xml?view=auto&rev=491386 ============================================================================== --- struts/sandbox/trunk/struts2-scope-plugin/src/main/resources/struts-plugin.xml (added) +++ struts/sandbox/trunk/struts2-scope-plugin/src/main/resources/struts-plugin.xml Sat Dec 30 21:02:02 2006 @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<!DOCTYPE struts PUBLIC + "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" + "http://struts.apache.org/dtds/struts-2.0.dtd"> + +<struts> + <package name="scope-default"> + <interceptors> + <interceptor name="bean-scope" class="org.apache.struts2.interceptor.scope.ScopeInterceptor"/> + </interceptors> + </package> +</struts>