Whoa, I obviously missed an important thread. Can someone provide a link, please, to the thread in which this was discussed?
Do we have a software grant on file? That's the very minimum we need if this is a donation from Google and not from an (unaffiliated) individual. We probably also need to go through IP Clearance. -- Martin Cooper On Tue, Jan 26, 2010 at 11:44 PM, <lukaszlen...@apache.org> wrote: > Author: lukaszlenart > Date: Wed Jan 27 07:44:32 2010 > New Revision: 903559 > > URL: http://svn.apache.org/viewvc?rev=903559&view=rev > Log: > Initial commit of all code donated by Google, all packages were renamed to > org.apache.struts > > Added: > struts/sandbox/trunk/struts2-gxp-plugin/ > struts/sandbox/trunk/struts2-gxp-plugin/pom.xml > struts/sandbox/trunk/struts2-gxp-plugin/src/ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/ > > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/ > > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/ > > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxp.java > > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxpResult.java > > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Gxp.java > > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpInstance.java > > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpResult.java > > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Param.java > struts/sandbox/trunk/struts2-gxp-plugin/src/test/ > struts/sandbox/trunk/struts2-gxp-plugin/src/test/java/ > > Added: struts/sandbox/trunk/struts2-gxp-plugin/pom.xml > URL: > http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/pom.xml?rev=903559&view=auto > ============================================================================== > --- struts/sandbox/trunk/struts2-gxp-plugin/pom.xml (added) > +++ struts/sandbox/trunk/struts2-gxp-plugin/pom.xml Wed Jan 27 07:44:32 2010 > @@ -0,0 +1,71 @@ > +<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.2.0-SNAPSHOT</version> > + </parent> > + > + <groupId>org.apache.struts</groupId> > + <artifactId>struts2-gxp-plugin</artifactId> > + <packaging>jar</packaging> > + <name>Struts 2 GXP Plugin</name> > + <url>http://struts.apache.org</url> > + > + <scm> > + > <connection>scm:svn:http://svn.apache.org/repos/asf/struts/sandbox/trunk/struts2-gxp-plugin</connection> > + > <developerConnection>scm:svn:https://svn.apache.org/repos/asf/struts/sandbox/trunk/struts2-gxp-plugin</developerConnection> > + > <url>http://svn.apache.org/viewcvs.cgi/struts/sandbox/trunk/struts2-gxp-plugin</url> > + </scm> > + > + <dependencies> > + <dependency> > + <groupId>org.apache.struts</groupId> > + <artifactId>struts2-core</artifactId> > + <version>2.2.0-SNAPSHOT</version> > + </dependency> > + <dependency> > + <groupId>javax.servlet</groupId> > + <artifactId>servlet-api</artifactId> > + <version>2.4</version> > + <scope>provided</scope> > + </dependency> > + <!-- > + Until GXP is added to the Google Maven Repository in early 2010 > (http://code.google.com/p/gxp/issues/detail?id=5) > + Download http://gxp.googlecode.com/files/gxp-0.2.4-beta.jar > + Run the following at the command line (do not use > WindowsPowershell) > + mvn install:install-file -DgroupId=com.google -DartifactId=gxp > -Dversion=0.2.4-BETA -Dpackaging=jar -Dfile=gxp-0.2.4-beta.jar > + --> > + <dependency> > + <groupId>com.google</groupId> > + <artifactId>gxp</artifactId> > + <version>0.2.4-BETA</version> > + </dependency> > + <dependency> > + <groupId>junit</groupId> > + <artifactId>junit</artifactId> > + <version>3.8.1</version> > + <scope>test</scope> > + </dependency> > + <dependency> > + <groupId>com.google.collections</groupId> > + <artifactId>google-collections</artifactId> > + <version>1.0</version> > + </dependency> > + </dependencies> > + > + <build> > + <defaultGoal>install</defaultGoal> > + <plugins> > + <plugin> > + <artifactId>maven-compiler-plugin</artifactId> > + <configuration> > + <source>1.5</source> > + <target>1.5</target> > + </configuration> > + </plugin> > + </plugins> > + </build> > + > +</project> > > Added: > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxp.java > URL: > http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxp.java?rev=903559&view=auto > ============================================================================== > --- > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxp.java > (added) > +++ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxp.java > Wed Jan 27 07:44:32 2010 > @@ -0,0 +1,348 @@ > +/* > + * $Id$ > + * > + * Licensed to the Apache Software Foundation (ASF) under one > + * or more contributor license agreements. See the NOTICE file > + * distributed with this work for additional information > + * regarding copyright ownership. The ASF licenses this file > + * to you under the Apache License, Version 2.0 (the > + * "License"); you may not use this file except in compliance > + * with the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, > + * software distributed under the License is distributed on an > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > + * KIND, either express or implied. See the License for the > + * specific language governing permissions and limitations > + * under the License. > + */ > +package org.apache.struts2.views.gxp; > + > +import com.google.common.annotations.VisibleForTesting; > +import com.google.gxp.base.GxpContext; > +import com.google.gxp.base.MarkupClosure; > +import com.opensymphony.xwork2.ActionContext; > +import com.opensymphony.xwork2.inject.Inject; > +import com.opensymphony.xwork2.util.ValueStack; > +import com.opensymphony.xwork2.util.ValueStackFactory; > + > +import java.io.PrintWriter; > +import java.io.StringWriter; > +import java.lang.reflect.InvocationTargetException; > +import java.lang.reflect.Method; > +import java.util.ArrayList; > +import java.util.Arrays; > +import java.util.Collections; > +import java.util.HashMap; > +import java.util.Iterator; > +import java.util.List; > +import java.util.Map; > + > +/** > + * Struts2 to GXP adapter. Can be used to write a GXP or create a > + * {...@link MarkupClosure}. Pulls GXP parameters from Struts2 value stack. > + * > + * @author Bob Lee > + */ > +public abstract class AbstractGxp<T extends MarkupClosure> { > + > + ValueStackFactory valueStackFactory; > + Map defaultValues = new HashMap(); > + List<Param> params; > + Class gxpClass; > + Method writeMethod; > + Method getGxpClosureMethod; > + boolean hasBodyParam; > + > + protected AbstractGxp(Class gxpClass) { > + this(gxpClass, lookupMethodByName(gxpClass, "write"), > lookupMethodByName(gxpClass, "getGxpClosure")); > + } > + > + protected AbstractGxp(Class gxpClass, Method writeMethod, Method > getGxpClosureMethod) { > + this.gxpClass = gxpClass; > + this.writeMethod = writeMethod; > + this.getGxpClosureMethod = getGxpClosureMethod; > + this.params = lookupParams(); > + } > + > + /** > + * Writes GXP. Pulls GXP parameters from Struts2's value stack. > + */ > + public void write(Appendable out, GxpContext gxpContext) { > + write(out, gxpContext, null); > + } > + > + /** > + * Writes GXP. Pulls GXP parameters from Struts2's value stack. > + * > + * @param overrides parameter map pushed onto the value stack > + */ > + protected void write(Appendable out, GxpContext gxpContext, Map > overrides) { > + Object[] args = getArgs(out, gxpContext, overrides); > + > + try { > + writeMethod.invoke(getGxpInstance(), args); > + } catch (Exception e) { > + throw new RuntimeException(createDebugString(args, e), e); > + } > + } > + > + protected Object[] getArgs(Appendable out, GxpContext gxpContext, Map > overrides) { > + List<Object> argList = getArgListFromValueStack(overrides); > + Object[] args = new Object[argList.size() + 2]; > + args[0] = out; > + args[1] = gxpContext; > + int index = 2; > + for (Iterator<Object> i = argList.iterator(); i.hasNext(); index++) { > + args[index] = i.next(); > + } > + return args; > + } > + > + /** > + * @return the object on which to call the write and getGxpClosure > methods. If > + * the methods are static, this can return {...@code null} > + */ > + protected Object getGxpInstance() { > + return null; > + } > + > + /** > + * Creates GXP closure. Pulls GXP parameters from Struts 2 value stack. > + */ > + public T getGxpClosure() { > + return getGxpClosure(null, null); > + } > + > + /** > + * Creates GXP closure. Pulls GXP parameters from Struts 2 value stack. > + * > + * @param body is pushed onto the stack if this GXP has a > + * {...@link MarkupClosure} (or subclass) parameter named > "body". > + * @param params comes first on the value stack. > + */ > + �...@suppresswarnings("unchecked") > + protected T getGxpClosure(T body, Map params) { > + final Map overrides = getOverrides(body, params); > + > + Object[] args = getArgListFromValueStack(overrides).toArray(); > + > + try { > + return (T) getGxpClosureMethod.invoke(getGxpInstance(), args); > + } catch (IllegalArgumentException e) { > + throw new RuntimeException(createDebugString(args, e), e); > + } catch (IllegalAccessException e) { > + throw new RuntimeException(createDebugString(args, e), e); > + } catch (InvocationTargetException e) { > + throw new RuntimeException(createDebugString(args, e), e); > + } > + } > + > + protected Map getOverrides(T body, Map params) { > + final Map overrides = new HashMap(); > + if (hasBodyParam && body != null) { > + overrides.put(Param.BODY_PARAM_NAME, body); > + } > + if (params != null) { > + overrides.putAll(params); > + } > + return overrides; > + } > + > + /** > + * Iterates over GXP parameters, pulls value from value stack for each > + * parameter, and appends the values to an argument list which will > + * be passed to a method on a GXP. > + * > + * @param overrides parameter map pushed onto the value stack > + */ > + List getArgListFromValueStack(Map overrides) { > + > + ValueStack valueStack = > valueStackFactory.createValueStack(ActionContext.getContext().getValueStack()); > + > + // add default values to the bottom of the stack. if no action > provides > + // a getter for a param, the default value will be used. > + valueStack.getRoot().add(this.defaultValues); > + > + // push override parameters onto the stack. > + if (overrides != null && !overrides.isEmpty()) { > + valueStack.push(overrides); > + } > + > + List args = new ArrayList(params.size()); > + for (Param param : getParams()) { > + try { > + args.add(valueStack.findValue(param.getName(), > param.getType())); > + } catch (Exception e) { > + throw new RuntimeException("Exception while finding '" + > param.getName() + "'.", e); > + } > + } > + > + return args; > + } > + > + /** > + * Combines parameter names and types into <code>Param</code> objects. > + */ > + List<Param> lookupParams() { > + List<Param> params = new ArrayList<Param>(); > + > + List<String> parameterNames = lookupParameterNames(); > + List<Class<?>> parameterTypes = lookupParameterTypes(); > + Iterator<Class<?>> parameterTypeIterator = parameterTypes.iterator(); > + > + // If there are more parameter names than parameter types it means > that we are > + // using instantiable GXPs and there are 1 or more constructor > parameters. > + // Constructor params will always be first in the list, so just drop > an appropriate > + // number of elements from the beginning of the list. > + if (parameterNames.size() > parameterTypes.size()) { > + parameterNames = parameterNames.subList(parameterNames.size() - > parameterTypes.size(), parameterNames.size()); > + } > + > + for (String name : parameterNames) { > + Class paramType = parameterTypeIterator.next(); > + Param param = new Param(gxpClass, name, paramType); > + params.add(param); > + > + if (param.isBody()) { > + hasBodyParam = true; > + } > + > + if (param.isOptional()) { > + defaultValues.put(param.getName(), param.getDefaultValue()); > + } > + } > + > + this.defaultValues = Collections.unmodifiableMap(this.defaultValues); > + return Collections.unmodifiableList(params); > + } > + > + /** > + * Gets list of parameter types. > + */ > + List<Class<?>> lookupParameterTypes() { > + List<Class<?>> parameterTypes = > Arrays.asList(writeMethod.getParameterTypes()); > + // skip the first two, gxp_out and gxp_context. they are for > internal use. > + return parameterTypes.subList(2, parameterTypes.size()); > + } > + > + /** > + * Gets list of parameter names. > + */ > + List<String> lookupParameterNames() { > + try { > + return (List<String>) > gxpClass.getMethod("getArgList").invoke(null); > + } catch (Exception e) { > + throw new RuntimeException(e); > + } > + } > + > + /** > + * Returns first method with the given name. Should not be used if the > + * method is overloaded. > + */ > + protected static Method lookupMethodByName(Class clazz, String name) { > + Method[] methods = clazz.getMethods(); > + for (int i = 0; i < methods.length; i++) { > + if (methods[i].getName().equals(name)) { > + return methods[i]; > + } > + } > + throw new RuntimeException("No " + name + "(...) method found for " > + + clazz.getName() + "."); > + } > + > + public Class getGxpClass() { > + return this.gxpClass; > + } > + > + /** > + * Returns list of parameters requested by GXP. > + */ > + public List<Param> getParams() { > + return params; > + } > + > + /** > + * Returns generated GXP class given an absolute path to a GXP file. > + * The current implementation assumes that the GXP and generated Java > source > + * file share the same name with different extensions. > + */ > + �...@visiblefortesting > + public static Class getGxpClassForPath(String gxpPath) { > + int offset = (gxpPath.charAt(0) == '/') ? 1 : 0; > + String className = gxpPath.substring(offset, gxpPath.length() - > 4).replace('/', '.'); > + try { > + return getClassLoader().loadClass(className); > + } catch (ClassNotFoundException e) { > + throw new RuntimeException(e); > + } > + } > + > + static ClassLoader getClassLoader() { > + ClassLoader loader = Thread.currentThread().getContextClassLoader(); > + return (loader == null) ? ClassLoader.getSystemClassLoader() : > loader; > + } > + > + /** > + * Creates debug String which can be tacked onto an exception. > + */ > + String createDebugString(Object[] args, Exception exception) { > + StringBuffer buffer = new StringBuffer(); > + printExceptionTraceToBuffer(exception, buffer); > + buffer.append("\nException in GXP: > ").append(gxpClass.getName()).append(". Params:"); > + int index = 2; > + for (Param param : getParams()) { > + try { > + Object arg = args[index++]; > + String typesMatch = "n/a (null)"; > + if (arg != null) { > + if (doesArgumentTypeMatchParamType(param, arg)) { > + typesMatch = "YES"; > + } else { > + typesMatch = "NO"; > + } > + } > + buffer.append("\n ") > + .append(param.toString()) > + .append(" = ") > + .append(arg) > + .append("; ") > + .append("[types match? ") > + .append(typesMatch) > + .append("]"); > + } catch (Exception e) { > + buffer.append(" >Error getting information for param # > ").append(index).append("< "); > + } > + } > + buffer.append("\nStack trace: "); > + return buffer.toString(); > + } > + > + private void printExceptionTraceToBuffer(Exception e, > + StringBuffer buffer) { > + StringWriter out = new StringWriter(); > + e.printStackTrace(new PrintWriter(out)); > + buffer.append(out.getBuffer().toString()); > + } > + > + private boolean doesArgumentTypeMatchParamType(Param param, Object arg) { > + Class paramType = param.getType(); > + Class<? extends Object> argClass = arg.getClass(); > + > + // TODO(jpelly): Handle all primitive unwrapping (ie, Boolean --> > boolean). > + if (boolean.class.equals(paramType) && > Boolean.class.equals(argClass)) { > + return true; > + } else if (char.class.equals(paramType) && > Character.class.equals(argClass)) { > + return true; > + } > + return paramType.isAssignableFrom(argClass); > + } > + > + �...@inject > + public void setValueStackFactory(ValueStackFactory valueStackFactory) { > + this.valueStackFactory = valueStackFactory; > + } > +} > > Added: > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxpResult.java > URL: > http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxpResult.java?rev=903559&view=auto > ============================================================================== > --- > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxpResult.java > (added) > +++ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/AbstractGxpResult.java > Wed Jan 27 07:44:32 2010 > @@ -0,0 +1,126 @@ > +/* > + * $Id$ > + * > + * Licensed to the Apache Software Foundation (ASF) under one > + * or more contributor license agreements. See the NOTICE file > + * distributed with this work for additional information > + * regarding copyright ownership. The ASF licenses this file > + * to you under the Apache License, Version 2.0 (the > + * "License"); you may not use this file except in compliance > + * with the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, > + * software distributed under the License is distributed on an > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > + * KIND, either express or implied. See the License for the > + * specific language governing permissions and limitations > + * under the License. > + */ > +package org.apache.struts2.views.gxp; > + > +import com.opensymphony.xwork2.Result; > +import org.apache.struts2.ServletActionContext; > + > +import javax.servlet.http.HttpServletResponse; > +import java.io.IOException; > +import java.io.Writer; > +import java.util.Locale; > + > +/** > + * The abstract base class for our Struts 2 GXP result type implementation. > It > + * outputs GXP, and pulls GXP parameters from Struts 2's value stack. > Implementing > + * classes have to: > + * <ol> > + * <li>Implement <code>execute(ActionInvocation)</code>, which must instruct > the > + * GXP to write itself to the output stream. See {...@link GxpResult} for a > + * sample implementation.</li> > + * <li>Add a <code>public static final</code> field <b>DEFAULT_PARAM</b> with > + * the value 'gxpName'. Struts 2 needs this to set the name of your > + * template into this object.</li> > + * </ol> > + * <p/> > + * <p>If you want to use instantiated GXPs (using the nested > + * {...@code Interface}), you can set the u...@code useInstances} parameter > to > + * {...@code true}: > + * <pre> > + * <result-types> > + * <result-type name="gxp" > class="org.apache.struts2.views.gxp.GxpResult"> > + * <param name="useInstances">true</param> > + * </result-type> > + * </result-types> > + * </pre> > + * This means that Struts 2 will attempt to instantiate the {...@code > Interface} > + * using the {...@link com.opensymphony.xwork2.ObjectFactory}. If > + * {...@link com.google.webwork.GuiceWebWorkIntegrationModule} is installed, > or > + * {...@link com.google.webwork.ContainerObjectFactory} is set as the static > + * {...@code ObjectFactory} instance, then Guice will be used to instantiate > the > + * GXP instance; otherwise, only GXPs with no constructor parameters will > work. > + * > + * @author Bob Lee > + */ > +public abstract class AbstractGxpResult implements Result { > + > + private boolean useInstances = false; > + private String gxpName; > + > + public void setGxpName(String gxpName) { > + this.gxpName = gxpName; > + } > + > + protected final String getGxpName() { > + return gxpName; > + } > + > + public void setUseInstances(boolean useInstances) { > + this.useInstances = useInstances; > + } > + > + protected final boolean getUseInstances() { > + return useInstances; > + } > + > + /** > + * Provides resources necessary to execute a GXP. > + */ > + protected interface GxpResourceProvider { > + Writer getWriter() throws IOException; > + > + Locale getLocale(); > + } > + > + /** > + * Uses reasonable defaults to provide resources. > + */ > + protected static class DefaultProvider implements GxpResourceProvider { > + > + private final String contentType; > + > + public DefaultProvider(String contentType) { > + this.contentType = contentType; > + } > + > + public Writer getWriter() throws IOException { > + setContentType(); > + return ServletActionContext.getResponse().getWriter(); > + } > + > + public Locale getLocale() { > + return ServletActionContext.getRequest().getLocale(); > + } > + > + void setContentType() { > + HttpServletResponse response = > ServletActionContext.getResponse(); > + // set content type if it hasn't already been set. > + if (response.getContentType() == null || > response.getContentType().isEmpty()) { > + response.setContentType(contentType); > + } > + // If no character encoding was set in the content type, default > to UTF-8. > + if (response.getCharacterEncoding() == null || > response.getCharacterEncoding().isEmpty()) { > + response.setCharacterEncoding("UTF-8"); > + } > + } > + } > + > +} > > Added: > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Gxp.java > URL: > http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Gxp.java?rev=903559&view=auto > ============================================================================== > --- > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Gxp.java > (added) > +++ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Gxp.java > Wed Jan 27 07:44:32 2010 > @@ -0,0 +1,82 @@ > +/* > + * $Id$ > + * > + * Licensed to the Apache Software Foundation (ASF) under one > + * or more contributor license agreements. See the NOTICE file > + * distributed with this work for additional information > + * regarding copyright ownership. The ASF licenses this file > + * to you under the Apache License, Version 2.0 (the > + * "License"); you may not use this file except in compliance > + * with the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, > + * software distributed under the License is distributed on an > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > + * KIND, either express or implied. See the License for the > + * specific language governing permissions and limitations > + * under the License. > + */ > +package org.apache.struts2.views.gxp; > + > +import com.google.common.base.Function; > +import com.google.common.collect.MapMaker; > +import com.google.gxp.html.HtmlClosure; > + > +import java.lang.reflect.Method; > +import java.util.Map; > + > +/** > + * Struts 2 to GXP adapter. Can be used to write a GXP or create > + * a HtmlClosure. Pulls GXP parameters from Struts 2 value stack. > + * > + * @author Bob Lee > + */ > +public class Gxp extends AbstractGxp<HtmlClosure> { > + > + Gxp(Class gxpClass) { > + this(gxpClass, lookupMethodByName(gxpClass, "write"), > lookupMethodByName(gxpClass, "getGxpClosure")); > + } > + > + Gxp(Class gxpClass, Method writeMethod, Method getGxpClosureMethod) { > + super(gxpClass, writeMethod, getGxpClosureMethod); > + } > + > + static final Map<Class, Gxp> classToGxp = new > MapMaker().weakKeys().softValues().makeComputingMap(new Function<Class, > Gxp>() { > + public Gxp apply(Class from) { > + return classToGxp.containsKey(from) ? classToGxp.get(from) : new > Gxp(from); > + } > + }); > + > + static final Map<String, Gxp> pathToGxp = new > MapMaker().softValues().makeComputingMap(new Function<String, Gxp>() { > + public Gxp apply(String from) { > + return pathToGxp.containsKey(from) ? pathToGxp.get(from) : > getInstance(getGxpClassForPath(from)); > + } > + }); > + > + /** > + * Looks up Gxp instance for GXP with given path. > + */ > + public static Gxp getInstance(String gxpPath) { > + try { > + return pathToGxp.get(gxpPath); > + } catch (RuntimeException e) { > + if (e.getCause() instanceof ClassNotFoundException) { > + // Couldn't find or load the GXP class. Return null. > + // It would be simpler if classToGxp.create() could return > null, > + // but the contract of ReferenceCache doesn't allow it to. > + return null; > + } > + throw e; > + } > + } > + > + /** > + * Looks up Gxp instance for the given GXP class. > + */ > + public static Gxp getInstance(Class gxpClass) { > + return classToGxp.get(gxpClass); > + } > + > +} > > Added: > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpInstance.java > URL: > http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpInstance.java?rev=903559&view=auto > ============================================================================== > --- > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpInstance.java > (added) > +++ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpInstance.java > Wed Jan 27 07:44:32 2010 > @@ -0,0 +1,138 @@ > +/* > + * $Id$ > + * > + * Licensed to the Apache Software Foundation (ASF) under one > + * or more contributor license agreements. See the NOTICE file > + * distributed with this work for additional information > + * regarding copyright ownership. The ASF licenses this file > + * to you under the Apache License, Version 2.0 (the > + * "License"); you may not use this file except in compliance > + * with the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, > + * software distributed under the License is distributed on an > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > + * KIND, either express or implied. See the License for the > + * specific language governing permissions and limitations > + * under the License. > + */ > +package org.apache.struts2.views.gxp; > + > +import com.google.common.base.Function; > +import com.google.common.collect.MapMaker; > +import com.opensymphony.xwork2.ObjectFactory; > +import com.opensymphony.xwork2.inject.Inject; > + > +import java.util.Map; > +import java.util.logging.Level; > +import java.util.logging.Logger; > + > +/** > + * Struts 2 to GXP adapter that uses instances of GXP Interfaces, as created > by > + * the {...@link ObjectFactory}. Can be used to write a GXP or create a > + * HtmlClosure. Pulls non-constructor GXP parameters from Struts 2 value > stack. > + * > + * @author David P. Baker > + */ > +public class GxpInstance extends Gxp { > + > + private static final Logger logger = > Logger.getLogger(GxpInstance.class.getCanonicalName()); > + > + private Class<?> gxpInterface; > + private Class<?> gxpInstance; > + private ObjectFactory objectFactory; > + > + GxpInstance(Class<?> gxpClass) { > + super(gxpClass, > + lookupMethodByName(getNestedClass(gxpClass, "Interface"), > "write"), > + lookupMethodByName(getNestedClass(gxpClass, "Interface"), > "getGxpClosure")); > + this.gxpInterface = getNestedClass(gxpClass, "Interface"); > + this.gxpInstance = getNestedClass(gxpClass, "Instance"); > + } > + > + private static Class<?> getNestedClass(Class<?> clazz, String > nestedClassName) { > + for (Class<?> nested : clazz.getDeclaredClasses()) { > + if (nestedClassName.equals(nested.getSimpleName())) { > + return nested; > + } > + } > + throw new IllegalArgumentException(String.format("Cannot find class > %s.%s", clazz.getCanonicalName(), nestedClassName)); > + } > + > + /** > + * {...@inheritdoc} > + * <p> This implementation uses the {...@link ObjectFactory} to try to > create an > + * instance of the {...@code Interface} class that is nested within the > GXP > + * class. If that doesn't work, it falls back to trying to use the > + * {...@code ObjectFactory} to create an instance of the nested > {...@code Instance} > + * class, in case there is no binding for the {...@code Interface}. > + */ > + �...@override > + protected Object getGxpInstance() { > + try { > + return objectFactory.buildBean(gxpInterface, null); > + } catch (Exception e) { > + logger.log( > + Level.INFO, "Error instantiating {0}; trying {1}", > + new Object[]{gxpInterface.getCanonicalName(), > gxpInstance.getCanonicalName(),}); > + try { > + return objectFactory.buildBean(gxpInstance, null); > + } catch (Exception e1) { > + throw new RuntimeException(String.format("Error > instantiating %s", > + gxpInterface.getCanonicalName(), > gxpInstance.getCanonicalName()), > + e1); > + } > + } > + } > + > + �...@override > + public Class<?> getGxpClass() { > + return this.gxpInterface; > + } > + > + private static final Map<Class<?>, GxpInstance> classToGxpInstance = new > MapMaker().weakKeys().softValues() > + .makeComputingMap(new Function<Class<?>, GxpInstance>() { > + public GxpInstance apply(Class<?> from) { > + return classToGxpInstance.containsKey(from) ? > classToGxpInstance.get(from) : new GxpInstance(from); > + } > + }); > + > + private static final Map<String, GxpInstance> pathToGxpInstance = new > MapMaker().softValues() > + .makeComputingMap(new Function<String, GxpInstance>() { > + public GxpInstance apply(String from) { > + return pathToGxpInstance.containsKey(from) ? > pathToGxpInstance.get(from) : getInstance(getGxpClassForPath(from)); > + } > + }); > + > + /** > + * Looks up Gxp instance for GXP with given path. > + */ > + public static GxpInstance getInstance(String gxpPath) { > + try { > + return pathToGxpInstance.get(gxpPath); > + } catch (RuntimeException e) { > + if (e.getCause() instanceof ClassNotFoundException) { > + // Couldn't find or load the GXP class. Return null. > + // It would be simpler if classToGxp.create() could return > null, > + // but the contract of ReferenceCache doesn't allow it to. > + return null; > + } > + throw e; > + } > + } > + > + /** > + * Looks up {...@code GxpInstance} instance for the given GXP class. > + */ > + public static GxpInstance getInstance(Class<?> gxpClass) { > + return classToGxpInstance.get(gxpClass); > + } > + > + �...@inject > + public void setObjectFactory(ObjectFactory objectFactory) { > + this.objectFactory = objectFactory; > + } > + > +} > > Added: > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpResult.java > URL: > http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpResult.java?rev=903559&view=auto > ============================================================================== > --- > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpResult.java > (added) > +++ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/GxpResult.java > Wed Jan 27 07:44:32 2010 > @@ -0,0 +1,145 @@ > +/* > + * $Id$ > + * > + * Licensed to the Apache Software Foundation (ASF) under one > + * or more contributor license agreements. See the NOTICE file > + * distributed with this work for additional information > + * regarding copyright ownership. The ASF licenses this file > + * to you under the Apache License, Version 2.0 (the > + * "License"); you may not use this file except in compliance > + * with the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, > + * software distributed under the License is distributed on an > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > + * KIND, either express or implied. See the License for the > + * specific language governing permissions and limitations > + * under the License. > + */ > +package org.apache.struts2.views.gxp; > + > +import com.google.gxp.base.GxpContext; > +import com.google.gxp.html.HtmlClosure; > +import com.opensymphony.xwork2.ActionInvocation; > +import com.opensymphony.xwork2.inject.Container; > +import com.opensymphony.xwork2.inject.Inject; > + > +import java.io.IOException; > + > +/** > + * Struts 2 GXP result type implementation. Outputs GXP. Pulls GXP parameters > + * from Struts 2's value stack. > + * <p/> > + * <p>Declare the GXP result type for your package in the xwork.xml file:</p> > + * <p/> > + * <pre> > + * <result-types> > + * <result-type name="gxp" > class="org.apache.struts2.views.gxp.GxpResult"/> > + * </result-types> > + * </pre> > + * <p/> > + * <p>Or if you want to output XML instead of HTML:</p> > + * <p/> > + * <pre> > + * <result-types> > + * <result-type name="gxp" > class="org.apache.struts2.views.gxp.GxpResult"> > + * <param name="outputXml">true</param> > + * </result-type> > + * </result-types> > + * </pre> > + * <p/> > + * <p>Outputting XML changes the content type from text/html to > application/xml > + * and configures the {...@link GxpContext} to output XML. This is useful in > + * situations like specifying the doctype of your GXP to be 'mobile'.</p> > + * <p/> > + * <p>Use the GXP result type for the result of an action. For example:</p> > + * <p/> > + * <pre> > + * <result name="success" type="gxp">/myPackage/MyGxp.gxp</result> > + * </pre> > + * > + * @author Bob Lee > + * @see org.apache.struts2.views.gxp.AbstractGxpResult > + */ > +public class GxpResult extends AbstractGxpResult { > + > + /** > + * Tells Struts 2 which parameter name to use if it's not already > specified. > + */ > + public static final String DEFAULT_PARAM = "gxpName"; > + > + private Container container; > + private boolean outputXml = false; > + > + /** > + * Whether or not this GXP should output XML. > + */ > + public void setOutputXml(boolean outputXml) { > + this.outputXml = outputXml; > + } > + > + protected HtmlClosure getGxpClosure() { > + final Gxp gxp = getUseInstances() ? > GxpInstance.getInstance(getGxpName()) : Gxp.getInstance(getGxpName()); > + > + if (null == gxp) { > + // TODO(lwerner): OGNL or Struts 2 seems to be swallowing this > exception > + // rather than logging or rethrowing it, so you never see this > message > + // TODO(dpb): Is this true now that this work is not done in a > setter? > + throw new NullPointerException("The GXP " + getGxpName() > + + " could not be loaded. This is probably because you > have" > + + " a typo in your config."); > + } > + container.inject(gxp); > + return new HtmlClosure() { > + public void write(Appendable out, GxpContext gxpContext) throws > IOException { > + gxp.write(out, gxpContext); > + } > + }; > + } > + > + /** > + * Tells the GXP to write itself to the output stream. > + */ > + public void execute(ActionInvocation actionInvocation) { > + GxpResourceProvider provider = getProvider(); > + try { > + getGxpClosure().write(provider.getWriter(), new > GxpContext(provider.getLocale(), outputXml)); > + } catch (Exception e) { > + throw new RuntimeException("Exception while rendering " > + + getGxpName() > + + " coming from " > + + actionInvocation.getAction().getClass().getName() + > ".", > + e); > + } > + } > + > + /** > + * Gets appropriate provider. > + */ > + GxpResourceProvider getProvider() { > + return new HtmlOrXmlProvider(outputXml); > + } > + > + /** > + * Uses reasonable defaults to provide resources. > + */ > + private static class HtmlOrXmlProvider extends DefaultProvider { > + > + /** > + * Default content-type to use for responses. > + */ > + private static final String HTML_CONTENT_TYPE = "text/html; > charset=UTF-8"; > + private static final String XML_CONTENT_TYPE = "application/xml; > charset=UTF-8"; > + > + HtmlOrXmlProvider(boolean outputXml) { > + super(outputXml ? XML_CONTENT_TYPE : HTML_CONTENT_TYPE); > + } > + } > + > + �...@inject > + public void setContainer(Container container) { > + this.container = container; > + } > +} > > Added: > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Param.java > URL: > http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Param.java?rev=903559&view=auto > ============================================================================== > --- > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Param.java > (added) > +++ > struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/views/gxp/Param.java > Wed Jan 27 07:44:32 2010 > @@ -0,0 +1,96 @@ > +/* > + * $Id$ > + * > + * Licensed to the Apache Software Foundation (ASF) under one > + * or more contributor license agreements. See the NOTICE file > + * distributed with this work for additional information > + * regarding copyright ownership. The ASF licenses this file > + * to you under the Apache License, Version 2.0 (the > + * "License"); you may not use this file except in compliance > + * with the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, > + * software distributed under the License is distributed on an > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > + * KIND, either express or implied. See the License for the > + * specific language governing permissions and limitations > + * under the License. > + */ > +package org.apache.struts2.views.gxp; > + > +import com.google.gxp.html.HtmlClosure; > + > +import java.lang.reflect.Method; > + > +/** > + * @author Bob Lee > + */ > +public class Param { > + > + public static final String BODY_PARAM_NAME = "body"; > + > + String name; > + Class type; > + Class gxpClass; > + boolean optional; > + Object defaultValue; > + > + Param(Class gxpClass, String name, Class type) { > + this.gxpClass = gxpClass; > + this.name = name; > + this.type = type; > + > + // if you specify a default parameter value in a GXP, a > getDefaultXxx() > + // method will be present. > + try { > + Method defaultGetter = gxpClass.getMethod("getDefault" + > capitalize(name)); > + this.defaultValue = defaultGetter.invoke(null); > + this.optional = true; > + } catch (NoSuchMethodException ignored) { > + } catch (Exception e) { > + throw new RuntimeException(e); > + } > + } > + > + private static String capitalize(String s) { > + if (s.isEmpty()) { > + return s; > + } > + char first = s.charAt(0); > + char capitalized = Character.toUpperCase(first); > + return (first == capitalized) ? s : capitalized + s.substring(1); > + } > + > + public String getName() { > + return name; > + } > + > + public Class getType() { > + return type; > + } > + > + boolean isBody() { > + return name.equals(BODY_PARAM_NAME) && > type.equals(HtmlClosure.class); > + } > + > + public String toString() { > + return "Param[name: " + name > + + ", type: " + type.getName() > + + ", optional: " + optional > + + (optional ? ", defaultValue: " + defaultValue : "") > + + "]"; > + } > + > + public boolean isOptional() { > + return optional; > + } > + > + public Object getDefaultValue() { > + if (!optional) > + throw new RuntimeException("Parameter '" + name + "' in " + > gxpClass.getName() + " is not optional."); > + return defaultValue; > + } > + > +} > \ No newline at end of file > > > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@struts.apache.org For additional commands, e-mail: dev-h...@struts.apache.org