Commits

aldrinleal  committed 4f6aef4

Versao Inicial

  • Participants

Comments (0)

Files changed (38)

+<?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>
+    <artifactId>modafocas-parent</artifactId>
+    <groupId>org.modafocas</groupId>
+    <version>1</version>
+  </parent>
+
+  <groupId>org.modafocas</groupId>
+  <artifactId>modafocas-gae-archetype</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>maven-archetype</packaging>
+
+  <name>modafocas-gae-archetype</name>
+
+  <scm>
+    <connection>scm:hg:http://hg.modafocas.org/${project.artifactId}</connection>
+    <developerConnection>http://hg.modafocas.org/${project.artifactId}</developerConnection>
+  </scm>
+
+  <issueManagement>
+    <system>bitbucket</system>
+    <url>http://hg.modafocas.org/${project.artifactId}/issues</url>
+  </issueManagement>
+
+  <build>
+    <extensions>
+      <extension>
+        <groupId>org.apache.maven.archetype</groupId>
+        <artifactId>archetype-packaging</artifactId>
+        <version>2.0-alpha-5</version>
+      </extension>
+    </extensions>
+
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <artifactId>maven-archetype-plugin</artifactId>
+          <version>2.0-alpha-5</version>
+          <extensions>true</extensions>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+</project>

