click-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From med...@apache.org
Subject svn commit: r912334 - in /click/trunk/click: documentation/docs/ examples/src/org/apache/click/examples/interceptor/ examples/webapp/WEB-INF/ framework/src/org/apache/click/ framework/src/org/apache/click/service/
Date Sun, 21 Feb 2010 11:49:42 GMT
Author: medgar
Date: Sun Feb 21 11:49:41 2010
New Revision: 912334

URL: http://svn.apache.org/viewvc?rev=912334&view=rev
Log:
CLK-598 - added page interceptor support, still more doco to do.

Added:
    click/trunk/click/examples/src/org/apache/click/examples/interceptor/
    click/trunk/click/examples/src/org/apache/click/examples/interceptor/ProfilingInterceptor.java
    click/trunk/click/examples/src/org/apache/click/examples/interceptor/package.html
    click/trunk/click/framework/src/org/apache/click/PageInterceptor.java
Modified:
    click/trunk/click/documentation/docs/click-dtd.html
    click/trunk/click/examples/webapp/WEB-INF/click.xml
    click/trunk/click/framework/src/org/apache/click/ClickServlet.java
    click/trunk/click/framework/src/org/apache/click/click.dtd
    click/trunk/click/framework/src/org/apache/click/service/ConfigService.java
    click/trunk/click/framework/src/org/apache/click/service/XmlConfigService.java

Modified: click/trunk/click/documentation/docs/click-dtd.html
URL: http://svn.apache.org/viewvc/click/trunk/click/documentation/docs/click-dtd.html?rev=912334&r1=912333&r2=912334&view=diff
==============================================================================
--- click/trunk/click/documentation/docs/click-dtd.html (original)
+++ click/trunk/click/documentation/docs/click-dtd.html Sun Feb 21 11:49:41 2010
@@ -48,7 +48,7 @@
 
 <!-- The Click Application (click.xml) Document Type Definition. -->
 &lt;!ELEMENT <span class="red">click-app</span> (<span class="blue">pages</span>*,
<span class="blue">headers</span>?, <span class="blue">format</span>?,
<span class="blue">mode</span>?, <span class="blue">controls</span>?,

-                     <span class="blue">file-upload-service</span>?, <span
class="blue">log-service</span>?, <span class="blue">template-service</span>?)&gt;
+                     <span class="blue">file-upload-service</span>?, <span
class="blue">log-service</span>?, <span class="blue">template-service</span>?),
<span class="blue">page-interceptor</span>*)&gt;
   &lt;!ATTLIST <span class="red">click-app</span> <span class="blue">charset</span>
CDATA #IMPLIED&gt;
   &lt;!ATTLIST <span class="red">click-app</span> <span class="blue">locale</span>
CDATA #IMPLIED&gt;
 
@@ -112,14 +112,12 @@
   &lt;!-- Template Service. --&gt;
   &lt;!ELEMENT <span class="red">template-service</span> (property*)&gt;
     &lt;!ATTLIST <span class="red">template-service</span> <span class="blue">classname</span>
CDATA #FIXED "<span class="green">org.apache.click.service.VelocityTemplateService</span>"&gt;
+
+  &lt;!-- Application page interceptors. --&gt;
+  &lt;!ELEMENT page-interceptor (property*)&gt;
+    &lt;!ATTLIST page-interceptor classname CDATA #REQUIRED&gt;
+    &lt;!ATTLIST <span class="red">page-interceptor</span> <span class="blue">scope</span>
(application|request) "<span class="green">request</span>"&gt;
  
 </pre>
 </body>
 </html>
-   
-
-  
-   
-  
-  
-  

