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 2009/06/18 23:49:22 UTC

svn commit: r786301 - in /incubator/click/trunk/click: documentation/docs/ extras/src/org/apache/click/extras/gae/

Author: sabob
Date: Thu Jun 18 21:49:21 2009
New Revision: 786301

URL: http://svn.apache.org/viewvc?rev=786301&view=rev
Log:
added Google App Engine support. CLK-560

Added:
    incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/
    incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java
    incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItem.java
    incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItemFactory.java
    incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileUploadService.java
Modified:
    incubator/click/trunk/click/documentation/docs/roadmap-changes.html

Modified: incubator/click/trunk/click/documentation/docs/roadmap-changes.html
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/documentation/docs/roadmap-changes.html?rev=786301&r1=786300&r2=786301&view=diff
==============================================================================
--- incubator/click/trunk/click/documentation/docs/roadmap-changes.html (original)
+++ incubator/click/trunk/click/documentation/docs/roadmap-changes.html Thu Jun 18 21:49:21 2009
@@ -147,6 +147,14 @@
     </div>
     <ul style="padding: 0em; margin-left:0em;margin-bottom: 2em">
       <li class="change">
+          Added support for <a class="external" target="_blank" href="http://code.google.com/appengine/docs/java/overview.html">Google App Engine</a>,
+          a free Java hosting environment from Google. This provides an ideal
+          environment for students and startups to quickly host their Click
+          applications online. See <a href="extras-api/org/apache/click/extras/gae/GoogleAppEngineListener.html">GoogleAppEngineListener</a>
+          for details
+          [<a target="_blank" href="https://issues.apache.org/jira/browse/CLK-560">560</a>].
+      </li>
+      <li class="change">
           Added new Calendar popup to DateField. This Calendar popup uses
           <a target="_blank" class="external" href="http://code.google.com/p/calendardateselect/">Calendar Date Select</a>
           which is based on the Prototype JavaScript library.

Added: incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java?rev=786301&view=auto
==============================================================================
--- incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java (added)
+++ incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/GoogleAppEngineListener.java Thu Jun 18 21:49:21 2009
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.click.extras.gae;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import ognl.OgnlRuntime;
+
+/**
+ * Provides Google App Engine (GAE) support for Click applications.
+ * <p/>
+ * To deploy Click applications to GAE, you need to set the
+ * <tt>GoogleAppEngineListener</tt> listener in your <tt>web.xml</tt>:
+ *
+ * <pre class="codeHtml">
+ * &lt;?xml version="1.0" encoding="utf-8"?&gt;
+ * &lt;web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"&gt;
+ *
+ *     &lt;listener&gt;
+ *         &lt;listener-class&gt;<span class="red">org.apache.click.extras.gae.GoogleAppEngineListener</span>&lt;/listener-class&gt;
+ *     &lt;/listener&gt;
+ *
+ *     &lt;servlet&gt;
+ *         &lt;servlet-name&gt;ClickClickServlet&lt;/servlet-name&gt;
+ *         &lt;servlet-class&gt;net.sf.clickclick.ClickClickServlet&lt;/servlet-class&gt;
+ *         &lt;load-on-startup&gt;0&lt;/load-on-startup&gt;
+ *     &lt;/servlet&gt;
+ *     &lt;servlet-mapping&gt;
+ *         &lt;servlet-name&gt;ClickClickServlet&lt;/servlet-name&gt;
+ *         &lt;url-pattern&gt;*.htm&lt;/url-pattern&gt;
+ *     &lt;/servlet-mapping&gt;
+ * &lt;/web-app&gt; </pre>
+ *
+ * You also need to configure GAE to exclude <tt>*.htm</tt> files from being
+ * served as static resources. Also you should enable <tt>http-session</tt>
+ * support. You set these changes in the GAE file <tt>war/WEB-INF/appengine-web.xml</tt>:
+ *
+ * <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;application>myapp&lt;/application&gt;
+ *     &lt;version&gt;1&lt;/version&gt;
+ *
+ *	   &lt;!-- Configure java.util.logging --&gt;
+ *     &lt;system-properties&gt;
+ *         &lt;property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/&gt;
+ *     &lt;/system-properties&gt;
+ *
+ *     &lt;!-- Enable HttpSession usage --&gt;
+ *     &lt;<span class="blue">sessions-enabled</span>&gt;<span class="red">true</span>&lt;/<span class="blue">sessions-enabled</span>&gt;
+ *
+ *     &lt;!-- Exclude *.htm files from being served as static files by GAE,
+ *             because the *.htm extension is mapped to ClickServlet. --&gt;
+ *     &lt;static-files&gt;
+ *         &lt;<span class="blue">exclude</span> path="<span class="red">**.htm</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
+ * problem for the {@link org.apache.click.control.FileField} control that
+ * depends on <a class="external" target="_blank" href="http://commons.apache.org/fileupload/">Commons FileUpload</a>
+ * which stores uploaded files on disk. To work around this limitation Click
+ * provides the {@link MemoryFileUploadService} which stores uploaded files in
+ * memory.
+ * <p/>
+ * Below is an example configuration of a {@link MemoryFileUploadService}:
+ *
+ * <pre class="prettyprint">
+ * &lt;click-app charset="UTF-8"&gt;
+ *     &lt;pages package="com.myapp.pages"/&gt;
+ *     &lt;mode value="production"/&gt;
+ *
+ *     &lt;file-upload-service classname="org.apache.click.extras.gae.MemoryFileUploadService"&gt;
+ *         &lt;!-- Set the total request maximum size to 10mb (10 x 1024 x 1024 = 10485760).
+ *                 The default request upload size is unlimited. --&gt;
+ *         &lt;property name="sizeMax" value="10485760"/&gt;
+ *
+ *         &lt;!-- Set the maximum individual file size to 2mb (2 x 1024 x 1024 = 2097152).
+ *             The default file upload size is unlimited. --&gt;
+ *         &lt;property name="fileSizeMax" value="2097152"/&gt;
+ *    &lt;/file-upload-service&gt;
+ * &lt;/click-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>
+ *
+ * @author Bob Schellink
+ */
+public class GoogleAppEngineListener implements ServletContextListener {
+
+    /**
+     * Creates a default GoogleAppEngineListener.
+     */
+    public GoogleAppEngineListener() {
+    }
+
+    /**
+     * This method does nothing.
+     *
+     * @param servletContextEvent the event class for notifications about
+     * changes to the servlet context
+     */
+    public void contextDestroyed(ServletContextEvent servletContextEvent) {
+    }
+
+    /**
+     * Sets the Ognl Runtime SecurityManager to <tt>null</tt> so as not to
+     * interfere with Google App Engine (GAE). GAE provides its own strict
+     * SecurityManager which clashes with Ognl security checks.
+     *
+     * <pre class="prettyprint">
+     * OgnlRuntime.setSecurityManager(null); </pre>
+     *
+     * @param servletContextEvent the event class for notifications about
+     * changes to the servlet context
+     */
+    public void contextInitialized(ServletContextEvent servletContextEvent) {
+        OgnlRuntime.setSecurityManager(null);
+    }
+}

