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/ViewAction.java b/api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java new file mode 100644 index 0000000..aeb8ea3 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/ViewAction.java @@ -0,0 +1,37 @@ +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.RenderResponse; + +/** + * The EL MethodExpression is executed when this annotation is applied to a ViewConfig. + * + * 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 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ViewAction { + /** + * 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/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..6f54760 --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/action/ViewActionBindingType.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; + +/** + * Applied to an annotation to indicate that it is a faces action binding type. + * + * 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 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ViewActionBindingType { +} 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 : + *

+ * + * @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..bba2d1e --- /dev/null +++ b/api/src/main/java/org/jboss/seam/faces/view/config/ViewConfigDescriptor.java @@ -0,0 +1,110 @@ +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; + } + + public String toString() { + return super.toString()+"{viewId="+getViewId()+"}"; + } +} 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/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/MyAppViewConfig.java b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyAppViewConfig.java index f74ada0..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 @@ -22,6 +22,8 @@ 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 +41,21 @@ static enum Pages { @UrlMapping(pattern = "/item/#{id}/") @ViewPattern("/item.xhtml") + @ViewController(PageController.class) @Owner 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 new file mode 100644 index 0000000..8725143 --- /dev/null +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/MyViewAction.java @@ -0,0 +1,15 @@ +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(); +} 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..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,6 +25,9 @@ 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; @@ -53,6 +56,21 @@ public Item getItem() { return item; } + @Before + @RenderResponse + 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 viewActionBindingType(@Current Item 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/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..bb7bc22 --- /dev/null +++ b/examples/viewconfig/src/main/java/org/jboss/seam/faces/examples/viewconfig/ViewController.java @@ -0,0 +1,80 @@ +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; + +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"); + } + + @Before + @RenderResponse + 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 new file mode 100644 index 0000000..1b0766d --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionPhaseListener.java @@ -0,0 +1,53 @@ +package org.jboss.seam.faces.view.action; + +import java.util.List; + +import javax.enterprise.event.Observes; +import javax.faces.event.PhaseEvent; +import javax.faces.event.PhaseId; +import javax.inject.Inject; + +import org.jboss.seam.faces.event.qualifier.After; +import org.jboss.seam.faces.event.qualifier.Before; +import org.jboss.solder.logging.Logger; + +/** + * 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 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; + } + List viewControllers = viewControllerStore.getControllerDescriptors(event.getFacesContext() + .getViewRoot().getViewId()); + for (int i = viewControllers.size(); --i >= 0;) { + ViewControllerDescriptor viewControllerDescriptor = viewControllers.get(i); + viewControllerDescriptor.executeBeforePhase(event.getPhaseId()); + } + } + + 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()); + } + } +} 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..938ceb0 --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewActionUtils.java @@ -0,0 +1,99 @@ +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 (isPhaseQualifier(annotationType)) { + if (phaseId != null) { + throw new IllegalStateException("invalid " + parentElement + + ". Cannot be annotated simultaneously with multiples JSF lifecycle annotations (" + + annotationType + " and " + phaseId + ")"); + } + phaseId = convert(annotationType); + } + } + 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 + "}"); + } + } + + /** + * 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/ViewControllerDescriptor.java b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java new file mode 100644 index 0000000..d63c83f --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerDescriptor.java @@ -0,0 +1,267 @@ +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; + 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 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("}, 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; + } + + public String getMethodExpressionString() { + return 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..becc122 --- /dev/null +++ b/impl/src/main/java/org/jboss/seam/faces/view/action/ViewControllerStore.java @@ -0,0 +1,166 @@ +package org.jboss.seam.faces.view.action; + +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); + if (viewAction != null) { + ViewControllerDescriptor viewControllerDescriptor = new ViewControllerDescriptor( + viewConfigDescriptor.getViewId(), beanManager); + PhaseInstant phaseInstant = new PhaseInstant(ViewActionUtils.convert(viewAction.phase()), viewAction.before()); + 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 8d9cc59..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 @@ -20,9 +20,7 @@ import java.lang.reflect.Field; 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.AnnotatedType; @@ -33,7 +31,7 @@ /** * Extension that scans enums for view specific configuration - * + * * @author stuart * @author Brian Leathem */ @@ -41,7 +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 data = new HashMap(); public void processAnnotatedType(@Observes ProcessAnnotatedType event) { AnnotatedType tp = event.getAnnotatedType(); @@ -59,15 +57,19 @@ 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(); - data.put(viewConfig.value(), viewPattern); + 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); } } } @@ -76,8 +78,22 @@ public void processAnnotatedType(@Observes ProcessAnnotatedType event) { } } - public Map> getData() { - return Collections.unmodifiableMap(data); + /** + * 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); + } } 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 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 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; + + } +}