Added: click/trunk/click/examples/src/org/apache/click/examples/interceptor/ProfilingInterceptor.java
URL: http://svn.apache.org/viewvc/click/trunk/click/examples/src/org/apache/click/examples/interceptor/ProfilingInterceptor.java?rev=912334&view=auto
==============================================================================
--- click/trunk/click/examples/src/org/apache/click/examples/interceptor/ProfilingInterceptor.java
(added)
+++ click/trunk/click/examples/src/org/apache/click/examples/interceptor/ProfilingInterceptor.java
Sun Feb 21 11:49:41 2010
@@ -0,0 +1,97 @@
+/*
+ * 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.examples.interceptor;
+
+import org.apache.click.Context;
+import org.apache.click.Page;
+import org.apache.click.PageInterceptor;
+import org.apache.click.service.LogService;
+import org.apache.click.util.ClickUtils;
+
+/**
+ * Provides a basic page profiling PageInterceptor example.
+ * <p/>
+ * This interceptor must be configured with "request" scope as it not thread
+ * safe.
+ */
+public class ProfilingInterceptor implements PageInterceptor {
+
+    private String pageName;
+    private long startTime;
+    private long checkpointTime;
+    private long createDuration;
+    private long processDuration;
+    private long renderDuration;
+    private long totalDuration;
+
+    /**
+     * @see PageInterceptor#preCreate(Class, Context)
+     */
+    public boolean preCreate(Class<? extends Page> pageClass, Context context) {
+        checkpointTime = System.currentTimeMillis();
+        startTime = checkpointTime;
+        pageName = pageClass.getSimpleName();
+        return true;
+    }
+
+    /**
+     * @see PageInterceptor#pagePostCreate(Class, Context)
+     */
+    public boolean postCreate(Page page) {
+        createDuration = System.currentTimeMillis() - checkpointTime;
+        checkpointTime = System.currentTimeMillis();
+        return true;
+    }
+
+    /**
+     * @see PageInterceptor#preResponse(Page)
+     */
+    public boolean preResponse(Page page) {
+        processDuration = System.currentTimeMillis() - checkpointTime;
+        checkpointTime = System.currentTimeMillis();
+        return true;
+    }
+
+    /**
+     * @see PageInterceptor#postDestroy(Page)
+     */
+    public void postDestroy(Page page) {
+        renderDuration = System.currentTimeMillis() - checkpointTime;
+        totalDuration = System.currentTimeMillis() - startTime;
+
+        LogService logService = ClickUtils.getLogService();
+        if (logService.isInfoEnabled()) {
+            logService.info(this);
+        }
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString() {
+        return getClass().getSimpleName()
+            + "[page=" + pageName
+            + ", createDuration=" + createDuration
+            + ", processDuration=" + processDuration
+            + ", renderDuration=" + renderDuration
+            + ", totalDuration=" + totalDuration
+            + "]";
+    }
+
+}

Added: click/trunk/click/examples/src/org/apache/click/examples/interceptor/package.html
URL: http://svn.apache.org/viewvc/click/trunk/click/examples/src/org/apache/click/examples/interceptor/package.html?rev=912334&view=auto
==============================================================================
--- click/trunk/click/examples/src/org/apache/click/examples/interceptor/package.html (added)
+++ click/trunk/click/examples/src/org/apache/click/examples/interceptor/package.html Sun
Feb 21 11:49:41 2010
@@ -0,0 +1,22 @@
+<!--
+   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.
+-->
+
+<body>
+Provides a page profiling interceptor example.
+</body>
\ No newline at end of file

Modified: click/trunk/click/examples/webapp/WEB-INF/click.xml
URL: http://svn.apache.org/viewvc/click/trunk/click/examples/webapp/WEB-INF/click.xml?rev=912334&r1=912333&r2=912334&view=diff
==============================================================================
--- click/trunk/click/examples/webapp/WEB-INF/click.xml (original)
+++ click/trunk/click/examples/webapp/WEB-INF/click.xml Sun Feb 21 11:49:41 2010
@@ -24,4 +24,6 @@
 
   <mode value="profile"/>
 
+  <page-interceptor classname="org.apache.click.examples.interceptor.ProfilingInterceptor"/>
+
 </click-app>

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=912334&r1=912333&r2=912334&view=diff
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/ClickServlet.java (original)
+++ click/trunk/click/framework/src/org/apache/click/ClickServlet.java Sun Feb 21 11:49:41
2010
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.io.Writer;
 import java.lang.reflect.Field;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.Iterator;
@@ -36,9 +37,9 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+
 import ognl.DefaultMemberAccess;
 import ognl.MemberAccess;
-
 import ognl.Ognl;
 import ognl.OgnlException;
 import ognl.TypeConverter;
@@ -162,14 +163,18 @@
     /** The application log service. */
     protected LogService logger;
 
+    /** The OGNL member access handler. */
+    protected MemberAccess memberAccess;
+
     /** The application resource service. */
     protected ResourceService resourceService;
 
     /** The request parameters OGNL type converter. */
     protected TypeConverter typeConverter;
 
-    /** The OGNL member access handler. */
-    protected MemberAccess memberAccess;
+    /** The thread local page listeners. */
+    private static final ThreadLocal<List<PageInterceptor>>
+        THREAD_LOCAL_INTERCEPTORS = new ThreadLocal<List<PageInterceptor>>();
 
     // --------------------------------------------------------- Public Methods
 
@@ -349,7 +354,12 @@
                 }
             }
 
