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/05/29 16:24:26 UTC
svn commit: r949394 - in /click/trunk/click/framework/src/org/apache/click:
ClickServlet.java Page.java Partial.java util/ClickUtils.java
Author: sabob
Date: Sat May 29 14:24:25 2010
New Revision: 949394
URL: http://svn.apache.org/viewvc?rev=949394&view=rev
Log:
Preliminary support for page actions. CLK-677
Added:
click/trunk/click/framework/src/org/apache/click/Partial.java
Modified:
click/trunk/click/framework/src/org/apache/click/ClickServlet.java
click/trunk/click/framework/src/org/apache/click/Page.java
click/trunk/click/framework/src/org/apache/click/util/ClickUtils.java
Modified: click/trunk/click/framework/src/org/apache/click/ClickServlet.java
URL: http://svn.apache.org/viewvc/click/trunk/click/framework/src/org/apache/click/ClickServlet.java?rev=949394&r1=949393&r2=949394&view=diff
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/ClickServlet.java (original)
+++ click/trunk/click/framework/src/org/apache/click/ClickServlet.java Sat May 29 14:24:25 2010
@@ -545,8 +545,9 @@ public class ClickServlet extends HttpSe
ActionEventDispatcher eventDispatcher = ActionEventDispatcher.getThreadLocalDispatcher();
+ boolean errorOccurred = page instanceof ErrorPage;
// Support direct access of click-error.htm
- if (page instanceof ErrorPage) {
+ if (errorOccurred) {
ErrorPage errorPage = (ErrorPage) page;
errorPage.setMode(configService.getApplicationMode());
@@ -556,6 +557,16 @@ public class ClickServlet extends HttpSe
boolean continueProcessing = performOnSecurityCheck(page, context);
+ Partial partial = null;
+ if (continueProcessing && !errorOccurred) {
+ // Handle page method
+ String pageAction = context.getRequestParameter(Page.PAGE_ACTION);
+ if (pageAction != null) {
+ continueProcessing = false;
+ partial = ClickUtils.invokeAction(page, pageAction);
+ }
+ }
+
if (continueProcessing) {
performOnInit(page, context);
@@ -568,7 +579,7 @@ public class ClickServlet extends HttpSe
}
}
- performRender(page, context);
+ performRender(page, context, partial);
}
/**
@@ -742,7 +753,8 @@ public class ClickServlet extends HttpSe
* @param context the request context
* @throws java.lang.Exception if error occurs
*/
- protected void performRender(Page page, Context context) throws Exception {
+ protected void performRender(Page page, Context context, Partial partial)
+ throws Exception {
// Process page interceptors, and abort rendering if specified
for (PageInterceptor interceptor : getThreadLocalInterceptors()) {
@@ -789,6 +801,9 @@ public class ClickServlet extends HttpSe
dispatcher.forward(request, response);
}
+ } else if (partial != null) {
+ partial.render(context);
+
} else if (page.getPath() != null) {
String pagePath = page.getPath();
@@ -928,6 +943,7 @@ public class ClickServlet extends HttpSe
// Log request parameters
if (logger.isTraceEnabled()) {
+ logger.trace(" is Ajax request: " + context.isAjaxRequest());
logRequestParameters(request);
}
Modified: click/trunk/click/framework/src/org/apache/click/Page.java
URL: http://svn.apache.org/viewvc/click/trunk/click/framework/src/org/apache/click/Page.java?rev=949394&r1=949393&r2=949394&view=diff
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/Page.java (original)
+++ click/trunk/click/framework/src/org/apache/click/Page.java Sat May 29 14:24:25 2010
@@ -150,6 +150,11 @@ public class Page implements Serializabl
*/
public static final String PAGE_MESSAGES = "click-page";
+ /**
+ * The Page action request parameter: "<tt>pageAction</tt>"
+ */
+ public final static String PAGE_ACTION = "pageAction";
+
// Instance Variables -----------------------------------------------------
/** The list of page controls. */
Added: click/trunk/click/framework/src/org/apache/click/Partial.java
URL: http://svn.apache.org/viewvc/click/trunk/click/framework/src/org/apache/click/Partial.java?rev=949394&view=auto
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/Partial.java (added)
+++ click/trunk/click/framework/src/org/apache/click/Partial.java Sat May 29 14:24:25 2010
@@ -0,0 +1,392 @@
+/*
+ * 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;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.click.util.ClickUtils;
+
+/**
+ * Partial encapsulates a fragment of an HTTP response. A Partial can be used
+ * to stream back a String or byte array to the browser.
+ *
+ * <h3>Usage</h3>
+ * A Partial is often used to stream back html, json, xml, plain text and
+ * byte array e.g. jpg, gif, png, pdf and excel documents.
+ *
+ * <h3>Ajax</h3>
+ * TODO
+ *
+ * <h3>Page Action</h3>
+ * TODO
+ *
+ * <h3>Example</h3>
+ * TODO
+ */
+public class Partial {
+
+ // -------------------------------------------------------------- Constants
+
+ /** The plain text content type constant <tt>text/plain</tt>. */
+ public static final String TEXT = "text/plain";
+
+ /** The html content type constant <tt>text/html</tt>. */
+ public static final String HTML = "text/html";
+
+ /** The The xhtml content type constant <tt>application/xhtml+xml</tt>. */
+ public static final String XHTML = "application/xhtml+xml";
+
+ /** The json content type constant <tt>text/json</tt>. */
+ public static final String JSON = "text/json";
+
+ /** The javascript content type constant <tt>text/javascript</tt>. */
+ public static final String JAVASCRIPT = "text/javascript";
+
+ /** The xml content type constant <tt>text/xml</tt>. */
+ public static final String XML = "text/xml";
+
+ /** The Partial writer buffer size. */
+ private static final int WRITER_BUFFER_SIZE = 256;
+
+ // -------------------------------------------------------- Variables
+
+ /** The content to render. */
+ private Object content;
+
+ /** The servlet response reader. */
+ private Reader reader;
+
+ /** The servlet response input stream. */
+ private InputStream inputStream;
+
+ /** The response content type. */
+ private String contentType;
+
+ /** The resposne character encoding. */
+ private String characterEncoding;
+
+ /** The response headers. */
+ private Map headers;
+
+ /** Indicates whether the Partial should be cached by browser. */
+ private boolean cachePartial = false;
+
+ // ----------------------------------------------------------- Constructors
+
+ /**
+ * Construct the Partial for the given reader and content type.
+ *
+ * @param reader the reader which characters must be streamed back to the
+ * client
+ * @param contentType the response content type
+ */
+ public Partial(Reader reader, String contentType) {
+ this.reader = reader;
+ this.contentType = contentType;
+ }
+
+ /**
+ * Construct the Partial for the given inputStream and content type.
+ *
+ * @param inputStream the input stream to stream back to the client
+ * @param contentType the response content type
+ */
+ public Partial(InputStream inputStream, String contentType) {
+ this.inputStream = inputStream;
+ this.contentType = contentType;
+ }
+
+ /**
+ * Construct the Partial for the given content and content type.
+ * <p/>
+ * At rendering time the partial invokes the Object's <tt>toString()</tt>
+ * method and streams the resulting <tt>String</tt> back to the client.
+ *
+ * @param content the content to stream back to the client
+ * @param contentType the response content type
+ */
+ public Partial(Object content, String contentType) {
+ this.content = content;
+ this.contentType = contentType;
+ }
+
+ /**
+ * Construct the Partial for the given content. The
+ * <tt>{@link javax.servlet.http.HttpServletResponse#setContentType(java.lang.String) response content type}</tt>
+ * will default to {@link #TEXT}.
+ * <p/>
+ * At rendering time the partial invokes the Object's <tt>toString()</tt>
+ * method and streams the resulting <tt>String</tt> back to the client.
+ *
+ * @param content the content to stream back to the client
+ */
+ public Partial(Object content) {
+ this.content = content;
+ this.contentType = TEXT;
+ }
+
+ /**
+ * Construct a new empty Partial. The
+ * <tt>{@link javax.servlet.http.HttpServletResponse#setContentType(java.lang.String) response content type}</tt>
+ * will default to {@link #TEXT}.
+ *
+ */
+ public Partial() {
+ this.contentType = TEXT;
+ }
+
+ // ---------------------------------------------------------- Public Methds
+
+ /**
+ * Indicates whether the partial should be cached by the clients browser
+ * or not, defaults to false.
+ * <p/>
+ * If false, Click will set the following headers to prevent browsers
+ * from caching the result:
+ * <pre class="prettyprint">
+ * response.setHeader("Pragma", "no-cache");
+ * response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
+ * response.setDateHeader("Expires", new Date(1L).getTime());
+ * </pre>
+ *
+ * @param cachePartial indicates whether the partial should be cached
+ * by clients browser or not
+ */
+ public void setCachePartial(boolean cachePartial) {
+ this.cachePartial = cachePartial;
+ }
+
+ /**
+ * Return the partial character encoding.
+ *
+ * @return the partial character encoding.
+ */
+ public String getCharacterEncoding() {
+ return characterEncoding;
+ }
+
+ /**
+ * Set the partial character encoding.
+ *
+ * @param characterEncoding the partial character encoding
+ */
+ public void setCharacterEncoding(String characterEncoding) {
+ this.characterEncoding = characterEncoding;
+ }
+
+ /**
+ * Set the partial response content type.
+ *
+ * @param contentType the partial response content type
+ */
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ /**
+ * Return the partial content type.
+ *
+ * @return the response content type
+ */
+ public String getContentType() {
+ return contentType;
+ }
+
+ /**
+ * Return the map of response header values.
+ *
+ * @return the map of response header values
+ */
+ public Map getHeaders() {
+ if (headers == null) {
+ return new HashMap();
+ }
+ return headers;
+ }
+
+ /**
+ * Set the content to stream back to the client.
+ *
+ * @param content the content to stream back to the client
+ */
+ public void setContent(Object content) {
+ this.content = content;
+ }
+
+ /**
+ * Return the content to stream back to the client.
+ *
+ * @return the content to stream back to the client
+ */
+ public Object getContent() {
+ return content;
+ }
+
+ /**
+ * Set the content to stream back to the client.
+ *
+ * @param inputStream the inputStream to stream back to the client
+ */
+ public void setInputStream(InputStream inputStream) {
+ this.inputStream = inputStream;
+ }
+
+ /**
+ * Return the inputStream to stream back to the client.
+ *
+ * @return the inputStream to stream back to the client
+ */
+ public InputStream getInputStream() {
+ return inputStream;
+ }
+
+ /**
+ * Set the reader which characters are streamed back to the client.
+ *
+ * @param reader the reader which characters are streamed back to the client.
+ */
+ public void setReader(Reader reader) {
+ this.reader = reader;
+ }
+
+ /**
+ * Return the reader which characters are streamed back to the client.
+ *
+ * @return the reader which characters are streamed back to the client.
+ */
+ public Reader getReader() {
+ return reader;
+ }
+
+ /**
+ * Process the partial with the given context.
+ *
+ * @param context the request context to use
+ */
+ public final void render(Context context) {
+ prepare(context);
+ render(context.getRequest(), context.getResponse());
+ }
+
+ /**
+ * Render the partial to the specified response.
+ *
+ * @param request the page servlet request
+ * @param response the page servlet response
+ */
+ protected void render(HttpServletRequest request, HttpServletResponse response) {
+
+ try {
+ if (content != null) {
+ this.reader = new StringReader(content.toString());
+ }
+
+ if (reader != null) {
+ PrintWriter writer = response.getWriter();
+ char[] buffer = new char[WRITER_BUFFER_SIZE];
+ int len = 0;
+ while (-1 != (len = reader.read(buffer))) {
+ writer.write(buffer, 0, len);
+ }
+
+ } else if (inputStream != null) {
+ byte[] buffer = new byte[WRITER_BUFFER_SIZE];
+ int len = 0;
+ OutputStream outputStream = response.getOutputStream();
+ while (-1 != (len = inputStream.read(buffer))) {
+ outputStream.write(buffer, 0, len);
+ }
+ }
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+
+ } finally {
+ ClickUtils.close(inputStream);
+ ClickUtils.close(reader);
+ }
+ }
+
+ // -------------------------------------------------------- Private Methods
+
+ private void applyHeaders(HttpServletResponse response) {
+
+ if (!cachePartial) {
+ response.setHeader("Pragma", "no-cache");
+ response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
+ response.setDateHeader("Expires", new Date(1L).getTime());
+ }
+
+ if (headers != null) {
+ setResponseHeaders(response, getHeaders());
+ }
+ }
+
+ private void setResponseHeaders(HttpServletResponse response, Map headers) {
+
+ for (Iterator i = headers.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String name = entry.getKey().toString();
+ Object value = entry.getValue();
+
+ if (value instanceof String) {
+ String strValue = (String) value;
+ if (!strValue.equalsIgnoreCase("Content-Encoding")) {
+ response.setHeader(name, strValue);
+ }
+
+ } else if (value instanceof Date) {
+ long time = ((Date) value).getTime();
+ response.setDateHeader(name, time);
+
+ } else {
+ int intValue = ((Integer) value).intValue();
+ response.setIntHeader(name, intValue);
+ }
+ }
+ }
+
+ private void prepare(Context context) {
+ HttpServletResponse response = context.getResponse();
+ applyHeaders(response);
+
+ if (getCharacterEncoding() == null) {
+
+ // Fallback to request character encoding
+ if (context.getRequest().getCharacterEncoding() != null) {
+ response.setContentType(getContentType() + "; charset="
+ + context.getRequest().getCharacterEncoding());
+ } else {
+ response.setContentType(getContentType());
+ }
+
+ } else {
+ response.setContentType(getContentType() + "; charset=" + getCharacterEncoding());
+ }
+ }
+}
Modified: click/trunk/click/framework/src/org/apache/click/util/ClickUtils.java
URL: http://svn.apache.org/viewvc/click/trunk/click/framework/src/org/apache/click/util/ClickUtils.java?rev=949394&r1=949393&r2=949394&view=diff
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/util/ClickUtils.java (original)
+++ click/trunk/click/framework/src/org/apache/click/util/ClickUtils.java Sat May 29 14:24:25 2010
@@ -61,6 +61,7 @@ import javax.xml.parsers.DocumentBuilder
import org.apache.click.Context;
import org.apache.click.Control;
import org.apache.click.Page;
+import org.apache.click.Partial;
import org.apache.click.control.AbstractLink;
import org.apache.click.control.Container;
import org.apache.click.control.Field;
@@ -1908,91 +1909,62 @@ public class ClickUtils {
*
* @see org.apache.click.Control#setListener(Object, String)
*
- * @param listener
- * the object with the method to invoke
- * @param method
- * the name of the method to invoke
+ * @param listener the object with the method to invoke
+ * @param method the name of the method to invoke
* @return true if the listener method returned true
*/
public static boolean invokeListener(Object listener, String method) {
- if (listener == null) {
- throw new IllegalArgumentException("Null listener parameter");
- }
- if (method == null) {
- throw new IllegalArgumentException("Null method parameter");
- }
- Method targetMethod = null;
- boolean isAccessible = true;
- try {
- Class listenerClass = listener.getClass();
- targetMethod = listenerClass.getMethod(method);
+ Object result = invokeMethod(listener, method);
- // Change accessible for anonymous inner classes public methods
- // only. Conditional checks:
- // #1 - Target method is not accessible
- // #2 - Anonymous inner classes are not public
- // #3 - Only modify public methods
- // #4 - Anonymous inner classes have no declaring class
- // #5 - Anonymous inner classes have $ in name
- if (!targetMethod.isAccessible()
- && !Modifier.isPublic(listenerClass.getModifiers())
- && Modifier.isPublic(targetMethod.getModifiers())
- && listenerClass.getDeclaringClass() == null
- && listenerClass.getName().indexOf('$') != -1) {
+ if (result instanceof Boolean) {
+ return (Boolean) result;
- isAccessible = false;
- targetMethod.setAccessible(true);
- }
-
-
- Object result = targetMethod.invoke(listener);
+ } else {
- if (result instanceof Boolean) {
- return (Boolean) result;
+ Method targetMethod = null;
+ try {
+ targetMethod = listener.getClass().getMethod(method);
- } else {
String msg =
"Invalid listener method, missing boolean return type: "
+ targetMethod;
throw new RuntimeException(msg);
+ } catch (Exception e) {
+ String msg = "Exception occurred invoking public method: " + targetMethod;
+ throw new RuntimeException(msg, e);
}
+ }
+ }
- } catch (InvocationTargetException ite) {
-
- Throwable e = ite.getTargetException();
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
+ /**
+ * Invoke the named method on the given target and return the Object result.
+ *
+ * @param target the target object with the method to invoke
+ * @param method the name of the method to invoke
+ * @return a Partial response
+ */
+ public static Partial invokeAction(Object target, String method) {
- } else if (e instanceof Exception) {
- String msg =
- "Exception occurred invoking public method: " + targetMethod;
+ Object result = invokeMethod(target, method);
- throw new RuntimeException(msg, e);
+ if (result == null || result instanceof Partial) {
+ return (Partial) result;
- } else if (e instanceof Error) {
- String msg =
- "Error occurred invoking public method: " + targetMethod;
+ } else {
- throw new RuntimeException(msg, e);
+ Method targetMethod = null;
+ try {
+ targetMethod = target.getClass().getMethod(method);
- } else {
String msg =
- "Error occurred invoking public method: " + targetMethod;
-
+ "Invalid target method, missing Partial return type: "
+ + targetMethod;
+ throw new RuntimeException(msg);
+ } catch (Exception e) {
+ String msg = "Exception occurred invoking public method: " + targetMethod;
throw new RuntimeException(msg, e);
}
-
- } catch (Exception e) {
- String msg =
- "Exception occurred invoking public method: " + targetMethod;
-
- throw new RuntimeException(msg, e);
-
- } finally {
- if (targetMethod != null && !isAccessible) {
- targetMethod.setAccessible(false);
- }
}
}
@@ -2919,4 +2891,81 @@ public class ClickUtils {
return false;
}
+ /**
+ * Invoke the named method on the given target object and return the result.
+ *
+ * @param target the target object with the method to invoke
+ * @param method the name of the method to invoke
+ * @return Object the target method result
+ */
+ private static Object invokeMethod(Object target, String method) {
+ if (target == null) {
+ throw new IllegalArgumentException("Null target parameter");
+ }
+ if (method == null) {
+ throw new IllegalArgumentException("Null method parameter");
+ }
+
+ Method targetMethod = null;
+ boolean isAccessible = true;
+ try {
+ Class targetClass = target.getClass();
+ targetMethod = targetClass.getMethod(method);
+
+ // Change accessible for anonymous inner classes public methods
+ // only. Conditional checks:
+ // #1 - Target method is not accessible
+ // #2 - Anonymous inner classes are not public
+ // #3 - Only modify public methods
+ // #4 - Anonymous inner classes have no declaring class
+ // #5 - Anonymous inner classes have $ in name
+ if (!targetMethod.isAccessible()
+ && !Modifier.isPublic(targetClass.getModifiers())
+ && Modifier.isPublic(targetMethod.getModifiers())
+ && targetClass.getDeclaringClass() == null
+ && targetClass.getName().indexOf('$') != -1) {
+
+ isAccessible = false;
+ targetMethod.setAccessible(true);
+ }
+
+ return targetMethod.invoke(target);
+
+ } catch (InvocationTargetException ite) {
+
+ Throwable e = ite.getTargetException();
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+
+ } else if (e instanceof Exception) {
+ String msg =
+ "Exception occurred invoking public method: " + targetMethod;
+
+ throw new RuntimeException(msg, e);
+
+ } else if (e instanceof Error) {
+ String msg =
+ "Error occurred invoking public method: " + targetMethod;
+
+ throw new RuntimeException(msg, e);
+
+ } else {
+ String msg =
+ "Error occurred invoking public method: " + targetMethod;
+
+ throw new RuntimeException(msg, e);
+ }
+
+ } catch (Exception e) {
+ String msg =
+ "Exception occurred invoking public method: " + targetMethod;
+
+ throw new RuntimeException(msg, e);
+
+ } finally {
+ if (targetMethod != null && !isAccessible) {
+ targetMethod.setAccessible(false);
+ }
+ }
+ }
}