@Join, @RequestAction and dispatch

Splash Forums Rewrite Users @Join, @RequestAction and dispatch

This topic contains 5 replies, has 2 voices, and was last updated by  elbow 16 hours, 50 minutes ago.

Viewing 6 posts - 1 through 6 (of 6 total)
  • Author
    Posts
  • #27556

    elbow
    Participant

    Hi all,

    Having finally worked out my issue with @Deferred (hint to future me, when using jsf/spring, the rewrite filter has to come *after* the spring context filter in web.xml), I now have an issue with request actions.

    In my request action, I’m loading the requested object from the db.

    @RequestAction
    @IgnorePostback
    @Deferred
    public void load() {
        this.object = manager.findBySlug(slug);
        if (this.object == null) {
            throw new NotFoundException();
        }
    }

    The NotFoundException is picked up by an exception handler, which then calls (with some bumpf around it)

    FacesContext.getCurrentInstance().getExternalContext().dispatch("/404/");

    thus preserving the original URL, but returning the 404 page and code.

    So, going to /object/foo/, where foo is valid, shows the page for foo, whereas going to /object/bar/, where bar is invalid, remains on the url /object/bar/ but shows the 404 page.

    This works fine if /object/{slug}/ is configured in rewrite configuration rules. However, if I configure the join using an @Join on the same bean with the @RequestAction, then the request action is invoked even if I am in the processing of redirecting to 404. This results in an infinite loop of NotFoundExceptions being thrown.

    So the two ways that currently work are:
    1. have @RequestAction on the bean and Join.path().to() in external configuration
    2. have @Join(path, to) on the bean and <f:viewAction> in the view

    Number 1 is preferable for reusability, but is there a way to have both @RequestAction and @Join on the bean, without it throwing a fit when I’m trying to do a .dispatch() on that URL?

    I suspect the answer is no, but in the spirit of the Journal of Null Hypothesis, I figured I’d ask anyway so anyone else having the same problem can share the pain 🙂

    Many thanks,
    Bill

    • This topic was modified 19 hours, 21 minutes ago by  elbow.
    #27558

    elbow
    Participant

    Actually, scratch all of that – @RequestAction only works when the bean is annotated with @Join, which is obvious now I think about it…

    So, the question is now – how can I use annotations instead of viewaction, and still be able to use dispatch?

    #27559

    You say that your use case is working fine if you are using Join.path().to() in the central Rewrite configuration but not if you are using annotations? Could you show us example code for both cases?

    IMHO your use case only works if you use .withChaining() on the join, which is currently not supported for annotations.

    #27560

    elbow
    Participant
    public abstract class BaseDetailWebBean<E extends SluggableEntity, M extends SecureManager<E>> {
    
        protected String slug;
    
        public String getSlug() {
            return slug;
        }
    
        public void setSlug(String slug) {
            this.slug = slug;
        }
    
        protected E object;
    
        @Autowired
        private M manager;
    
        public void load() {
            this.object = manager.findBySlug(slug);
            if (this.object == null) {
                throw new NotFoundException();
            }
        }
    
    }

    implemented as

    @Component
    @Scope("view")
    public class DepartmentDetailWebBean extends BaseDetailWebBean<Department, SecureDepartmentManager> {
    
        public Department getDepartment() {
            return object;
        }
    
    }

    with configuration

    @Component
    @RewriteConfiguration
    public class DepartmentRewriteRules extends HttpConfigurationProvider {
    
        @Override
        public Configuration getConfiguration(ServletContext context) {
            return ConfigurationBuilder.begin()
                    .addRule(Join.path("/department/{slug}/").to("/WEB-INF/resources/secure/department/detail.xhtml"))
                    .addRule(Join.path("/department/").to("/WEB-INF/resources/secure/department/list.xhtml"));
        }
    
        @Override
        public int priority() {
            return 10;
        }
    }

    and view

    <!DOCTYPE html>
    <html lang="en" xml:lang="en"
          xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:f="http://xmlns.jcp.org/jsf/core">
    
    <ui:composition template="/WEB-INF/resources/templates/layout.xhtml">
    
        <f:metadata>
            <f:viewParam name="slug" value="#{departmentDetailWebBean.slug}" />
            <f:viewAction action="#{departmentDetailWebBean.load}" />
        </f:metadata>
    
        <ui:define name="title">#{departmentDetailWebBean.department.name}</ui:define>
    
    </ui:composition>
    </html>

    works.

    If I use annotations, then I don’t have the <f:metadata> section, nor the rules, but instead have

    public abstract class BaseDetailWebBean<E extends SluggableEntity, M extends SecureManager<E>> {
    
        @Parameter
        @Deferred
        protected String slug;
    
        public String getSlug() {
            return slug;
        }
    
        public void setSlug(String slug) {
            this.slug = slug;
        }
    
        protected E object;
    
        @Autowired
        private M manager;
    
        @RequestAction
        @Deferred
        @IgnorePostback
        public void load() {
            this.object = manager.findBySlug(slug);
            if (this.object == null) {
                throw new NotFoundException();
            }
        }
    
    }

    implemented as

    @Component
    @Scope("view")
    @Join(path = '/department/{slug}/', to = '/WEB-INF/resources/secure/department/detail.xhtml')
    public class DepartmentDetailWebBean extends BaseDetailWebBean<Department, SecureDepartmentManager> {
    
        public Department getDepartment() {
            return object;
        }
    
    }

    works in ‘happy days’ cases, but not when I’m attempting a dispatch on the joined URL.

    #27561

    Thanks for sharing the code.

    To be honest, I don’t have an idea why the annotation case isn’t working as expected. I don’t see any obvious error here. Perhaps the inheritance is causing problems? I’m not sure if having @Parameter and @RequestAction in an abstract base class will work without problems.

    However, apart from these problems, if I were you, I would prefer using <f:viewParam> and <f:viewAction> over the Rewrite annotations. In my my experience it is preferable to use the features provided by core JSF instead of using too much of the fancy features of 3rd party libraries (including Rewrite). If you use the core JSF features to implement the parameter binding and the action method invocation, you could in theory even simply drop Rewrite from your project and it should still work fine (even if the URLs aren’t pretty any more after that).

    Or is there any specific reason why you would like to use the annotations? You can BTW also use <f:viewParam> and <f:viewAction> and the Rewrite @View annotation. That will work fine.

    However, if you want to find out why the annotations aren’t working in the dispatch case, you will have to dig deeper into the Rewrite code, which can be very difficult. I know this from personal experience 🙂

    #27562

    elbow
    Participant

    I think you’re right about using the native jsf configurations – it’s certainly thrown no obstacles. My prime issue with it is that it’s not inheritable – the <f:metadata> section must be declared in the view to which it pertains, not in a parent view.

    I had hoped to be able to use @RequestAction in a similar way to @PostConstruct – my DepartmentDetailWebBean makes no sense without having loaded the department. However, because that seems to bind the Join rule to the bean rather than the view, every time I evaluate the join rule I end up invoking the RequestAction, which is not a desired outcome (e.g., going to /department/bar/ tries to load the department bar, even if I’ve already done a dispatch to a new view because bar doesn’t exist).

    When I use the <f:metadata> however, and the rule is bound to the view rather than a bean, when evaluating the rule I don’t invoke the <f:viewaction> (nor the setter on the <f:viewparam>.

    So, things are working entirely as designed it would appear, just not as desired. Thanks for helping guiding my thoughts!

Viewing 6 posts - 1 through 6 (of 6 total)

You must be logged in to reply to this topic.

Comments are closed.