Added: incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItem.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItem.java?rev=786301&view=auto
==============================================================================
--- incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItem.java (added)
+++ incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItem.java Thu Jun 18 21:49:21 2009
@@ -0,0 +1,318 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.click.extras.gae;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import java.util.Map;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileItemHeaders;
+import org.apache.commons.fileupload.FileItemHeadersSupport;
+import org.apache.commons.fileupload.ParameterParser;
+
+/**
+ * Provides an In-Memory FileItem implementation which represents a file or
+ * form item that was received within a <tt>multipart/form-data</tt> POST
+ * request.
+ * <p/>
+ * This FileItem implementation can safely be used with Google App Engine (GAE)
+ * since the file content is not written to disk.
+ * <p/>
+ * The MemoryFileItem is based on the
+ * <a class="external" target="_blank" href="http://commons.apache.org/fileupload/">Commons FileUpload</a>
+ * project.
+ *
+ * @author Bob Schellink
+ */
+public class MemoryFileItem implements FileItem, FileItemHeadersSupport {
+
+    // -------------------------------------------------------------- Constants
+
+    /**
+     * Default content charset to be used when no explicit charset
+     * parameter is provided by the sender. Media subtypes of the
+     * "text" type are defined to have a default charset value of
+     * "ISO-8859-1" when received via HTTP.
+     */
+    public static final String DEFAULT_CHARSET = "ISO-8859-1";
+
+    // -------------------------------------------------------------- Variables
+
+    /**
+     * The name of the form field as provided by the browser.
+     */
+    private String fieldName;
+
+    /**
+     * The content type passed by the browser, or null if not defined.
+     */
+    private String contentType;
+
+    /**
+     * Whether or not this item is a simple form field.
+     */
+    private boolean isFormField;
+
+    /**
+     * The original filename in the user's filesystem.
+     */
+    private String fileName;
+
+    /**
+     * The file items headers.
+     */
+    private FileItemHeaders headers;
+
+    /**
+     * The file item's uploaded content.
+     */
+    private ByteArrayOutputStream content;
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Constructs a new MemoryFileItem for the given fieldName, contentType
+     * isFormField and fileName parameters.
+     *
+     * @param fieldName the name of the form field as provided by the browser
+     * @param contentType the content type passed by the browser
+     * @param isFormField specifies whether or not this item is a simple form field
+     * @param fileName the original filename in the user's filesystem
+     */
+    public MemoryFileItem(String fieldName, String contentType, boolean isFormField,
+        String fileName) {
+
+        this.fieldName = fieldName;
+        this.contentType = contentType;
+        this.isFormField = isFormField;
+        this.fileName = fileName;
+        this.content = new ByteArrayOutputStream();
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * This method does nothing since the file is stored in memory only.
+     */
+    public void delete() {
+    }
+
+    /**
+     * Returns the contents of the file item as an array of bytes.
+     *
+     * @return the contents of the file item as an array of bytes
+     */
+    public byte[] get() {
+        return content.toByteArray();
+    }
+
+    /**
+     * Returns the content type passed by the browser or null if not defined.
+     *
+     * @return the content type passed by the browser or null if not defined
+     */
+    public String getContentType() {
+        return contentType;
+    }
+
+    /**
+     * Returns the name of the field in the multipart form corresponding to
+     * this file item.
+     *
+     * @return the name of the form field
+     */
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    /**
+     * Sets the field name used to reference this file item.
+     *
+     * @param fieldName the name of the form field
+     */
+    public void setFieldName(String fieldName) {
+        this.fieldName = fieldName;
+    }
+
+    /**
+     * Determines whether or not a <tt>FileItem</tt> instance represents
+     * a simple form field.
+     *
+     * @return true if the instance represents a simple form field; false if it
+     * represents an uploaded file
+     */
+    public boolean isFormField() {
+        return isFormField;
+    }
+
+    /**
+     * Specifies whether or not a <tt>FileItem</tt> instance represents
+     * a simple form field.
+     *
+     * @param isFormField true if the instance represents a simple form field;
+     * false if it represents an uploaded file
+     */
+    public void setFormField(boolean isFormField) {
+        this.isFormField = isFormField;
+    }
+
+    /**
+     * Returns an {@link java.io.InputStream InputStream} that can be used to
+     * retrieve the contents of the file.
+     *
+     * @return an {@link java.io.InputStream InputStream} that can be used to
+     * retrieve the contents of the file
+     * @throws IOException if an error occurs
+     */
+    public InputStream getInputStream() throws IOException {
+        return new ByteArrayInputStream(get());
+    }
+
+    /**
+     * Returns the original filename in the client's filesystem, as provided by
+     * the browser (or other client software). In most cases, this will be the
+     * base file name, without path information. However, some clients, such as
+     * Internet Explorer, do include path information.
+     * <p/>
+     * You can easily remove the path information with the following snippet:
+     *
+     * <pre class="prettyprint">
+     * String fileName = fileItem.getName();
+     * if (fileName != null) {
+     *     filename = FilenameUtils.getName(filename);
+     * } </pre>
+     *
+     * @return the original filename in the client's filesystem.
+     */
+    public String getName() {
+        return fileName;
+    }
+
+    /**
+     * Returns an {@link java.io.OutputStream OutputStream} that can be used for
+     * storing the contents of the file.
+     *
+     * @return an {@link java.io.OutputStream OutputStream} that can be used
+     * for storing the contensts of the file
+     * @throws IOException if an error occurs
+     */
+    public OutputStream getOutputStream() throws IOException {
+        return content;
+    }
+
+    /**
+     * Returns the size of the file item, in bytes.
+     *
+     * @return the size of the file item, in bytes
+     */
+    public long getSize() {
+        return content.size();
+    }
+
+    /**
+     * Returns the contents of the file as a String, using the default
+     * character encoding.  This method uses {@link #get()} to retrieve the
+     * contents of the file.
+     *
+     * @return the contents of the file, as a string.
+     */
+    public String getString() {
+        byte[] rawdata = get();
+        String charset = getCharSet();
+        if (charset == null) {
+            charset = DEFAULT_CHARSET;
+        }
+        try {
+            return new String(rawdata, charset);
+        } catch (UnsupportedEncodingException e) {
+            return new String(rawdata);
+        }
+    }
+
+    /**
+     * Returns the contents of the file as a String, using the specified
+     * encoding. This method uses {@link #get()} to retrieve the contents of the
+     * file.
+     *
+     * @param charset the charset to use
+     * @return the contents of the file, as a string
+     * @throws UnsupportedEncodingException if the requested character
+     * encoding is not available.
+     */
+    public String getString(final String charset)
+        throws UnsupportedEncodingException {
+        return new String(get(), charset);
+    }
+
+    /**
+     * Provides a hint as to whether or not the file contents will be read
+     * from memory. This method always returns true.
+     *
+     * @return true to indicate that the file contents will be read from memory
+     */
+    public boolean isInMemory() {
+        return true;
+    }
+
+    /**
+     * This method does nothing since the file is stored in memory only.
+     *
+     * @param file the File into which the uploaded item should be stored
+     * @throws Exception if an error occurs
+     */
+    public void write(File file) throws Exception {
+    }
+
+    /**
+     * Returns the file item headers.
+     *
+     * @return the file items headers
+     */
+    public FileItemHeaders getHeaders() {
+        return headers;
+    }
+
+    /**
+     * Sets the file item headers.
+     *
+     * @param headers the file items headers
+     */
+    public void setHeaders(FileItemHeaders headers) {
+        this.headers = headers;
+    }
+
+    /**
+     * Returns the content charset passed by the agent or null if not defined.
+     *
+     * @return The content charset passed by the agent or null if not defined
+     */
+    public String getCharSet() {
+        ParameterParser parser = new ParameterParser();
+        parser.setLowerCaseNames(true);
+        // Parameter parser can handle null input
+        Map params = parser.parse(getContentType(), ';');
+        return (String) params.get("charset");
+    }
+}

Added: incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItemFactory.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItemFactory.java?rev=786301&view=auto
==============================================================================
--- incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItemFactory.java (added)
+++ incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileItemFactory.java Thu Jun 18 21:49:21 2009
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.click.extras.gae;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileItemFactory;
+
+/**
+ * Provides a FileItemFactory implementation that creates {@link MemoryFileItem}
+ * instances which always keep their content in memory.
+ *
+ * @author Bob Schellink
+ */
+public class MemoryFileItemFactory implements FileItemFactory {
+
+    /**
+     * Create a new {@link MemoryFileItem} instance from the supplied parameters.
+     *
+     * @param fieldName the name of the form field
+     * @param contentType the content type of the form field
+     * @param isFormField true if this is a plain form field, false otherwise
+     * @param fileName the name of the uploaded file, if any, as supplied by the
+     * browser or other client
+     * @return the newly created file item
+     */
+    public FileItem createItem(String fieldName, String contentType,
+        boolean isFormField, String fileName) {
+
+        FileItem result =
+            new MemoryFileItem(fieldName, contentType, isFormField, fileName);
+        return result;
+    }
+}

Added: incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileUploadService.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileUploadService.java?rev=786301&view=auto
==============================================================================
--- incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileUploadService.java (added)
+++ incubator/click/trunk/click/extras/src/org/apache/click/extras/gae/MemoryFileUploadService.java Thu Jun 18 21:49:21 2009
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.click.extras.gae;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.click.service.CommonsFileUploadService;
+import org.apache.commons.fileupload.FileItemFactory;
+
+/**
+ * Provides an Apache Commons In-Memory FileUploadService class.
+ * <p/>
+ * This service creates an {@link MemoryFileItemFactory} for creating
+ * {@link MemoryFileItem In-Memory FileItem instances} which content is never
+ * written to disk.
+ * <p/>
+ * This service is recommended to be used with Google App Engine (GAE) which
+ * doesn't allow Web Application access to disk.
+ * <p/>
+ * To use this service in your GAE applications, add the following to your
+ * <tt>click.xml</tt> config:
+ *
+ * <pre class="prettyprint">
+ * &lt;file-upload-service classname="org.apache.click.extras.gae.MemoryFileUploadService"&gt;
+ *     &lt;!-- Set the total request maximum size to 10mb (10 x 1024 x 1024 = 10485760). --&gt;
+ *     &lt;property name="sizeMax" value="10485760"/&gt;
+ *
+ *     &lt;!-- Set the maximum individual file size to 2mb (2 x 1024 x 1024 = 2097152). --&gt;
+ *     &lt;property name="fileSizeMax" value="2097152"/&gt;
+ * &lt;/file-upload-service&gt; </pre>
+ *
+ * To prevent users from uploading exceedingly large files you can configure
+ * MemoryFileUploadService through the properties {@link #setSizeMax(long)} and
+ * {@link #setFileSizeMax(long)}, as demonstrated above.
+ * <p/>
+ * <b>Please note:</b> Google App Engine further restricts the size of file
+ * uploads as well. Currently the limit is 10MB.
+ *
+ * @author Bob Schellink
+ */
+public class MemoryFileUploadService extends CommonsFileUploadService {
+
+    /**
+     * Create and return a new {@link MemoryFileItemFactory} instance.
+     *
+     * @param request the servlet request
+     * @return a new MemoryFileItemFactory instance
+     */
+    public FileItemFactory createFileItemFactory(HttpServletRequest request) {
+        return new MemoryFileItemFactory();
+    }
+}