Author: musachy Date: Thu Aug 7 14:44:33 2008 New Revision: 683730 URL: http://svn.apache.org/viewvc?rev=683730&view=rev Log: Initial import
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/gxp/ struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/ struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpTemplateEngine.java struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcConfiguration.java struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcUtil.java struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/ struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/struts-plugin.xml struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/ struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/ struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/a.gxp struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/aclose.gxp Added: struts/sandbox/trunk/struts2-gxp-plugin/pom.xml URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/pom.xml?rev=683730&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-gxp-plugin/pom.xml (added) +++ struts/sandbox/trunk/struts2-gxp-plugin/pom.xml Thu Aug 7 14:44:33 2008 @@ -0,0 +1,56 @@ +<?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/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.struts</groupId> + <artifactId>struts2-plugins</artifactId> + <version>2.1.2</version> + </parent> + <artifactId>struts2-gxp-plugin</artifactId> + <version>1.0-SNAPSHOT</version> + <name>Struts 2 GXP Plugin</name> + + <dependencies> + <dependency> + <groupId>org.apache.struts</groupId> + <artifactId>struts2-core</artifactId> + <version>2.1.3-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + <version>2.4</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>mockobjects</groupId> + <artifactId>mockobjects-core</artifactId> + <version>0.09</version> + <scope>test</scope> + </dependency> + <!-- + C:\src\struts2\core> mvn install:install-file -DgroupId=com.google -DartifactId=gxp -Dversion=0.2.2-BETA -Dpackaging=jar -Dfile=c:\src\gxp\build\gxp-snapshot.jar + --> + <dependency> + <groupId>com.google</groupId> + <artifactId>gxp</artifactId> + <version>0.2.2-BETA</version> + </dependency> + + + </dependencies> + + <build> + <defaultGoal>install</defaultGoal> + </build> + +</project> Added: struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpTemplateEngine.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpTemplateEngine.java?rev=683730&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpTemplateEngine.java (added) +++ struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpTemplateEngine.java Thu Aug 7 14:44:33 2008 @@ -0,0 +1,43 @@ +package org.apache.struts2.gxp.template; + +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.components.template.BaseTemplateEngine; +import org.apache.struts2.components.template.Template; +import org.apache.struts2.components.template.TemplateRenderingContext; + +import com.google.gxp.base.GxpContext; + +public class GxpTemplateEngine extends BaseTemplateEngine { + + @Override + protected String getSuffix() { + return "gxp"; + } + + public void renderTemplate(TemplateRenderingContext templateContext) throws Exception { + List<Template> templates = templateContext.getTemplate().getPossibleTemplates(this); + Map actionContext = templateContext.getStack().getContext(); + HttpServletRequest req = (HttpServletRequest) actionContext.get(ServletActionContext.HTTP_REQUEST); + + for (Template template : templates) { + GxpcUtil.buildAndExec(template.getDir(), getFinalTemplateName(template), true, templateContext + .getWriter(), new GxpContext(req.getLocale())); + } + } + + protected String getFinalTemplateName(Template template) { + StringBuilder sb = new StringBuilder(); + sb.append(template.getTheme()).append("/").append(template.getName().replaceAll("-", "")); + if (template.getName().indexOf(".") <= 0) { + sb.append(".").append(getSuffix()); + } + + return sb.toString(); + } + +} Added: struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcConfiguration.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcConfiguration.java?rev=683730&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcConfiguration.java (added) +++ struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcConfiguration.java Thu Aug 7 14:44:33 2008 @@ -0,0 +1,207 @@ + package org.apache.struts2.gxp.template; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; + +import com.google.gxp.com.google.common.collect.ImmutableSet; +import com.google.gxp.com.google.common.collect.ImmutableSortedSet; +import com.google.gxp.com.google.common.collect.Lists; +import com.google.gxp.com.google.common.collect.Sets; +import com.google.gxp.compiler.Configuration; +import com.google.gxp.compiler.Phase; +import com.google.gxp.compiler.alerts.AlertPolicy; +import com.google.gxp.compiler.base.OutputLanguage; +import com.google.gxp.compiler.codegen.CodeGeneratorFactory; +import com.google.gxp.compiler.codegen.DefaultCodeGeneratorFactory; +import com.google.gxp.compiler.fs.FileRef; +import com.google.gxp.compiler.fs.FileSystem; +import com.google.gxp.compiler.fs.InMemoryFileSystem; +import com.google.gxp.compiler.fs.ResourceFileSystem; +import com.google.gxp.compiler.fs.SourcePathFileSystem; +import com.google.gxp.compiler.parser.FileSystemEntityResolver; +import com.google.gxp.compiler.parser.SourceEntityResolver; + +public class GxpcConfiguration implements Configuration { + private final FileSystem fs = new ResourceFileSystem(); + private SourcePathFileSystem sourcePathFs; + private Set<FileRef> sourceFiles; + private Set<FileRef> schemaFiles = Sets.newHashSet(); + private List<FileRef> sourcePaths; + private FileRef outputDir; + private AlertPolicy alertPolicy; + private ImmutableSortedSet<Phase> dotPhases; + + private String srcpaths; + private String destdir; + private String target; + private boolean dynamic = false; + + private InMemoryFileSystem memoryFileSystem = new InMemoryFileSystem(); + + private GxpcConfiguration() { + } + + public void configure(List<String> files) { + List<FileRef> underlyingInputFiles = Lists.newArrayList(); + for (String file : files) { + underlyingInputFiles.add(fs.parseFilename(file)); + } + + if (destdir == null) { + // log("Attribute 'destdir' was not set, the current working + // directory will be used.", + // Project.MSG_WARN); + destdir = System.getProperty("user.dir"); + } + outputDir = dynamic ? memoryFileSystem.getRoot() : fs.parseFilename(destdir); + + sourcePaths = Lists.newArrayList(); + sourcePaths.addAll(fs.parseFilenameList(srcpaths)); + + sourcePathFs = new SourcePathFileSystem(fs, sourcePaths, underlyingInputFiles, outputDir); + dotPhases = computeDotPhases(); + + sourceFiles = ImmutableSet.copyOf(sourcePathFs.getSourceFileRefs()); + } + + // ////////////////////////////////////////////////////////////////////////////// + // Setters + // ////////////////////////////////////////////////////////////////////////////// + + public void setSrcpaths(String srcpaths) { + this.srcpaths = srcpaths; + } + + public void setDestdir(String destdir) { + this.destdir = destdir; + } + + public void setSchemas(String schemas) { + for (String schema : schemas.split(",")) { + schemaFiles.add(fs.parseFilename(schema)); + } + } + + public void setTarget(String target) { + this.target = target; + } + + public void setDynamic(boolean dynamic) { + this.dynamic = dynamic; + } + + // ////////////////////////////////////////////////////////////////////////////// + // Getters (Configuration implementation) + // ////////////////////////////////////////////////////////////////////////////// + + public Set<FileRef> getSourceFiles() { + return sourceFiles; + } + + public Set<FileRef> getSchemaFiles() { + return schemaFiles; + } + + public Set<OutputLanguage> getOutputLanguages() { + Set<OutputLanguage> result = EnumSet.noneOf(OutputLanguage.class); + result.add(OutputLanguage.JAVA); + return Collections.unmodifiableSet(result); + } + + public CodeGeneratorFactory getCodeGeneratorFactory() { + DefaultCodeGeneratorFactory result = new DefaultCodeGeneratorFactory(); + result.setRuntimeMessageSource(target); + result.setDynamicModeEnabled(dynamic); + result.setSourceFiles(getSourceFiles()); + result.setSchemaFiles(getSchemaFiles()); + result.setSourcePaths(sourcePaths); + return result; + } + + public Set<FileRef> getAllowedOutputFileRefs() { + Set<FileRef> result = Sets.newHashSet(); + return Collections.unmodifiableSet(result); + } + + public FileRef getDependencyFile() { + return null; + } + + public FileRef getPropertiesFile() { + return (target != null) ? outputDir.join("/" + target.replace(".", "/") + "_en.properties") : null; + } + + public boolean isVerboseEnabled() { + return false; + } + + public boolean isDebugEnabled() { + return false; + } + + public AlertPolicy getAlertPolicy() { + return alertPolicy; + } + + public SortedSet<Phase> getDotPhases() { + return dotPhases; + } + + public SourceEntityResolver getEntityResolver() { + return new FileSystemEntityResolver(sourcePathFs); + } + + private static ImmutableSortedSet<Phase> computeDotPhases() { + return ImmutableSortedSet.<Phase> of(); + } + + public void setAlertPolicy(AlertPolicy alertPolicy) { + this.alertPolicy = alertPolicy; + } + + public InMemoryFileSystem getMemoryFileSystem() { + return memoryFileSystem; + } + + public static class Builder { + private GxpcConfiguration target = new GxpcConfiguration(); + + public Builder setAlertPolicy(AlertPolicy alertPolicy) { + target.setAlertPolicy(alertPolicy); + return this; + } + + public Builder setDestdir(String destdir) { + target.setDestdir(destdir); + return this; + } + + public Builder setDynamic(boolean dynamic) { + target.setDynamic(dynamic); + return this; + } + + public Builder setSchemas(String schemas) { + target.setSchemas(schemas); + return this; + } + + public Builder setSrcpaths(String srcpaths) { + target.setSrcpaths(srcpaths); + return this; + } + + public Builder setTarget(String targetDir) { + target.setTarget(targetDir); + return this; + } + + public GxpcConfiguration build(List<String> files) { + target.configure(files); + return target; + } + } +} Added: struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcUtil.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcUtil.java?rev=683730&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcUtil.java (added) +++ struts/sandbox/trunk/struts2-gxp-plugin/src/main/java/org/apache/struts2/gxp/template/GxpcUtil.java Thu Aug 7 14:44:33 2008 @@ -0,0 +1,237 @@ +package org.apache.struts2.gxp.template; + + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; + +import com.google.gxp.base.GxpContext; +import com.google.gxp.base.dynamic.GxpCompilationException; +import com.google.gxp.base.dynamic.StubGxpTemplate; +import com.google.gxp.com.google.common.base.Charsets; +import com.google.gxp.com.google.common.collect.ImmutableList; +import com.google.gxp.com.google.common.collect.Lists; +import com.google.gxp.com.google.common.io.Bytes; +import com.google.gxp.compiler.Compiler; +import com.google.gxp.compiler.InvalidConfigException; +import com.google.gxp.compiler.alerts.AlertSink; +import com.google.gxp.compiler.alerts.ConfigurableAlertPolicy; +import com.google.gxp.compiler.alerts.PrintingAlertSink; +import com.google.gxp.compiler.cli.Gxpc; +import com.google.gxp.compiler.fs.FileRef; +import com.google.gxp.compiler.fs.JavaFileManagerImpl; +import com.google.gxp.compiler.fs.JavaFileRef; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +public abstract class GxpcUtil { + private static final Logger LOG = LoggerFactory.getLogger(GxpcUtil.class); + + private static final Method DEFINE_CLASS = AccessController.doPrivileged(new PrivilegedAction<Method>() { + public Method run() { + try { + Class<ClassLoader> loader = ClassLoader.class; + Method m = loader.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, + Integer.TYPE, Integer.TYPE, ProtectionDomain.class }); + m.setAccessible(true); + return m; + } catch (NoSuchMethodException e) { + throw new RuntimeException(); + } + } + }); + + public static void buildAndExec(String srcPath, String file, boolean dynamic, Appendable appendable, + GxpContext context) throws ClassNotFoundException, IllegalArgumentException, IllegalAccessException, + InvocationTargetException, InstantiationException, InvalidConfigException { + // create instance of template + String className = toJavaFileName(file); + Class clazz = null; + try { + clazz = Class.forName(className); + } catch (Exception e) { + // ok ok, lets compile it + } + + if (clazz == null) + clazz = build(srcPath, file, dynamic); + + Method method = getWriteMethod(clazz); + method.invoke(clazz.newInstance(), appendable, context); + } + + private static Method getWriteMethod(Class clazz) { + try { + return clazz.getMethod("write", Appendable.class, GxpContext.class); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (NoSuchMethodException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + + public static Class build(String srcPath, String file, boolean dynamic) throws InvalidConfigException { + ConfigurableAlertPolicy alertPolicy = new ConfigurableAlertPolicy(); + alertPolicy.setTreatWarningsAsErrors(false); + + AlertSink alertSink = new PrintingAlertSink(alertPolicy, true, System.err); + + GxpcConfiguration.Builder builder = new GxpcConfiguration.Builder(); + builder.setSrcpaths(srcPath); + builder.setDynamic(dynamic); + builder.setAlertPolicy(alertPolicy); + + List<String> fullPathFiles = new ArrayList<String>(); + fullPathFiles.add(srcPath + "/" + file); + + GxpcConfiguration configuration = builder.build(fullPathFiles); + Compiler compiler = new Compiler(configuration); + compiler.call(alertSink); + + // compile java files + return compileJava(file.substring(0, file.indexOf(".")), configuration); + + } + + private static String toJavaFileName(String file) { + return file.substring(0, file.indexOf(".")).replace("/", "."); + } + + private static Class compileJava(String javaFile, GxpcConfiguration configuration) { + // compile java + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>(); + + JavaFileManager javaFileManager = new JavaFileManagerImpl(javaCompiler.getStandardFileManager( + diagnosticCollector, Locale.US, Charsets.US_ASCII), configuration.getMemoryFileSystem()); + + try { + String className = javaFile.replace("/", "."); + JavaFileObject compilationUnit = javaFileManager.getJavaFileForInput(StandardLocation.SOURCE_PATH, + javaFile, JavaFileObject.Kind.SOURCE); + + Iterable<JavaFileObject> compilationUnits = ImmutableList.of(compilationUnit); + + // find the GXP jar file and add it to the classpath + ProtectionDomain protectionDomain = Gxpc.class.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + File gxpJar = new File(codeSource.getLocation().getFile()); + + List<String> optionList = new ArrayList<String>(); + optionList.addAll(Arrays.asList("-classpath", gxpJar.getAbsolutePath())); + + javaCompiler.getTask(null, javaFileManager, diagnosticCollector, optionList, null, compilationUnits) + .call(); + + List<Diagnostic<? extends JavaFileObject>> diagnostics = filterErrors(diagnosticCollector + .getDiagnostics()); + + if (!diagnostics.isEmpty()) { + throw new GxpCompilationException.Java(diagnostics); + } + + List<byte[]> classFiles = Lists.newArrayList(); + for (FileRef fileRef : configuration.getMemoryFileSystem().getManifest()) { + if (fileRef.getKind().equals(JavaFileObject.Kind.CLASS)) { + String outputClassName = javaFileManager.inferBinaryName(StandardLocation.CLASS_OUTPUT, + new JavaFileRef(fileRef)); + if (outputClassName.equals(className) || outputClassName.startsWith(className + "$")) { + classFiles.add(Bytes.toByteArray(fileRef.openInputStream())); + } + } + } + + // A single java compile can generate many .class files due to inner + // classes, and it + // is difficult to know what order to load them in to avoid + // NoClassDefFoundErrors, + // so what we do is go through the whole list attempting to load + // them all, keeping + // track of which ones file with NoClassDefFoundError. Then we loop + // and try again. + // This should eventually work no matter what order the files come + // in. + // + // We have an additional check to make sure that at least one file + // is loaded each + // time through the loop to prevent infinite looping. + // + // I'm not entirely happy with this schema, but it's the best I can + // come up with + // for now. + int oldCount, newCount; + do { + oldCount = classFiles.size(); + classFiles = defineClasses(classFiles); + newCount = classFiles.size(); + } while (newCount != 0 && newCount != oldCount); + + // get the main class generated durring this compile + Class c = Class.forName(className); + + return c; + } catch (GxpCompilationException e) { + throw e; + } catch (Throwable e) { + throw new GxpCompilationException.Throw(e); + } + } + + private static <T> List<Diagnostic<? extends T>> filterErrors(List<Diagnostic<? extends T>> diagnostics) { + List<Diagnostic<? extends T>> newList = Lists.newArrayList(); + for (Diagnostic<? extends T> diagnostic : diagnostics) { + if (diagnostic.getKind().equals(Diagnostic.Kind.ERROR)) { + newList.add(diagnostic); + } + } + return Collections.unmodifiableList(newList); + } + + private static List<byte[]> defineClasses(List<byte[]> classFiles) throws Throwable { + List<byte[]> failures = Lists.newArrayList(); + for (byte[] classFile : classFiles) { + try { + Class clazz = defineClass(classFile); + LOG.debug("Template class [#0] loaded", clazz.getName()); + } catch (NoClassDefFoundError e) { + failures.add(classFile); + } + } + return failures; + } + + /** + * Define a class using the SystemClassLoader so that the class has access + * to package private items in its java package. + */ + private static Class defineClass(byte[] classFile) throws Throwable { + ProtectionDomain PROTECTION_DOMAIN = StubGxpTemplate.class.getProtectionDomain(); + Object[] args = new Object[] { null, classFile, new Integer(0), new Integer(classFile.length), + PROTECTION_DOMAIN }; + try { + return (Class) DEFINE_CLASS.invoke(Thread.currentThread().getContextClassLoader(), args); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } +} Added: struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/struts-plugin.xml URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/struts-plugin.xml?rev=683730&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/struts-plugin.xml (added) +++ struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/struts-plugin.xml Thu Aug 7 14:44:33 2008 @@ -0,0 +1,10 @@ +<?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> + <bean type="org.apache.struts2.components.template.TemplateEngine" name="gxp" class="org.apache.struts2.gxp.template.GxpTemplateEngine" /> + <constant name="struts.ui.templateSuffix" value="gxp"/> +</struts> Added: struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/a.gxp URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/a.gxp?rev=683730&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/a.gxp (added) +++ struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/a.gxp Thu Aug 7 14:44:33 2008 @@ -0,0 +1,5 @@ +<gxp:template name='simple.a' + xmlns='http://www.w3.org/1999/xhtml' + xmlns:gxp='http://google.com/2001/gxp'> +[Open template] +</gxp:template> Added: struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/aclose.gxp URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/aclose.gxp?rev=683730&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/aclose.gxp (added) +++ struts/sandbox/trunk/struts2-gxp-plugin/src/main/resources/template/simple/aclose.gxp Thu Aug 7 14:44:33 2008 @@ -0,0 +1,5 @@ +<gxp:template name='simple.aclose' + xmlns='http://www.w3.org/1999/xhtml' + xmlns:gxp='http://google.com/2001/gxp'> +[Close template] +</gxp:template> \ No newline at end of file