Author: craigmcc Date: Sun May 21 20:51:47 2006 New Revision: 408560 URL: http://svn.apache.org/viewvc?rev=408560&view=rev Log: Implement servlet scoped (request, session, application) event callbacks via annotations, in addition to the support found in org.apache.shale.view. Abstract{Request,Session,Application}Bean. This support enables zero-config lifecycle support for arbitrary application beans, without having to implement things like a ServletContextAttributeListener. Includes tests cases.
Added: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Activate.java struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Application.java struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Passivate.java struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Request.java struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Session.java struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean1.java struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean2.java struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/LifecycleListener2TestCase.java struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean1.java struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean2.java struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean1.java struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean2.java Modified: struts/shale/trunk/sql-browser/nbproject/project.xml struts/shale/trunk/tiger/nbproject/private/private.xml struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Destroy.java struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Init.java struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/package.html Modified: struts/shale/trunk/sql-browser/nbproject/project.xml URL: http://svn.apache.org/viewvc/struts/shale/trunk/sql-browser/nbproject/project.xml?rev=408560&r1=408559&r2=408560&view=diff ============================================================================== --- struts/shale/trunk/sql-browser/nbproject/project.xml (original) +++ struts/shale/trunk/sql-browser/nbproject/project.xml Sun May 21 20:51:47 2006 @@ -94,7 +94,7 @@ <package-root>src/systest</package-root> <package-root>src/test</package-root> <package-root>src/web</package-root> - <classpath mode="compile">../target/dist/shale-core.jar:../target/dist/shale-tiger.jar:../lib/myfaces/myfaces-api.jar:../lib/servlet-api/servlet-api.jar:../lib/myfaces/myfaces-api.jar:../../../../../../../usr/local/db-derby-10.1.2.1-bin/lib/derby.jar</classpath> + <classpath mode="compile">../target/dist/shale-core.jar;../target/dist/shale-tiger.jar;../lib/myfaces/myfaces-api.jar;../lib/servlet-api/servlet-api.jar;../lib/myfaces/myfaces-api.jar;../../../../../usr/local/db-derby-10.1.2.1-bin/lib/derby.jar;../lib/jsf-ri/jsf-api.jar</classpath> <source-level>1.5</source-level> </compilation-unit> </java-data> Modified: struts/shale/trunk/tiger/nbproject/private/private.xml URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/nbproject/private/private.xml?rev=408560&r1=408559&r2=408560&view=diff ============================================================================== --- struts/shale/trunk/tiger/nbproject/private/private.xml (original) +++ struts/shale/trunk/tiger/nbproject/private/private.xml Sun May 21 20:51:47 2006 @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project-private xmlns="http://www.netbeans.org/ns/project-private/1"> - <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/> -</project-private> +<?xml version="1.0" encoding="UTF-8"?> +<project-private xmlns="http://www.netbeans.org/ns/project-private/1"> + <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/> +</project-private> Added: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Activate.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Activate.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Activate.java (added) +++ struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Activate.java Sun May 21 20:51:47 2006 @@ -0,0 +1,41 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>Method-level annotation indicating that the decorated method should + * have the semantics of + * <code>org.apache.shale.view.AbstractSessionBean.activate()</code>, + * even if it is named differently.</p> + * + * <p><strong>ASSERTION</strong> - the annotated method is public, has + * a return type of <code>void</code>, and takes no parameters.</p> + * + * <p><strong>ASSERTION</strong> - the annotated method is part of a class + * that is annotated with the [EMAIL PROTECTED] Session} annotation.</p> + * + * @since 1.0.3 + */ [EMAIL PROTECTED](RetentionPolicy.RUNTIME) [EMAIL PROTECTED](ElementType.METHOD) +public @interface Activate { +} Added: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Application.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Application.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Application.java (added) +++ struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Application.java Sun May 21 20:51:47 2006 @@ -0,0 +1,38 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>Class-level annotation indicating that the decorated class should be + * treated semantically as if it extended + * <code>org.apache.shale.view.AbstractApplicationBean</code>, even if it does not.</p> + * + * <p><strong>WARNING</strong> - it may turn out that this annotation is + * unnecessary, because an annotation processor can simply look for a class + * that has method(s) implementing the method level annotations.</p> + * + * @since 1.0.3 + */ [EMAIL PROTECTED](RetentionPolicy.RUNTIME) [EMAIL PROTECTED](ElementType.TYPE) +public @interface Application { +} Modified: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Destroy.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Destroy.java?rev=408560&r1=408559&r2=408560&view=diff ============================================================================== --- struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Destroy.java (original) +++ struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Destroy.java Sun May 21 20:51:47 2006 @@ -25,13 +25,15 @@ * <p>Method-level annotation indicating that the decorated method should * have the semantics of * <code>org.apache.shale.view.ViewController.destroy()</code>, + * or one of the scoped data bean destroy() methods, * even if it is named differently.</p> * * <p><strong>ASSERTION</strong> - the annotated method is public, has * a return type of <code>void</code>, and takes no parameters.</p> * * <p><strong>ASSERTION</strong> - the annotated method is part of a class - * that is annotated with the [EMAIL PROTECTED] View} annotation.</p> + * that is annotated with the [EMAIL PROTECTED] Application}, [EMAIL PROTECTED] Request}, + * [EMAIL PROTECTED] Session}, or [EMAIL PROTECTED] View} annotation.</p> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) Modified: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Init.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Init.java?rev=408560&r1=408559&r2=408560&view=diff ============================================================================== --- struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Init.java (original) +++ struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Init.java Sun May 21 20:51:47 2006 @@ -25,13 +25,15 @@ * <p>Method-level annotation indicating that the decorated method should * have the semantics of * <code>org.apache.shale.view.ViewController.init()</code>, + * or one of the scoped data bean init() methods, * even if it is named differently.</p> * * <p><strong>ASSERTION</strong> - the annotated method is public, has * a return type of <code>void</code>, and takes no parameters.</p> * * <p><strong>ASSERTION</strong> - the annotated method is part of a class - * that is annotated with the [EMAIL PROTECTED] View} annotation.</p> + * that is annotated with the [EMAIL PROTECTED] Application}, [EMAIL PROTECTED] Request}, + * [EMAIL PROTECTED] Session}, or [EMAIL PROTECTED] View} annotation.</p> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) Added: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Passivate.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Passivate.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Passivate.java (added) +++ struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Passivate.java Sun May 21 20:51:47 2006 @@ -0,0 +1,41 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>Method-level annotation indicating that the decorated method should + * have the semantics of + * <code>org.apache.shale.view.AbstractSessionBean.passivate()</code>, + * even if it is named differently.</p> + * + * <p><strong>ASSERTION</strong> - the annotated method is public, has + * a return type of <code>void</code>, and takes no parameters.</p> + * + * <p><strong>ASSERTION</strong> - the annotated method is part of a class + * that is annotated with the [EMAIL PROTECTED] Session} annotation.</p> + * + * @since 1.0.3 + */ [EMAIL PROTECTED](RetentionPolicy.RUNTIME) [EMAIL PROTECTED](ElementType.METHOD) +public @interface Passivate { +} Added: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Request.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Request.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Request.java (added) +++ struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Request.java Sun May 21 20:51:47 2006 @@ -0,0 +1,38 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>Class-level annotation indicating that the decorated class should be + * treated semantically as if it extended + * <code>org.apache.shale.view.AbstractRequestBean</code>, even if it does not.</p> + * + * <p><strong>WARNING</strong> - it may turn out that this annotation is + * unnecessary, because an annotation processor can simply look for a class + * that has method(s) implementing the method level annotations.</p> + * + * @since 1.0.3 + */ [EMAIL PROTECTED](RetentionPolicy.RUNTIME) [EMAIL PROTECTED](ElementType.TYPE) +public @interface Request { +} Added: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Session.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Session.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Session.java (added) +++ struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/Session.java Sun May 21 20:51:47 2006 @@ -0,0 +1,38 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <p>Class-level annotation indicating that the decorated class should be + * treated semantically as if it extended + * <code>org.apache.shale.view.AbstractSessionBean</code>, even if it does not.</p> + * + * <p><strong>WARNING</strong> - it may turn out that this annotation is + * unnecessary, because an annotation processor can simply look for a class + * that has method(s) implementing the method level annotations.</p> + * + * @since 1.0.3 + */ [EMAIL PROTECTED](RetentionPolicy.RUNTIME) [EMAIL PROTECTED](ElementType.TYPE) +public @interface Session { +} Added: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java (added) +++ struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java Sun May 21 20:51:47 2006 @@ -0,0 +1,597 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view.faces; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletContextAttributeEvent; +import javax.servlet.ServletRequestAttributeEvent; +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionEvent; +import org.apache.shale.tiger.view.Activate; +import org.apache.shale.tiger.view.Application; +import org.apache.shale.tiger.view.Destroy; +import org.apache.shale.tiger.view.Init; +import org.apache.shale.tiger.view.Passivate; +import org.apache.shale.tiger.view.Preprocess; +import org.apache.shale.tiger.view.Prerender; +import org.apache.shale.tiger.view.Request; +import org.apache.shale.tiger.view.Session; +import org.apache.shale.tiger.view.View; +import org.apache.shale.view.AbstractApplicationBean; +import org.apache.shale.view.AbstractRequestBean; +import org.apache.shale.view.AbstractSessionBean; +import org.apache.shale.view.faces.LifecycleListener; + +/** + * <p>Specialized version of <code>org.apache.shale.view.faces.LifecycleListener</code> + * that implements callbacks to methods tagged by appropriate annotations, + * rather than requiring the containing classes to implement a particular + * interface or extend a particular subclass.</p> + * + * $Id$ + * + * @since 1.0.3 + */ +public class LifecycleListener2 extends LifecycleListener { + + + // ------------------------------------------------------------- Constructor + + + /** + * <p>Create a new lifecycle listener.</p> + */ + public LifecycleListener2() { + } + + + // ------------------------------------------------------ Instance Variables + + + /** + * <p>Parameter values array that passes no parameters.</p> + */ + private static final Object PARAMETERS[] = new Object[0]; + + + // --------------------------------- ServletContextAttributeListener Methods + + + /** + * <p>Respond to an application scope attribute being added. If the + * value is an [EMAIL PROTECTED] AbstractApplicationBean}, call its + * <code>init()</code> method.</p> + * + * @param event Event to be processed + */ + public void attributeAdded(ServletContextAttributeEvent event) { + + Object value = event.getValue(); + if (value != null) { + fireApplicationInit(value); + } + + } + + + /** + * <p>Respond to an application scope attribute being replaced. + * If the old value was an [EMAIL PROTECTED] AbstractApplicationBean}, call + * its <code>destroy()</code> method. If the new value is an + * [EMAIL PROTECTED] AbstractApplicationBean}, call its <code>init()</code> + * method.</p> + * + * @param event Event to be processed + */ + public void attributeReplaced(ServletContextAttributeEvent event) { + + Object value = event.getValue(); + if (value != null) { + fireApplicationDestroy(value); + } + + value = event.getServletContext().getAttribute(event.getName()); + if (value != null) { + fireApplicationInit(value); + } + + } + + + /** + * <p>Respond to an application scope attribute being removed. + * If the old value was an [EMAIL PROTECTED] AbstractApplicationBean}, call + * its <code>destroy()</code> method.</p> + * + * @param event Event to be processed + */ + public void attributeRemoved(ServletContextAttributeEvent event) { + + Object value = event.getValue(); + if (value != null) { + fireApplicationDestroy(value); + } + + } + + + // ----------------------------------- HttpSessionActivationListener Methods + + + /** + * <p>Respond to a "session will passivate" event. Notify all session + * scope attributes that are [EMAIL PROTECTED] AbstractSessionBean}s.</p> + * + * @param event Event to be processed + */ + public void sessionWillPassivate(HttpSessionEvent event) { + + Enumeration names = event.getSession().getAttributeNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + Object value = event.getSession().getAttribute(name); + if (value != null) { + fireSessionPassivate(value); + } + } + + } + + + /** + * <p>Respond to a "session did activate" event. Notify all session + * scope attributes that are [EMAIL PROTECTED] AbstractSessionBean}s.</p> + * + * @param event Event to be processed + */ + public void sessionDidActivate(HttpSessionEvent event) { + + Enumeration names = event.getSession().getAttributeNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + Object value = event.getSession().getAttribute(name); + if (value != null) { + fireSessionActivate(value); + } + } + + } + + + // ------------------------------------ HttpSessionAttributeListener Methods + + + /** + * <p>Respond to a session scope attribute being added. If the + * value is an [EMAIL PROTECTED] AbstractSessionBean}, call its + * <code>init()</code> method.</p> + * + * @param event Event to be processed + */ + public void attributeAdded(HttpSessionBindingEvent event) { + + Object value = event.getValue(); + if (value != null) { + fireSessionInit(value); + } + + } + + + /** + * <p>Respond to a session scope attribute being replaced. + * If the old value was an [EMAIL PROTECTED] AbstractSessionBean}, call + * its <code>destroy()</code> method. If the new value is an + * [EMAIL PROTECTED] AbstractSessionBean}, call its <code>init()</code> + * method.</p> + * + * @param event Event to be processed + */ + public void attributeReplaced(HttpSessionBindingEvent event) { + + Object value = event.getValue(); + if (value != null) { + fireSessionDestroy(value); + } + + value = event.getSession().getAttribute(event.getName()); + if (value != null) { + fireSessionInit(value); + } + + } + + + /** + * <p>Respond to a session scope attribute being removed. + * If the old value was an [EMAIL PROTECTED] AbstractSessionBean}, call + * its <code>destroy()</code> method.</p> + * + * @param event Event to be processed + */ + public void attributeRemoved(HttpSessionBindingEvent event) { + + Object value = event.getValue(); + if (value != null) { + fireSessionDestroy(value); + } + + } + + + // --------------------------------- ServletRequestAttributeListener Methods + + + /** + * <p>Respond to a request scope attribute being added. If the + * value is an [EMAIL PROTECTED] AbstractRequestBean}, call its <code>init()</code> method. + * </p> + * + * @param event Event to be processed + */ + public void attributeAdded(ServletRequestAttributeEvent event) { + + Object value = event.getValue(); + if (value != null) { + fireRequestInit(value); + } + + } + + + /** + * <p>Respond to a request scope attribute being replaced. + * If the old value was an [EMAIL PROTECTED] AbstractRequestBean}, + * call its <code>destroy()</code> method. If the new value is an + * [EMAIL PROTECTED] AbstractRequestBean}, call its <code>init()</code> method.</p> + * + * @param event Event to be processed + */ + public void attributeReplaced(ServletRequestAttributeEvent event) { + + Object value = event.getValue(); + if (value != null) { + fireRequestDestroy(value); + } + + value = event.getServletRequest().getAttribute(event.getName()); + if (value != null) { + fireRequestInit(value); + } + + } + + + /** + * <p>Respond to a request scope attribute being removed. + * If the old value was an [EMAIL PROTECTED] AbstractRequestBean}, + * call its <code>destroy()</code> method.</p> + * + * @param event Event to be processed + */ + public void attributeRemoved(ServletRequestAttributeEvent event) { + + Object value = event.getValue(); + if (value != null) { + fireRequestDestroy(value); + } + + } + + + // ------------------------------------------------------- Protected Methods + + + /** + * <p>Fire a destroy event on an @{link AbstractApplicationBean}.</p> + * + * @param bean [EMAIL PROTECTED] AbstractApplicationBean} to fire event on + */ + protected void fireApplicationDestroy(Object bean) { + + if (bean instanceof AbstractApplicationBean) { + super.fireApplicationDestroy(bean); + return; + } + + try { + Method method = method(bean, Destroy.class); + if (method != null) { + method.invoke(bean, PARAMETERS); + } + } catch (InvocationTargetException e) { + cacheException((Exception) e.getCause()); + } catch (Exception e) { + cacheException(e); + } + + } + + + /** + * <p>Fire an init event on an [EMAIL PROTECTED] AbstractApplicationBean}.</p> + * + * @param bean [EMAIL PROTECTED] AbstractApplicationBean} to fire event on + */ + protected void fireApplicationInit(Object bean) { + + if (bean instanceof AbstractApplicationBean) { + super.fireApplicationInit(bean); + return; + } + + try { + Method method = method(bean, Init.class); + if (method != null) { + method.invoke(bean, PARAMETERS); + } + } catch (InvocationTargetException e) { + cacheException((Exception) e.getCause()); + } catch (Exception e) { + cacheException(e); + } + + } + + + /** + * <p>Fire a destroy event on an @{link AbstractRequestBean}.</p> + * + * @param bean [EMAIL PROTECTED] AbstractRequestBean} to fire event on + */ + protected void fireRequestDestroy(Object bean) { + + if (bean instanceof AbstractRequestBean) { + super.fireRequestDestroy(bean); + return; + } + + try { + Method method = method(bean, Destroy.class); + if (method != null) { + method.invoke(bean, PARAMETERS); + } + } catch (InvocationTargetException e) { + cacheException((Exception) e.getCause()); + } catch (Exception e) { + cacheException(e); + } + + } + + + /** + * <p>Fire an init event on an [EMAIL PROTECTED] AbstractRequestBean}.</p> + * + * @param bean [EMAIL PROTECTED] AbstractRequestBean} to fire event on + */ + protected void fireRequestInit(Object bean) { + + if (bean instanceof AbstractRequestBean) { + super.fireRequestInit(bean); + return; + } + + try { + Method method = method(bean, Init.class); + if (method != null) { + method.invoke(bean, PARAMETERS); + } + } catch (InvocationTargetException e) { + cacheException((Exception) e.getCause()); + } catch (Exception e) { + cacheException(e); + } + + } + + + /** + * <p>Fire an activate event on an @{link AbstractSessionBean}.</p> + * + * @param bean [EMAIL PROTECTED] AbstractSessionBean} to fire event on + */ + protected void fireSessionActivate(Object bean) { + + if (bean instanceof AbstractSessionBean) { + super.fireSessionActivate(bean); + return; + } + + try { + Method method = method(bean, Activate.class); + if (method != null) { + method.invoke(bean, PARAMETERS); + } + } catch (InvocationTargetException e) { + cacheException((Exception) e.getCause()); + } catch (Exception e) { + cacheException(e); + } + + } + + + /** + * <p>Fire a destroy event on an @{link AbstractSessionBean}.</p> + * + * @param bean [EMAIL PROTECTED] AbstractSessionBean} to fire event on + */ + protected void fireSessionDestroy(Object bean) { + + if (bean instanceof AbstractSessionBean) { + super.fireSessionDestroy(bean); + return; + } + + try { + Method method = method(bean, Destroy.class); + if (method != null) { + method.invoke(bean, PARAMETERS); + } + } catch (InvocationTargetException e) { + cacheException((Exception) e.getCause()); + } catch (Exception e) { + cacheException(e); + } + + } + + + /** + * <p>Fire an init event on an [EMAIL PROTECTED] AbstractSessionBean}.</p> + * + * @param bean [EMAIL PROTECTED] AbstractSessionBean} to fire event on + */ + protected void fireSessionInit(Object bean) { + + if (bean instanceof AbstractSessionBean) { + super.fireSessionInit(bean); + return; + } + + try { + Method method = method(bean, Init.class); + if (method != null) { + method.invoke(bean, PARAMETERS); + } + } catch (InvocationTargetException e) { + cacheException((Exception) e.getCause()); + } catch (Exception e) { + cacheException(e); + } + + } + + + /** + * <p>Fire an passivate event on an @{link AbstractSessionBean}.</p> + * + * @param bean [EMAIL PROTECTED] AbstractSessionBean} to fire event on + */ + protected void fireSessionPassivate(Object bean) { + + if (bean instanceof AbstractSessionBean) { + super.fireSessionPassivate(bean); + return; + } + + try { + Method method = method(bean, Passivate.class); + if (method != null) { + method.invoke(bean, PARAMETERS); + } + } catch (InvocationTargetException e) { + cacheException((Exception) e.getCause()); + } catch (Exception e) { + cacheException(e); + } + + } + + + // --------------------------------------------------------- Private Methods + + + /** + * <p>The set of method annotations for callbacks of interest.</p> + */ + private static final Class annotations[] = + { Init.class, Preprocess.class, Prerender.class, Destroy.class, + Activate.class, Passivate.class }; + + + + /** + * <p>The set of class annotations for classes of interest.</p> + */ + private static final Class markers[] = + { View.class, Request.class, Session.class, Application.class }; + + + /** + * <p>Data structure to maintain information about annotated + * methods. In this map, the key is the Class being analyzed, + * and the value is an inner map. In the inner map, the key + * is an Annotation class, and the value is the corresponding + * Method instance.</p> + */ + private transient Map<Class,Map<Class,Method>> maps = + new HashMap<Class,Map<Class,Method>>(); + + + /** + * <p>Return the <code>Method</code> to be called for the specified + * annotation on the specified instance, if any. If there is no such + * method, return <code>null</code>.</p> + * + * @param instance Instance on which callbacks will be performed + * @param annotation Annotation for which to return a method + */ + private Method method(Object instance, Class annotation) { + + + // Does the underlying class implement a relevant class annotation? + // If not, exit early + Class clazz = instance.getClass(); + boolean found = false; + for (Class marker : markers) { + if (clazz.getAnnotation(marker) != null) { + found = true; + break; + } + } + if (!found) { + return null; + } + + synchronized (maps) { + + // If we have seen this Class already, simply return the + // previously located Method (if any) + Map<Class,Method> map = maps.get(clazz); + if (map != null) { + return map.get(annotation); + } + + // Construct and cache a new Map identifying the + // methods of interest for these callbacks + map = new HashMap<Class,Method>(); + Method methods[] = clazz.getMethods(); + for (Method method : methods) { + if (method.getParameterTypes().length > 0) { + continue; + } + for (Class anno : annotations) { + if (method.getAnnotation(anno) != null) { + map.put(anno, method); + } + } + } + maps.put(clazz, map); + return map.get(annotation); + + } + + } + + +} Modified: struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/package.html URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/package.html?rev=408560&r1=408559&r2=408560&view=diff ============================================================================== --- struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/package.html (original) +++ struts/shale/trunk/tiger/src/java/org/apache/shale/tiger/view/package.html Sun May 21 20:51:47 2006 @@ -39,6 +39,14 @@ } </pre> +<p>In addition, the class level annotations <code>Application</code>, +<code>Request</code>, and <code>Session</code> mark beans that should +be treated as if they extended <code>AbstractApplicationBean</code>, +<code>AbstractRequestBean</code>, or <code>AbstractSessionBean</code> +respectively, even if they do not. Such beans will have lifecycle event +callbacks performed on methods that are marked with the corresponding +method level annotation.</p> + <p>API STATUS: Experimental.</p> <p>IMPLEMENTATION STATUS: Implemented, but only lightly tested.</p> Added: struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean1.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean1.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean1.java (added) +++ struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean1.java Sun May 21 20:51:47 2006 @@ -0,0 +1,52 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view.faces; + +import org.apache.shale.view.AbstractApplicationBean; + +/** + * <p>Test bean that subclasses <code>AbstractApplicationBean</code> so we can + * test pass-through calls to "regular" bean instances.</p> + */ +public class ApplicationBean1 extends AbstractApplicationBean { + + + private StringBuffer sb = new StringBuffer(); + + + // -------------------------------------------------------- Callback Methods + + + public void init() { + sb.append("init/"); + } + + + public void destroy() { + sb.append("destroy/"); + } + + + // ---------------------------------------------------------- Public Methods + + + public String log() { + return sb.toString(); + } + + +} Added: struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean2.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean2.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean2.java (added) +++ struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/ApplicationBean2.java Sun May 21 20:51:47 2006 @@ -0,0 +1,58 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view.faces; + +import org.apache.shale.tiger.view.Application; +import org.apache.shale.tiger.view.Destroy; +import org.apache.shale.tiger.view.Init; +import org.apache.shale.view.AbstractApplicationBean; + +/** + * <p>Test bean that does not subclass <code>AbstractApplicationBean</code>, + * but uses corresponding annotations, so that we can test callback events.</p> + */ [EMAIL PROTECTED] +public class ApplicationBean2 { + + + private StringBuffer sb = new StringBuffer(); + + + // -------------------------------------------------------- Callback Methods + + + @Init + public void doInit() { + sb.append("init/"); + } + + + @Destroy + public void doDestroy() { + sb.append("destroy/"); + } + + + // ---------------------------------------------------------- Public Methods + + + public String log() { + return sb.toString(); + } + + +} Added: struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/LifecycleListener2TestCase.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/LifecycleListener2TestCase.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/LifecycleListener2TestCase.java (added) +++ struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/LifecycleListener2TestCase.java Sun May 21 20:51:47 2006 @@ -0,0 +1,183 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view.faces; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.apache.shale.test.base.AbstractJsfTestCase; +import org.apache.shale.view.faces.LifecycleListener; + +/** + * <p>Test case for <code>org.apache.shale.tiger.view.faces.LifecycleListener</code>.</p> + */ +public class LifecycleListener2TestCase extends AbstractJsfTestCase { + + + // ------------------------------------------------------------ Constructors + + + // Construct a new instance of this test case. + public LifecycleListener2TestCase(String name) { + super(name); + } + + + // ---------------------------------------------------- Overall Test Methods + + + // Set up instance variables required by this test case. + public void setUp() { + + super.setUp(); + + // Set up our listener and bind it to context/session/request objects + listener = new LifecycleListener(); + servletContext.addAttributeListener(listener); + session.addAttributeListener(listener); + request.addAttributeListener(listener); + + } + + + // Return the tests included in this test case. + public static Test suite() { + + return (new TestSuite(LifecycleListener2TestCase.class)); + + } + + + // Tear down instance variables required by this test case. + public void tearDown() { + +// callbacks = null; + lifecycle = null; + + super.tearDown(); + + } + + + // ------------------------------------------------------ Instance Variables + + + private LifecycleListener listener = null; + + + // ------------------------------------------------------------ Test Methods + + + // Test standard application bean + public void testApplicationBean1() { + + ApplicationBean1 bean = new ApplicationBean1(); + assertEquals("", bean.log()); + servletContext.setAttribute("bean", bean); + assertEquals("init/", bean.log()); + servletContext.setAttribute("bean", bean); + assertEquals("init/destroy/init/", bean.log()); + servletContext.removeAttribute("bean"); + assertEquals("init/destroy/init/destroy/", bean.log()); + + } + + + // Test annotated application bean + public void testApplicationBean2() { + + ApplicationBean2 bean = new ApplicationBean2(); + assertEquals("", bean.log()); + servletContext.setAttribute("bean", bean); + assertEquals("init/", bean.log()); + servletContext.setAttribute("bean", bean); + assertEquals("init/destroy/init/", bean.log()); + servletContext.removeAttribute("bean"); + assertEquals("init/destroy/init/destroy/", bean.log()); + + } + + + // Test a prisine instance + public void testPristine() { + + ; + + } + + + // Test standard request bean + public void testRequestBean1() { + + RequestBean1 bean = new RequestBean1(); + assertEquals("", bean.log()); + request.setAttribute("bean", bean); + assertEquals("init/", bean.log()); + request.setAttribute("bean", bean); + assertEquals("init/destroy/init/", bean.log()); + request.removeAttribute("bean"); + assertEquals("init/destroy/init/destroy/", bean.log()); + + } + + + // Test annotated request bean + public void testRequestBean2() { + + RequestBean2 bean = new RequestBean2(); + assertEquals("", bean.log()); + request.setAttribute("bean", bean); + assertEquals("init/", bean.log()); + request.setAttribute("bean", bean); + assertEquals("init/destroy/init/", bean.log()); + request.removeAttribute("bean"); + assertEquals("init/destroy/init/destroy/", bean.log()); + + } + + + // Test standard session bean + public void testSessionBean1() { + + SessionBean1 bean = new SessionBean1(); + assertEquals("", bean.log()); + session.setAttribute("bean", bean); + assertEquals("init/", bean.log()); + session.setAttribute("bean", bean); + assertEquals("init/destroy/init/", bean.log()); + session.removeAttribute("bean"); + assertEquals("init/destroy/init/destroy/", bean.log()); + + } + + + // Test annotated session bean + public void testSessionBean2() { + + SessionBean2 bean = new SessionBean2(); + assertEquals("", bean.log()); + session.setAttribute("bean", bean); + assertEquals("init/", bean.log()); + session.setAttribute("bean", bean); + assertEquals("init/destroy/init/", bean.log()); + session.removeAttribute("bean"); + assertEquals("init/destroy/init/destroy/", bean.log()); + + } + + +} Added: struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean1.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean1.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean1.java (added) +++ struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean1.java Sun May 21 20:51:47 2006 @@ -0,0 +1,52 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view.faces; + +import org.apache.shale.view.AbstractRequestBean; + +/** + * <p>Test bean that subclasses <code>AbstractRequestBean</code> so we can + * test pass-through calls to "regular" bean instances.</p> + */ +public class RequestBean1 extends AbstractRequestBean { + + + private StringBuffer sb = new StringBuffer(); + + + // -------------------------------------------------------- Callback Methods + + + public void init() { + sb.append("init/"); + } + + + public void destroy() { + sb.append("destroy/"); + } + + + // ---------------------------------------------------------- Public Methods + + + public String log() { + return sb.toString(); + } + + +} Added: struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean2.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean2.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean2.java (added) +++ struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/RequestBean2.java Sun May 21 20:51:47 2006 @@ -0,0 +1,57 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view.faces; + +import org.apache.shale.tiger.view.Destroy; +import org.apache.shale.tiger.view.Init; +import org.apache.shale.tiger.view.Request; + +/** + * <p>Test bean that does not subclass <code>AbstractRequestBean</code>, + * but uses corresponding annotations, so that we can test callback events.</p> + */ [EMAIL PROTECTED] +public class RequestBean2 { + + + private StringBuffer sb = new StringBuffer(); + + + // -------------------------------------------------------- Callback Methods + + + @Init + public void doInit() { + sb.append("init/"); + } + + + @Destroy + public void doDestroy() { + sb.append("destroy/"); + } + + + // ---------------------------------------------------------- Public Methods + + + public String log() { + return sb.toString(); + } + + +} Added: struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean1.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean1.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean1.java (added) +++ struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean1.java Sun May 21 20:51:47 2006 @@ -0,0 +1,52 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view.faces; + +import org.apache.shale.view.AbstractSessionBean; + +/** + * <p>Test bean that subclasses <code>AbstractSessionBean</code> so we can + * test pass-through calls to "regular" bean instances.</p> + */ +public class SessionBean1 extends AbstractSessionBean { + + + private StringBuffer sb = new StringBuffer(); + + + // -------------------------------------------------------- Callback Methods + + + public void init() { + sb.append("init/"); + } + + + public void destroy() { + sb.append("destroy/"); + } + + + // ---------------------------------------------------------- Public Methods + + + public String log() { + return sb.toString(); + } + + +} Added: struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean2.java URL: http://svn.apache.org/viewvc/struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean2.java?rev=408560&view=auto ============================================================================== --- struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean2.java (added) +++ struts/shale/trunk/tiger/src/test/org/apache/shale/tiger/view/faces/SessionBean2.java Sun May 21 20:51:47 2006 @@ -0,0 +1,57 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed 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.shale.tiger.view.faces; + +import org.apache.shale.tiger.view.Destroy; +import org.apache.shale.tiger.view.Init; +import org.apache.shale.tiger.view.Session; + +/** + * <p>Test bean that does not subclass <code>AbstractSessionBean</code>, + * but uses corresponding annotations, so that we can test callback events.</p> + */ [EMAIL PROTECTED] +public class SessionBean2 { + + + private StringBuffer sb = new StringBuffer(); + + + // -------------------------------------------------------- Callback Methods + + + @Init + public void doInit() { + sb.append("init/"); + } + + + @Destroy + public void doDestroy() { + sb.append("destroy/"); + } + + + // ---------------------------------------------------------- Public Methods + + + public String log() { + return sb.toString(); + } + + +}