-            page = createPage(request);
+            page = createPage(context);
+
+            // If no page created, then an PageInterceptor has aborted processing
+            if (page == null) {
+                return;
+            }
 
             if (page.isStateful()) {
                 synchronized (page) {
@@ -392,6 +402,12 @@
                     }
                 }
 
+                for (PageInterceptor interceptor : getThreadLocalInterceptors()) {
+                    interceptor.postDestroy(page);
+                }
+
+                setThreadLocalInterceptors(null);
+
             } finally {
                 // Only clear the context when running in normal mode.
                 if (request.getAttribute(MOCK_MODE_ENABLED) == null) {
@@ -715,6 +731,13 @@
      */
     protected void performRender(Page page, Context context) throws Exception {
 
+        // Process page interceptors, and abort rendering if specified
+        for (PageInterceptor interceptor : getThreadLocalInterceptors()) {
+            if (!interceptor.preResponse(page)) {
+                return;
+            }
+        }
+
         final HttpServletRequest request = context.getRequest();
         final HttpServletResponse response = context.getResponse();
 
@@ -876,14 +899,17 @@
     }
 
     /**
-     * Return a new Page instance for the given request. This method will
+     * Return a new Page instance for the given request context. This method will
      * invoke {@link #initPage(String, Class, HttpServletRequest)} to create
      * the Page instance and then set the properties on the page.
      *
-     * @param request the servlet request
-     * @return a new Page instance for the given request
+     * @param context the page request context
+     * @return a new Page instance for the given request, or null if an
+     * PageInterceptor has aborted page creation
      */
-    protected Page createPage(HttpServletRequest request) {
+    protected Page createPage(Context context) {
+
+        HttpServletRequest request = context.getRequest();
 
         // Log request parameters
         if (logger.isTraceEnabled()) {
@@ -929,6 +955,15 @@
             pageClass = configService.getNotFoundPageClass();
             path = ConfigService.NOT_FOUND_PATH;
         }
+        // Set thread local app page listeners
+        List<PageInterceptor> interceptors = configService.getPageInterceptors();
+        setThreadLocalInterceptors(interceptors);
+
+        for (PageInterceptor listener : interceptors) {
+            if (!listener.preCreate(pageClass, context)) {
+                return null;
+            }
+        }
 
         final Page page = initPage(path, pageClass, request);
 
@@ -936,6 +971,12 @@
             page.setFormat(configService.createFormat());
         }
 
+        for (PageInterceptor listener : interceptors) {
+            if (!listener.postCreate(page)) {
+                return null;
+            }
+        }
+
         return page;
     }
 
@@ -1692,6 +1733,21 @@
         }
     }
 
