You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@click.apache.org by sa...@apache.org on 2010/07/15 13:56:18 UTC
svn commit: r964393 - in /click/trunk/click:
extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java
extras/src/org/apache/click/extras/gae/package.html
framework/src/org/apache/click/service/XmlConfigService.java
Author: sabob
Date: Thu Jul 15 11:56:17 2010
New Revision: 964393
URL: http://svn.apache.org/viewvc?rev=964393&view=rev
Log:
added automapping support for GAE with the caveat that page templates must be placed under the folders page or pages. CLK-639
Modified:
click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java
click/trunk/click/extras/src/org/apache/click/extras/gae/package.html
click/trunk/click/framework/src/org/apache/click/service/XmlConfigService.java
Modified: click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java
URL: http://svn.apache.org/viewvc/click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java?rev=964393&r1=964392&r2=964393&view=diff
==============================================================================
--- click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java (original)
+++ click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java Thu Jul 15 11:56:17 2010
@@ -24,8 +24,13 @@ import javax.servlet.ServletContextListe
import ognl.OgnlRuntime;
/**
- * Provides Google App Engine (GAE) support for Click applications.
- * <p/>
+ * Provides <a href="http://code.google.com/appengine/docs/java/overview.html" class="external" target="_blank">Google App Engine</a>
+ * (GAE) support for Click applications. GAE is a free Java hosting service
+ * provided by Google that allows you to quickly and easily make your Click
+ * applications available online.
+ *
+ * <h3>Configuration</h3>
+ *
* To deploy Click applications to GAE, you need to set the
* <tt>GoogleAppEngineListener</tt> listener in your <tt>web.xml</tt>:
*
@@ -74,6 +79,31 @@ import ognl.OgnlRuntime;
*
* </appengine-web-app> </pre>
*
+ * <h3>Performance Filter</h3>
+ *
+ * If you use Click's {@link org.apache.click.extras.filter.PerformanceFilter}
+ * you should also exclude the following static files from GAE, so that
+ * PerformanceFilter can set their <tt>expiry headers</tt>:
+ * <tt>*.css</tt>, <tt>*.js</tt>, <tt>*.png</tt> and <tt>*.gif</tt>. For example:
+ *
+ * <pre class="codeHtml">
+ * <?xml version="1.0" encoding="utf-8"?>
+ * <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
+ *
+ * ...
+ *
+ * <!-- Exclude the following files from being served as static files by GAE,
+ * as they will be processed by Click's PerformanceFilter. -->
+ * <static-files>
+ * <<span class="blue">exclude</span> path="<span class="red">**.htm</span>" />
+ * <<span class="blue">exclude</span> path="<span class="red">**.css</span>" />
+ * <<span class="blue">exclude</span> path="<span class="red">**.js</span>" />
+ * <<span class="blue">exclude</span> path="<span class="red">**.png</span>" />
+ * <<span class="blue">exclude</span> path="<span class="red">**.gif</span>" />
+ * </static-files>
+ *
+ * </appengine-web-app> </pre>
+ *
* <h3>File Uploads</h3>
*
* GAE does not allow web application to write to files on disk. This poses a
@@ -101,32 +131,76 @@ import ognl.OgnlRuntime;
* </file-upload-service>
* </click-app> </pre>
*
- * <h3>Performance Filter</h3>
+ * <h2>Limitations</h2>
*
- * If you use Click's {@link org.apache.click.extras.filter.PerformanceFilter}
- * you should also exclude the following static files from GAE, so that
- * PerformanceFilter can set their <tt>expiry headers</tt>:
- * <tt>*.css</tt>, <tt>*.js</tt>, <tt>*.png</tt> and <tt>*.gif</tt>. For example:
+ * <h3>Page Automapping</h3>
+ * GAE does not always adhere to the Servlet specification. One of the areas that affects
+ * Click directly is the <a href="../../../../../../user-guide/html/ch04s02.html#application-automapping">automatic mapping</a>
+ * of Page templates to page classes. GAE does not implement the ServletContext
+ * method <tt>getResourcePaths("/")</tt>. Instead of returning the resources under
+ * the web-app root, it returns an empty set. Click needs these resources to map
+ * between page templates and classes, and since GAE does not return anything,
+ * it isn't possible to perform the automapping.
+ * <p/>
+ * Fortunately GAE does work properly for resources under subfolders of the web-app root.
+ * For example if the folders <span class="blue">/path</span> or <span class="blue">/paths</span>
+ * exists under the web-app root, calling <tt>getResourcePaths("/path")</tt> or
+ * <tt>getResourcePaths("/paths")</tt> will return the set of resources contained
+ * under these folders.
+ * <p/>
+ * Taking advantage of the fact that GAE supports subfolders, Click
+ * provides automapping support to GAE applications with a slight caveat: Page
+ * templates <b>must</b> be placed under the folders <span class="blue">/path</span>
+ * or <span class="blue">/paths</span> of the web-app root. Click explicitly
+ * maps these two folders if it is running on GAE, other subfolders are not supported.
+ * <p/>
+ * <b>Please note:</b> manual mapping works as expected.
+ * <p/>
+ * Below is an automapping example for the folder <span class="blue">/page</span>
+ * (note the page template <tt>index.htm</tt> is not placed under the folder
+ * <tt>/page</tt>, and has to be mapped manually):
+ *
+ * <pre class="codeConfig">
+ * /index.htm
+ * <span class="blue">/page</span>/search.htm
+ * <span class="blue">/page</span>/customer/customer-edit.htm
+ * <span class="blue">/page</span>/customer/customer-search.htm </pre>
+ *
+ * The Page classes are placed under the <span class="blue">page</span> package:
+ *
+ * <pre class="codeConfig">
+ * com.mycorp<span class="blue">.page</span>.IndexPage.java
+ * com.mycorp<span class="blue">.page</span>.SearchPage.java
+ * com.mycorp<span class="blue">.page</span>.customer.CustomerEditPage.java
+ * com.mycorp<span class="blue">.page</span>.customer.CustomerSearchPage.java </pre>
*
- * <pre class="codeHtml">
- * <?xml version="1.0" encoding="utf-8"?>
- * <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
+ * Lastly define the <tt>click.xml</tt> to automatically map page templates
+ * and classes under the package <tt>com.mycorp</tt>:
*
- * ...
+ * <pre class="prettyprint">
+ * <click-app>
*
- * <!-- Exclude the following files from being served as static files by GAE,
- * as they will be processed by Click's PerformanceFilter. -->
- * <static-files>
- * <<span class="blue">exclude</span> path="<span class="red">**.htm</span>" />
- * <<span class="blue">exclude</span> path="<span class="red">**.css</span>" />
- * <<span class="blue">exclude</span> path="<span class="red">**.js</span>" />
- * <<span class="blue">exclude</span> path="<span class="red">**.png</span>" />
- * <<span class="blue">exclude</span> path="<span class="red">**.gif</span>" />
- * </static-files>
+ * <pages package="com.mycorp">
+ * <page path="/index.htm" classname="page.IndexPage"/>
+ * </pages>
*
- * </appengine-web-app> </pre>
+ * <mode value="production"/>
+ *
+ * </click-app> </pre>
+ *
+ * <b>Please note:</b> automapping will work in a GAE <tt>development</tt> environment
+ * but not when hosted on the server. GAE uses the Jetty server for local development
+ * which properly implements <tt>getResourcePaths("/")</tt>.
+ * <p/>
+ * <b>Also note:</b> when running Click on GAE in development mode, it will appear
+ * that automapping is working when it really isn't. This is because
+ * Click uses a variety of ways to detect new page templates in development mode.
+ * So even though automapping failed, Click still serves page requests because it
+ * used an alternative way of looking up the template template. While these techniques
+ * are useful for development modes it impacts performance and is not used
+ * in production mode.
*
- * <h3>Deployment issues</h3>
+ * <h3>Deployment limitation</h3>
*
* On application startup, Click automatically deploys all its JavaScript, CSS
* and image resources to the "<tt>/click</tt>" folder in the root directory of
Modified: click/trunk/click/extras/src/org/apache/click/extras/gae/package.html
URL: http://svn.apache.org/viewvc/click/trunk/click/extras/src/org/apache/click/extras/gae/package.html?rev=964393&r1=964392&r2=964393&view=diff
==============================================================================
--- click/trunk/click/extras/src/org/apache/click/extras/gae/package.html (original)
+++ click/trunk/click/extras/src/org/apache/click/extras/gae/package.html Thu Jul 15 11:56:17 2010
@@ -23,4 +23,6 @@ a free Java hosting environment from Goo
<p/>
GAE provides an ideal environment for students and startups to easily host their
Click applications online.
+<p/>
+To start off with see {@link org.apache.click.extras.gae.GoogleAppEngineListener}.
</body>
\ No newline at end of file
Modified: click/trunk/click/framework/src/org/apache/click/service/XmlConfigService.java
URL: http://svn.apache.org/viewvc/click/trunk/click/framework/src/org/apache/click/service/XmlConfigService.java?rev=964393&r1=964392&r2=964393&view=diff
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/service/XmlConfigService.java (original)
+++ click/trunk/click/framework/src/org/apache/click/service/XmlConfigService.java Thu Jul 15 11:56:17 2010
@@ -141,6 +141,8 @@ public class XmlConfigService implements
DEFAULT_HEADERS.put("Expires", new Date(1L));
}
+ private static final String GOOGLE_APP_ENGINE = "Google App Engine";
+
// ------------------------------------------------ Package Private Members
/** The Map of global page headers. */
@@ -200,6 +202,9 @@ public class XmlConfigService implements
/** The application TemplateService. */
private MessagesMapService messagesMapService;
+ /** Flag indicating whether Click is running on Google App Engine. */
+ private boolean onGoogleAppEngine = false;
+
// --------------------------------------------------------- Public Methods
/**
@@ -214,6 +219,8 @@ public class XmlConfigService implements
this.servletContext = servletContext;
+ onGoogleAppEngine = servletContext.getServerInfo().startsWith(GOOGLE_APP_ENGINE);
+
// Set default logService early to log errors when services fail.
logService = new ConsoleLogService();
messagesMapService = new DefaultMessagesMapService();
@@ -1307,43 +1314,44 @@ public class XmlConfigService implements
*/
private void deployFiles(Element rootElm) throws Exception {
- boolean isResourcesDeployable = isResourcesDeployable();
+ boolean isResourcesDeployable = onGoogleAppEngine ? false : isResourcesDeployable();
if (isResourcesDeployable) {
- try {
- if (getLogService().isTraceEnabled()) {
- String deployTarget = servletContext.getRealPath("/");
- getLogService().trace("resource deploy folder: "
- + deployTarget);
- }
-
- deployControls(getResourceRootElement("/click-controls.xml"));
- deployControls(getResourceRootElement("/extras-controls.xml"));
- deployControls(rootElm);
- deployControlSets(rootElm);
- deployResourcesOnClasspath();
-
- } catch (Error ignore) {
- // Google App Engine (GAE) can indicate that resources are deployable
- // when they are not, and will throw an error if restricted classes
- // such as FileOutputStream are accessed. We will log a warning
- // to indicate that resources could not be deployed.
- isResourcesDeployable = false;
+ if (getLogService().isTraceEnabled()) {
+ String deployTarget = servletContext.getRealPath("/");
+ getLogService().trace("resource deploy folder: "
+ + deployTarget);
}
+
+ deployControls(getResourceRootElement("/click-controls.xml"));
+ deployControls(getResourceRootElement("/extras-controls.xml"));
+ deployControls(rootElm);
+ deployControlSets(rootElm);
+ deployResourcesOnClasspath();
}
if (!isResourcesDeployable) {
- String msg = "could not deploy Click resources to the"
- + " 'click' web folder.\nThis can occur if the call to"
- + " ServletContext.getRealPath(\"/\") returns null, which means"
- + " the web application cannot determine the file system path"
- + " to deploy files to. Another common problem is if the web"
- + " application is not allowed to write to the file"
- + " system.\nTo resolve this issue please see the Click user-guide:"
- + " http://click.apache.org/docs/user-guide/html/ch04s03.html#deploying-restricted-env"
- + " \nIgnore this warning in the future once you have settled on a"
- + " deployment strategy";
- getLogService().warn(msg);
+
+ HtmlStringBuffer buffer = new HtmlStringBuffer();
+ if (onGoogleAppEngine) {
+ buffer.append("Google App Engine does not support deploying");
+ buffer.append(" resources to the 'click' web folder.\n");
+
+ } else {
+ buffer.append("could not deploy Click resources to the 'click'");
+ buffer.append(" web folder.\nThis can occur if the call to");
+ buffer.append(" ServletContext.getRealPath(\"/\") returns null, which means");
+ buffer.append(" the web application cannot determine the file system path");
+ buffer.append(" to deploy files to. This issue also occurs if the web");
+ buffer.append(" application is not allowed to write to the file");
+ buffer.append(" system.\n");
+ }
+
+ buffer.append("To resolve this issue please see the Click user-guide:");
+ buffer.append(" http://click.apache.org/docs/user-guide/html/ch04s03.html#deploying-restricted-env");
+ buffer.append(" \nIgnore this warning once you have settled on a");
+ buffer.append(" deployment strategy");
+ getLogService().warn(buffer.toString());
}
}
@@ -1729,9 +1737,9 @@ public class XmlConfigService implements
}
private void loadCharset(Element rootElm) {
- String charset = rootElm.getAttribute("charset");
- if (charset != null && charset.length() > 0) {
- this.charset = charset;
+ String localCharset = rootElm.getAttribute("charset");
+ if (localCharset != null && localCharset.length() > 0) {
+ this.charset = localCharset;
}
}
@@ -1759,6 +1767,18 @@ public class XmlConfigService implements
List fileList = new ArrayList();
Set resources = servletContext.getResourcePaths("/");
+ if (onGoogleAppEngine) {
+ // resources could be immutable so create copy
+ Set tempResources = new HashSet();
+
+ // Load the two GAE preconfigured automapped folders
+ tempResources.addAll(servletContext.getResourcePaths("/page"));
+ tempResources.addAll(servletContext.getResourcePaths("/pages"));
+ tempResources.addAll(resources);
+
+ // assign copy to resources
+ resources = Collections.unmodifiableSet(tempResources);
+ }
// Add all resources within web application
for (Iterator i = resources.iterator(); i.hasNext();) {