File src/main/resources/META-INF/maven/archetype-metadata.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<archetype-descriptor xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" name="mdfcm"
+    xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <fileSets>
+    <fileSet filtered="true" packaged="true" encoding="UTF-8">
+      <directory>src/main/java</directory>
+      <includes>
+        <include>**/*.java</include>
+      </includes>
+    </fileSet>
+    <fileSet filtered="true" encoding="UTF-8">
+      <directory>src/main/webapp</directory>
+      <includes>
+        <include>**/*.jsp</include>
+        <include>**/*.xml</include>
+      </includes>
+    </fileSet>
+    <fileSet filtered="true" encoding="UTF-8">
+      <directory>src/main/resources</directory>
+      <includes>
+        <include>**/*.properties</include>
+      </includes>
+    </fileSet>
+    <fileSet encoding="UTF-8">
+      <directory>src/main/webapp</directory>
+      <includes>
+        <include>**/*.jpg</include>
+        <include>**/*.MF</include>
+        <include>**/*.png</include>
+        <include>**/*.ico</include>
+        <include>**/*.tld</include>
+        <include>**/*.gif</include>
+        <include>**/*.css</include>
+      </includes>
+    </fileSet>
+    <fileSet filtered="true" encoding="UTF-8">
+      <directory>.settings</directory>
+      <includes>
+        <include>**/*.xml</include>
+      </includes>
+    </fileSet>
+    <fileSet encoding="UTF-8">
+      <directory>.settings</directory>
+      <includes>
+        <include>**/*.prefs</include>
+        <include>**/*.component</include>
+        <include>**/*.container</include>
+        <include>**/*.name</include>
+        <include>**/*.jsdtscope</include>
+      </includes>
+    </fileSet>
+    <fileSet filtered="true" encoding="UTF-8">
+      <directory></directory>
+      <includes>
+        <include>.classpath</include>
+        <include>.project</include>
+      </includes>
+    </fileSet>
+  </fileSets>
+</archetype-descriptor>

File src/main/resources/archetype-resources/pom.xml

+<?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>
+		<artifactId>modafocas-parent</artifactId>
+		<groupId>org.modafocas</groupId>
+		<version>1</version>
+	</parent>
+	<groupId>${groupId}</groupId>
+	<artifactId>${artifactId}</artifactId>
+	<version>${version}</version>
+	<packaging>war</packaging>
+	<name>${artifactId}</name>
+	<scm>
+		<connection>scm:hg:http://hg.modafocas.org/${artifactId}</connection>
+		<developerConnection>http://hg.modafocas.org/${artifactId}</developerConnection>	
+	</scm>
+
+	<issueManagement>
+    	<system>bitbucket</system>
+    	<url>http://hg.modafocas.org/${artifactId}/issues</url>
+	</issueManagement>
+
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-war-plugin</artifactId>
+				<version>2.1-alpha-2</version>
+				<configuration>
+					<webResources>
+						<resource>
+							<directory>src/main/webapp</directory>
+							<filtering>true</filtering>
+							<includes>
+								<include>**/appengine-web.xml</include>
+							</includes>
+						</resource>
+					</webResources>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>net.kindleit</groupId>
+				<artifactId>maven-gae-plugin</artifactId>
+				<version>0.5.9</version>
+				<configuration>
+					<unpackVersion>${gae.version}</unpackVersion>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-release-plugin</artifactId>
+				<configuration>
+					<goals>gae:deploy</goals>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>2.0</version>
+				<configuration>
+					<source>1.6</source>
+					<target>1.6</target>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+	<profiles>
+		<profile>
+			<id>integration-build</id>
+			<properties>
+				<gae.application.version>1</gae.application.version>
+			</properties>
+		</profile>
+		<profile>
+			<id>release-build</id>
+			<activation>
+				<property>
+					<name>performRelease</name>
+					<value>true</value>
+				</property>
+			</activation>
+			<properties>
+				<gae.application.version>2</gae.application.version>
+			</properties>
+		</profile>
+	</profiles>
+	<dependencies>
+		<dependency>
+			<groupId>com.google.appengine</groupId>
+			<artifactId>geronimo-jpa_3.0_spec</artifactId>
+			<version>1.1.1</version>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.google.appengine</groupId>
+			<artifactId>appengine-api-1.0-sdk</artifactId>
+			<version>${gae.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-servlet_2.5_spec</artifactId>
+			<version>1.2</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>taglibs</groupId>
+			<artifactId>standard</artifactId>
+			<version>1.1.2</version>
+			<scope>runtime</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>1.5.6</version>
+		</dependency>
+		<dependency>
+			<groupId>ch.qos.logback</groupId>
+			<artifactId>logback-classic</artifactId>
+			<version>0.9.15</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.5</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.google.appengine</groupId>
+			<artifactId>appengine-api-labs</artifactId>
+			<version>${gae.version}</version>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.google.appengine</groupId>
+			<artifactId>appengine-api-stubs</artifactId>
+			<version>${gae.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.google.appengine</groupId>
+			<artifactId>appengine-testing</artifactId>
+			<version>${gae.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.google.code.guice</groupId>
+			<artifactId>guice</artifactId>
+			<version>2.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.code.guice</groupId>
+			<artifactId>guice-servlet</artifactId>
+			<version>2.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>aopalliance</groupId>
+			<artifactId>aopalliance</artifactId>
+			<version>1.0</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-codec</groupId>
+			<artifactId>commons-codec</artifactId>
+			<version>1.4</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>jcl-over-slf4j</artifactId>
+			<version>1.5.11</version>
+		</dependency>
+		<dependency>
+			<groupId>com.googlecode.objectify</groupId>
+			<artifactId>objectify</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-beanutils</groupId>
+			<artifactId>commons-beanutils-core</artifactId>
+			<version>1.8.3</version>
+			<scope>compile</scope>
+			<exclusions>
+				<exclusion>
+					<artifactId>commons-logging</artifactId>
+					<groupId>commons-logging</groupId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-jsp_2.1_spec</artifactId>
+			<version>1.0.1</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.google.appengine</groupId>
+			<artifactId>appengine-tools-api</artifactId>
+			<version>1.3.4</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>commons-lang</groupId>
+			<artifactId>commons-lang</artifactId>
+			<version>2.5</version>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>commons-collections</groupId>
+			<artifactId>commons-collections</artifactId>
+			<version>3.2.1</version>
+			<scope>compile</scope>
+		</dependency>
+	</dependencies>
+	<properties>
+		<downloadSources>true</downloadSources>
+		<gae.version>1.3.4</gae.version>
+		<gae.application.version>0</gae.application.version>
+	</properties>
+</project>

File src/main/resources/archetype-resources/src/main/java/modafocas/model/Contato.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+package ${packageInPathFormat}.modafocas.model;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.UUID;
+
+import javax.persistence.Id;
+
+public class Contato implements Serializable {
+	private static final long serialVersionUID = -1763843494527143088L;
+
+	public static final String USER_KEY = Contato.class.getName();
+
+	@Id
+	private Long id;
+
+	private String login;
+
+	private String uuid;
+
+	private String token;
+
+	private String tokenSecret;
+
+	private Date lastUpdated;
+
+	public Contato() {
+		this.uuid = UUID.randomUUID().toString().toUpperCase();
+	}
+
+	public Contato(String login) {
+		this();
+		this.login = login;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getLogin() {
+		return login;
+	}
+
+	public void setLogin(String login) {
+		this.login = login;
+	}
+
+	public String getUuid() {
+		return uuid;
+	}
+
+	public void setUuid(String uuid) {
+		this.uuid = uuid;
+	}
+
+	public String getToken() {
+		return token;
+	}
+
+	public void setToken(String token) {
+		this.token = token;
+	}
+
+	public String getTokenSecret() {
+		return tokenSecret;
+	}
+
+	public void setTokenSecret(String tokenSecret) {
+		this.tokenSecret = tokenSecret;
+	}
+
+	public Date getLastUpdated() {
+		return lastUpdated;
+	}
+
+	public void setLastUpdated(Date lastUpdated) {
+		this.lastUpdated = lastUpdated;
+	}
+}

File src/main/resources/archetype-resources/src/main/java/modafocas/server/ContatoDao.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+package ${packageInPathFormat}.modafocas.server;
+
+import java.util.Collection;
+import java.util.Date;
+
+import ${packageInPathFormat}.modafocas.model.Contato;
+
+import com.google.inject.Inject;
+import com.googlecode.objectify.Objectify;
+
+public class ContatoDao {
+	Objectify objectify;
+
+	@Inject
+	public void setObjectify(Objectify objectify) {
+		this.objectify = objectify;
+	}
+
+	public Collection<Contato> findAll() {
+		return objectify.query(Contato.class).list();
+	}
+
+	public void create(Contato user) {
+		objectify.put(user);
+	}
+
+	public Contato findByUUID(String uuid) {
+		return objectify.query(Contato.class).filter("uuid", uuid).get();
+	}
+
+	public Contato findByLogin(String login) {
+		return objectify.query(Contato.class).filter("login", login).get();
+	}
+
+	public boolean loginExistsP(String login) {
+		return null != findByLogin(login);
+	}
+
+	public boolean uuidExistsP(String uuid) {
+		return null != findByUUID(uuid);
+	}
+
+	public void refreshLastUpdated(Contato sourceUser) {
+		sourceUser.setLastUpdated(new Date());
+		objectify.put(sourceUser);
+	}
+}

File src/main/resources/archetype-resources/src/main/java/modafocas/util/GuiceContextListener.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+package ${packageInPathFormat}.modafocas.util;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.servlet.GuiceServletContextListener;
+
+public class GuiceContextListener extends GuiceServletContextListener {
+	@Override
+	protected Injector getInjector() {
+		Injector injector = Guice.createInjector(new PersistenceModule(),
+				new WebModule());
+
+		return injector;
+	}
+}

File src/main/resources/archetype-resources/src/main/java/modafocas/util/PersistenceModule.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+package ${packageInPathFormat}.modafocas.util;
+
+import ${packageInPathFormat}.modafocas.model.Contato;
+import ${packageInPathFormat}.modafocas.server.ContatoDao;
+
+import com.google.appengine.api.users.UserService;
+import com.google.appengine.api.users.UserServiceFactory;
+import com.google.inject.Binder;
+import com.google.inject.Module;
+import com.googlecode.objectify.Objectify;
+import com.googlecode.objectify.ObjectifyService;
+
+public class PersistenceModule implements Module {
+	@Override
+	public void configure(Binder binder) {
+		configurePersistence(binder);
+	}
+
+	private void configurePersistence(Binder binder) {
+		ObjectifyService.register(Contato.class);
+
+		binder.bind(UserService.class).toInstance(
+				UserServiceFactory.getUserService());
+
+		binder.bind(Objectify.class).toInstance(ObjectifyService.begin());
+
+		binder.bind(ContatoDao.class).toInstance(new ContatoDao());
+	}
+}

File src/main/resources/archetype-resources/src/main/java/modafocas/util/URIUtil.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+package ${packageInPathFormat}.modafocas.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class URIUtil {
+	private static final URIUtil INSTANCE = new URIUtil();
+
+	public static final URIUtil getInstance() {
+		return INSTANCE;
+	}
+
+	private static final Pattern PATTERN_ROOT = Pattern
+			.compile("^(http://[^/]+).*${symbol_dollar}");
+
+	public StringBuilder getRequestBase(String requestPath) {
+		Matcher matcher = PATTERN_ROOT.matcher(requestPath);
+
+		if (!matcher.find())
+			return new StringBuilder("");
+
+		return new StringBuilder(matcher.group(1));
+	}
+	
+	public StringBuilder getRequestBase(StringBuffer buffer) {
+		return getRequestBase(buffer.toString());
+	}
+}

File src/main/resources/archetype-resources/src/main/java/modafocas/util/WebModule.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+package ${packageInPathFormat}.modafocas.util;
+
+import ${packageInPathFormat}.modafocas.web.LoginServlet;
+import ${packageInPathFormat}.modafocas.web.MainServlet;
+
+import com.google.inject.servlet.ServletModule;
+
+public class WebModule extends ServletModule {
+	@Override
+	protected void configureServlets() {
+		serve("/app/login").with(LoginServlet.class);
+		serve("/app/main").with(MainServlet.class);
+	}
+}

File src/main/resources/archetype-resources/src/main/java/modafocas/web/BaseHttpServlet.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+package ${packageInPathFormat}.modafocas.web;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@SuppressWarnings("serial")
+public class BaseHttpServlet extends HttpServlet {
+	private String page;
+
+	public BaseHttpServlet(String page) {
+		this.page = page;
+	}
+
+	@Override
+	protected final void service(HttpServletRequest req,
+			HttpServletResponse resp) throws ServletException, IOException {
+		try {
+			serviceInternal(req, resp);
+		} catch (Exception exc) {
+			getServletContext().log("Failure", exc);
+			throw new ServletException(exc);
+		}
+	}
+
+	protected void serviceInternal(HttpServletRequest req,
+			HttpServletResponse resp) throws Exception {
+		forwardJsp(req, resp, page);
+	}
+
+	protected void forwardJsp(HttpServletRequest req, HttpServletResponse resp,
+			String pageId) throws ServletException, IOException {
+		String path = String.format("/WEB-INF/pages/%s.jsp", pageId);
+
+		req.getRequestDispatcher(path).forward(req, resp);
+	}
+}

File src/main/resources/archetype-resources/src/main/java/modafocas/web/LoginServlet.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+package ${packageInPathFormat}.modafocas.web;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.google.appengine.api.users.User;
+import com.google.appengine.api.users.UserService;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class LoginServlet extends HttpServlet {
+	private static final long serialVersionUID = 3829746399446849584L;
+
+	@Inject
+	private UserService userService;
+
+	public void setUserService(UserService userService) {
+		this.userService = userService;
+	}
+
+	@Override
+	protected void service(HttpServletRequest req, HttpServletResponse resp)
+			throws ServletException, IOException {
+		User currentUser = userService.getCurrentUser();
+
+		if (null == currentUser) {
+			resp.sendRedirect(userService.createLoginURL(req.getRequestURI()));
+		} else {
+			resp.sendRedirect("/app/main");
+		}
+	}
+}

File src/main/resources/archetype-resources/src/main/java/modafocas/web/MainServlet.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+package ${packageInPathFormat}.modafocas.web;
+
+import com.google.inject.Singleton;
+
+@Singleton
+public class MainServlet extends BaseHttpServlet {
+	private static final long serialVersionUID = -1518290665919075089L;
+
+	public MainServlet() {
+		super("main");
+	}
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/AbstractAction.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+package ${packageInPathFormat}.tagonist;
+
+import ${packageInPathFormat}.apache.commons.beanutils.BeanUtils;
+
+/**
+ * Client developers typically extend this class rather than implement Action
+ * themselves.  It provides for population of bean properties on the model
+ * from the http request parameters.
+ * 
+ * @author Jeff Schnitzer
+ */
+public abstract class AbstractAction implements Action
+{
+	/**
+	 * Maintain this as a member variable so it need not be passed around.
+	 */
+	private ActionContext ctx;
+	
+	/**
+	 * Provide access to the context to any subclasses.
+	 */
+	protected ActionContext getCtx() { return this.ctx; }
+	
+	/** 
+	 */
+	abstract public void execute() throws Exception;
+	
+	/**
+	 * Called prior to bean population of the model.  If you would like a different
+	 * object populated, call getCtx().setModel() from here.
+	 */
+	protected void initialize() throws Exception
+	{
+		// Do nothing by default
+	}
+	
+	/**
+	 * Override execute() instead.
+	 * 
+	 * @see ${packageInPathFormat}.tagonist.Action${symbol_pound}execute(${packageInPathFormat}.tagonist.ActionContext)
+	 */
+	public final void execute(ActionContext ctx) throws Exception
+	{
+		this.ctx = ctx;
+		
+		this.getCtx().setModel(this);
+		
+		this.initialize();
+		
+		Object model = this.getCtx().getModel();
+		if (model != null)
+		{
+			BeanUtils.populate(model, this.getCtx().getRequest().getParameterMap());
+			BeanUtils.populate(model, this.getCtx().getActionParams());
+		}
+	
+		this.execute();
+	}
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/Action.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+
+package ${packageInPathFormat}.tagonist;
+
+/**
+ * Actions must implement this interface in order to be processed
+ * by the Tagonist framework.  While you may wish to implement this
+ * interface directly, you probably should extend AbstractAction instead.
+ * 
+ * @author Jeff Schnitzer
+ */
+public interface Action
+{
+	/**
+	 * @throws RedirectException, ForwardException 
+	 */
+	public void execute(ActionContext ctx) throws Exception;
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/ActionContext.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+package ${packageInPathFormat}.tagonist;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.PageContext;
+
+/**
+ * Interface that provides container APIs to action classes.
+ *
+ * @author Jeff Schnitzer
+ */
+public interface ActionContext
+{
+	/**
+	 * Assigns an object to be the model for rendering in the view.
+	 */
+	public void setModel(Object value);
+
+	/**
+	 * @return the model set by this action.
+	 */
+	public Object getModel();
+
+	/**
+	 * @return a Map of parameters passed into the action using the
+	 * 	param tag.  Not to be confused with http parameters.
+	 */
+	public Map<String, Object> getActionParams();
+
+	/**
+	 * Sets a key/value error flag.
+	 */
+	public void setError(String errorName, Object value);
+
+	/**
+	 * Gets an error or null if no error with that name has been set.
+	 */
+	public Object getError(String errorName);
+
+	/**
+	 * Gets a list of all errors
+	 */
+	public Map<String, Object> getErrors();
+
+	/**
+	 * Removes an error
+	 */
+	public void removeError(String errorName);
+
+	/**
+	 * Do we have any errors?
+	 */
+	public boolean hasErrors();
+
+	/**
+	 * @return the JSP page context.
+	 */
+	public PageContext getPageContext();
+
+	/**
+	 * Convenience method.
+	 */
+	public HttpServletRequest getRequest();
+
+	/**
+	 * Convenience method.
+	 */
+	public HttpServletResponse getResponse();
+
+	/**
+	 * Convenience method.
+	 */
+	public HttpSession getSession();
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/ActionTag.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+package ${packageInPathFormat}.tagonist;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.TagSupport;
+
+import ${packageInPathFormat}.apache.commons.logging.Log;
+import ${packageInPathFormat}.apache.commons.logging.LogFactory;
+
+import com.google.inject.Injector;
+
+/**
+ * JSP custom tag that instantiates Action classes.
+ *
+ * @author Jeff Schnitzer
+ */
+@SuppressWarnings("serial")
+public class ActionTag extends TagSupport implements ActionContext
+{
+	/** */
+	private static Log log = LogFactory.getLog(ActionTag.class);
+
+	/** */
+	public final String SCOPE_PAGE = "page";
+	public final String SCOPE_REQUEST = "request";
+	public final String SCOPE_SESSION = "session";
+	public final String SCOPE_APPLICATION = "application";
+
+	/** */
+	protected String var;
+	public String getVar() { return this.var; }
+	public void setVar(String value) { this.var = value; }
+
+	/** */
+	protected String varErrors;
+	public String getVarErrors() { return this.varErrors; }
+	public void setVarErrors(String value)
+	{
+		this.varErrors = value;
+		this.errors = null;	// need to reset this 'cause the tag lifecycle is lame
+	}
+
+	/** */
+	protected String type;
+	public String getType() { return this.type; }
+	public void setType(String value) { this.type = value; }
+
+	/** */
+	protected String scope;
+	public String getScope() { return this.scope; }
+	public void setScope(String value) { this.scope = value; }
+
+	/** */
+	protected boolean force;
+	public boolean getForce() { return this.force; }
+	public void setForce(boolean value) { this.force = value; }
+
+	/**
+	 * Lazily created Map of action parameters.
+	 */
+	protected Map<String, Object> actionParams;
+
+	/**
+	 * The model that will (hopefully) be assigned by an action.
+	 */
+	protected Object model;
+
+	/**
+	 * This needs to be set fairly quickly, then we ignore the textual
+	 * scope property.
+	 */
+	protected int pageContextScope = PageContext.REQUEST_SCOPE;
+
+	/**
+	 * Lazily created Map of errors.
+	 */
+	protected Map<String, Object> errors;
+
+	private Injector injector;
+
+	/* (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}setModel(java.lang.Object)
+	 */
+	public void setModel(Object value)
+	{
+		if (log.isDebugEnabled())
+		{
+			if (value == null)
+				log.debug("Setting model to null");
+			else
+				log.debug("Setting model to " + value.getClass().getName());
+		}
+
+		this.model = value;
+
+		// Keep the page context scope up to date
+		if (this.var != null)
+			this.pageContext.setAttribute(this.var, this.model, this.pageContextScope);
+	}
+
+	/* (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}getModel()
+	 */
+	public Object getModel() { return this.model; }
+
+	/* (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}getActionParams()
+	 */
+	public Map<String, Object> getActionParams()
+	{
+		if (this.actionParams == null)
+			return Collections.emptyMap();
+		else
+			return this.actionParams;
+	}
+
+	/**
+	 * Used only by nested ParamTag to pass parameters up.
+	 */
+	void setActionParam(String name, Object value)
+	{
+		if (this.actionParams == null)
+			this.actionParams = new HashMap<String, Object>();
+
+		if (log.isDebugEnabled())
+			log.debug("With param " + name + ":  " + value);
+
+		this.actionParams.put(name, value);
+	}
+
+	/**
+	 * Sets an error in a map.
+	 */
+	public void setError(String errorName, Object value)
+	{
+		if (this.errors == null)
+		{
+			this.errors = new HashMap<String, Object>();
+
+			// Keep the page context scope up to date
+			if (this.varErrors != null)
+				this.pageContext.setAttribute(this.varErrors, this.errors, this.pageContextScope);
+		}
+
+		this.errors.put(errorName, value);
+	}
+
+	/**
+	 * Gets an error from the map.
+	 */
+	public Object getError(String errorName)
+	{
+		if (this.errors != null)
+			return this.errors.get(errorName);
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}hasErrors()
+	 */
+	public boolean hasErrors()
+	{
+		return this.errors != null && !this.errors.isEmpty();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}getErrors()
+	 */
+	@SuppressWarnings("unchecked")
+	public Map<String, Object> getErrors()
+	{
+		if (this.errors == null)
+			return Collections.EMPTY_MAP;
+		else
+			return this.errors;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}removeError(java.lang.String)
+	 */
+	public void removeError(String errorName)
+	{
+		this.errors.remove(errorName);
+	}
+
+	/* (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}getPageContext()
+	 */
+	public PageContext getPageContext()
+	{
+		return this.pageContext;
+	}
+
+	/* (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}getRequest()
+	 */
+	public HttpServletRequest getRequest()
+	{
+		return (HttpServletRequest)this.pageContext.getRequest();
+	}
+
+	/* (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}getResponse()
+	 */
+	public HttpServletResponse getResponse()
+	{
+		return (HttpServletResponse)this.pageContext.getResponse();
+	}
+
+	/* (non-Javadoc)
+	 * @see ${packageInPathFormat}.tagonist.ActionContext${symbol_pound}getSession()
+	 */
+	public HttpSession getSession()
+	{
+		return this.getRequest().getSession();
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.servlet.jsp.tagext.Tag${symbol_pound}doStartTag()
+	 */
+	@Override
+	public int doStartTag() throws JspException
+	{
+		this.model = null;
+
+		if (this.errors != null)
+			this.errors.clear();
+
+		if (this.actionParams != null)
+			this.actionParams.clear();
+		
+		this.injector = (Injector) this.pageContext.getServletContext().getAttribute(Injector.class.getName());
+
+		return EVAL_BODY_INCLUDE;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.servlet.jsp.tagext.Tag${symbol_pound}doEndTag()
+	 */
+	@Override
+	public int doEndTag() throws JspException
+	{
+		// Figure out this.pageContextScope immediately
+		if (this.scope != null)
+		{
+			if (this.SCOPE_PAGE.equals(this.scope))
+				this.pageContextScope = PageContext.PAGE_SCOPE;
+			else if (this.SCOPE_REQUEST.equals(this.scope))
+				this.pageContextScope = PageContext.REQUEST_SCOPE;
+			else if (this.SCOPE_SESSION.equals(this.scope))
+				this.pageContextScope = PageContext.SESSION_SCOPE;
+			else if (this.SCOPE_APPLICATION.equals(this.scope))
+				this.pageContextScope = PageContext.APPLICATION_SCOPE;
+		}
+
+		// Check to see if we should just recycle existing object
+		if (this.var != null && !this.force)
+		{
+			if (this.pageContext.findAttribute(this.var) != null)
+				return SKIP_BODY;
+		}
+
+		try
+		{
+			Class<?> actionClass = Class.forName(this.type);
+			Action act = (Action)actionClass.newInstance();
+			
+			injector.injectMembers(act);
+
+			if (log.isDebugEnabled())
+				log.debug("Executing action class " + actionClass.getName());
+
+			// Note that when the action sets the model, it will
+			// at that time establish the context attribute.
+			act.execute(this);
+
+			return EVAL_PAGE;
+		}
+		catch (ClassNotFoundException ex)
+		{
+			log.error("Problem with action " + this.type, ex);
+			throw new JspException(ex);
+		}
+		catch (InstantiationException ex)
+		{
+			log.error("Problem with action " + this.type, ex);
+			throw new JspException(ex);
+		}
+		catch (IllegalAccessException ex)
+		{
+			log.error("Problem with action " + this.type, ex);
+			throw new JspException(ex);
+		}
+		catch (ForwardException ex)
+		{
+			if (log.isDebugEnabled())
+				log.debug("Forwarding to " + ex.getPath());
+
+			try
+			{
+				this.pageContext.forward(ex.getPath());
+			}
+			catch (ServletException sex) { throw new JspException(sex); }
+			catch (IOException iox) { throw new JspException(iox); }
+
+			return SKIP_PAGE;
+		}
+		catch (RedirectException ex)
+		{
+			if (log.isDebugEnabled())
+				log.debug("Redirecting to " + ex.getPath());
+
+			try
+			{
+				this.getResponse().sendRedirect(ex.getPath());
+			}
+			catch (IOException iox) { throw new JspException(iox); }
+
+			return SKIP_PAGE;
+		}
+		catch (SkipPageException ex)
+		{
+			return SKIP_PAGE;
+		}
+		catch (RuntimeException ex)
+		{
+			log.error("Problem with action " + this.type, ex);
+			// No need to catch these
+			throw ex;
+		}
+		catch (Exception ex)
+		{
+			log.error("Problem with action " + this.type, ex);
+			throw new JspException(ex);
+		}
+	}
+	
+	
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/ErrorTrappingAction.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+package ${packageInPathFormat}.tagonist;
+
+import ${packageInPathFormat}.tagonist.beanutils.ErrorTrappingBeanUtilsBean;
+
+/**
+ * This action replaces AbstractAction.  Instead of ignoring or throwing
+ * an exception when http parameters cannot be converted to the correct
+ * model type, an error message is put in the error collection.
+ *
+ * @author Jeff Schnitzer
+ */
+public abstract class ErrorTrappingAction implements Action
+{
+	/** Smarter than the average BeanUtilsBean */
+	protected static ErrorTrappingBeanUtilsBean beanUtils = new ErrorTrappingBeanUtilsBean();
+
+	/**
+	 * Maintain this as a member variable so it need not be passed around.
+	 */
+	private ActionContext ctx;
+
+	/**
+	 * Provide access to the context to any subclasses.
+	 */
+	protected ActionContext getCtx() { return this.ctx; }
+
+	/**
+	 */
+	abstract public void execute() throws Exception;
+
+	/**
+	 * Called prior to bean population of the model. If you would like a
+	 * different object populated, call getCtx().setModel() from here.
+	 */
+	protected void initialize() throws Exception
+	{
+		// Do nothing by default
+	}
+
+	/**
+	 * Override execute() instead.
+	 *
+	 * @see ${packageInPathFormat}.tagonist.Action${symbol_pound}execute(${packageInPathFormat}.tagonist.ActionContext)
+	 */
+	@SuppressWarnings("unchecked")
+	public final void execute(ActionContext ctx) throws Exception
+	{
+		this.ctx = ctx;
+
+		this.getCtx().setModel(this);
+
+		this.initialize();
+
+		Object model = this.getCtx().getModel();
+		if (model != null)
+		{
+			beanUtils.populate(model, this.getCtx().getRequest().getParameterMap(), this.getCtx());
+			beanUtils.populate(model, this.getCtx().getActionParams(), this.getCtx());
+		}
+
+		this.execute();
+	}
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/ForwardException.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+package ${packageInPathFormat}.tagonist;
+
+/**
+ * Throwing a ForwardException from within the body of Action.execute() will
+ * result in a server-side forward.  Be cautious that the response buffer has
+ * not already been committed.
+ * 
+ * @author Jeff Schnitzer
+ */
+@SuppressWarnings("serial")
+public class ForwardException extends RuntimeException
+{
+	/**
+	 * @param path must be suitable for passing to PageContext.forward() 
+	 */
+	public ForwardException(String path)
+	{
+		super(path);
+	}
+	
+	/**
+	 * @return the path to forward to
+	 */
+	public String getPath() { return this.getMessage(); }
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/ParamTag.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+package ${packageInPathFormat}.tagonist;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.TagSupport;
+
+/**
+ * JSP custom tag designed to be nested within an ActionTag, allows passing
+ * special parameters which can be recognized by actions.  Not the same as
+ * http request parameters, although Action implementations may choose to
+ * populate models from this collection.
+ * 
+ * @author Jeff Schnitzer
+ */
+@SuppressWarnings("serial")
+public class ParamTag extends TagSupport
+{
+	/** */
+	protected String name;
+	public String getName() { return this.name; }
+	public void setName(String value) { this.name = value; }
+	
+	/** */
+	protected Object value;
+	public Object getValue() { return this.value; }
+	public void setValue(Object value) { this.value = value; }
+
+	/* (non-Javadoc)
+	 * @see javax.servlet.jsp.tagext.Tag${symbol_pound}doEndTag()
+	 */
+	public int doEndTag() throws JspException
+	{
+		((ActionTag)this.getParent()).setActionParam(this.name, this.value);
+		
+		return EVAL_PAGE;
+	}
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/RedirectException.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+package ${packageInPathFormat}.tagonist;
+
+/**
+ * Throwing a RedirectException from within the body of Action.execute() will
+ * result in a client-side redirect.  Be cautious that the response buffer has
+ * not already been committed.
+ * 
+ * @author Jeff Schnitzer
+ */
+@SuppressWarnings("serial")
+public class RedirectException extends RuntimeException
+{
+	/**
+	 * @param path must be suitable for passing to HttpServletResponse.sendRedirect(). 
+	 */
+	public RedirectException(String path)
+	{
+		super(path);
+	}
+	
+	/**
+	 */
+	public String getPath()
+	{
+		return this.getMessage();
+	}
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/SkipPageException.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+package ${packageInPathFormat}.tagonist;
+
+/**
+ * Throwing a SkipPageException from within the body of Action.execute() will
+ * prevent execution of the remainder of the JSP.  We use this rather than
+ * javax.servlet.jsp.SkipPageException because this extends RuntimeException.
+ * 
+ * @author Jeff Schnitzer
+ */
+@SuppressWarnings("serial")
+public class SkipPageException extends RuntimeException
+{
+}

File src/main/resources/archetype-resources/src/main/java/tagonist/beanutils/ErrorTrappingBeanUtilsBean.java

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ */
+package ${packageInPathFormat}.tagonist.beanutils;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URL;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Iterator;
+import java.util.Map;
+
+import ${packageInPathFormat}.apache.commons.beanutils.BeanUtilsBean;
+import ${packageInPathFormat}.apache.commons.beanutils.ConversionException;
+import ${packageInPathFormat}.apache.commons.beanutils.ConvertUtilsBean;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.ArrayConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.BigDecimalConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.BigIntegerConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.BooleanConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.ByteConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.CharacterConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.ClassConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.DoubleConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.FileConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.FloatConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.IntegerConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.LongConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.ShortConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.SqlDateConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.SqlTimeConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.SqlTimestampConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.StringConverter;
+import ${packageInPathFormat}.apache.commons.beanutils.converters.URLConverter;
+import ${packageInPathFormat}.tagonist.ActionContext;
+
+/**
+ * A much smarter BeanUtilsBean that traps errors instead of ignoring
+ * them or throwing exceptions.
+ *
+ * @author Jeff Schnitzer
+ * @author Jon Stevens
+ */
+public class ErrorTrappingBeanUtilsBean extends BeanUtilsBean
+{
+	/** */
+	public ErrorTrappingBeanUtilsBean()
+	{
+		// Must reregister everything so that we get exceptions
+
+		ConvertUtilsBean cu = this.getConvertUtils();
+
+        cu.register(new BigDecimalConverter(), BigDecimal.class);
+        cu.register(new BigIntegerConverter(), BigInteger.class);
+		cu.register(new BooleanConverter(), Boolean.TYPE);
+		cu.register(new BooleanConverter(), Boolean.class);
+		cu.register(new ArrayConverter(boolean[].class, new BooleanConverter()), boolean[].class);
+		cu.register(new ByteConverter(), Byte.TYPE);
+		cu.register(new ByteConverter(), Byte.class);
+		cu.register(new ArrayConverter(byte[].class, new ByteConverter()), byte[].class);
+		cu.register(new CharacterConverter(), Character.TYPE);
+		cu.register(new CharacterConverter(), Character.class);
+		cu.register(new ArrayConverter(char[].class, new CharacterConverter()), char[].class);
+		cu.register(new ClassConverter(), Class.class);
+		cu.register(new DoubleConverter(), Double.TYPE);
+		cu.register(new DoubleConverter(), Double.class);
+		cu.register(new ArrayConverter(double[].class, new DoubleConverter()), double[].class);
+		cu.register(new FloatConverter(), Float.TYPE);
+		cu.register(new FloatConverter(), Float.class);
+		cu.register(new ArrayConverter(float[].class, new FloatConverter()), float[].class);
+		cu.register(new IntegerConverter(), Integer.TYPE);
+		cu.register(new IntegerConverter(), Integer.class);
+		cu.register(new ArrayConverter(int[].class, new IntegerConverter()), int[].class);
+		cu.register(new LongConverter(), Long.TYPE);
+		cu.register(new LongConverter(), Long.class);
+		cu.register(new ArrayConverter(long[].class, new LongConverter()), long[].class);
+		cu.register(new ShortConverter(), Short.TYPE);
+		cu.register(new ShortConverter(), Short.class);
+		cu.register(new ArrayConverter(short[].class, new ShortConverter()), short[].class);
+		cu.register(new StringConverter(), String.class);
+		cu.register(new ArrayConverter(String[].class, new StringConverter()), String[].class);
+		cu.register(new SqlDateConverter(), Date.class);
+		cu.register(new SqlTimeConverter(), Time.class);
+		cu.register(new SqlTimestampConverter(), Timestamp.class);
+		cu.register(new FileConverter(), File.class);
+		cu.register(new URLConverter(), URL.class);
+	}
+
+	/**
+	 * Populates the bean from the properties Map, storing any errors
+	 * in the errorSink.
+	 */
+    public void populate(Object bean, Map<String, Object> properties, ActionContext errorSink)
+	{
+		// Do nothing unless both arguments have been specified
+		if ((bean == null) || (properties == null))
+		{
+			return;
+		}
+
+		// Loop through the property name/value pairs to be set
+		Iterator<String> names = properties.keySet().iterator();
+		while (names.hasNext())
+		{
+
+			// Identify the property name and value(s) to be assigned
+			String name = names.next();
+			if (name == null)
+				continue;
+
+			Object value = properties.get(name);
+
+			// Perform the assignment for this property
+			try
+			{
+				this.setProperty(bean, name, value);
+			}
+			catch (ConversionException ex)
+			{
+				errorSink.setError(name, ex.getMessage());
+			}
+			catch (IllegalAccessException ex)
+			{
+				errorSink.setError(name, ex.getMessage());
+			}
+			catch (InvocationTargetException ex)
+			{
+				errorSink.setError(name, ex.getMessage());
+			}
+			catch (IllegalArgumentException ex)
+			{
+				errorSink.setError(name, ex.getMessage());
+			}
+			catch (StringIndexOutOfBoundsException ex)
+			{
+				errorSink.setError(name, ex.getMessage());
+			}
+		}
+	}
+}

File src/main/resources/archetype-resources/src/main/resources/log4j.properties

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+${symbol_pound} A default log4j configuration for log4j users.
+${symbol_pound}
+${symbol_pound} To use this configuration, deploy it into your application's WEB-INF/classes
+${symbol_pound} directory.  You are also encouraged to edit it as you like.
+
+${symbol_pound} Configure the console as our one appender
+log4j.appender.A1=${packageInPathFormat}.apache.log4j.ConsoleAppender
+log4j.appender.A1.layout=${packageInPathFormat}.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n
+
+${symbol_pound} tighten logging on the DataNucleus Categories
+log4j.category.DataNucleus.JDO=WARN, A1
+log4j.category.DataNucleus.Persistence=WARN, A1
+log4j.category.DataNucleus.Cache=WARN, A1
+log4j.category.DataNucleus.MetaData=WARN, A1
+log4j.category.DataNucleus.General=WARN, A1
+log4j.category.DataNucleus.Utility=WARN, A1
+log4j.category.DataNucleus.Transaction=WARN, A1
+log4j.category.DataNucleus.Datastore=WARN, A1
+log4j.category.DataNucleus.ClassLoading=WARN, A1
+log4j.category.DataNucleus.Plugin=WARN, A1
+log4j.category.DataNucleus.ValueGeneration=WARN, A1
+log4j.category.DataNucleus.Enhancer=WARN, A1
+log4j.category.DataNucleus.SchemaTool=WARN, A1

File src/main/resources/archetype-resources/src/main/resources/logging.properties

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+${symbol_pound} A default java.util.logging configuration.
+${symbol_pound} (All App Engine logging is through java.util.logging by default).
+${symbol_pound}
+${symbol_pound} To use this configuration, copy it into your application's WEB-INF
+${symbol_pound} folder and add the following to your appengine-web.xml:
+${symbol_pound} 
+${symbol_pound} <system-properties>
+${symbol_pound}   <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
+${symbol_pound} </system-properties>
+${symbol_pound}
+
+${symbol_pound} Set the default logging level for all loggers to WARNING
+.level = WARNING
+
+${symbol_pound} Set the default logging level for ORM, specifically, to WARNING
+DataNucleus.JDO.level=WARNING
+DataNucleus.Persistence.level=WARNING
+DataNucleus.Cache.level=WARNING
+DataNucleus.MetaData.level=WARNING
+DataNucleus.General.level=WARNING
+DataNucleus.Utility.level=WARNING
+DataNucleus.Transaction.level=WARNING
+DataNucleus.Datastore.level=WARNING
+DataNucleus.ClassLoading.level=WARNING
+DataNucleus.Plugin.level=WARNING
+DataNucleus.ValueGeneration.level=WARNING
+DataNucleus.Enhancer.level=WARNING
+DataNucleus.SchemaTool.level=WARNING

File src/main/resources/archetype-resources/src/main/webapp/META-INF/MANIFEST.MF

+Manifest-Version: 1.0
+Class-Path: 
+

File src/main/resources/archetype-resources/src/main/webapp/WEB-INF/appengine-web.xml

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+<?xml version="1.0" encoding="utf-8"?>
+<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"
+	xmlns:xsi="http://www.w3.${packageInPathFormat}/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://appengine.google.com/ns/1.0 http://googleappengine.googlecode.com/svn/branches/1.2.1/java/docs/appengine-web.xsd">
+	<application>${symbol_dollar}{project.name}</application>
+	<version>1</version>
+
+	<system-properties>
+		<property name="java.util.logging.config.file" value="WEB-INF/classes/logging.properties" />
+	</system-properties>
+
+	<sessions-enabled>true</sessions-enabled>
+</appengine-web-app>

File src/main/resources/archetype-resources/src/main/webapp/WEB-INF/pages/sql.jsp

+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+<%@ page contentType="text/plain; charset=UTF-8"%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
+<%@ taglib prefix="tagonist" uri="http://www.tagonist.${packageInPathFormat}/tagonist"%>
+
+// CREATE TABLE timeline (
+//   id BIGINT not null PRIMARY KEY,
+//   account VARCHAR(32) NOT NULL,
+//   url VARCHAR(128) NOT NULL UNIQUE,
+//   dateTime TIMESTAMP NOT NULL,
+//   text VARCHAR(140) NOT NULL,
+//   point VARCHAR(32)
+// );
+
+<c:forEach var="entry" items="${symbol_dollar}{entries}">
+INSERT INTO timeline (id, account, url, dateTime, text, point) VALUES (${symbol_dollar}{entry.id}, '${symbol_dollar}{entry.user}', '${symbol_dollar}{entry.url}', '${symbol_dollar}{entry.dateTime}', '${symbol_dollar}{entry.text}', ${symbol_dollar}{entry.geoSource});</c:forEach>