Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seamfaces 147 2 - second approach #66

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from

Conversation

gonzalad
Copy link
Contributor

This approach build on top of #64 :

  • enables the end-user to easily create it's own viewAction annotations.
    Mechanism similar to Hibernate Validator custom annotations.
  • enforces order execution between Restrict annotation and view actions (pb is CDI events aren't ordered).
  • Extension now depend on Store and not the other way around.
  • removed completely ViewControllerStore (and the duplicated logic)

Abstract : ViewActionHandler / ViewConfigDescriptor / ViewConfigStore

ViewActionHandler

ViewActionHandler is the main interface. It encapsulate action execution.

public interface ViewActionHandler {
    boolean handles(PhaseInstant phaseInstant);
    Integer getOrder();
    Object execute();
}

order enable to have some viewAction executing before the others (handy for @SecurityBindingType annotations).

ViewConfigDescriptor

Describes a view (@ViewPattern).
It contains a list of ViewActionHandler applying to the view.

public class ViewConfigDescriptor {
    public String getViewId();
    public void addValue(Object value);
    public List<Object> getValues();
    public void setValues(List<Object> values);
    public void addMetaData(Annotation metaData);
    public List<ViewActionHandler> getViewActionHandlers();
    public void addViewActionHandler(ViewActionHandler viewActionHandler);
    public void executeBeforePhase(PhaseId phaseId);
    public void executeAfterPhase(PhaseId phaseId);
    public List<Annotation> getMetaData();
    public <T extends Annotation> T getMetaData(Class<T> type);
    public List<? extends Annotation> getAllQualifierData(Class<? extends Annotation> qualifier);
}

ViewConfigStore

This one already existed and is a registry of every ViewConfigDescriptor.
Note : I've added the notion of runtimeViewConfigDescriptor.
Non-runtime ViewConfigDescriptor are regexp based.
runtime are merged regexp ViewConfigDescriptor corresponding to a requested view-id.
Mostly for performance reason (and also for code simplicity).

Now : what is a viewAction ?

As in the previous post, 4 flavours : El based, Controller and ActionBindingType (meta-annotation), and SecurityBindingType (it's now just one more viewAction type).
I've added another layer El based annotation is built on the same logic as Hibernate Validator, so you could easily create your own view action annotations.

ElViewAction

Just executes an El method.

Sample usage :

@ViewConfig
public interface ElViewActionConfigEnum {
    static enum Pages {
        @ViewPattern("/*")
        DEFAULT,

        @ViewPattern("/client/*")
        @ElViewAction("#{elViewActionBean.viewAction}")
        CLIENTS,

        @ViewPattern("/client/done.xhtml")
        CLIENT_CONFIRMED(),
    }
}

ElViewAction accepts phase and before attributes. By default, ElViewAction is executed before RenderResponse (as the other page actions).

Controller

IMO, the best approach.
Sample usage :

@ViewConfig
public interface ViewConfigEnum {
    static enum Pages {
        @ViewPattern("/country/*")
        @ViewController(CountryController.class)
        COUNTRIES()

        @ViewPattern("/client/done.xhtml")
        @ViewController(ClientController.class)
        CLIENT_CONFIRMED()
    }
}

And you annotated any controller method with @Before/After and any annotation from org.jboss.seam.faces.event.qualifier package :

@Named
@ApplicationScoped
public class CountryController {

    private CountryController mock;

    @Before
    @RenderResponse
    public void beforeRenderResponse() {
        mock.beforeRenderResponse();
    }

    @After
    @RenderResponse
    public void afterRenderResponse(DependentBean dependenBean) {
        mock.afterRenderResponse(dependenBean);
    }

    public CountryController getMock() {
        return mock;
    }

    public void setMock(CountryController mock) {
        this.mock = mock;
    }
}

Controller methods accept CDI injected parameters.

Meta-annotation ActionBindingType

You :

  • create a custom annotation with ActionBindingType meta-annotation.
  • and use this annotation on a method and specify the corresponding view.
  • this method will be called as a view action.

Sample :

@ViewConfig
public interface ViewConfigEnum {
    static enum Pages {
        @ViewPattern("/*")
        DEFAULT,

        @ViewPattern("/client/*")
        CLIENTS,

        @ViewPattern("/country/*")
        COUNTRIES(),

        @ViewPattern("/order.xhtml")
        ORDER_TEST(),

        @ViewPattern("/client/done.xhtml")
        CLIENT_CONFIRMED(),
    }
}
@ViewActionBindingType
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface BeforeRenderResponseViewAction {
    ViewConfigEnum.Pages value();
}
@Named
@ApplicationScoped
public class AController {

    @BeforeRenderResponseViewAction(ViewConfigEnum.Pages.CLIENTS)
    public void beforeRenderResponse() {
    }

    @AfterInvokeApplicationViewAction(ViewConfigEnum.Pages.CLIENTS)
    public void afterInvokeApplication() {
    }
}

Develop you own view action

As said earlier, I've used the same approach as Hibernate Validator, so you can create whatever annotation you want.

You'll just need to create the view action annotation (with meta-annotation @ViewAction) and a ViewActionHandlerProvider for the logic).

For instance :

@ViewAction(BeginConversationHandlerProvider.class)
@Before
@ApplyRequestValues
@Order(OrderDefault.DEFAULT-10)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BeginConversation {
    boolean join() default false;
}

And :

public class BeginConversationHandlerProvider extends SimpleViewActionHandlerProvider<BeginConversation> {
    private boolean join;

    @Inject
    private Conversation conversation;

    @Override
    public void doInitialize(BeginConversation annotation) {
        join = annotation.join();
    }

    @Override
    public Object execute() {
        if (join && !conversation.isTransient()) {
            return null;
        }
        conversation.begin();
        return null;
    }
}

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).
@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
Those where shortcut annotations duplicating @Before/@after @RenderResponse
And removed usage of @Before/@after and phase annotation with @ViewAction.
Attribute usage is more explicit here.
This approach :
* enables the end-user to easily create it's own viewAction annotations.
  Mechanism similar to Hibernate Validator custom annotations.
  * enforces order execution between Restrict annotation and view actions
  * (pb is CDI events aren't ordered).
  * Extension now depend on Store and not the other way around.
  * removed completely ViewControllerStore (and the duplicated logic)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant