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;
  *
  * &lt;/appengine-web-app&gt; </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">
+ * &lt;?xml version="1.0" encoding="utf-8"?&gt;
+ * &lt;appengine-web-app xmlns="http://appengine.google.com/ns/1.0"&gt;
+ *
+ *     ...
+ *
+ *     &lt;!-- Exclude the following files from being served as static files by GAE,
+ *             as they will be processed by Click's PerformanceFilter. --&gt;
+ *     &lt;static-files&gt;
+ *         &lt;<span class="blue">exclude</span> path="<span class="red">**.htm</span>" /&gt;
+ *         &lt;<span class="blue">exclude</span> path="<span class="red">**.css</span>" /&gt;
+ *         &lt;<span class="blue">exclude</span> path="<span class="red">**.js</span>" /&gt;
+ *         &lt;<span class="blue">exclude</span> path="<span class="red">**.png</span>" /&gt;
+ *         &lt;<span class="blue">exclude</span> path="<span class="red">**.gif</span>" /&gt;
+ *     &lt;/static-files&gt;
+ *
+ * &lt;/appengine-web-app&gt; </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;
  *    &lt;/file-upload-service&gt;
  * &lt;/click-app&gt; </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">
- * &lt;?xml version="1.0" encoding="utf-8"?&gt;
- * &lt;appengine-web-app xmlns="http://appengine.google.com/ns/1.0"&gt;
+ * Lastly define the <tt>click.xml</tt> to automatically map page templates
+ * and classes under the package <tt>com.mycorp</tt>:
  *
- *     ...
+ * <pre class="prettyprint">
+ * &lt;click-app&gt;
  *
- *     &lt;!-- Exclude the following files from being served as static files by GAE,
- *             as they will be processed by Click's PerformanceFilter. --&gt;
- *     &lt;static-files&gt;
- *         &lt;<span class="blue">exclude</span> path="<span class="red">**.htm</span>" /&gt;
- *         &lt;<span class="blue">exclude</span> path="<span class="red">**.css</span>" /&gt;
- *         &lt;<span class="blue">exclude</span> path="<span class="red">**.js</span>" /&gt;
- *         &lt;<span class="blue">exclude</span> path="<span class="red">**.png</span>" /&gt;
- *         &lt;<span class="blue">exclude</span> path="<span class="red">**.gif</span>" /&gt;
- *     &lt;/static-files&gt;
+ *   &lt;pages package="com.mycorp"&gt;
+ *     &lt;page path="/index.htm" classname="page.IndexPage"/&gt;
+ *   &lt;/pages&gt;
  *
- * &lt;/appengine-web-app&gt; </pre>
+ *   &lt;mode value="production"/&gt;
+ *
+ * &lt;/click-app&gt; </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();) {