From ae67cf6faae6c759625a108cafda86f66caa88f4 Mon Sep 17 00:00:00 2001 From: gonzalad Date: Thu, 27 Oct 2011 17:48:49 +0200 Subject: [PATCH 1/7] Intermediate commit ViewAction are registered in ViewConfigStore. Limitation due to ViewConfigStore storing only Annotation. I need to store at least AnnotatedMethod otherwise I won't be able to know which method to call if 2 methods have been annotated with exactly the same annotation (same attribute values I think). --- .../seam/faces/view/action/Condition.java | 21 ++ .../seam/faces/view/action/Immediate.java | 22 ++ .../seam/faces/view/action/OnPostback.java | 22 ++ .../jboss/seam/faces/view/action/Phase.java | 23 ++ .../seam/faces/view/action/PhaseDefault.java | 22 ++ .../view/action/ViewActionBindingType.java | 27 ++ examples/viewconfig/pom.xml | 23 +- .../examples/viewconfig/MyViewAction.java | 21 ++ .../examples/viewconfig/PageController.java | 7 + .../view/action/ViewActionPhaseListener.java | 230 ++++++++++++++++++ .../view/config/ViewConfigExtension.java | 66 ++++- pom.xml | 8 +- 12 files changed, 482 insertions(+), 10 deletions(-) create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/Condition.java create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/Immediate.java create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/OnPostback.java create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/Phase.java create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/PhaseDefault.java create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingType.java create mode 100644 examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyViewAction.java create mode 100644 impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/Condition.java b/api/src/main/java/org/jboss/seam/faces/view/action/Condition.java new file mode 100644 index 0000000..a5448e1 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/Condition.java @@ -0,0 +1,21 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Conditionnally executes a viewAction. + * + * TODO : refactor me, I'm not type-safe ! + * + * @author Adriàn Gonzalez + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Condition { + public String condition = null; +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/Immediate.java b/api/src/main/java/org/jboss/seam/faces/view/action/Immediate.java new file mode 100644 index 0000000..6f1a98d --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/Immediate.java @@ -0,0 +1,22 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.jboss.seam.faces.component.UIViewAction; + +/** + * Can be used instead of Phase. + * + * @see UIViewAction#isImmediate() + * @author Adriàn Gonzalez + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Immediate { + public Boolean immediate = null; +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/OnPostback.java b/api/src/main/java/org/jboss/seam/faces/view/action/OnPostback.java new file mode 100644 index 0000000..156a7d2 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/OnPostback.java @@ -0,0 +1,22 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.jboss.seam.faces.component.UIViewAction; + +/** + * Determines if viewAction is executed on postback. + * + * @see UIViewAction#isOnPostback() + * @author Adriàn Gonzalez + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface OnPostback { + public Boolean onPostback = false; +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/Phase.java b/api/src/main/java/org/jboss/seam/faces/view/action/Phase.java new file mode 100644 index 0000000..28d0487 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/Phase.java @@ -0,0 +1,23 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.jboss.seam.faces.component.UIViewAction; +import org.jboss.seam.faces.event.PhaseIdType; + +/** + * Phase on which a viewAction is executed. + * + * @see UIViewAction#getPhase() + * @author Adriàn Gonzalez + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Phase { + public PhaseIdType value() default PhaseIdType.RENDER_RESPONSE; +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/PhaseDefault.java b/api/src/main/java/org/jboss/seam/faces/view/action/PhaseDefault.java new file mode 100644 index 0000000..b21e929 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/PhaseDefault.java @@ -0,0 +1,22 @@ +package org.jboss.seam.faces.view.action; + +import org.jboss.seam.faces.event.PhaseIdType; + +/** + * The Default values for Phase annotation, extracted as constant. + * + * @author Adriàn Gonzalez + */ +public class PhaseDefault { + public static final PhaseIdType DEFAULT_PHASE; + + static { + try { + DEFAULT_PHASE = (PhaseIdType) Phase.class.getMethod("value").getDefaultValue(); + } catch (NoSuchMethodException ex) { + throw new IllegalStateException("Error initialising values", ex); + } catch (SecurityException ex) { + throw new IllegalStateException("Error initialising values", ex); + } + } +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingType.java b/api/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingType.java new file mode 100644 index 0000000..93115e5 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingType.java @@ -0,0 +1,27 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Applied to an annotation to indicate that it is a faces action binding type + * + * Additionally, you can customize your view annotation follogin one of those approaches : + * + * + * @author Adriàn Gonzalez + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ViewActionBindingType { +} diff --git a/examples/viewconfig/pom.xml b/examples/viewconfig/pom.xml index 55e6f76..e40c71a 100644 --- a/examples/viewconfig/pom.xml +++ b/examples/viewconfig/pom.xml @@ -36,6 +36,26 @@ org.jboss.seam.faces seam-faces-api + + + org.jboss.seam.transaction + seam-transaction-api + + + + org.jboss.seam.transaction + seam-transaction + + + + org.jboss.seam.international + seam-international-api + + + + org.jboss.seam.international + seam-international + org.jboss.seam.security @@ -47,7 +67,6 @@ org.jboss.seam.security seam-security compile - 3.1.0.Beta2 @@ -57,6 +76,8 @@ com.ocpsoft prettyfaces-jsf2 + + 3.3.0 diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyViewAction.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyViewAction.java new file mode 100644 index 0000000..2e97b03 --- /dev/null +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyViewAction.java @@ -0,0 +1,21 @@ +package org.jboss.seam.faces.examples.viewconfig; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.jboss.seam.faces.view.action.ViewActionBindingType; + +@ViewActionBindingType +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +public @interface MyViewAction { + MyAppViewConfig.Pages value(); + + //just testing value override +// public PhaseIdType phase() default PhaseIdType.RENDER_RESPONSE; +// public Boolean immediate = null; +// public Boolean onPostback = false; +// public String condition = null; +} diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java index 122b3b7..34bdf36 100644 --- a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java @@ -25,6 +25,7 @@ import javax.inject.Inject; import javax.inject.Named; +import org.jboss.seam.faces.examples.viewconfig.MyAppViewConfig.Pages; import org.jboss.seam.faces.examples.viewconfig.model.Current; import org.jboss.seam.faces.examples.viewconfig.model.Item; import org.jboss.seam.faces.examples.viewconfig.model.ItemDao; @@ -53,6 +54,12 @@ public Item getItem() { return item; } + @MyViewAction(Pages.ITEM) + public void loadEntry() { + System.out.println("loadEntry called"); + } + + @MyViewAction(Pages.ITEM) public void setItem(Item item) { this.item = item; } diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java new file mode 100644 index 0000000..80f3d57 --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java @@ -0,0 +1,230 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.BeanManager; +import javax.faces.component.UIViewRoot; +import javax.faces.context.FacesContext; +import javax.faces.event.PhaseEvent; +import javax.inject.Inject; + +import org.jboss.seam.faces.event.PhaseIdType; +import org.jboss.seam.faces.event.qualifier.After; +import org.jboss.seam.faces.event.qualifier.ApplyRequestValues; +import org.jboss.seam.faces.event.qualifier.Before; +import org.jboss.seam.faces.event.qualifier.InvokeApplication; +import org.jboss.seam.faces.event.qualifier.ProcessValidations; +import org.jboss.seam.faces.event.qualifier.RenderResponse; +import org.jboss.seam.faces.event.qualifier.RestoreView; +import org.jboss.seam.faces.event.qualifier.UpdateModelValues; +import org.jboss.seam.faces.view.config.ViewConfigStore; +import org.jboss.solder.logging.Logger; +import org.jboss.solder.reflection.AnnotationInspector; + +/** + * Use the annotations stored in the ViewConfigStore to execute action (MethodExpression) calls. + * + * Class similar to SecurityPhaseListener. + * + * @author Adriàn Gonzalez + */ +public class ViewActionPhaseListener { + + private transient final Logger log = Logger.getLogger(ViewActionPhaseListener.class); + + @Inject + private ViewConfigStore viewConfigStore; + @Inject + private BeanManager beanManager; + + /** + * Execute any action annotations applicable to the RestoreView phase + * + * @param event + */ + public void observeRestoreView(@Observes @After @RestoreView PhaseEvent event) { + log.debug("After Restore View event"); + performObservation(event, PhaseIdType.RESTORE_VIEW); + } + + /** + * Execute any action annotations applicable to the ApplyRequestValues phase + * + * @param event + */ + public void observeApplyRequestValues(@Observes @Before @ApplyRequestValues PhaseEvent event) { + log.debug("After Apply Request Values event"); + performObservation(event, PhaseIdType.APPLY_REQUEST_VALUES); + } + + /** + * Execute any action annotations applicable to the ProcessValidations phase + * + * @param event + */ + public void observeProcessValidations(@Observes @Before @ProcessValidations PhaseEvent event) { + log.debug("After Process Validations event"); + performObservation(event, PhaseIdType.PROCESS_VALIDATIONS); + } + + /** + * Execute any action annotations applicable to the UpdateModelValues phase + * + * @param event + */ + public void observeUpdateModelValues(@Observes @Before @UpdateModelValues PhaseEvent event) { + log.debug("After Update Model Values event"); + performObservation(event, PhaseIdType.UPDATE_MODEL_VALUES); + } + + /** + * Execute any action annotations applicable to the InvokeApplication phase + * + * @param event + */ + public void observeInvokeApplication(@Observes @Before @InvokeApplication PhaseEvent event) { + log.debug("Before Render Response event"); + performObservation(event, PhaseIdType.INVOKE_APPLICATION); + } + + /** + * Execute any action annotations applicable to the RenderResponse phase + * + * @param event + */ + public void observeRenderResponse(@Observes @Before @RenderResponse PhaseEvent event) { + log.debug("Before Render Response event"); + performObservation(event, PhaseIdType.RENDER_RESPONSE); + } + + /** + * Inspect the annotations in the ViewConfigStore, executing any actions applicable to this phase + * + * @param event + * @param phaseIdType + */ + private void performObservation(PhaseEvent event, PhaseIdType phaseIdType) { + UIViewRoot viewRoot = (UIViewRoot) event.getFacesContext().getViewRoot(); + List actionsForPhase = getViewActionsForPhase(phaseIdType, viewRoot.getViewId()); + if (actionsForPhase != null) { + log.debugf("Enforcing on phase %s", phaseIdType); + execute(event.getFacesContext(), viewRoot, actionsForPhase); + } + } + + /** + * Retrieve all annotations from the ViewConfigStore for a given a JSF phase, and a view id, + * and where the annotation is qualified by @SecurityBindingType + * + * @param currentPhase + * @param viewId + * @return list of restrictions applicable to this viewId and PhaseTypeId + */ + public List getViewActionsForPhase(PhaseIdType currentPhase, String viewId) { + List allViewActionAnnotations = viewConfigStore.getAllQualifierData(viewId, ViewActionBindingType.class); + List applicableViewActionAnnotations = null; + for (Annotation annotation : allViewActionAnnotations) { + PhaseIdType defaultPhase = getDefaultPhase(viewId); + if (isAnnotationApplicableToPhase(annotation, currentPhase, defaultPhase)) { + if (applicableViewActionAnnotations == null) { // avoid spawning arrays at all phases of the lifecycle + applicableViewActionAnnotations = new ArrayList(); + } + applicableViewActionAnnotations.add(annotation); + } + } + return applicableViewActionAnnotations; + } + + /** + * Inspect an annotation to see if it specifies a view in which it should be. Fall back on default view otherwise. + * + * @param annotation + * @param currentPhase + * @param defaultPhases + * @return true if the annotation is applicable to this view and phase, false otherwise + */ + public boolean isAnnotationApplicableToPhase(Annotation annotation, PhaseIdType currentPhase, PhaseIdType defaultPhase) { + Method phaseAtViewActionMethod = getPhaseAtViewActionMethod(annotation); + PhaseIdType phasedId = null; + if (phaseAtViewActionMethod != null) { + log.debug("Annotation %s is using the phase method."); + phasedId = getViewActionPhaseId(phaseAtViewActionMethod, annotation); + } + Phase phaseQualifier = AnnotationInspector.getAnnotation(annotation.annotationType(), Phase.class, beanManager); + if (phaseQualifier != null) { + log.debug("Using Phase found in @Phase qualifier on the annotation."); + phasedId = phaseQualifier.value(); + } + if (phasedId == null) { + log.debug("Falling back on default phase id"); + phasedId = defaultPhase; + } + return phasedId == currentPhase; + } + + /** + * Get the default phases at which restrictions should be applied, by looking for a @Phase default value + * + * @param viewId + * @return default phase + */ + public PhaseIdType getDefaultPhase(String viewId) { + return PhaseDefault.DEFAULT_PHASE; + } + + /** + * Utility method to extract the "phase" method from an annotation + * + * @param annotation + * @return phaseAtViewActionMethod if found, null otherwise + */ + public Method getPhaseAtViewActionMethod(Annotation annotation) { + Method phaseAtViewActionMethod; + try { + phaseAtViewActionMethod = annotation.annotationType().getDeclaredMethod("phase"); + } catch (NoSuchMethodException ex) { + phaseAtViewActionMethod = null; + } catch (SecurityException ex) { + throw new IllegalArgumentException("phase method must be accessible", ex); + } + return phaseAtViewActionMethod; + } + + /** + * Retrieve the default PhaseIdType defined by the phaseAtViewActionMethod in the annotation + * + * @param phaseAtViewActionMethod + * @param annotation + * @return PhaseIdType from the phaseAtViewActionMethod, null if empty + */ + public PhaseIdType getViewActionPhaseId(Method phaseAtViewActionMethod, Annotation annotation) { + PhaseIdType phaseId; + try { + phaseId = (PhaseIdType) phaseAtViewActionMethod.invoke(annotation); + } catch (IllegalAccessException ex) { + throw new IllegalArgumentException("phase method must be accessible", ex); + } catch (InvocationTargetException ex) { + throw new RuntimeException(ex); + } + return phaseId; + } + + /** + * Execute the list of applicable view action annotations, TODO... + * + * @param context + * @param viewRoot + * @param annotations + */ + private void execute(FacesContext context, UIViewRoot viewRoot, List annotations) { + if (annotations == null || annotations.isEmpty()) { + log.debug("Annotations is null/empty"); + return; + } + } +} diff --git a/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigExtension.java b/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigExtension.java index 8d9cc59..2cdc7a7 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigExtension.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigExtension.java @@ -18,6 +18,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -25,10 +27,12 @@ import java.util.Set; import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AnnotatedMethod; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ProcessAnnotatedType; +import org.jboss.seam.faces.view.action.ViewActionBindingType; import org.jboss.solder.logging.Logger; /** @@ -43,6 +47,8 @@ public class ViewConfigExtension implements Extension { private final Map> data = new HashMap>(); + private final Map viewFieldValueToViewId = new HashMap(); + public void processAnnotatedType(@Observes ProcessAnnotatedType event) { AnnotatedType tp = event.getAnnotatedType(); if (log.isTraceEnabled()) { @@ -64,20 +70,76 @@ public void processAnnotatedType(@Observes ProcessAnnotatedType event) { if (enumm.isAnnotationPresent(ViewPattern.class)) { ViewPattern viewConfig = enumm.getAnnotation(ViewPattern.class); Set viewPattern = new HashSet(); - data.put(viewConfig.value(), viewPattern); for (Annotation a : enumm.getAnnotations()) { if (a.annotationType() != ViewPattern.class) { viewPattern.add(a); } } + data.put(viewConfig.value(), viewPattern); + viewFieldValueToViewId.put(getViewFieldValue(enumm), viewConfig.value()); } } } } + // viewAction processing + for (final AnnotatedMethod m : tp.getMethods()) { + for (final Annotation annotation : m.getAnnotations()) { + if (annotation.annotationType().isAnnotationPresent(ViewActionBindingType.class)) { + Object viewField = getValue (annotation); + String viewId = viewFieldValueToViewId.get(viewField); + if (viewId == null) { + throw new IllegalArgumentException("Annotation "+annotation+" invalid : the view specified" + + "("+viewField+") doesn't correspond to a registered view in an annotated @ViewConfig class/interface."); + } + data.get(viewId).add(annotation); + } + } + } } - public Map> getData() { + /** + * Returns the value of a view field. + * @throws IllegalArgumentException if an error happens + */ + private Object getViewFieldValue(Field enumm) { + try { + return enumm.get(null); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid view field "+enumm+" - error getting value "+e.toString(), e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Invalid view field "+enumm+" - error getting value "+e.toString(), e); + } + } + + public Map> getData() { return Collections.unmodifiableMap(data); } + + // Utility methods for viewAction, TODO move this block out of this class + + /** + * Retrieve the view defined by the value() method in the annotation + * + * @param annotation + * @return the result of value() call + * @throws IllegalArgumentException if no value() method was found + */ + private Object getValue(Annotation annotation) { + Method valueMethod; + try { + valueMethod = annotation.annotationType().getDeclaredMethod("value"); + } catch (NoSuchMethodException ex) { + throw new IllegalArgumentException("value method must be declared and must resolve to a valid view", ex); + } catch (SecurityException ex) { + throw new IllegalArgumentException("value method must be accessible", ex); + } + try { + return valueMethod.invoke(annotation); + } catch (IllegalAccessException ex) { + throw new IllegalArgumentException("value method must be accessible", ex); + } catch (InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } } diff --git a/pom.xml b/pom.xml index 42abf6f..0bb2997 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.jboss.seam seam-parent - 16 + 17-SNAPSHOT seam-faces-parent @@ -31,12 +31,6 @@ - - com.ocpsoft - prettyfaces-jsf2 - 3.2.0 - - org.jboss.seam seam-bom From 8ba51cd905d0b718a768e893d73d14924b91143e Mon Sep 17 00:00:00 2001 From: gonzalad Date: Wed, 2 Nov 2011 20:36:12 +0100 Subject: [PATCH 2/7] @ViewAction, @ViewActionBindingType and @ViewController handling @ViewAction and @ViewActionBindingType usage are as per https://issues.jboss.org/browse/SEAMFACES-147 @ViewController usage is like MyFaces CODI's @PageBean (https://cwiki.apache.org/confluence/display/EXTCDI/JSF+Usage#JSFUsage-PageBeans) Sample usage in seam-faces-example-viewconfig application --- .../seam/faces/event/qualifier/After.java | 12 +- .../event/qualifier/ApplyRequestValues.java | 4 +- .../seam/faces/event/qualifier/Before.java | 12 +- .../event/qualifier/InvokeApplication.java | 4 +- .../event/qualifier/ProcessValidations.java | 4 +- .../faces/event/qualifier/RenderResponse.java | 4 +- .../faces/event/qualifier/RestoreView.java | 4 +- .../event/qualifier/UpdateModelValues.java | 4 +- .../view/action/AfterRenderResponse.java | 22 ++ .../view/action/BeforeRenderReponse.java | 22 ++ .../seam/faces/view/action/ViewAction.java | 24 ++ .../view/action/ViewActionBindingType.java | 13 +- .../faces/view/action/ViewController.java | 39 +++ .../view/config/ViewConfigDescriptor.java | 106 ++++++ .../faces/view/config/ViewConfigStore.java | 4 + .../examples/viewconfig/MyAppViewConfig.java | 18 + .../examples/viewconfig/MyViewAction.java | 6 - .../examples/viewconfig/PageController.java | 15 +- .../ViewActionBindingTypeController.java | 18 + .../viewconfig/ViewActionController.java | 15 + .../examples/viewconfig/ViewController.java | 71 ++++ .../viewconfig/src/main/webapp/index.xhtml | 14 + .../src/main/webapp/viewaction.xhtml | 19 ++ .../main/webapp/viewactionbindingtype.xhtml | 19 ++ .../src/main/webapp/viewcontroller.xhtml | 20 ++ .../seam/faces/view/action/PhaseInstant.java | 54 +++ .../ViewActionBindingTypeDescriptor.java | 52 +++ .../view/action/ViewActionPhaseListener.java | 228 ++----------- .../faces/view/action/ViewActionStrategy.java | 18 + .../faces/view/action/ViewActionUtils.java | 114 +++++++ .../view/action/ViewControllerDescriptor.java | 315 ++++++++++++++++++ .../view/action/ViewControllerExtension.java | 80 +++++ .../view/action/ViewControllerStore.java | 172 ++++++++++ .../view/config/ViewConfigExtension.java | 88 ++--- .../view/config/ViewConfigStoreImpl.java | 14 +- .../javax.enterprise.inject.spi.Extension | 1 + 36 files changed, 1327 insertions(+), 302 deletions(-) create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/AfterRenderResponse.java create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/BeforeRenderReponse.java create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java create mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/ViewController.java create mode 100644 api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigDescriptor.java create mode 100644 examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewActionBindingTypeController.java create mode 100644 examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewActionController.java create mode 100644 examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewController.java create mode 100644 examples/viewconfig/src/main/webapp/viewaction.xhtml create mode 100644 examples/viewconfig/src/main/webapp/viewactionbindingtype.xhtml create mode 100644 examples/viewconfig/src/main/webapp/viewcontroller.xhtml create mode 100644 impl/src/main/java/org/jboss/seam/faces/view/action/PhaseInstant.java create mode 100644 impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingTypeDescriptor.java create mode 100644 impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionStrategy.java create mode 100644 impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java create mode 100644 impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java create mode 100644 impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerExtension.java create mode 100644 impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java diff --git a/api/src/main/java/org/jboss/seam/faces/event/qualifier/After.java b/api/src/main/java/org/jboss/seam/faces/event/qualifier/After.java index 8965d1d..d281a3d 100644 --- a/api/src/main/java/org/jboss/seam/faces/event/qualifier/After.java +++ b/api/src/main/java/org/jboss/seam/faces/event/qualifier/After.java @@ -16,22 +16,24 @@ */ package org.jboss.seam.faces.event.qualifier; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.inject.Qualifier; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - /** * Qualifies observer method parameters to select events that occur in a "after" phase in the JSF lifecycle * * @author Nicklas Karlsson */ @Qualifier -@Target({FIELD, PARAMETER}) +@Target({TYPE, FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) public @interface After { } diff --git a/api/src/main/java/org/jboss/seam/faces/event/qualifier/ApplyRequestValues.java b/api/src/main/java/org/jboss/seam/faces/event/qualifier/ApplyRequestValues.java index 6202718..4773bb4 100644 --- a/api/src/main/java/org/jboss/seam/faces/event/qualifier/ApplyRequestValues.java +++ b/api/src/main/java/org/jboss/seam/faces/event/qualifier/ApplyRequestValues.java @@ -23,7 +23,9 @@ import javax.inject.Qualifier; import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** @@ -36,7 +38,7 @@ * @see javax.faces.event.PhaseEvent */ @Qualifier -@Target({FIELD, PARAMETER}) +@Target({TYPE, FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) public @interface ApplyRequestValues { } diff --git a/api/src/main/java/org/jboss/seam/faces/event/qualifier/Before.java b/api/src/main/java/org/jboss/seam/faces/event/qualifier/Before.java index b82c509..a27adca 100644 --- a/api/src/main/java/org/jboss/seam/faces/event/qualifier/Before.java +++ b/api/src/main/java/org/jboss/seam/faces/event/qualifier/Before.java @@ -16,22 +16,24 @@ */ package org.jboss.seam.faces.event.qualifier; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.inject.Qualifier; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - /** * Qualifies observer method parameters to select events that occur in a "before" phase in the JSF lifecycle * * @author Nicklas Karlsson */ @Qualifier -@Target({FIELD, PARAMETER}) +@Target({TYPE, FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) public @interface Before { } diff --git a/api/src/main/java/org/jboss/seam/faces/event/qualifier/InvokeApplication.java b/api/src/main/java/org/jboss/seam/faces/event/qualifier/InvokeApplication.java index a911a38..e4fbf19 100644 --- a/api/src/main/java/org/jboss/seam/faces/event/qualifier/InvokeApplication.java +++ b/api/src/main/java/org/jboss/seam/faces/event/qualifier/InvokeApplication.java @@ -23,7 +23,9 @@ import javax.inject.Qualifier; import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** @@ -34,7 +36,7 @@ * @author Nicklas Karlsson */ @Qualifier -@Target({FIELD, PARAMETER}) +@Target({TYPE, FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) public @interface InvokeApplication { } diff --git a/api/src/main/java/org/jboss/seam/faces/event/qualifier/ProcessValidations.java b/api/src/main/java/org/jboss/seam/faces/event/qualifier/ProcessValidations.java index 94bd20c..fe8e1c6 100644 --- a/api/src/main/java/org/jboss/seam/faces/event/qualifier/ProcessValidations.java +++ b/api/src/main/java/org/jboss/seam/faces/event/qualifier/ProcessValidations.java @@ -23,7 +23,9 @@ import javax.inject.Qualifier; import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** @@ -35,7 +37,7 @@ * @After}. The event parameter is a {@link PhaseEvent}. */ @Qualifier -@Target({FIELD, PARAMETER}) +@Target({TYPE, FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) public @interface ProcessValidations { } diff --git a/api/src/main/java/org/jboss/seam/faces/event/qualifier/RenderResponse.java b/api/src/main/java/org/jboss/seam/faces/event/qualifier/RenderResponse.java index d259613..f27b3a5 100644 --- a/api/src/main/java/org/jboss/seam/faces/event/qualifier/RenderResponse.java +++ b/api/src/main/java/org/jboss/seam/faces/event/qualifier/RenderResponse.java @@ -23,7 +23,9 @@ import javax.inject.Qualifier; import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** @@ -34,7 +36,7 @@ * @author Nicklas Karlsson */ @Qualifier -@Target({FIELD, PARAMETER}) +@Target({TYPE, FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) public @interface RenderResponse { } diff --git a/api/src/main/java/org/jboss/seam/faces/event/qualifier/RestoreView.java b/api/src/main/java/org/jboss/seam/faces/event/qualifier/RestoreView.java index 5943387..38ec850 100644 --- a/api/src/main/java/org/jboss/seam/faces/event/qualifier/RestoreView.java +++ b/api/src/main/java/org/jboss/seam/faces/event/qualifier/RestoreView.java @@ -23,7 +23,9 @@ import javax.inject.Qualifier; import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** @@ -34,7 +36,7 @@ * @author Nicklas Karlsson */ @Qualifier -@Target({FIELD, PARAMETER}) +@Target({TYPE, FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) public @interface RestoreView { } diff --git a/api/src/main/java/org/jboss/seam/faces/event/qualifier/UpdateModelValues.java b/api/src/main/java/org/jboss/seam/faces/event/qualifier/UpdateModelValues.java index 91b58a5..00b8fea 100644 --- a/api/src/main/java/org/jboss/seam/faces/event/qualifier/UpdateModelValues.java +++ b/api/src/main/java/org/jboss/seam/faces/event/qualifier/UpdateModelValues.java @@ -23,7 +23,9 @@ import javax.inject.Qualifier; import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** @@ -35,7 +37,7 @@ * @After}. The event parameter is a {@link PhaseEvent}. */ @Qualifier -@Target({FIELD, PARAMETER}) +@Target({TYPE, FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) public @interface UpdateModelValues { } diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/AfterRenderResponse.java b/api/src/main/java/org/jboss/seam/faces/view/action/AfterRenderResponse.java new file mode 100644 index 0000000..5316d80 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/AfterRenderResponse.java @@ -0,0 +1,22 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This lifecycle annotation can be used on viewController methods. + * + * These methods will be called after JSF RENDER_VIEW phase. + * Typically used for cleanup purposes + * Shortcut for @After @RenderResponse. + * + * @author Adriàn Gonzalez + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface AfterRenderResponse { +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/BeforeRenderReponse.java b/api/src/main/java/org/jboss/seam/faces/view/action/BeforeRenderReponse.java new file mode 100644 index 0000000..bd6556c --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/BeforeRenderReponse.java @@ -0,0 +1,22 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This lifecycle annotation can be used on viewController methods. + * + * These methods will be called before JSF RENDER_VIEW phase. + * Typically used for view initialization purposes. + * Shortcut for @Before @RenderResponse. + * + * @author Adriàn Gonzalez + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface BeforeRenderReponse { +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java b/api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java new file mode 100644 index 0000000..96c9131 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java @@ -0,0 +1,24 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The EL MethodExpression is executed when this annotation is applied to a ViewConfig. + * + * The MethodExpression is called before RENDER_RESPONSE phase. + * + * @author Adriàn Gonzalez + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ViewAction { + /** + * El MethodExpression + */ + String value(); +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingType.java b/api/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingType.java index 93115e5..6f54760 100644 --- a/api/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingType.java +++ b/api/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingType.java @@ -7,16 +7,11 @@ import java.lang.annotation.Target; /** - * Applied to an annotation to indicate that it is a faces action binding type + * Applied to an annotation to indicate that it is a faces action binding type. * - * Additionally, you can customize your view annotation follogin one of those approaches : - *
    - *
  • any other annotations in this package can also be applied to this annotation - * to customize view action behaviour (phase, postback, ...). This enables to customize behaviour - * per annotation definition
  • - *
  • those annotations can be replaced by methods of the same name in the view action annotation. - * This enables to customize behaviour per annotation usage.
  • - *
+ * By default, this method will be called before RENDER_RESPONSE phase. + * You can change the jsf phase by using the annotations from org.jboss.seam.faces.event.qualifier package + * on your custom annotation. * * @author Adriàn Gonzalez */ diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/ViewController.java b/api/src/main/java/org/jboss/seam/faces/view/action/ViewController.java new file mode 100644 index 0000000..f040d1a --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/ViewController.java @@ -0,0 +1,39 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.jboss.seam.faces.event.qualifier.After; +import org.jboss.seam.faces.event.qualifier.ApplyRequestValues; +import org.jboss.seam.faces.event.qualifier.Before; +import org.jboss.seam.faces.event.qualifier.InvokeApplication; +import org.jboss.seam.faces.event.qualifier.ProcessValidations; +import org.jboss.seam.faces.event.qualifier.RenderResponse; +import org.jboss.seam.faces.event.qualifier.UpdateModelValues; + +/** + * This annotation must be used on a ViewConfig to specify its viewControllers. + * + *

A viewController is a managed bean handling a specific view. + * Some methods of the bean can be called during the lifecycle of the view. + * Those methods must be annotated with {@link BeforeRenderResponse}, {@link AfterRenderResponse}, or a mixture of + * {@link Before}, {@link After}, {@link ApplyRequestValues}, {@link ProcessValidations}, {@link UpdateModelValues}, + * {@link InvokeApplication} or {@link RenderResponse}.

+ * + *

Classic use case are : + *

    + *
  • {@link BeforeRenderResponse} for handling view initialization data (i.e. fetching data from database).
  • + *
  • {@link AfterRenderResponse} for view cleanup.
  • + *

+ * + * @author Adriàn Gonzalez + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface ViewController { + Class[] value(); +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigDescriptor.java b/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigDescriptor.java new file mode 100644 index 0000000..4a70c79 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigDescriptor.java @@ -0,0 +1,106 @@ +package org.jboss.seam.faces.view.config; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import org.jboss.solder.logging.Logger; + +/** + * Information about {@link ViewConfig} enum. + * + * @author Adriàn Gonzalez + */ +public class ViewConfigDescriptor { + private transient final Logger log = Logger.getLogger(ViewConfigDescriptor.class); + + private String viewId; + private List values = new ArrayList(); + private List metaData = new ArrayList(); + private final ConcurrentHashMap, Annotation> metaDataByAnnotation = new ConcurrentHashMap, Annotation>(); + private ConcurrentHashMap, List> metaDataByQualifier = new ConcurrentHashMap, List>(); + + /** + * ViewConfigDescriptor for view viewId + */ + public ViewConfigDescriptor(String viewId, Object value) { + this.viewId = viewId; + this.values = new ArrayList(); + values.add(value); + } + + public String getViewId() { + return viewId; + } + + public void setViewId(String viewId) { + this.viewId = viewId; + } + + public void addValue(Object value) { + if (!values.contains(value)) { + values.add(value); + } + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + + public void addMetaData(Annotation metaData) { + this.metaData.add(metaData); + //add to metaDataByAnnotation + metaDataByAnnotation.put(metaData.annotationType(), metaData); + log.debugf("Putting new annotation (type: %s) for viewId: %s", metaData.annotationType().getName(), getViewId()); + //add to metaDataByQualifier + Annotation[] annotations = metaData.annotationType().getAnnotations(); + for (Annotation qualifier : annotations) { + if (qualifier.annotationType().getName().startsWith("java.")) { + log.debugf("Disregarding java.* package %s", qualifier.annotationType().getName()); + continue; + } + List qualifiedAnnotations = new ArrayList(); + List exisitngQualifiedAnnotations = metaDataByQualifier.get(qualifier + .annotationType()); + if (exisitngQualifiedAnnotations != null && !exisitngQualifiedAnnotations.isEmpty()) { + qualifiedAnnotations.addAll(exisitngQualifiedAnnotations); + } + qualifiedAnnotations.add(metaData); + log.debugf("Adding new annotation (type: %s) for Qualifier %s", metaData.annotationType().getName(), qualifier.annotationType().getName()); + metaDataByQualifier.put(qualifier.annotationType(), qualifiedAnnotations); + } + } + + /** + * Returns read-only list. + * + * Use {@link #addMetaData(Annotation)} to modify metaDatas. + */ + public List getMetaData() { + return Collections.unmodifiableList(metaData); + } + + /** + * returns all metaData of the corresponding type. + */ + public T getMetaData(Class type) { + return (T) metaDataByAnnotation.get(type); + } + + /** + * returns all qualified data from metadata annotations. + * + * returns empty list if there's no metaData for the qualifier. + */ + @SuppressWarnings("unchecked") + public List getAllQualifierData(Class qualifier) { + List metaData = metaDataByQualifier.get(qualifier); + return metaData!=null ? Collections.unmodifiableList(metaData) : Collections.EMPTY_LIST; + } +} diff --git a/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigStore.java b/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigStore.java index da7b462..f8b85de 100644 --- a/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigStore.java +++ b/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigStore.java @@ -56,4 +56,8 @@ public interface ViewConfigStore { */ public Map getAllAnnotationViewMap(Class type); + /** + * return the registered viewConfigs + */ + public List getAllViewConfigDescriptors(); } diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyAppViewConfig.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyAppViewConfig.java index f74ada0..dcc26d0 100644 --- a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyAppViewConfig.java +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyAppViewConfig.java @@ -16,12 +16,16 @@ */ package org.jboss.seam.faces.examples.viewconfig; +import org.jboss.seam.faces.event.qualifier.ApplyRequestValues; +import org.jboss.seam.faces.event.qualifier.Before; import org.jboss.seam.faces.examples.viewconfig.security.Admin; import org.jboss.seam.faces.examples.viewconfig.security.Owner; import org.jboss.seam.faces.rewrite.FacesRedirect; import org.jboss.seam.faces.rewrite.UrlMapping; import org.jboss.seam.faces.security.AccessDeniedView; import org.jboss.seam.faces.security.LoginView; +import org.jboss.seam.faces.view.action.ViewAction; +import org.jboss.seam.faces.view.action.ViewController; import org.jboss.seam.faces.view.config.ViewConfig; import org.jboss.seam.faces.view.config.ViewPattern; @@ -39,9 +43,23 @@ static enum Pages { @UrlMapping(pattern = "/item/#{id}/") @ViewPattern("/item.xhtml") + @ViewController(PageController.class) @Owner + @ViewAction("#{pageController.viewAction(pageController.item)}") + @Before @ApplyRequestValues ITEM, + @ViewPattern("/viewcontroller.xhtml") + @ViewController(org.jboss.seam.faces.examples.viewconfig.ViewController.class) + VIEW_CONTROLLER, + + @ViewPattern("/viewactionbindingtype.xhtml") + VIEW_ACTION_BINDING_TYPE, + + @ViewPattern("/viewaction.xhtml") + @ViewAction("#{viewActionController.preRenderAction}") + VIEW_ACTION, + @FacesRedirect @ViewPattern("/*") @AccessDeniedView("/denied.xhtml") diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyViewAction.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyViewAction.java index 2e97b03..8725143 100644 --- a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyViewAction.java +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyViewAction.java @@ -12,10 +12,4 @@ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) public @interface MyViewAction { MyAppViewConfig.Pages value(); - - //just testing value override -// public PhaseIdType phase() default PhaseIdType.RENDER_RESPONSE; -// public Boolean immediate = null; -// public Boolean onPostback = false; -// public String condition = null; } diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java index 34bdf36..c540cfa 100644 --- a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java @@ -29,6 +29,7 @@ import org.jboss.seam.faces.examples.viewconfig.model.Current; import org.jboss.seam.faces.examples.viewconfig.model.Item; import org.jboss.seam.faces.examples.viewconfig.model.ItemDao; +import org.jboss.seam.faces.view.action.BeforeRenderReponse; /** * @author Brian Leathem @@ -54,12 +55,20 @@ public Item getItem() { return item; } + @BeforeRenderReponse + public void beforeRenderView(@Current Item item) { + System.out.println("beforeRenderView called "+item); + } + + public void viewAction(@Current Item item) { + System.out.println("viewAction "+item); + } + @MyViewAction(Pages.ITEM) - public void loadEntry() { - System.out.println("loadEntry called"); + public void viewActionBindingType(@Current Item item) { + System.out.println("viewActionBindingType "+item); } - @MyViewAction(Pages.ITEM) public void setItem(Item item) { this.item = item; } diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewActionBindingTypeController.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewActionBindingTypeController.java new file mode 100644 index 0000000..0285282 --- /dev/null +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewActionBindingTypeController.java @@ -0,0 +1,18 @@ +package org.jboss.seam.faces.examples.viewconfig; + +import javax.enterprise.context.RequestScoped; +import javax.faces.application.FacesMessage; +import javax.faces.context.FacesContext; +import javax.inject.Named; + +import org.jboss.seam.faces.examples.viewconfig.MyAppViewConfig.Pages; + +@Named +@RequestScoped +public class ViewActionBindingTypeController { + @MyViewAction(Pages.VIEW_ACTION_BINDING_TYPE) + public void beforeRenderAction() { + FacesMessage facesMessages = new FacesMessage("ViewActionBindingTypeController.beforeRenderAction was called"); + FacesContext.getCurrentInstance().addMessage(null, facesMessages); + } +} diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewActionController.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewActionController.java new file mode 100644 index 0000000..0a299a1 --- /dev/null +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewActionController.java @@ -0,0 +1,15 @@ +package org.jboss.seam.faces.examples.viewconfig; + +import javax.enterprise.context.RequestScoped; +import javax.faces.application.FacesMessage; +import javax.faces.context.FacesContext; +import javax.inject.Named; + +@Named +@RequestScoped +public class ViewActionController { + public void preRenderAction() { + FacesMessage facesMessages = new FacesMessage("ViewActionController.preRenderAction was called"); + FacesContext.getCurrentInstance().addMessage(null, facesMessages); + } +} diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewController.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewController.java new file mode 100644 index 0000000..951c922 --- /dev/null +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewController.java @@ -0,0 +1,71 @@ +package org.jboss.seam.faces.examples.viewconfig; + +import javax.faces.application.FacesMessage; +import javax.faces.context.FacesContext; + +import org.jboss.seam.faces.event.qualifier.After; +import org.jboss.seam.faces.event.qualifier.ApplyRequestValues; +import org.jboss.seam.faces.event.qualifier.Before; +import org.jboss.seam.faces.event.qualifier.InvokeApplication; +import org.jboss.seam.faces.event.qualifier.ProcessValidations; +import org.jboss.seam.faces.event.qualifier.RenderResponse; +import org.jboss.seam.faces.event.qualifier.UpdateModelValues; +import org.jboss.seam.faces.view.action.BeforeRenderReponse; + +public class ViewController { + + @Before @ApplyRequestValues + public void beforeApplyRequestValues() { + addFacesMessage(this.getClass().getSimpleName()+".beforeApplyRequestValues was called"); + } + + @After @ApplyRequestValues + public void afterApplyRequestValues() { + addFacesMessage(this.getClass().getSimpleName()+".afterApplyRequestValues was called"); + } + + @Before @ProcessValidations + public void beforeProcessValidations() { + addFacesMessage(this.getClass().getSimpleName()+".beforeProcessValidations was called"); + } + + @After @ProcessValidations + public void afterProcessValidations() { + addFacesMessage(this.getClass().getSimpleName()+".afterProcessValidations was called"); + } + + @Before @UpdateModelValues + public void beforeUpdateModelValues() { + addFacesMessage(this.getClass().getSimpleName()+".beforeUpdateModelValues was called"); + } + + @After @UpdateModelValues + public void afterUpdateModelValues() { + addFacesMessage(this.getClass().getSimpleName()+".afterUpdateModelValues was called"); + } + + @Before @InvokeApplication + public void beforeInvokeApplication() { + addFacesMessage(this.getClass().getSimpleName()+".beforeInvokeApplication was called"); + } + + @After @InvokeApplication + public void afterInvokeApplication() { + addFacesMessage(this.getClass().getSimpleName()+".afterInvokeApplication was called"); + } + + @BeforeRenderReponse + public void beforeRenderResponse() { + addFacesMessage(this.getClass().getSimpleName()+".beforeRenderResponse was called"); + } + + @After @RenderResponse + public void afterRenderResponse() { + addFacesMessage(this.getClass().getSimpleName()+".RenderResponse was called"); + } + + private void addFacesMessage(String message) { + FacesMessage facesMessages = new FacesMessage(message); + FacesContext.getCurrentInstance().addMessage(null, facesMessages); + } +} diff --git a/examples/viewconfig/src/main/webapp/index.xhtml b/examples/viewconfig/src/main/webapp/index.xhtml index 7407433..8bc57ac 100644 --- a/examples/viewconfig/src/main/webapp/index.xhtml +++ b/examples/viewconfig/src/main/webapp/index.xhtml @@ -22,6 +22,20 @@ +

This example demonstrates shows View Actions in action

+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
diff --git a/examples/viewconfig/src/main/webapp/viewaction.xhtml b/examples/viewconfig/src/main/webapp/viewaction.xhtml new file mode 100644 index 0000000..ac71eb7 --- /dev/null +++ b/examples/viewconfig/src/main/webapp/viewaction.xhtml @@ -0,0 +1,19 @@ + + + + + + + @ViewAction usage + + + + + + + + diff --git a/examples/viewconfig/src/main/webapp/viewactionbindingtype.xhtml b/examples/viewconfig/src/main/webapp/viewactionbindingtype.xhtml new file mode 100644 index 0000000..2793fd6 --- /dev/null +++ b/examples/viewconfig/src/main/webapp/viewactionbindingtype.xhtml @@ -0,0 +1,19 @@ + + + + + + + @ViewActionBindingType usage + + + + + + + + diff --git a/examples/viewconfig/src/main/webapp/viewcontroller.xhtml b/examples/viewconfig/src/main/webapp/viewcontroller.xhtml new file mode 100644 index 0000000..535fccd --- /dev/null +++ b/examples/viewconfig/src/main/webapp/viewcontroller.xhtml @@ -0,0 +1,20 @@ + + + + + + + @ViewController usage + + + + + + + + + diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/PhaseInstant.java b/impl/src/main/java/org/jboss/seam/faces/view/action/PhaseInstant.java new file mode 100644 index 0000000..98b4419 --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/PhaseInstant.java @@ -0,0 +1,54 @@ +package org.jboss.seam.faces.view.action; + +import javax.faces.event.PhaseId; + +/** + * Identifies when the viewAction must take place in the JSF lifecycle. + */ +public class PhaseInstant { + private PhaseId phaseId; + private boolean before; + + public static final PhaseInstant BEFORE_RENDER_RESPONSE = new PhaseInstant(PhaseId.RENDER_RESPONSE, true); + + public PhaseInstant(PhaseId phaseId, boolean before) { + this.phaseId = phaseId; + this.before = before; + } + + public PhaseId getPhaseId() { + return phaseId; + } + + public void setPhaseId(PhaseId phaseId) { + this.phaseId = phaseId; + } + + public boolean isBefore() { + return before; + } + + public void setBefore(boolean before) { + this.before = before; + } + + @Override + public int hashCode() { + return phaseId.hashCode(); + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof PhaseInstant)) { + return false; + } + PhaseInstant instant = (PhaseInstant) object; + if (getPhaseId() != instant.getPhaseId()) { + return false; + } + if (isBefore() != instant.isBefore()) { + return false; + } + return true; + } +} diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingTypeDescriptor.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingTypeDescriptor.java new file mode 100644 index 0000000..4e4e4e6 --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingTypeDescriptor.java @@ -0,0 +1,52 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Annotation; +import java.util.Arrays; + +import javax.enterprise.inject.spi.AnnotatedMethod; + +import static org.jboss.seam.faces.view.action.PhaseInstant.BEFORE_RENDER_RESPONSE; + +public class ViewActionBindingTypeDescriptor { + private Annotation annotation; + private Object viewControllerValue; + private AnnotatedMethod annotatedMethod; + private PhaseInstant phaseInstant; + + public ViewActionBindingTypeDescriptor(AnnotatedMethod annotatedMethod, Annotation annotation, + Object viewControllerValue) { + this.annotatedMethod = annotatedMethod; + this.annotation = annotation; + this.viewControllerValue = viewControllerValue; + this.phaseInstant = ViewActionUtils.getPhaseInstantOrDefault(Arrays.asList(annotation.annotationType().getAnnotations()), + annotation.annotationType(), BEFORE_RENDER_RESPONSE); + } + + public Annotation getAnnotation() { + return annotation; + } + + public void setAnnotation(Annotation annotation) { + this.annotation = annotation; + } + + public Object getViewControllerValue() { + return viewControllerValue; + } + + public void setViewControllerValue(Object viewControllerValue) { + this.viewControllerValue = viewControllerValue; + } + + public AnnotatedMethod getAnnotatedMethod() { + return annotatedMethod; + } + + public void setAnnotatedMethod(AnnotatedMethod annotatedMethod) { + this.annotatedMethod = annotatedMethod; + } + + public PhaseInstant getPhaseInstant() { + return phaseInstant; + } +} diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java index 80f3d57..c2d05b9 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java @@ -1,30 +1,15 @@ package org.jboss.seam.faces.view.action; -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.List; import javax.enterprise.event.Observes; -import javax.enterprise.inject.spi.BeanManager; -import javax.faces.component.UIViewRoot; -import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; +import javax.faces.event.PhaseId; import javax.inject.Inject; -import org.jboss.seam.faces.event.PhaseIdType; import org.jboss.seam.faces.event.qualifier.After; -import org.jboss.seam.faces.event.qualifier.ApplyRequestValues; import org.jboss.seam.faces.event.qualifier.Before; -import org.jboss.seam.faces.event.qualifier.InvokeApplication; -import org.jboss.seam.faces.event.qualifier.ProcessValidations; -import org.jboss.seam.faces.event.qualifier.RenderResponse; -import org.jboss.seam.faces.event.qualifier.RestoreView; -import org.jboss.seam.faces.event.qualifier.UpdateModelValues; -import org.jboss.seam.faces.view.config.ViewConfigStore; import org.jboss.solder.logging.Logger; -import org.jboss.solder.reflection.AnnotationInspector; /** * Use the annotations stored in the ViewConfigStore to execute action (MethodExpression) calls. @@ -38,193 +23,42 @@ public class ViewActionPhaseListener { private transient final Logger log = Logger.getLogger(ViewActionPhaseListener.class); @Inject - private ViewConfigStore viewConfigStore; - @Inject - private BeanManager beanManager; - - /** - * Execute any action annotations applicable to the RestoreView phase - * - * @param event - */ - public void observeRestoreView(@Observes @After @RestoreView PhaseEvent event) { - log.debug("After Restore View event"); - performObservation(event, PhaseIdType.RESTORE_VIEW); - } - - /** - * Execute any action annotations applicable to the ApplyRequestValues phase - * - * @param event - */ - public void observeApplyRequestValues(@Observes @Before @ApplyRequestValues PhaseEvent event) { - log.debug("After Apply Request Values event"); - performObservation(event, PhaseIdType.APPLY_REQUEST_VALUES); - } - - /** - * Execute any action annotations applicable to the ProcessValidations phase - * - * @param event - */ - public void observeProcessValidations(@Observes @Before @ProcessValidations PhaseEvent event) { - log.debug("After Process Validations event"); - performObservation(event, PhaseIdType.PROCESS_VALIDATIONS); - } - - /** - * Execute any action annotations applicable to the UpdateModelValues phase - * - * @param event - */ - public void observeUpdateModelValues(@Observes @Before @UpdateModelValues PhaseEvent event) { - log.debug("After Update Model Values event"); - performObservation(event, PhaseIdType.UPDATE_MODEL_VALUES); - } - - /** - * Execute any action annotations applicable to the InvokeApplication phase - * - * @param event - */ - public void observeInvokeApplication(@Observes @Before @InvokeApplication PhaseEvent event) { - log.debug("Before Render Response event"); - performObservation(event, PhaseIdType.INVOKE_APPLICATION); - } - - /** - * Execute any action annotations applicable to the RenderResponse phase - * - * @param event - */ - public void observeRenderResponse(@Observes @Before @RenderResponse PhaseEvent event) { - log.debug("Before Render Response event"); - performObservation(event, PhaseIdType.RENDER_RESPONSE); - } - - /** - * Inspect the annotations in the ViewConfigStore, executing any actions applicable to this phase - * - * @param event - * @param phaseIdType - */ - private void performObservation(PhaseEvent event, PhaseIdType phaseIdType) { - UIViewRoot viewRoot = (UIViewRoot) event.getFacesContext().getViewRoot(); - List actionsForPhase = getViewActionsForPhase(phaseIdType, viewRoot.getViewId()); - if (actionsForPhase != null) { - log.debugf("Enforcing on phase %s", phaseIdType); - execute(event.getFacesContext(), viewRoot, actionsForPhase); - } - } - - /** - * Retrieve all annotations from the ViewConfigStore for a given a JSF phase, and a view id, - * and where the annotation is qualified by @SecurityBindingType - * - * @param currentPhase - * @param viewId - * @return list of restrictions applicable to this viewId and PhaseTypeId - */ - public List getViewActionsForPhase(PhaseIdType currentPhase, String viewId) { - List allViewActionAnnotations = viewConfigStore.getAllQualifierData(viewId, ViewActionBindingType.class); - List applicableViewActionAnnotations = null; - for (Annotation annotation : allViewActionAnnotations) { - PhaseIdType defaultPhase = getDefaultPhase(viewId); - if (isAnnotationApplicableToPhase(annotation, currentPhase, defaultPhase)) { - if (applicableViewActionAnnotations == null) { // avoid spawning arrays at all phases of the lifecycle - applicableViewActionAnnotations = new ArrayList(); - } - applicableViewActionAnnotations.add(annotation); - } - } - return applicableViewActionAnnotations; - } - - /** - * Inspect an annotation to see if it specifies a view in which it should be. Fall back on default view otherwise. - * - * @param annotation - * @param currentPhase - * @param defaultPhases - * @return true if the annotation is applicable to this view and phase, false otherwise - */ - public boolean isAnnotationApplicableToPhase(Annotation annotation, PhaseIdType currentPhase, PhaseIdType defaultPhase) { - Method phaseAtViewActionMethod = getPhaseAtViewActionMethod(annotation); - PhaseIdType phasedId = null; - if (phaseAtViewActionMethod != null) { - log.debug("Annotation %s is using the phase method."); - phasedId = getViewActionPhaseId(phaseAtViewActionMethod, annotation); - } - Phase phaseQualifier = AnnotationInspector.getAnnotation(annotation.annotationType(), Phase.class, beanManager); - if (phaseQualifier != null) { - log.debug("Using Phase found in @Phase qualifier on the annotation."); - phasedId = phaseQualifier.value(); + private ViewControllerStore viewControllerStore; + + // TODO : should be executed after SecurityPhaseListener + public void observerBeforePhase(@Observes @Before PhaseEvent event) { + PhaseId phaseId = event.getPhaseId(); + log.debugf("Before {1} event", phaseId); + if (event.getFacesContext().getViewRoot() == null) { + log.debug("viewRoot null, skipping view actions"); + return; } - if (phasedId == null) { - log.debug("Falling back on default phase id"); - phasedId = defaultPhase; + List viewControllers = viewControllerStore.getControllerDescriptors(event.getFacesContext() + .getViewRoot().getViewId()); + for (int i = viewControllers.size(); --i >= 0;) { + ViewControllerDescriptor viewControllerDescriptor = viewControllers.get(i); + viewControllerDescriptor.executeBeforePhase(event.getPhaseId()); } - return phasedId == currentPhase; - } - - /** - * Get the default phases at which restrictions should be applied, by looking for a @Phase default value - * - * @param viewId - * @return default phase - */ - public PhaseIdType getDefaultPhase(String viewId) { - return PhaseDefault.DEFAULT_PHASE; - } - - /** - * Utility method to extract the "phase" method from an annotation - * - * @param annotation - * @return phaseAtViewActionMethod if found, null otherwise - */ - public Method getPhaseAtViewActionMethod(Annotation annotation) { - Method phaseAtViewActionMethod; - try { - phaseAtViewActionMethod = annotation.annotationType().getDeclaredMethod("phase"); - } catch (NoSuchMethodException ex) { - phaseAtViewActionMethod = null; - } catch (SecurityException ex) { - throw new IllegalArgumentException("phase method must be accessible", ex); + if (phaseId == PhaseId.RENDER_RESPONSE) { + for (int i = viewControllers.size(); --i >= 0;) { + ViewControllerDescriptor viewControllerDescriptor = viewControllers.get(i); + viewControllerDescriptor.executeBeforeRenderView(); + } } - return phaseAtViewActionMethod; } - /** - * Retrieve the default PhaseIdType defined by the phaseAtViewActionMethod in the annotation - * - * @param phaseAtViewActionMethod - * @param annotation - * @return PhaseIdType from the phaseAtViewActionMethod, null if empty - */ - public PhaseIdType getViewActionPhaseId(Method phaseAtViewActionMethod, Annotation annotation) { - PhaseIdType phaseId; - try { - phaseId = (PhaseIdType) phaseAtViewActionMethod.invoke(annotation); - } catch (IllegalAccessException ex) { - throw new IllegalArgumentException("phase method must be accessible", ex); - } catch (InvocationTargetException ex) { - throw new RuntimeException(ex); + public void observerAfterPhase(@Observes @After PhaseEvent event) { + PhaseId phaseId = event.getPhaseId(); + log.debugf("After {1} event", phaseId); + List viewControllers = viewControllerStore.getControllerDescriptors(event.getFacesContext() + .getViewRoot().getViewId()); + for (ViewControllerDescriptor viewControllerDescriptor : viewControllers) { + viewControllerDescriptor.executeAfterPhase(event.getPhaseId()); } - return phaseId; - } - - /** - * Execute the list of applicable view action annotations, TODO... - * - * @param context - * @param viewRoot - * @param annotations - */ - private void execute(FacesContext context, UIViewRoot viewRoot, List annotations) { - if (annotations == null || annotations.isEmpty()) { - log.debug("Annotations is null/empty"); - return; + if (phaseId == PhaseId.RENDER_RESPONSE) { + for (ViewControllerDescriptor viewControllerDescriptor : viewControllers) { + viewControllerDescriptor.executeAfterRenderView(); + } } } } diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionStrategy.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionStrategy.java new file mode 100644 index 0000000..676a956 --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionStrategy.java @@ -0,0 +1,18 @@ +package org.jboss.seam.faces.view.action; + +/** + * Interface encapsulating view action implementation. + * + * The implementation can be : + *
    + *
  • a viewController method call.
  • + *
  • an annotated ViewActionBindingType method call.
  • + *
  • an El contained in a ViewAction annotation.
  • + *
  • ... or any other logic...
  • + *
+ * + * @author Adriàn Gonzalez + */ +public interface ViewActionStrategy { + public Object execute(); +} diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java new file mode 100644 index 0000000..33f44d7 --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java @@ -0,0 +1,114 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Annotation; +import java.util.List; + +import javax.faces.event.PhaseId; + +import org.jboss.seam.faces.event.qualifier.After; +import org.jboss.seam.faces.event.qualifier.ApplyRequestValues; +import org.jboss.seam.faces.event.qualifier.Before; +import org.jboss.seam.faces.event.qualifier.InvokeApplication; +import org.jboss.seam.faces.event.qualifier.ProcessValidations; +import org.jboss.seam.faces.event.qualifier.RenderResponse; +import org.jboss.seam.faces.event.qualifier.UpdateModelValues; + +public class ViewActionUtils { + + // Utility class - no instanciation + private ViewActionUtils() { + } + + public static PhaseInstant getPhaseInstantOrDefault(List annotations, Object parentElement, PhaseInstant defaultInstant) { + PhaseInstant phaseInstant = getPhaseInstant(annotations, parentElement); + return phaseInstant!=null ? phaseInstant : defaultInstant; + } + + public static PhaseInstant getPhaseInstant(List annotations, Object parentElement) { + Boolean before = null; + PhaseId phaseId = null; + for (Annotation annotation : annotations) { + Class annotationType = annotation.annotationType(); + if (annotationType == Before.class) { + if (before != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples @Before and @After"); + } + before = true; + } else if (annotationType == After.class) { + if (before != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples @Before and @After"); + } + before = false; + } else if (annotationType == ApplyRequestValues.class) { + if (phaseId != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType + + " and " + phaseId + ")"); + } + phaseId = PhaseId.APPLY_REQUEST_VALUES; + } else if (annotationType == ProcessValidations.class) { + if (phaseId != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType + + " and " + phaseId + ")"); + } + phaseId = PhaseId.PROCESS_VALIDATIONS; + } else if (annotationType == UpdateModelValues.class) { + if (phaseId != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType + + " and " + phaseId + ")"); + } + phaseId = PhaseId.UPDATE_MODEL_VALUES; + } else if (annotationType == InvokeApplication.class) { + if (phaseId != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType + + " and " + phaseId + ")"); + } + phaseId = PhaseId.INVOKE_APPLICATION; + } else if (annotationType == RenderResponse.class) { + if (phaseId != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType + + " and " + phaseId + ")"); + } + phaseId = PhaseId.RENDER_RESPONSE; + } else if (annotationType == BeforeRenderReponse.class) { + if (phaseId != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType + + " and " + phaseId + ")"); + } + if (before != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples @Before/@After and " + annotationType); + } + phaseId = PhaseId.RENDER_RESPONSE; + before = true; + } else if (annotationType == AfterRenderView.class) { + if (phaseId != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType + + " and " + phaseId + ")"); + } + if (before != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples @Before/@After and " + annotationType); + } + phaseId = PhaseId.RENDER_RESPONSE; + before = false; + } + } + if (before==null && phaseId==null) { + return null; + } else if (before!=null && phaseId!=null) { + return new PhaseInstant(phaseId, before); + } else { + throw new IllegalStateException("invalid " + parentElement + + ". both phaseId and @Before/@After must be specified {phaseId: " + phaseId+", before: "+before+"}"); + } + } +} diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java new file mode 100644 index 0000000..68b813b --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java @@ -0,0 +1,315 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.el.MethodExpression; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedMethod; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.faces.context.FacesContext; +import javax.faces.event.PhaseId; + +import org.jboss.seam.faces.view.config.ViewConfig; +import org.jboss.solder.logging.Logger; +import org.jboss.solder.reflection.annotated.InjectableMethod; + +/** + * Information about a particular controller. + * + * A Controller is managed bean which is associated to a given view (a given {@link ViewConfig}). + * + * @author Adriàn Gonzalez + */ +public class ViewControllerDescriptor { + + private transient final Logger log = Logger.getLogger(ViewControllerDescriptor.class); + + private String viewId; + private Class viewControllerClass; + private BeanManager beanManager; + + // call this one in an additional actionListener (faces-config.xml) + private List beforePageActionMethods = new ArrayList(); + private List beforeRenderViewMethods = new ArrayList(); + private List afterRenderViewMethods = new ArrayList(); + private Map> phaseMethods = new HashMap>(); + + /** + * Creates descriptor. + * + * Lifecycle callback registration is up to the caller. + * + * Note : beanManager parameter is horrible, should be a way to send beanManager more elegantly + */ + public ViewControllerDescriptor(String viewId, BeanManager beanManager) { + this.viewId = viewId; + this.beanManager = beanManager; + log.debugf("Created viewController #0", this); + } + + /** + * Creates descriptor by reading controllerViewClass methods. + * + * Register controllerViewClass lifecycle callback methods. + * + * Note : beanManager parameter is horrible, should be a way to send beanManager more elegantly + */ + public ViewControllerDescriptor(String viewId, Class viewControllerClass, BeanManager beanManager) { + this.viewId = viewId; + this.viewControllerClass = viewControllerClass; + this.beanManager = beanManager; + registerCallbacks(); + log.debugf("Created viewController #0", this); + } + + /** + * Register any lifecycle methods declared in this viewController class (or inherited). + */ + private void registerCallbacks() { + Class current = viewControllerClass; + while (current != Object.class) { + for (Method method : current.getDeclaredMethods()) { +// if (method.isAnnotationPresent(BeforeRenderView.class)) { +// beforeRenderViewMethods.add(new MethodInvoker(method, beanManager)); +// } +// if (method.isAnnotationPresent(AfterRenderView.class)) { +// afterRenderViewMethods.add(new MethodInvoker(method, beanManager)); +// } + PhaseInstant phaseInstant = ViewActionUtils.getPhaseInstant(Arrays.asList(method.getAnnotations()), method); + if (phaseInstant != null) { + addMethod(phaseInstant, new MethodInvoker(method, beanManager)); + } + } + current = current.getSuperclass(); + } + } + + public void executeBeforePhase(PhaseId phaseId) { + List actions = phaseMethods.get(new PhaseInstant(phaseId, true)); + if (actions != null) { + for (ViewActionStrategy action : actions) { + action.execute(); + } + } + } + + public void executeAfterPhase(PhaseId phaseId) { + List actions = phaseMethods.get(new PhaseInstant(phaseId, false)); + if (actions != null) { + for (ViewActionStrategy action : actions) { + action.execute(); + } + } + } + + public void executeBeforePageAction() { + throw new NoSuchMethodError("unimplemented"); + } + + public void executeBeforeRenderView() { + for (ViewActionStrategy invoker : getBeforeRenderViewMethods()) { + invoker.execute(); + } + } + + public void executeAfterRenderView() { + for (ViewActionStrategy invoker : getAfterRenderViewMethods()) { + invoker.execute(); + } + } + + public List getBeforePageActionMethods() { + return beforePageActionMethods; + } + + public void setBeforePageActionMethods(List beforePageActionMethods) { + this.beforePageActionMethods = beforePageActionMethods; + } + + public void addBeforeRenderViewMethod(ViewActionStrategy beforeRenderViewMethod) { + beforeRenderViewMethods.add(beforeRenderViewMethod); + } + + public List getBeforeRenderViewMethods() { + return beforeRenderViewMethods; + } + + public void setBeforeRenderViewMethods(List beforeRenderViewMethods) { + this.beforeRenderViewMethods = beforeRenderViewMethods; + } + + public List getAfterRenderViewMethods() { + return afterRenderViewMethods; + } + + public void setAfterRenderViewMethods(List afterRenderViewMethods) { + this.afterRenderViewMethods = afterRenderViewMethods; + } + + public void addMethod(PhaseInstant phaseInstant, ViewActionStrategy method) { + List methods = phaseMethods.get(phaseInstant); + if (methods == null) { + methods = new ArrayList(); + phaseMethods.put(phaseInstant, methods); + } + methods.add(method); + } + + public Map> getPhaseMethods() { + return phaseMethods; + } + + public void setPhaseMethods(Map> phaseMethods) { + this.phaseMethods = phaseMethods; + } + + public String getViewId() { + return viewId; + } + + public void setViewId(String viewId) { + this.viewId = viewId; + } + + public Class getViewControllerClass() { + return viewControllerClass; + } + + public void setViewControllerClass(Class viewControllerClass) { + this.viewControllerClass = viewControllerClass; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(super.toString()); + builder.append("{viewId: ").append(getViewId()).append(", viewControllerClass: ").append(getViewControllerClass()) + .append(", beforePageActionMethods: {").append(getBeforePageActionMethods()) + .append("}, beforeRenderViewMethods: ").append(getAfterRenderViewMethods()) + .append("}, afterRenderViewMethods: {").append(getAfterRenderViewMethods()).append("}, phaseMethods: {") + .append(getPhaseMethods()).append("}}"); + return builder.toString(); + } + + /** + * Invokes method on a CDI bean. + * + * Note : copy from Seam Security's SecurityExtension class. Should be extracted into common utility. + */ + public static class AnnotatedMethodInvoker implements ViewActionStrategy { + private Bean targetBean; + private BeanManager beanManager; + private InjectableMethod injectableMethod; + private AnnotatedMethod annotatedMethod; + + public AnnotatedMethodInvoker(AnnotatedMethod annotatedMethod, BeanManager beanManager) { + this.beanManager = beanManager; + this.annotatedMethod = annotatedMethod; + } + + public AnnotatedMethod getAnnotatedMethod() { + return annotatedMethod; + } + + public void setAnnotatedMethod(AnnotatedMethod annotatedMethod) { + this.annotatedMethod = annotatedMethod; + } + + public BeanManager getBeanManager() { + return beanManager; + } + + public Object execute() { + if (targetBean == null) { + lookupTargetBean(); + } + CreationalContext cc = beanManager.createCreationalContext(targetBean); + Object reference = beanManager.getReference(targetBean, getAnnotatedMethod().getJavaMember().getDeclaringClass(), + cc); + return injectableMethod.invoke(reference, cc, null); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private synchronized void lookupTargetBean() { + if (targetBean == null) { + AnnotatedMethod annotatedMethod = getAnnotatedMethod(); + Method method = annotatedMethod.getJavaMember(); + Set> beans = beanManager.getBeans(method.getDeclaringClass()); + if (beans.size() == 1) { + targetBean = beans.iterator().next(); + } else if (beans.isEmpty()) { + throw new IllegalStateException("Exception looking up method bean - " + "no beans found for method [" + + method.getDeclaringClass() + "." + method.getName() + "]"); + } else if (beans.size() > 1) { + throw new IllegalStateException("Exception looking up method bean - " + "multiple beans found for method [" + + method.getDeclaringClass().getName() + "." + method.getName() + "]"); + } + injectableMethod = new InjectableMethod(annotatedMethod, targetBean, beanManager); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(this.getClass().getSimpleName()); + builder.append("{method: ").append(getAnnotatedMethod()).append("}"); + return builder.toString(); + } + } + + /** + * Invokes method on a CDI bean. + * + * Note : copy from Seam Security's SecurityExtension class. Should be extracted into common utility. + */ + public static class MethodInvoker extends AnnotatedMethodInvoker { + + public MethodInvoker(Method method, BeanManager beanManager) { + super(null, beanManager); + setAnnotatedMethod(convert(method)); + + } + + private AnnotatedMethod convert(Method method) { + AnnotatedType annotatedType = getBeanManager().createAnnotatedType(method.getDeclaringClass()); + AnnotatedMethod annotatedMethod = null; + for (AnnotatedMethod current : annotatedType.getMethods()) { + if (current.getJavaMember().equals(method)) { + annotatedMethod = current; + } + } + if (annotatedMethod == null) { + throw new IllegalStateException("No matching annotated method found for method : " + method); + } + return annotatedMethod; + } + } + + /** + * Invokes a method expression. + */ + public static class MethodExpressionInvoker implements ViewActionStrategy { + private MethodExpression methodExpression; + private String methodExpressionAsString; + + public MethodExpressionInvoker(String methodExpressionAsString) { + this.methodExpressionAsString = methodExpressionAsString; + } + + @Override + public Object execute() { + FacesContext facesContext = FacesContext.getCurrentInstance(); + if (methodExpression == null) { + methodExpression = facesContext.getApplication().getExpressionFactory() + .createMethodExpression(facesContext.getELContext(), methodExpressionAsString, null, new Class[] {}); + } + return methodExpression.invoke(FacesContext.getCurrentInstance().getELContext(), null); + } + } +} diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerExtension.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerExtension.java new file mode 100644 index 0000000..6eedc5e --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerExtension.java @@ -0,0 +1,80 @@ +package org.jboss.seam.faces.view.action; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AnnotatedMethod; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.ProcessAnnotatedType; + +/** + * Scans for viewController classes and view actions. + * + * @author Adriàn Gonzalez + */ +public class ViewControllerExtension implements Extension { + + private final Map> descriptors = new HashMap>(); + + public void processAnnotatedType(@Observes ProcessAnnotatedType event) { + AnnotatedType tp = event.getAnnotatedType(); + for (final AnnotatedMethod m : tp.getMethods()) { + for (final Annotation annotation : m.getAnnotations()) { + if (annotation.annotationType().isAnnotationPresent(ViewActionBindingType.class)) { + Object viewConfigValue = getValue(annotation); + if (viewConfigValue == null) { + throw new IllegalArgumentException("Annotation " + annotation + + " invalid : no view specified"); + } + List actions = descriptors.get(viewConfigValue); + if (actions == null) { + actions = new ArrayList(); + descriptors.put(viewConfigValue, actions); + } + ViewActionBindingTypeDescriptor descriptor = new ViewActionBindingTypeDescriptor(m, annotation, viewConfigValue); + actions.add(descriptor); + } + } + } + } + + public Map> getViewActionBindingTypeDescriptors() { + return descriptors; + } + + + // Utility methods for viewAction, TODO move this block out of this class + + /** + * Retrieve the view defined by the value() method in the annotation + * + * @param annotation + * @return the result of value() call + * @throws IllegalArgumentException if no value() method was found + */ + private Object getValue(Annotation annotation) { + Method valueMethod; + try { + valueMethod = annotation.annotationType().getDeclaredMethod("value"); + } catch (NoSuchMethodException ex) { + throw new IllegalArgumentException("value method must be declared and must resolve to a valid view", ex); + } catch (SecurityException ex) { + throw new IllegalArgumentException("value method must be accessible", ex); + } + try { + return valueMethod.invoke(annotation); + } catch (IllegalAccessException ex) { + throw new IllegalArgumentException("value method must be accessible", ex); + } catch (InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java new file mode 100644 index 0000000..362026b --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java @@ -0,0 +1,172 @@ +package org.jboss.seam.faces.view.action; + +import static org.jboss.seam.faces.view.action.PhaseInstant.BEFORE_RENDER_RESPONSE; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import javax.enterprise.inject.spi.BeanManager; +import javax.inject.Inject; + +import org.jboss.seam.faces.view.config.ViewConfigDescriptor; +import org.jboss.seam.faces.view.config.ViewConfigStore; + +/** + * Data store for view controllers. + * + * @author Adriàn gonzalez + */ +public class ViewControllerStore { + /** map containing view pattern / controller */ + private Map> viewPatternControllerDescriptors = new HashMap>(); + private ConcurrentHashMap> viewControllerDescriptorsCache = new ConcurrentHashMap>(); + + /** + * Initialization : Retrieves any ViewControllers associated to ViewConfig objects + */ + @Inject + public void setup(ViewControllerExtension viewControllerExtension, ViewConfigStore viewConfigStore, BeanManager beanManager) { + registerViewControllers(viewConfigStore, beanManager); + registerViewActions(viewConfigStore, beanManager); + registerViewActionBindingTypes(viewControllerExtension, viewConfigStore, beanManager); + } + + private void registerViewControllers(ViewConfigStore viewConfigStore, BeanManager beanManager) { + Map views = viewConfigStore.getAllAnnotationViewMap(ViewController.class); + for (Map.Entry entry : views.entrySet()) { + ViewController annotation = (ViewController) entry.getValue(); + if (annotation.value() == null) { + throw new IllegalArgumentException("Invalid ViewConfig for view '" + entry.getKey() + + "' : @ViewController must have a non null value."); + } + for (Class viewControllerClass : annotation.value()) { + ViewControllerDescriptor viewControllerDescriptor = createViewControllerDescriptor(entry.getKey(), + viewControllerClass, beanManager); + addControllerDescriptor(viewControllerDescriptor); + } + } + } + + private void registerViewActionBindingTypes(ViewControllerExtension viewControllerExtension, + ViewConfigStore viewConfigStore, BeanManager beanManager) { + List viewConfigDescriptors = viewConfigStore.getAllViewConfigDescriptors(); + for (ViewConfigDescriptor viewConfigDescriptor : viewConfigDescriptors) { + List viewActionBindingTypes = new ArrayList(); + for (Object value : viewConfigDescriptor.getValues()) { + List current = viewControllerExtension.getViewActionBindingTypeDescriptors() + .get(value); + if (current != null) { + viewActionBindingTypes.addAll(current); + } + } + if (viewActionBindingTypes.size() > 0) { + ViewControllerDescriptor viewControllerDescriptor = new ViewControllerDescriptor( + viewConfigDescriptor.getViewId(), beanManager); + for (ViewActionBindingTypeDescriptor viewActionBindingTypeDescriptor : viewActionBindingTypes) { + viewControllerDescriptor.addMethod( + viewActionBindingTypeDescriptor.getPhaseInstant(), + new ViewControllerDescriptor.AnnotatedMethodInvoker(viewActionBindingTypeDescriptor + .getAnnotatedMethod(), beanManager)); + } + addControllerDescriptor(viewControllerDescriptor); + } + } + } + + private void registerViewActions(ViewConfigStore viewConfigStore, BeanManager beanManager) { + List viewConfigDescriptors = viewConfigStore.getAllViewConfigDescriptors(); + for (ViewConfigDescriptor viewConfigDescriptor : viewConfigDescriptors) { + ViewAction viewAction = viewConfigDescriptor.getMetaData(ViewAction.class); + // TODO : don't know using annotations @Before / @RenderResponse / ... with @ViewAction + // is a good idea (possible collision with future annotations ?) + // would it be better to add attributes to @ViewAction ? + if (viewAction != null) { + ViewControllerDescriptor viewControllerDescriptor = new ViewControllerDescriptor( + viewConfigDescriptor.getViewId(), beanManager); + PhaseInstant phaseInstant = ViewActionUtils.getPhaseInstantOrDefault(viewConfigDescriptor.getMetaData(), + viewConfigDescriptor, BEFORE_RENDER_RESPONSE); + viewControllerDescriptor.addMethod(phaseInstant, new ViewControllerDescriptor.MethodExpressionInvoker( + viewAction.value())); + addControllerDescriptor(viewControllerDescriptor); + } + } + } + + private ViewControllerDescriptor createViewControllerDescriptor(String viewId, Class controllerViewClass, + BeanManager beanManager) { + return new ViewControllerDescriptor(viewId, controllerViewClass, beanManager); + } + + public void addControllerDescriptor(ViewControllerDescriptor controllerDescriptor) { + List descriptors = viewPatternControllerDescriptors.get(controllerDescriptor.getViewId()); + if (descriptors == null) { + descriptors = new ArrayList(); + } + descriptors.add(controllerDescriptor); + viewPatternControllerDescriptors.put(controllerDescriptor.getViewId(), descriptors); + } + + /** + * Returns contollers matching a viewId. + * + * Controllers are ordered from best matching viewId (longest) to least matching one. + */ + public List getControllerDescriptors(String viewId) { + List controllers = viewControllerDescriptorsCache.get(viewId); + if (controllers == null) { + controllers = new ArrayList(); + List viewPatterns = findViewsWithPatternsThatMatch(viewId, viewPatternControllerDescriptors.keySet()); + for (String viewPattern : viewPatterns) { + List viewPatternControllers = viewPatternControllerDescriptors.get(viewPattern); + controllers.addAll(viewPatternControllers); + } + viewControllerDescriptorsCache.putIfAbsent(viewId, controllers); + } + return controllers; + } + + // Copied from ViewConfigStoreImpl : extract into utility method // + + private List findViewsWithPatternsThatMatch(String viewId, Set viewPatterns) { + List resultingViews = new ArrayList(); + for (String viewPattern : viewPatterns) { + if (viewPattern.endsWith("*")) { + String cutView = viewPattern.substring(0, viewPattern.length() - 1); + if (viewId.startsWith(cutView)) { + resultingViews.add(viewPattern); + } + } else { + if (viewPattern.equals(viewId)) { + resultingViews.add(viewPattern); + } + } + } + // sort the keys by length, longest is the most specific and so should go first + Collections.sort(resultingViews, StringLengthComparator.INSTANCE); + return resultingViews; + } + + private static class StringLengthComparator implements Comparator { + + @Override + public int compare(String o1, String o2) { + if (o1.length() > o2.length()) { + return -1; + } + if (o1.length() < o2.length()) { + return 1; + } + return 0; + } + + public static final StringLengthComparator INSTANCE = new StringLengthComparator(); + + } +} diff --git a/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigExtension.java b/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigExtension.java index 2cdc7a7..5356c13 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigExtension.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigExtension.java @@ -18,26 +18,20 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import javax.enterprise.event.Observes; -import javax.enterprise.inject.spi.AnnotatedMethod; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ProcessAnnotatedType; -import org.jboss.seam.faces.view.action.ViewActionBindingType; import org.jboss.solder.logging.Logger; /** * Extension that scans enums for view specific configuration - * + * * @author stuart * @author Brian Leathem */ @@ -45,9 +39,7 @@ public class ViewConfigExtension implements Extension { private transient final Logger log = Logger.getLogger(ViewConfigExtension.class); - private final Map> data = new HashMap>(); - - private final Map viewFieldValueToViewId = new HashMap(); + private final Map data = new HashMap(); public void processAnnotatedType(@Observes ProcessAnnotatedType event) { AnnotatedType tp = event.getAnnotatedType(); @@ -65,81 +57,43 @@ public void processAnnotatedType(@Observes ProcessAnnotatedType event) { log.warn("ViewConfig annotation should only be applied to interfaces, and [" + tp.getJavaClass() + "] is not an interface."); } else { - for (Class clazz : tp.getJavaClass().getClasses()) { + for (Class clazz : tp.getJavaClass().getClasses()) { for (Field enumm : clazz.getFields()) if (enumm.isAnnotationPresent(ViewPattern.class)) { ViewPattern viewConfig = enumm.getAnnotation(ViewPattern.class); - Set viewPattern = new HashSet(); + String viewId = viewConfig.value(); + ViewConfigDescriptor viewConfigDescriptor = data.get(viewId); + if (viewConfigDescriptor == null) { + viewConfigDescriptor = new ViewConfigDescriptor(viewId, getViewFieldValue(enumm)); + data.put(viewId, viewConfigDescriptor); + } for (Annotation a : enumm.getAnnotations()) { if (a.annotationType() != ViewPattern.class) { - viewPattern.add(a); + viewConfigDescriptor.addMetaData(a); } } - data.put(viewConfig.value(), viewPattern); - viewFieldValueToViewId.put(getViewFieldValue(enumm), viewConfig.value()); } } } } - // viewAction processing - for (final AnnotatedMethod m : tp.getMethods()) { - for (final Annotation annotation : m.getAnnotations()) { - if (annotation.annotationType().isAnnotationPresent(ViewActionBindingType.class)) { - Object viewField = getValue (annotation); - String viewId = viewFieldValueToViewId.get(viewField); - if (viewId == null) { - throw new IllegalArgumentException("Annotation "+annotation+" invalid : the view specified" - + "("+viewField+") doesn't correspond to a registered view in an annotated @ViewConfig class/interface."); - } - data.get(viewId).add(annotation); - } - } - } } /** - * Returns the value of a view field. - * @throws IllegalArgumentException if an error happens + * Returns the value of a view field. + * + * @throws IllegalArgumentException if an error happens */ private Object getViewFieldValue(Field enumm) { try { - return enumm.get(null); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Invalid view field "+enumm+" - error getting value "+e.toString(), e); - } catch (IllegalAccessException e) { - throw new IllegalArgumentException("Invalid view field "+enumm+" - error getting value "+e.toString(), e); - } - } - - public Map> getData() { - return Collections.unmodifiableMap(data); + return enumm.get(null); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid view field " + enumm + " - error getting value " + e.toString(), e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Invalid view field " + enumm + " - error getting value " + e.toString(), e); + } } - - // Utility methods for viewAction, TODO move this block out of this class - - /** - * Retrieve the view defined by the value() method in the annotation - * - * @param annotation - * @return the result of value() call - * @throws IllegalArgumentException if no value() method was found - */ - private Object getValue(Annotation annotation) { - Method valueMethod; - try { - valueMethod = annotation.annotationType().getDeclaredMethod("value"); - } catch (NoSuchMethodException ex) { - throw new IllegalArgumentException("value method must be declared and must resolve to a valid view", ex); - } catch (SecurityException ex) { - throw new IllegalArgumentException("value method must be accessible", ex); - } - try { - return valueMethod.invoke(annotation); - } catch (IllegalAccessException ex) { - throw new IllegalArgumentException("value method must be accessible", ex); - } catch (InvocationTargetException ex) { - throw new RuntimeException(ex); - } + public Map getData() { + return Collections.unmodifiableMap(data); } } diff --git a/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigStoreImpl.java b/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigStoreImpl.java index 44de799..84f89af 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigStoreImpl.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/config/ViewConfigStoreImpl.java @@ -22,7 +22,6 @@ import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -47,6 +46,7 @@ public class ViewConfigStoreImpl implements ViewConfigStore { private final ConcurrentHashMap, ConcurrentHashMap>> annotationCache = new ConcurrentHashMap, ConcurrentHashMap>>(); private final ConcurrentHashMap, ConcurrentHashMap>> qualifierCache = new ConcurrentHashMap, ConcurrentHashMap>>(); + private Map viewConfigDescriptors = new ConcurrentHashMap(); private final ConcurrentHashMap, ConcurrentHashMap> viewPatternDataByAnnotation = new ConcurrentHashMap, ConcurrentHashMap>(); private final ConcurrentHashMap, ConcurrentHashMap>> viewPatternDataByQualifier = new ConcurrentHashMap, ConcurrentHashMap>>(); @@ -58,9 +58,10 @@ public class ViewConfigStoreImpl implements ViewConfigStore { */ @Inject public void setup(ViewConfigExtension extension) { - for (Entry> e : extension.getData().entrySet()) { - for (Annotation i : e.getValue()) { - addAnnotationData(e.getKey(), i); + viewConfigDescriptors = extension.getData(); + for (ViewConfigDescriptor viewConfigDescriptor : viewConfigDescriptors.values()) { + for (Annotation metaData : viewConfigDescriptor.getMetaData()) { + addAnnotationData(viewConfigDescriptor.getViewId(), metaData); } } } @@ -132,6 +133,11 @@ public List getAllQualifierData(String viewId, Class getAllViewConfigDescriptors() { + return Collections.unmodifiableList(new ArrayList(viewConfigDescriptors.values())); + } + private List prepareAnnotationCache(String viewId, Class annotationType, ConcurrentHashMap, ConcurrentHashMap>> cache, ConcurrentHashMap, ConcurrentHashMap> viewPatternData) { diff --git a/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension index f37ca0c..b6c1bcb 100644 --- a/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension +++ b/impl/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -4,5 +4,6 @@ org.jboss.seam.faces.context.ViewScopedExtension org.jboss.seam.faces.context.RenderScopedExtension org.jboss.seam.faces.context.FacesAnnotationsAdapterExtension org.jboss.seam.faces.view.config.ViewConfigExtension +org.jboss.seam.faces.view.action.ViewControllerExtension org.jboss.seam.faces.projectstage.ProjectStageExtension From aacce800241f556cec22580bf884aa9df82b72bf Mon Sep 17 00:00:00 2001 From: gonzalad Date: Wed, 2 Nov 2011 20:48:48 +0100 Subject: [PATCH 3/7] Removed unneeded files and corrected ViewActionUtils --- .../seam/faces/view/action/Condition.java | 21 --------- .../seam/faces/view/action/Immediate.java | 22 ---------- .../seam/faces/view/action/OnPostback.java | 22 ---------- .../jboss/seam/faces/view/action/Phase.java | 23 ---------- .../seam/faces/view/action/PhaseDefault.java | 22 ---------- .../faces/view/action/ViewActionUtils.java | 43 ++++++++++--------- 6 files changed, 22 insertions(+), 131 deletions(-) delete mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/Condition.java delete mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/Immediate.java delete mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/OnPostback.java delete mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/Phase.java delete mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/PhaseDefault.java diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/Condition.java b/api/src/main/java/org/jboss/seam/faces/view/action/Condition.java deleted file mode 100644 index a5448e1..0000000 --- a/api/src/main/java/org/jboss/seam/faces/view/action/Condition.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.jboss.seam.faces.view.action; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Conditionnally executes a viewAction. - * - * TODO : refactor me, I'm not type-safe ! - * - * @author Adriàn Gonzalez - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface Condition { - public String condition = null; -} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/Immediate.java b/api/src/main/java/org/jboss/seam/faces/view/action/Immediate.java deleted file mode 100644 index 6f1a98d..0000000 --- a/api/src/main/java/org/jboss/seam/faces/view/action/Immediate.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.jboss.seam.faces.view.action; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.jboss.seam.faces.component.UIViewAction; - -/** - * Can be used instead of Phase. - * - * @see UIViewAction#isImmediate() - * @author Adriàn Gonzalez - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface Immediate { - public Boolean immediate = null; -} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/OnPostback.java b/api/src/main/java/org/jboss/seam/faces/view/action/OnPostback.java deleted file mode 100644 index 156a7d2..0000000 --- a/api/src/main/java/org/jboss/seam/faces/view/action/OnPostback.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.jboss.seam.faces.view.action; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.jboss.seam.faces.component.UIViewAction; - -/** - * Determines if viewAction is executed on postback. - * - * @see UIViewAction#isOnPostback() - * @author Adriàn Gonzalez - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface OnPostback { - public Boolean onPostback = false; -} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/Phase.java b/api/src/main/java/org/jboss/seam/faces/view/action/Phase.java deleted file mode 100644 index 28d0487..0000000 --- a/api/src/main/java/org/jboss/seam/faces/view/action/Phase.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.jboss.seam.faces.view.action; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.jboss.seam.faces.component.UIViewAction; -import org.jboss.seam.faces.event.PhaseIdType; - -/** - * Phase on which a viewAction is executed. - * - * @see UIViewAction#getPhase() - * @author Adriàn Gonzalez - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface Phase { - public PhaseIdType value() default PhaseIdType.RENDER_RESPONSE; -} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/PhaseDefault.java b/api/src/main/java/org/jboss/seam/faces/view/action/PhaseDefault.java deleted file mode 100644 index b21e929..0000000 --- a/api/src/main/java/org/jboss/seam/faces/view/action/PhaseDefault.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.jboss.seam.faces.view.action; - -import org.jboss.seam.faces.event.PhaseIdType; - -/** - * The Default values for Phase annotation, extracted as constant. - * - * @author Adriàn Gonzalez - */ -public class PhaseDefault { - public static final PhaseIdType DEFAULT_PHASE; - - static { - try { - DEFAULT_PHASE = (PhaseIdType) Phase.class.getMethod("value").getDefaultValue(); - } catch (NoSuchMethodException ex) { - throw new IllegalStateException("Error initialising values", ex); - } catch (SecurityException ex) { - throw new IllegalStateException("Error initialising values", ex); - } - } -} diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java index 33f44d7..6a2fea6 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java @@ -19,11 +19,12 @@ public class ViewActionUtils { private ViewActionUtils() { } - public static PhaseInstant getPhaseInstantOrDefault(List annotations, Object parentElement, PhaseInstant defaultInstant) { + public static PhaseInstant getPhaseInstantOrDefault(List annotations, Object parentElement, + PhaseInstant defaultInstant) { PhaseInstant phaseInstant = getPhaseInstant(annotations, parentElement); - return phaseInstant!=null ? phaseInstant : defaultInstant; + return phaseInstant != null ? phaseInstant : defaultInstant; } - + public static PhaseInstant getPhaseInstant(List annotations, Object parentElement) { Boolean before = null; PhaseId phaseId = null; @@ -44,43 +45,43 @@ public static PhaseInstant getPhaseInstant(List annotations, Object } else if (annotationType == ApplyRequestValues.class) { if (phaseId != null) { throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType - + " and " + phaseId + ")"); + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + + annotationType + " and " + phaseId + ")"); } phaseId = PhaseId.APPLY_REQUEST_VALUES; } else if (annotationType == ProcessValidations.class) { if (phaseId != null) { throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType - + " and " + phaseId + ")"); + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + + annotationType + " and " + phaseId + ")"); } phaseId = PhaseId.PROCESS_VALIDATIONS; } else if (annotationType == UpdateModelValues.class) { if (phaseId != null) { throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType - + " and " + phaseId + ")"); + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + + annotationType + " and " + phaseId + ")"); } phaseId = PhaseId.UPDATE_MODEL_VALUES; } else if (annotationType == InvokeApplication.class) { if (phaseId != null) { throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType - + " and " + phaseId + ")"); + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + + annotationType + " and " + phaseId + ")"); } phaseId = PhaseId.INVOKE_APPLICATION; } else if (annotationType == RenderResponse.class) { if (phaseId != null) { throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType - + " and " + phaseId + ")"); + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + + annotationType + " and " + phaseId + ")"); } phaseId = PhaseId.RENDER_RESPONSE; } else if (annotationType == BeforeRenderReponse.class) { if (phaseId != null) { throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType - + " and " + phaseId + ")"); + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + + annotationType + " and " + phaseId + ")"); } if (before != null) { throw new IllegalStateException("invalid " + parentElement @@ -88,11 +89,11 @@ public static PhaseInstant getPhaseInstant(List annotations, Object } phaseId = PhaseId.RENDER_RESPONSE; before = true; - } else if (annotationType == AfterRenderView.class) { + } else if (annotationType == AfterRenderResponse.class) { if (phaseId != null) { throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType - + " and " + phaseId + ")"); + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + + annotationType + " and " + phaseId + ")"); } if (before != null) { throw new IllegalStateException("invalid " + parentElement @@ -102,13 +103,13 @@ public static PhaseInstant getPhaseInstant(List annotations, Object before = false; } } - if (before==null && phaseId==null) { + if (before == null && phaseId == null) { return null; - } else if (before!=null && phaseId!=null) { + } else if (before != null && phaseId != null) { return new PhaseInstant(phaseId, before); } else { throw new IllegalStateException("invalid " + parentElement - + ". both phaseId and @Before/@After must be specified {phaseId: " + phaseId+", before: "+before+"}"); + + ". both phaseId and @Before/@After must be specified {phaseId: " + phaseId + ", before: " + before + "}"); } } } From 6247f6b94053eb4a1a11d58db144ed10ce6e6983 Mon Sep 17 00:00:00 2001 From: gonzalad Date: Wed, 2 Nov 2011 23:46:33 +0100 Subject: [PATCH 4/7] Removed @BeforeRenderResponse and @AfterRenderResponse Those where shortcut annotations duplicating @Before/@After @RenderResponse --- .../view/action/AfterRenderResponse.java | 22 -------- .../view/action/BeforeRenderReponse.java | 22 -------- .../examples/viewconfig/PageController.java | 14 ++--- .../examples/viewconfig/ViewController.java | 51 +++++++++++-------- .../faces/view/action/ViewActionUtils.java | 24 --------- 5 files changed, 38 insertions(+), 95 deletions(-) delete mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/AfterRenderResponse.java delete mode 100644 api/src/main/java/org/jboss/seam/faces/view/action/BeforeRenderReponse.java diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/AfterRenderResponse.java b/api/src/main/java/org/jboss/seam/faces/view/action/AfterRenderResponse.java deleted file mode 100644 index 5316d80..0000000 --- a/api/src/main/java/org/jboss/seam/faces/view/action/AfterRenderResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.jboss.seam.faces.view.action; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This lifecycle annotation can be used on viewController methods. - * - * These methods will be called after JSF RENDER_VIEW phase. - * Typically used for cleanup purposes - * Shortcut for @After @RenderResponse. - * - * @author Adriàn Gonzalez - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface AfterRenderResponse { -} diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/BeforeRenderReponse.java b/api/src/main/java/org/jboss/seam/faces/view/action/BeforeRenderReponse.java deleted file mode 100644 index bd6556c..0000000 --- a/api/src/main/java/org/jboss/seam/faces/view/action/BeforeRenderReponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.jboss.seam.faces.view.action; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This lifecycle annotation can be used on viewController methods. - * - * These methods will be called before JSF RENDER_VIEW phase. - * Typically used for view initialization purposes. - * Shortcut for @Before @RenderResponse. - * - * @author Adriàn Gonzalez - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface BeforeRenderReponse { -} diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java index c540cfa..9d6e704 100644 --- a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/PageController.java @@ -25,11 +25,12 @@ import javax.inject.Inject; import javax.inject.Named; +import org.jboss.seam.faces.event.qualifier.Before; +import org.jboss.seam.faces.event.qualifier.RenderResponse; import org.jboss.seam.faces.examples.viewconfig.MyAppViewConfig.Pages; import org.jboss.seam.faces.examples.viewconfig.model.Current; import org.jboss.seam.faces.examples.viewconfig.model.Item; import org.jboss.seam.faces.examples.viewconfig.model.ItemDao; -import org.jboss.seam.faces.view.action.BeforeRenderReponse; /** * @author Brian Leathem @@ -55,20 +56,21 @@ public Item getItem() { return item; } - @BeforeRenderReponse + @Before + @RenderResponse public void beforeRenderView(@Current Item item) { - System.out.println("beforeRenderView called "+item); + System.out.println("beforeRenderView called " + item); } public void viewAction(@Current Item item) { - System.out.println("viewAction "+item); + System.out.println("viewAction " + item); } @MyViewAction(Pages.ITEM) public void viewActionBindingType(@Current Item item) { - System.out.println("viewActionBindingType "+item); + System.out.println("viewActionBindingType " + item); } - + public void setItem(Item item) { this.item = item; } diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewController.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewController.java index 951c922..bb7bc22 100644 --- a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewController.java +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewController.java @@ -10,58 +10,67 @@ import org.jboss.seam.faces.event.qualifier.ProcessValidations; import org.jboss.seam.faces.event.qualifier.RenderResponse; import org.jboss.seam.faces.event.qualifier.UpdateModelValues; -import org.jboss.seam.faces.view.action.BeforeRenderReponse; public class ViewController { - @Before @ApplyRequestValues + @Before + @ApplyRequestValues public void beforeApplyRequestValues() { - addFacesMessage(this.getClass().getSimpleName()+".beforeApplyRequestValues was called"); + addFacesMessage(this.getClass().getSimpleName() + ".beforeApplyRequestValues was called"); } - @After @ApplyRequestValues + @After + @ApplyRequestValues public void afterApplyRequestValues() { - addFacesMessage(this.getClass().getSimpleName()+".afterApplyRequestValues was called"); + addFacesMessage(this.getClass().getSimpleName() + ".afterApplyRequestValues was called"); } - @Before @ProcessValidations + @Before + @ProcessValidations public void beforeProcessValidations() { - addFacesMessage(this.getClass().getSimpleName()+".beforeProcessValidations was called"); + addFacesMessage(this.getClass().getSimpleName() + ".beforeProcessValidations was called"); } - @After @ProcessValidations + @After + @ProcessValidations public void afterProcessValidations() { - addFacesMessage(this.getClass().getSimpleName()+".afterProcessValidations was called"); + addFacesMessage(this.getClass().getSimpleName() + ".afterProcessValidations was called"); } - @Before @UpdateModelValues + @Before + @UpdateModelValues public void beforeUpdateModelValues() { - addFacesMessage(this.getClass().getSimpleName()+".beforeUpdateModelValues was called"); + addFacesMessage(this.getClass().getSimpleName() + ".beforeUpdateModelValues was called"); } - @After @UpdateModelValues + @After + @UpdateModelValues public void afterUpdateModelValues() { - addFacesMessage(this.getClass().getSimpleName()+".afterUpdateModelValues was called"); + addFacesMessage(this.getClass().getSimpleName() + ".afterUpdateModelValues was called"); } - @Before @InvokeApplication + @Before + @InvokeApplication public void beforeInvokeApplication() { - addFacesMessage(this.getClass().getSimpleName()+".beforeInvokeApplication was called"); + addFacesMessage(this.getClass().getSimpleName() + ".beforeInvokeApplication was called"); } - @After @InvokeApplication + @After + @InvokeApplication public void afterInvokeApplication() { - addFacesMessage(this.getClass().getSimpleName()+".afterInvokeApplication was called"); + addFacesMessage(this.getClass().getSimpleName() + ".afterInvokeApplication was called"); } - @BeforeRenderReponse + @Before + @RenderResponse public void beforeRenderResponse() { - addFacesMessage(this.getClass().getSimpleName()+".beforeRenderResponse was called"); + addFacesMessage(this.getClass().getSimpleName() + ".beforeRenderResponse was called"); } - @After @RenderResponse + @After + @RenderResponse public void afterRenderResponse() { - addFacesMessage(this.getClass().getSimpleName()+".RenderResponse was called"); + addFacesMessage(this.getClass().getSimpleName() + ".RenderResponse was called"); } private void addFacesMessage(String message) { diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java index 6a2fea6..734ce40 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java @@ -77,30 +77,6 @@ public static PhaseInstant getPhaseInstant(List annotations, Object + annotationType + " and " + phaseId + ")"); } phaseId = PhaseId.RENDER_RESPONSE; - } else if (annotationType == BeforeRenderReponse.class) { - if (phaseId != null) { - throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" - + annotationType + " and " + phaseId + ")"); - } - if (before != null) { - throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples @Before/@After and " + annotationType); - } - phaseId = PhaseId.RENDER_RESPONSE; - before = true; - } else if (annotationType == AfterRenderResponse.class) { - if (phaseId != null) { - throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" - + annotationType + " and " + phaseId + ")"); - } - if (before != null) { - throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples @Before/@After and " + annotationType); - } - phaseId = PhaseId.RENDER_RESPONSE; - before = false; } } if (before == null && phaseId == null) { From 24cc75d898b86c6eea92805e54ea74e5e1addce0 Mon Sep 17 00:00:00 2001 From: gonzalad Date: Thu, 3 Nov 2011 09:00:02 +0100 Subject: [PATCH 5/7] Add attributes phase, before to @ViewAction And removed usage of @Before/@After and phase annotation with @ViewAction. Attribute usage is more explicit here. --- .../seam/faces/view/action/ViewAction.java | 15 +++- .../examples/viewconfig/MyAppViewConfig.java | 4 -- .../faces/view/action/ViewActionUtils.java | 68 +++++++++++-------- .../view/action/ViewControllerStore.java | 6 +- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java b/api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java index 96c9131..aeb8ea3 100644 --- a/api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java +++ b/api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java @@ -6,10 +6,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.jboss.seam.faces.event.qualifier.RenderResponse; + /** * The EL MethodExpression is executed when this annotation is applied to a ViewConfig. * - * The MethodExpression is called before RENDER_RESPONSE phase. + * The MethodExpression is called by default before RENDER_RESPONSE phase. You can change this + * behaviour by using phase and before fields. * * @author Adriàn Gonzalez */ @@ -21,4 +24,14 @@ * El MethodExpression */ String value(); + + /** + * On which JSF phase must this viewAction be executed ? + */ + Class phase() default RenderResponse.class; + + /** + * Is this viewAction executed before phase ? + */ + boolean before() default true; } diff --git a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyAppViewConfig.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyAppViewConfig.java index dcc26d0..c2700e5 100644 --- a/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyAppViewConfig.java +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyAppViewConfig.java @@ -16,8 +16,6 @@ */ package org.jboss.seam.faces.examples.viewconfig; -import org.jboss.seam.faces.event.qualifier.ApplyRequestValues; -import org.jboss.seam.faces.event.qualifier.Before; import org.jboss.seam.faces.examples.viewconfig.security.Admin; import org.jboss.seam.faces.examples.viewconfig.security.Owner; import org.jboss.seam.faces.rewrite.FacesRedirect; @@ -45,8 +43,6 @@ static enum Pages { @ViewPattern("/item.xhtml") @ViewController(PageController.class) @Owner - @ViewAction("#{pageController.viewAction(pageController.item)}") - @Before @ApplyRequestValues ITEM, @ViewPattern("/viewcontroller.xhtml") diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java index 734ce40..938ceb0 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java @@ -42,41 +42,13 @@ public static PhaseInstant getPhaseInstant(List annotations, Object + ". Cannot be annotated simultaneously with multiples @Before and @After"); } before = false; - } else if (annotationType == ApplyRequestValues.class) { + } else if (isPhaseQualifier(annotationType)) { if (phaseId != null) { throw new IllegalStateException("invalid " + parentElement + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + annotationType + " and " + phaseId + ")"); } - phaseId = PhaseId.APPLY_REQUEST_VALUES; - } else if (annotationType == ProcessValidations.class) { - if (phaseId != null) { - throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" - + annotationType + " and " + phaseId + ")"); - } - phaseId = PhaseId.PROCESS_VALIDATIONS; - } else if (annotationType == UpdateModelValues.class) { - if (phaseId != null) { - throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" - + annotationType + " and " + phaseId + ")"); - } - phaseId = PhaseId.UPDATE_MODEL_VALUES; - } else if (annotationType == InvokeApplication.class) { - if (phaseId != null) { - throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" - + annotationType + " and " + phaseId + ")"); - } - phaseId = PhaseId.INVOKE_APPLICATION; - } else if (annotationType == RenderResponse.class) { - if (phaseId != null) { - throw new IllegalStateException("invalid " + parentElement - + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" - + annotationType + " and " + phaseId + ")"); - } - phaseId = PhaseId.RENDER_RESPONSE; + phaseId = convert(annotationType); } } if (before == null && phaseId == null) { @@ -88,4 +60,40 @@ public static PhaseInstant getPhaseInstant(List annotations, Object + ". both phaseId and @Before/@After must be specified {phaseId: " + phaseId + ", before: " + before + "}"); } } + + /** + * Converts the annotations from package org.jboss.seam.faces.event.qualifier to their corresponding JSF PhaseId. + * + * @throws IllegalArgumentException if annotationType isn't a valid Jsf annotation. + */ + public static PhaseId convert(Class annotationType) { + PhaseId phaseId; + if (annotationType == ApplyRequestValues.class) { + phaseId = PhaseId.APPLY_REQUEST_VALUES; + } else if (annotationType == ProcessValidations.class) { + phaseId = PhaseId.PROCESS_VALIDATIONS; + } else if (annotationType == UpdateModelValues.class) { + phaseId = PhaseId.UPDATE_MODEL_VALUES; + } else if (annotationType == InvokeApplication.class) { + phaseId = PhaseId.INVOKE_APPLICATION; + } else if (annotationType == RenderResponse.class) { + phaseId = PhaseId.RENDER_RESPONSE; + } else { + throw new IllegalArgumentException("Annotation " + annotationType + " doesn't correspond to valid a Jsf phase."); + } + return phaseId; + } + + /** + * Returns true if annotationType is a valid JSF annotation + */ + public static boolean isPhaseQualifier(Class annotationType) { + if (annotationType == ApplyRequestValues.class || annotationType == ProcessValidations.class + || annotationType == UpdateModelValues.class || annotationType == InvokeApplication.class + || annotationType == RenderResponse.class) { + return true; + } else { + return false; + } + } } diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java index 362026b..2de5354 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java @@ -84,14 +84,10 @@ private void registerViewActions(ViewConfigStore viewConfigStore, BeanManager be List viewConfigDescriptors = viewConfigStore.getAllViewConfigDescriptors(); for (ViewConfigDescriptor viewConfigDescriptor : viewConfigDescriptors) { ViewAction viewAction = viewConfigDescriptor.getMetaData(ViewAction.class); - // TODO : don't know using annotations @Before / @RenderResponse / ... with @ViewAction - // is a good idea (possible collision with future annotations ?) - // would it be better to add attributes to @ViewAction ? if (viewAction != null) { ViewControllerDescriptor viewControllerDescriptor = new ViewControllerDescriptor( viewConfigDescriptor.getViewId(), beanManager); - PhaseInstant phaseInstant = ViewActionUtils.getPhaseInstantOrDefault(viewConfigDescriptor.getMetaData(), - viewConfigDescriptor, BEFORE_RENDER_RESPONSE); + PhaseInstant phaseInstant = new PhaseInstant(ViewActionUtils.convert(viewAction.phase()), viewAction.before()); viewControllerDescriptor.addMethod(phaseInstant, new ViewControllerDescriptor.MethodExpressionInvoker( viewAction.value())); addControllerDescriptor(viewControllerDescriptor); From e5c770d82e0da82d281ed7856fd2840b4802eec0 Mon Sep 17 00:00:00 2001 From: gonzalad Date: Thu, 3 Nov 2011 23:27:33 +0100 Subject: [PATCH 6/7] Added unit tests --- .../view/config/ViewConfigDescriptor.java | 4 + .../view/action/ViewControllerDescriptor.java | 4 + .../view/action/ViewControllerStore.java | 2 - .../test/weld/config/ViewConfigStoreTest.java | 1 + .../test/weld/config/ViewConfigTest.java | 60 +++++++++++++++ .../action/ViewControllerDescriptorTest.java | 77 +++++++++++++++++++ .../AfterInvokeApplicationViewAction.java | 19 +++++ .../BeforeRenderResponseViewAction.java | 15 ++++ .../action/annotation/ClientController.java | 17 ++++ .../action/annotation/CountryController.java | 15 ++++ .../action/annotation/ViewConfigEnum.java | 51 ++++++++++++ 11 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/ViewControllerDescriptorTest.java create mode 100644 testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/AfterInvokeApplicationViewAction.java create mode 100644 testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/BeforeRenderResponseViewAction.java create mode 100644 testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/ClientController.java create mode 100644 testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/CountryController.java create mode 100644 testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/ViewConfigEnum.java diff --git a/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigDescriptor.java b/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigDescriptor.java index 4a70c79..bba2d1e 100644 --- a/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigDescriptor.java +++ b/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigDescriptor.java @@ -103,4 +103,8 @@ public List getAllQualifierData(Class metaData = metaDataByQualifier.get(qualifier); return metaData!=null ? Collections.unmodifiableList(metaData) : Collections.EMPTY_LIST; } + + public String toString() { + return super.toString()+"{viewId="+getViewId()+"}"; + } } diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java index 68b813b..74645d7 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java @@ -301,6 +301,10 @@ public static class MethodExpressionInvoker implements ViewActionStrategy { public MethodExpressionInvoker(String methodExpressionAsString) { this.methodExpressionAsString = methodExpressionAsString; } + + public String getMethodExpressionString() { + return methodExpressionAsString; + } @Override public Object execute() { diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java index 2de5354..becc122 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java @@ -1,7 +1,5 @@ package org.jboss.seam.faces.view.action; -import static org.jboss.seam.faces.view.action.PhaseInstant.BEFORE_RENDER_RESPONSE; - import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collections; diff --git a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/config/ViewConfigStoreTest.java b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/config/ViewConfigStoreTest.java index 2e78773..9a5c3e0 100644 --- a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/config/ViewConfigStoreTest.java +++ b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/config/ViewConfigStoreTest.java @@ -20,6 +20,7 @@ import java.util.List; import junit.framework.Assert; + import org.jboss.seam.faces.test.weld.config.annotation.Icon; import org.jboss.seam.faces.test.weld.config.annotation.IconLiteral; import org.jboss.seam.faces.test.weld.config.annotation.QualifiedIcon; diff --git a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/config/ViewConfigTest.java b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/config/ViewConfigTest.java index ecb9848..6eee302 100644 --- a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/config/ViewConfigTest.java +++ b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/config/ViewConfigTest.java @@ -16,16 +16,24 @@ */ package org.jboss.seam.faces.test.weld.config; +import java.lang.annotation.Annotation; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.inject.Inject; import junit.framework.Assert; + import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; +import org.jboss.seam.faces.security.RestrictAtPhase; import org.jboss.seam.faces.test.weld.config.annotation.Icon; import org.jboss.seam.faces.test.weld.config.annotation.IconLiteral; +import org.jboss.seam.faces.test.weld.config.annotation.QualifiedIcon; +import org.jboss.seam.faces.test.weld.config.annotation.RestrictedAtRestoreView; import org.jboss.seam.faces.test.weld.config.annotation.ViewConfigEnum; +import org.jboss.seam.faces.view.config.ViewConfigDescriptor; import org.jboss.seam.faces.view.config.ViewConfigStore; import org.jboss.seam.faces.view.config.ViewConfigStoreImpl; import org.jboss.shrinkwrap.api.Archive; @@ -84,4 +92,56 @@ public void testViewConfigStore() { Assert.assertEquals("default.gif", dlist.get(0).value()); } + + @Test + public void testViewConfigDescriptor() { + + Map descriptorMap = new HashMap(); + for (ViewConfigDescriptor descriptor : store.getAllViewConfigDescriptors()) { + Assert.assertFalse("duplicated viewId "+descriptor.getViewId(), descriptorMap.containsKey(descriptor.getViewId())); + descriptorMap.put(descriptor.getViewId(), descriptor); + } + + String viewId = "/happy/done.xhtml"; + ViewConfigDescriptor descriptor = descriptorMap.get(viewId); + Assert.assertEquals(viewId, descriptor.getViewId()); + Assert.assertEquals(ViewConfigEnum.Pages.HAPPY_DONE, descriptor.getValues().get(0)); + Icon data = descriptor.getMetaData(Icon.class); + Assert.assertEquals("finished.gif", data.value()); + + viewId = "/happy/*"; + descriptor = descriptorMap.get(viewId); + Assert.assertEquals(viewId, descriptor.getViewId()); + Assert.assertEquals(ViewConfigEnum.Pages.HAPPY, descriptor.getValues().get(0)); + data = descriptor.getMetaData(Icon.class); + Assert.assertEquals("happy.gif", data.value()); + + viewId = "/sad/*"; + descriptor = descriptorMap.get(viewId); + Assert.assertEquals(viewId, descriptor.getViewId()); + Assert.assertEquals(ViewConfigEnum.Pages.SAD, descriptor.getValues().get(0)); + data = descriptor.getMetaData(Icon.class); + Assert.assertEquals("sad.gif", data.value()); + + viewId = "/*"; + descriptor = descriptorMap.get(viewId); + Assert.assertEquals(viewId, descriptor.getViewId()); + Assert.assertEquals(ViewConfigEnum.Pages.DEFAULT, descriptor.getValues().get(0)); + data = descriptor.getMetaData(Icon.class); + Assert.assertEquals("default.gif", data.value()); + + QualifiedIcon qualifiedData; + descriptor = descriptorMap.get("/qualified/*"); + qualifiedData = descriptor.getMetaData(QualifiedIcon.class); + Assert.assertEquals(ViewConfigEnum.Pages.QUALIFIED, descriptor.getValues().get(0)); + Assert.assertEquals("qualified.gif", qualifiedData.value()); + + descriptor = descriptorMap.get("/qualified/yes.xhtml"); + Assert.assertEquals(ViewConfigEnum.Pages.QUALIFIED_YES, descriptor.getValues().get(0)); + List annotations = descriptor.getAllQualifierData(RestrictAtPhase.class); + Assert.assertEquals(1, annotations.size()); + Assert.assertTrue(RestrictedAtRestoreView.class.isAssignableFrom(annotations.get(0).getClass())); + + Assert.assertEquals(6, descriptorMap.size()); + } } diff --git a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/ViewControllerDescriptorTest.java b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/ViewControllerDescriptorTest.java new file mode 100644 index 0000000..1046724 --- /dev/null +++ b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/ViewControllerDescriptorTest.java @@ -0,0 +1,77 @@ +package org.jboss.seam.faces.test.weld.view.action; + +import java.util.List; +import java.util.Map; + +import javax.faces.event.PhaseId; +import javax.inject.Inject; + +import junit.framework.Assert; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.seam.faces.test.weld.view.action.annotation.AfterInvokeApplicationViewAction; +import org.jboss.seam.faces.test.weld.view.action.annotation.BeforeRenderResponseViewAction; +import org.jboss.seam.faces.test.weld.view.action.annotation.ClientController; +import org.jboss.seam.faces.test.weld.view.action.annotation.CountryController; +import org.jboss.seam.faces.test.weld.view.action.annotation.ViewConfigEnum; +import org.jboss.seam.faces.view.action.PhaseInstant; +import org.jboss.seam.faces.view.action.ViewActionStrategy; +import org.jboss.seam.faces.view.action.ViewControllerDescriptor; +import org.jboss.seam.faces.view.action.ViewControllerExtension; +import org.jboss.seam.faces.view.action.ViewControllerStore; +import org.jboss.seam.faces.view.config.ViewConfigStoreImpl; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ArchivePaths; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.ByteArrayAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(Arquillian.class) +public class ViewControllerDescriptorTest { + @Deployment + public static Archive createTestArchive() { + JavaArchive archive = ShrinkWrap.create(JavaArchive.class).addClass(ViewConfigStoreImpl.class) + .addClass(ViewControllerStore.class).addClass(ViewControllerExtension.class) + .addClass(AfterInvokeApplicationViewAction.class).addClass(BeforeRenderResponseViewAction.class) + .addClass(ClientController.class).addClass(CountryController.class).addClass(ViewConfigEnum.class) + .addPackage(ViewConfigEnum.class.getPackage()) + .addAsManifestResource(new ByteArrayAsset(new byte[0]), ArchivePaths.create("beans.xml")); + return archive; + } + + @Inject + private ViewControllerStore store; + + @Test + public void testGetControllerDescriptors() { + + List descriptors = store.getControllerDescriptors("/client/done.xhtml"); + Assert.assertEquals(2, descriptors.size()); + ViewControllerDescriptor descriptor = descriptors.get(0); + Assert.assertEquals("/client/*", descriptor.getViewId()); + Map> phaseMethods = descriptor.getPhaseMethods(); + List viewActions = phaseMethods.get(PhaseInstant.BEFORE_RENDER_RESPONSE); + Assert.assertEquals(1, viewActions.size()); + ViewActionStrategy actionStrategy = viewActions.get(0); + Assert.assertEquals("#{clientController.viewAction}", + ((ViewControllerDescriptor.MethodExpressionInvoker) actionStrategy).getMethodExpressionString()); + descriptor = descriptors.get(1); + Assert.assertEquals("/client/*", descriptor.getViewId()); + phaseMethods = descriptor.getPhaseMethods(); + Assert.assertEquals(2, phaseMethods.size()); + Assert.assertEquals(1, phaseMethods.get(PhaseInstant.BEFORE_RENDER_RESPONSE).size()); + Assert.assertEquals(1, phaseMethods.get(new PhaseInstant(PhaseId.INVOKE_APPLICATION, false)).size()); + + descriptors = store.getControllerDescriptors("/country/done.xhtml"); + Assert.assertEquals(1, descriptors.size()); + descriptor = descriptors.get(0); + Assert.assertEquals("/country/*", descriptor.getViewId()); + Assert.assertEquals(CountryController.class, descriptor.getViewControllerClass()); + phaseMethods = descriptor.getPhaseMethods(); + Assert.assertEquals(1, phaseMethods.size()); + Assert.assertEquals(1, phaseMethods.get(PhaseInstant.BEFORE_RENDER_RESPONSE).size()); + } +} diff --git a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/AfterInvokeApplicationViewAction.java b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/AfterInvokeApplicationViewAction.java new file mode 100644 index 0000000..60a1ef2 --- /dev/null +++ b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/AfterInvokeApplicationViewAction.java @@ -0,0 +1,19 @@ +package org.jboss.seam.faces.test.weld.view.action.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.jboss.seam.faces.event.qualifier.After; +import org.jboss.seam.faces.event.qualifier.InvokeApplication; +import org.jboss.seam.faces.view.action.ViewActionBindingType; + +@ViewActionBindingType +@After +@InvokeApplication +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +public @interface AfterInvokeApplicationViewAction { + ViewConfigEnum.Pages value(); +} diff --git a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/BeforeRenderResponseViewAction.java b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/BeforeRenderResponseViewAction.java new file mode 100644 index 0000000..168b393 --- /dev/null +++ b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/BeforeRenderResponseViewAction.java @@ -0,0 +1,15 @@ +package org.jboss.seam.faces.test.weld.view.action.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.jboss.seam.faces.view.action.ViewActionBindingType; + +@ViewActionBindingType +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +public @interface BeforeRenderResponseViewAction { + ViewConfigEnum.Pages value(); +} diff --git a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/ClientController.java b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/ClientController.java new file mode 100644 index 0000000..b6cd8dd --- /dev/null +++ b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/ClientController.java @@ -0,0 +1,17 @@ +package org.jboss.seam.faces.test.weld.view.action.annotation; + +import javax.inject.Named; + +@Named +public class ClientController { + public void viewAction() { + } + + @BeforeRenderResponseViewAction(ViewConfigEnum.Pages.CLIENTS) + public void beforeRenderResponse() { + } + + @AfterInvokeApplicationViewAction(ViewConfigEnum.Pages.CLIENTS) + public void afterInvokeApplication() { + } +} diff --git a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/CountryController.java b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/CountryController.java new file mode 100644 index 0000000..af6b5a4 --- /dev/null +++ b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/CountryController.java @@ -0,0 +1,15 @@ +package org.jboss.seam.faces.test.weld.view.action.annotation; + +import javax.inject.Named; + +import org.jboss.seam.faces.event.qualifier.Before; +import org.jboss.seam.faces.event.qualifier.RenderResponse; + +@Named +public class CountryController { + + @Before + @RenderResponse + public void beforeRenderResponse() { + } +} diff --git a/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/ViewConfigEnum.java b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/ViewConfigEnum.java new file mode 100644 index 0000000..39a0573 --- /dev/null +++ b/testsuite/src/test/java/org/jboss/seam/faces/test/weld/view/action/annotation/ViewConfigEnum.java @@ -0,0 +1,51 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2011, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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.jboss.seam.faces.test.weld.view.action.annotation; + +import org.jboss.seam.faces.event.PhaseIdType; +import org.jboss.seam.faces.security.RestrictAtPhase; +import org.jboss.seam.faces.view.action.ViewAction; +import org.jboss.seam.faces.view.action.ViewController; +import org.jboss.seam.faces.view.config.ViewConfig; +import org.jboss.seam.faces.view.config.ViewPattern; + +@ViewConfig +public interface ViewConfigEnum { + static enum Pages { + @ViewPattern("/*") + DEFAULT, + + @ViewPattern("/client/*") + @ViewAction("#{clientController.viewAction}") + CLIENTS, + + @ViewPattern("/country/*") + @ViewController(CountryController.class) + COUNTRIES(), + + @ViewPattern("/client/done.xhtml") + CLIENT_CONFIRMED(), + + @ViewPattern("/qualified/*") + @RestrictAtPhase(PhaseIdType.INVOKE_APPLICATION) + QUALIFIED, + + @ViewPattern("/qualified/yes.xhtml") + QUALIFIED_YES; + + } +} From 4e06e40655bd9820dc5319ab2483e37eaf397986 Mon Sep 17 00:00:00 2001 From: gonzalad Date: Thu, 3 Nov 2011 23:38:27 +0100 Subject: [PATCH 7/7] Removed commented / unused code --- .../view/action/ViewActionPhaseListener.java | 11 --- .../view/action/ViewControllerDescriptor.java | 68 +++---------------- 2 files changed, 8 insertions(+), 71 deletions(-) diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java index c2d05b9..1b0766d 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java @@ -39,12 +39,6 @@ public void observerBeforePhase(@Observes @Before PhaseEvent event) { ViewControllerDescriptor viewControllerDescriptor = viewControllers.get(i); viewControllerDescriptor.executeBeforePhase(event.getPhaseId()); } - if (phaseId == PhaseId.RENDER_RESPONSE) { - for (int i = viewControllers.size(); --i >= 0;) { - ViewControllerDescriptor viewControllerDescriptor = viewControllers.get(i); - viewControllerDescriptor.executeBeforeRenderView(); - } - } } public void observerAfterPhase(@Observes @After PhaseEvent event) { @@ -55,10 +49,5 @@ public void observerAfterPhase(@Observes @After PhaseEvent event) { for (ViewControllerDescriptor viewControllerDescriptor : viewControllers) { viewControllerDescriptor.executeAfterPhase(event.getPhaseId()); } - if (phaseId == PhaseId.RENDER_RESPONSE) { - for (ViewControllerDescriptor viewControllerDescriptor : viewControllers) { - viewControllerDescriptor.executeAfterRenderView(); - } - } } } diff --git a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java index 74645d7..d63c83f 100644 --- a/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java @@ -35,11 +35,6 @@ public class ViewControllerDescriptor { private String viewId; private Class viewControllerClass; private BeanManager beanManager; - - // call this one in an additional actionListener (faces-config.xml) - private List beforePageActionMethods = new ArrayList(); - private List beforeRenderViewMethods = new ArrayList(); - private List afterRenderViewMethods = new ArrayList(); private Map> phaseMethods = new HashMap>(); /** @@ -77,12 +72,12 @@ private void registerCallbacks() { Class current = viewControllerClass; while (current != Object.class) { for (Method method : current.getDeclaredMethods()) { -// if (method.isAnnotationPresent(BeforeRenderView.class)) { -// beforeRenderViewMethods.add(new MethodInvoker(method, beanManager)); -// } -// if (method.isAnnotationPresent(AfterRenderView.class)) { -// afterRenderViewMethods.add(new MethodInvoker(method, beanManager)); -// } + // if (method.isAnnotationPresent(BeforeRenderView.class)) { + // beforeRenderViewMethods.add(new MethodInvoker(method, beanManager)); + // } + // if (method.isAnnotationPresent(AfterRenderView.class)) { + // afterRenderViewMethods.add(new MethodInvoker(method, beanManager)); + // } PhaseInstant phaseInstant = ViewActionUtils.getPhaseInstant(Arrays.asList(method.getAnnotations()), method); if (phaseInstant != null) { addMethod(phaseInstant, new MethodInvoker(method, beanManager)); @@ -110,50 +105,6 @@ public void executeAfterPhase(PhaseId phaseId) { } } - public void executeBeforePageAction() { - throw new NoSuchMethodError("unimplemented"); - } - - public void executeBeforeRenderView() { - for (ViewActionStrategy invoker : getBeforeRenderViewMethods()) { - invoker.execute(); - } - } - - public void executeAfterRenderView() { - for (ViewActionStrategy invoker : getAfterRenderViewMethods()) { - invoker.execute(); - } - } - - public List getBeforePageActionMethods() { - return beforePageActionMethods; - } - - public void setBeforePageActionMethods(List beforePageActionMethods) { - this.beforePageActionMethods = beforePageActionMethods; - } - - public void addBeforeRenderViewMethod(ViewActionStrategy beforeRenderViewMethod) { - beforeRenderViewMethods.add(beforeRenderViewMethod); - } - - public List getBeforeRenderViewMethods() { - return beforeRenderViewMethods; - } - - public void setBeforeRenderViewMethods(List beforeRenderViewMethods) { - this.beforeRenderViewMethods = beforeRenderViewMethods; - } - - public List getAfterRenderViewMethods() { - return afterRenderViewMethods; - } - - public void setAfterRenderViewMethods(List afterRenderViewMethods) { - this.afterRenderViewMethods = afterRenderViewMethods; - } - public void addMethod(PhaseInstant phaseInstant, ViewActionStrategy method) { List methods = phaseMethods.get(phaseInstant); if (methods == null) { @@ -191,10 +142,7 @@ public void setViewControllerClass(Class viewControllerClass) { public String toString() { StringBuilder builder = new StringBuilder(super.toString()); builder.append("{viewId: ").append(getViewId()).append(", viewControllerClass: ").append(getViewControllerClass()) - .append(", beforePageActionMethods: {").append(getBeforePageActionMethods()) - .append("}, beforeRenderViewMethods: ").append(getAfterRenderViewMethods()) - .append("}, afterRenderViewMethods: {").append(getAfterRenderViewMethods()).append("}, phaseMethods: {") - .append(getPhaseMethods()).append("}}"); + .append("}, phaseMethods: {").append(getPhaseMethods()).append("}}"); return builder.toString(); } @@ -301,7 +249,7 @@ public static class MethodExpressionInvoker implements ViewActionStrategy { public MethodExpressionInvoker(String methodExpressionAsString) { this.methodExpressionAsString = methodExpressionAsString; } - + public String getMethodExpressionString() { return methodExpressionAsString; }