You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2010/01/27 08:44:32 UTC
svn commit: r903559 - in /struts/sandbox/trunk/struts2-gxp-plugin: ./ src/
src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/
src/main/java/org/apache/struts2/ src/main/java/org/apache/struts2/views/
src/main/java/org/apache/struts2/...
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 use{@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