+    List<PageInterceptor> getThreadLocalInterceptors() {
+        List<PageInterceptor> listeners =
+            (List<PageInterceptor>) THREAD_LOCAL_INTERCEPTORS.get();
+
+        if (listeners != null) {
+            return listeners;
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    void setThreadLocalInterceptors(List<PageInterceptor> listeners) {
+        THREAD_LOCAL_INTERCEPTORS.set(listeners);
+    }
+
     // ---------------------------------------------------------- Inner Classes
 
     /**

Added: click/trunk/click/framework/src/org/apache/click/PageInterceptor.java
URL: http://svn.apache.org/viewvc/click/trunk/click/framework/src/org/apache/click/PageInterceptor.java?rev=912334&view=auto
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/PageInterceptor.java (added)
+++ click/trunk/click/framework/src/org/apache/click/PageInterceptor.java Sun Feb 21 11:49:41
2010
@@ -0,0 +1,232 @@
+/*
+ * 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;
+
+/**
+ * Provides an Page life cycle interceptor. Classes implementing this interface
+ * can be used listen for key page life cycle events and abort further page
+ * processing if required.
+ * <p/>
+ * PageInterceptors can be used many different purposes including:
+ * <ul>
+ * <li>enforcing application wide page security policies</li>
+ * <li>injecting dependencies into page objects</li>
+ * <li>logging and profiling page performance</li>
+ * </ul>
+ *
+ * A Click application can define multiple page interceptors which are invoked in
+ * the order in which they are returned by the <tt>ConfigService</tt>.
+ *
+ * <h3>Scope</h3>
+ *
+ * Page interceptors can be define with a request level scope, where by a new
+ * page interceptor will be created with each page request providing a thread
+ * safe programming model. This is equivalent to a Spring "prototype" object.
+ * <p/>
+ * Please not with as new instances are created with each request, care should
+ * be taken to ensure that these objects are light weight and do not introduce a
+ * performance bottle neck into your application.
+ * <p/>
+ * Alternatively page interceptors can be defined with application level scope
+ * where by a single instance is created for the application and is used for
+ * all requests. This is the equivalent to Spring "singleton" object.
+ * <p/>
+ * Note application scope interceptors are more efficient that request scope
+ * interceptors, but you are responsible for ensuring that they are thread safe
+ * and support reentrant method invocations as multiple page requests are
+ * processed at the same time.
+ *
+ * <h3>Configuration</h3>
+ *
+ * Application PageInterceptors are configured in the <tt>click.xml</tt>
+ * configuration file. PageInterceptors must support a construction using a
+ * no-args public constructor.
+ * <p/>
+ * Page interceptors can have multiple properties configured with their XML
+ * definition which are set after the constructor has been called. Properties
+ * are set using OGNL via {@link org.apache.click.util.PropertyUtils}.
+ * <p/>
+ * An example configuration is provided below:
+ *
+ * <pre class="prettyprint">
+ * &lt;page-interceptor classname="com.mycorp.PageSecurityInterceptor" scope="application"&gt;
+ *     &lt;property name="notAuthenticatedPath" value="/not-authenticated.htm"/&gt;
+ *     &lt;property name="notAuthorizedPath" value="/not-authorized.htm"/&gt;
+ * &lt;/page-interceptor&gt; </pre>
+ *
+ * The default scope for page interceptors is "request", but this can be configured
+ * as "application" as is done in the example configuration above.
+ *
+ * <h3>Example</h3>
+ *
+ * <pre class="prettyprint">
+ * public class SecurityInterceptor implements PageInterceptor {
+ *
+ *    // The request not authenticated redirect path.
+ *    private String notAuthenticatedPath;
+ *
+ *    // The request not authorized redirect path.
+ *    private String notAuthorizedPath;
+ *
+ *    // Public Methods ---------------------------------------------------------
+ *
+ *    public boolean preCreate(Class<? extends Page> pageClass, Context context) {
+ *       // If authentication required, then ensure user is authenticated
+ *       Authentication authentication = pageClass.getAnnotation(Authentication.class);
+ *       // TODO: user context check.
+ *       if (authentication != null && authentication.required()) {
+ *          sendRedirect(getNotAuthenticatedPath(), context);
+ *          return false;
+ *       }
+ *
+ *       // If authorization permission defined, then ensure user is authorized to access
the page
+ *       Authorization authorization = pageClass.getAnnotation(Authorization.class);
+ *       if (authorization != null) {
+ *          if (!UserContext.getThreadUserContext().hasPermission(authorization.permission()))
{
+ *             sendRedirect(getNotAuthorizedPath(), context);
+ *             return false;
+ *          }
+ *       }
+ *
+ *       return true;
+ *    }
+ *
+ *    public boolean postCreate(Page page) {
+ *       return true;
+ *    }
+ *
+ *    public boolean preResponse(Page page) {
+ *       return true;
+ *    }
+ *
+ *    public void postDestroy(Page page) {
+ *    }
+ *
+ *    public String getNotAuthenticatedPath() {
+ *       return notAuthenticatedPath;
+ *    }
+ *
+ *    public void setNotAuthenticatedPath(String notAuthenticatedPath) {
+ *       this.notAuthenticatedPath = notAuthenticatedPath;
+ *    }
+ *
+ *    public String getNotAuthorizedPath() {
+ *       return notAuthorizedPath;
+ *    }
+ *
+ *    public void setNotAuthorizedPath(String notAuthorizedPath) {
+ *       this.notAuthorizedPath = notAuthorizedPath;
+ *    }
+ *
+ *    // Protected Methods ------------------------------------------------------
+ *
+ *    protected void sendRedirect(String location, Context context) {
+ *       if (StringUtils.isNotBlank(location)) {
+ *          if (location.charAt(0) == '/') {
+ *             String contextPath = context.getRequest().getContextPath();
+ *
+ *             // Guard against adding duplicate context path
+ *             if (!location.startsWith(contextPath + '/')) {
+ *                location = contextPath + location;
+ *             }
+ *          }
+ *       }
+ *
+ *       location = context.getResponse().encodeRedirectURL(location);
+ *
+ *       try {
+ *          context.getResponse().sendRedirect(location);
+ *
+ *       } catch (IOException ioe) {
+ *          throw new RuntimeException(ioe);
+ *       }
+ *   }
+ *
+ * }
+ *
+ * // Page class authentication annotation
+ * &#64;Retention(RetentionPolicy.RUNTIME)
+ * public @interface Authentication {
+ *    boolean required() default true;
+ * }
+ *
+ * // Page class authorization annotation
+ * &#64;Retention(RetentionPolicy.RUNTIME)
+ * public @interface Authorization {
+ *    String permission();
+ * }
+ * </pre>
+ */
+public interface PageInterceptor {
+
+    /**
+     * Provides a before page object creation interceptor method, which is passed
+     * the class of the page to be instantiated and the page request context.
+     * If this method returns true then the normal page processing is performed,
+     * otherwise if this method returns false the page instance is never created
+     * and the request is considered to have been handled.
+     *
+     * @param pageClass the class of the page to be instantiated
+     * @param context the page request context
+     * @return true to continue normal page processing or false whereby the
+     * request is considered to be handled
+     */
+    public boolean preCreate(Class<? extends Page> pageClass, Context context);
+
+    /**
+     * Provides a post page object creation interceptor method, which is passed
+     * the instance of the newly created page. This interceptor method is called
+     * before the page {@link Page#onSecurityCheck()} method is invoked.
+     * <p/>
+     * If this method returns true then the normal page processing is performed,
+     * otherwise if this method returns false the request is considered to have
+     * been handled. Please note the page {@link Page#onDestroy()} method will
+     * still be invoked.
+     *
+     * @param page the newly instantiated page instance
+     * @return true to continue normal page processing or false whereby the
+     * request is considered to be handled
+     */
+    public boolean postCreate(Page page);
+
+    /**
+     * Provides a page interceptor before response method. This method is invoked
+     * prior to the page redirect, forward or rendering phase.
+     * <p/>
+     * If this method returns true then the normal page processing is performed,
+     * otherwise if this method returns false request is considered to have been
+     * handled. Please note the page {@link Page#onDestroy()} method will
+     * still be invoked.
+     *
+     * @param page the newly instantiated page instance
+     * @return true to continue normal page processing or false whereby the
+     * request is considered to be handled
+     */
+    public boolean preResponse(Page page);
+
+    /**
+     * Provides a post page destroy interceptor method. This interceptor method
+     * is called immediately after the page {@link Page#onDestroy()} method is
+     * invoked.
+     *
+     * @param page the page object which has just been destroyed
+     */
+    public void postDestroy(Page page);
+
+}

Modified: click/trunk/click/framework/src/org/apache/click/click.dtd
URL: http://svn.apache.org/viewvc/click/trunk/click/framework/src/org/apache/click/click.dtd?rev=912334&r1=912333&r2=912334&view=diff
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/click.dtd (original)
+++ click/trunk/click/framework/src/org/apache/click/click.dtd Sun Feb 21 11:49:41 2010
@@ -20,7 +20,7 @@
 -->
 
 <!-- The Click Application (click.xml) Document Type Definition. -->
-<!ELEMENT click-app (pages*, headers?, format?, mode?, controls?, file-upload-service?,
log-service?, template-service?)>
+<!ELEMENT click-app (pages*, headers?, format?, mode?, controls?, file-upload-service?,
log-service?, template-service?, page-listener*)>
   <!ATTLIST click-app charset CDATA #IMPLIED>
   <!ATTLIST click-app locale CDATA #IMPLIED>
 
@@ -85,3 +85,8 @@
   <!ELEMENT template-service (property*)>
    <!ATTLIST template-service classname CDATA #FIXED "org.apache.click.service.VelocityTemplateService">
   
+  <!-- Application page listeners. -->
+  <!ELEMENT page-listener (property*)>
+    <!ATTLIST page-listener classname CDATA #REQUIRED>
+    <!ATTLIST page-listener scope (application|request) "request">
+  
\ No newline at end of file

Modified: click/trunk/click/framework/src/org/apache/click/service/ConfigService.java
URL: http://svn.apache.org/viewvc/click/trunk/click/framework/src/org/apache/click/service/ConfigService.java?rev=912334&r1=912333&r2=912334&view=diff
==============================================================================
--- click/trunk/click/framework/src/org/apache/click/service/ConfigService.java (original)
+++ click/trunk/click/framework/src/org/apache/click/service/ConfigService.java Sun Feb 21
11:49:41 2010
@@ -25,6 +25,7 @@
 
 import javax.servlet.ServletContext;
 
+import org.apache.click.PageInterceptor;
 import org.apache.click.util.Format;
 
 /**
@@ -329,6 +330,12 @@
      */
     public Field[] getPageFieldArray(Class pageClass);
 
+    /**
+     * Return the list of configured PageInterceptors instances.
+     *
+     * @return the list of configured PageInterceptors instances
+     */
+    public List<PageInterceptor> getPageInterceptors();
 
     /**
      * Return the page not found <tt>Page</tt> <tt>Class</tt>.

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=912334&r1=912333&r2=912334&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 Sun Feb
21 11:49:41 2010
@@ -43,10 +43,12 @@
 
 import org.apache.click.Control;
 import org.apache.click.Page;
+import org.apache.click.PageInterceptor;
 import org.apache.click.util.Bindable;
 import org.apache.click.util.ClickUtils;
 import org.apache.click.util.Format;
 import org.apache.click.util.HtmlStringBuffer;
+import org.apache.click.util.PropertyUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.Validate;
 import org.w3c.dom.Document;
@@ -182,6 +184,10 @@
      */
     private int mode;
 
+    /** The list of application page interceptor instances. */
+    private List<PageInterceptorConfig> pageInterceptorConfigList
+        = new ArrayList<PageInterceptorConfig>();
+
     /** The ServletContext instance. */
     private ServletContext servletContext;
 
@@ -251,6 +257,9 @@
             // Load the Resource service
             loadResourceService(rootElm);
 
+            // Load the PageInterceptors
+            loadPageInterceptors(rootElm);
+
         } finally {
             ClickUtils.close(inputStream);
         }
@@ -721,6 +730,27 @@
     }
 
     /**
+     * @see ConfigService#getPageInterceptors()
+     *
+     * @return the list of configured PageInterceptor instances
+     */
+    public List<PageInterceptor> getPageInterceptors() {
+
+        if (pageInterceptorConfigList.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        List<PageInterceptor> interceptorList =
+            new ArrayList<PageInterceptor>(pageInterceptorConfigList.size());
+
+        for (PageInterceptorConfig pageListenerConfig : pageInterceptorConfigList) {
+            interceptorList.add(pageListenerConfig.getPageInterceptor());
+        }
+
+        return interceptorList;
+    }
+
+    /**
      * @see ConfigService#getServletContext()
      *
      * @return the application servlet context
@@ -1506,6 +1536,35 @@
         }
     }
 
+    private void loadPageInterceptors(Element rootElm) throws Exception {
+        List<Element> interceptorList = (List<Element>)
+            ClickUtils.getChildren(rootElm, "page-interceptor");
+
+        for (Element interceptorElm : interceptorList) {
+            String classname = interceptorElm.getAttribute("classname");
+
+            String scopeValue = interceptorElm.getAttribute("scope");
+            boolean applicationScope = "application".equalsIgnoreCase(scopeValue);
+
+            Class interceptorClass = ClickUtils.classForName(classname);
+
+            Map propertyMap = loadPropertyMap(interceptorElm);
+            List<Property> propertyList = new ArrayList<Property>();
+
+            for (Iterator i = propertyMap.keySet().iterator(); i.hasNext();) {
+                String name = i.next().toString();
+                String value = propertyMap.get(name).toString();
+
+                propertyList.add(new Property(name, value));
+            }
+
+            PageInterceptorConfig pageListenerConfig =
+                new PageInterceptorConfig(interceptorClass, applicationScope, propertyList);
+
+            pageInterceptorConfigList.add(pageListenerConfig);
+        }
+    }
+
     private void loadResourceService(Element rootElm) throws Exception {
 
         Element resourceServiceElm = ClickUtils.getChild(rootElm, "resource-service");
@@ -1735,6 +1794,30 @@
 
     // ---------------------------------------------------------- Inner Classes
 
+    /**
+     * Provide an Excluded Page class.
+     * <p/>
+     * <b>PLEASE NOTE</b> this class is <b>not</b> for public use,
and can be
+     * ignored.
+     */
+    public static class ExcludePage extends Page {
+
+        static final Map HEADERS = new HashMap();
+
+        static {
+            HEADERS.put("Cache-Control", "max-age=3600, public");
+        }
+
+        /**
+         * @see Page#getHeaders()
+         *
+         * @return the map of HTTP header to be set in the HttpServletResponse
+         */
+        public Map getHeaders() {
+            return HEADERS;
+        }
+    }
+
     static class PageElm {
 
         final Map fields;
@@ -1904,27 +1987,71 @@
         }
     }
 
-    /**
-     * Provide an Excluded Page class.
-     * <p/>
-     * <b>PLEASE NOTE</b> this class is <b>not</b> for public use,
and can be
-     * ignored.
-     */
-    public static class ExcludePage extends Page {
+    static class PageInterceptorConfig {
 
-        static final Map HEADERS = new HashMap();
+        final Class<? extends PageInterceptor> interceptorClass;
+        final boolean applicationScope;
+        final List<Property> properties;
+        PageInterceptor pageInterceptor;
 
-        static {
-            HEADERS.put("Cache-Control", "max-age=3600, public");
+        PageInterceptorConfig(Class<? extends PageInterceptor> interceptorClass,
+                           boolean applicationScope,
+                           List<Property> properties) {
+
+            this.interceptorClass = interceptorClass;
+            this.applicationScope = applicationScope;
+            this.properties = properties;
         }
 
-        /**
-         * @see Page#getHeaders()
-         *
-         * @return the map of HTTP header to be set in the HttpServletResponse
-         */
-        public Map getHeaders() {
-            return HEADERS;
+        public PageInterceptor getPageInterceptor() {
+            PageInterceptor listener = null;
+
+            // If cached interceptor not already created (application scope)
+            // or is scope request then create a new interceptor
+            if (pageInterceptor == null || !applicationScope) {
+                try {
+                    listener = (PageInterceptor) interceptorClass.newInstance();
+
+                    Map ognlContext = new HashMap();
+
+                    for (Property property : properties) {
+                        PropertyUtils.setValueOgnl(listener,
+                                                   property.getName(),
+                                                   property.getValue(),
+                                                   ognlContext);
+                    }
+
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+
+                if (applicationScope) {
+                    pageInterceptor = listener;
+                }
+
+            } else {
+                listener = pageInterceptor;
+            }
+
+            return listener;
+        }
+    }
+
+    static class Property {
+        final String name;
+        final String value;
+
+        Property(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getValue() {
+            return value;
         }
     }
 



Mime
View raw message