DynaView, Outbound Rewrite and Action URL

Splash Forums PrettyFaces Users DynaView, Outbound Rewrite and Action URL

This topic contains 5 replies, has 2 voices, and was last updated by  Colm 4 years, 10 months ago.

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

    Colm
    Participant

    Dear Users,

    I am using Prettyfaces 3.3, JSF 2, My Faces 2.1 on Tomcat 7.

    I have successfully used DynaView mechainsm to map a URL to a Facelet View. Here is my pretty faces config file.

    <!--

    Simple rewrite rule to virtualise the inbound JSF URLs. The Rewrite filter is mapped to ".htm" files.

    The generic rule will map a URL like "/admin/Page.htm" to

    an actual facelet like "/WEB-INF/xhtml/views/Page.xhtml".

    The will allow us not to have one physical file for each view in MP reducing duplication.

    -->

    <url-mapping id="generic">

    <pattern value="/#{dynamicViewBean.context}/#{dynamicViewBean.path}" />

    <view-id value="#{dynamicViewBean.getViewId }" />

    </url-mapping>

    This allows me to virtualise my inbound URLS. However, from the pretty faces documentation, I noticed that “Out out-bound URL-rewriting will not function on pages with dynamic view-ID’s.” – I am wondering why this limitation exists and if there is a work around for me?

    My applicaiton renders a Tomahawk Navigation Menu, and when this menu renders – the form enclosing it will have an action “/context/WEB-INF/views/xhtml/ViewName.htm”. This obviously causes a 404 on my application.

    I understand that this is caused because prettyfaces has not re-written the URL.

    As a hack, to try and understand further, I tried to override getActionURL in my Custom View Handler..

    public String getActionURL(FacesContext ctx, String viewId) {

    String url = this.getWrapped().getActionURL( ctx, viewId );

    if (StringUtils.contains(url, "WEB-INF") ) {

    PrettyContext prettyContext = PrettyContext.getCurrentInstance(ctx);

    prettyContext.setInNavigation(false);

    return this.getWrapped().getActionURL( ctx, viewId );

    ....

    However, even though this renders the form action correctly – I get a View Expired exception when I reclick the menu. I dont understand how State Management works in JSF 2.0 to properly diagnose this issue and whether there is a work around avaiable to me?

    Im also open to moving to Prettyfaces 4.0 immediately if this usecase is supported? However with the lack of documetation available for 4.0 – I dont know where to start with this.

    Regards

    Colm

    #22762

    Unfortunately there are limitations, and I believe the dynaView URL can only be rewritten in the outbound URL if you are using pretty:mappingId strings for navigation/link-generation. We attempted to make this better, and it does work better for some scenarios, but it’s not perfect.

    That would be my recommendation, other than trying to avoid dynaview if possible. It’s a great idea in concept, but in practice it gets tricky quickly. It’s a difficult feature.

    Either way, the docs need to be updated to include the result of what we discover while trying to sort out your problem!

    ~Lincoln

    #22763

    Colm
    Participant

    I’ve actually hacked this together somewhat, and so far it appears to be working for my use case. (Im not sure if this will inadventantly cuase other issue – but so far this seems to be working OK)

    Firstly, I changed my Tomahawk menu navigation actions to be redirect actions ?faces-redirect=true&includeViewParams=true. (Not my ideal outcome)

    I overloaded “restoreView” to detect if a DynaView was used originally to transform URL by PrettyFaces, and if so – then I attempted to get an instance of the DynaView (using my variable resolver) – and used the URL to restore the view.

    @Override

    public UIViewRoot restoreView(final FacesContext context, final String viewId)

    {

    PrettyContext prettyContext = PrettyContext.getCurrentInstance(context);

    /*

    * If pretty faces has created this view via a DynaView mapping, this we should try to restore via the

    * rewritten view and not the POST view. This may be different.

    *

    * For example, the posted URL might be "/admin/View.htm", but the actual View would be

    * "WEB-INF/xhtml/view/View.xhtml" and JSF would have saved that view in the JSF State Manager.

    *

    * Its possible that we would get a ViewExpired Exception if we coudl not find the view in the state manager.

    */

    if ( prettyContext.isDynaviewProcessed() ) {

    MacallaDelegatingVariableResolver macallaDelegatingVariableResolver =

    new MacallaDelegatingVariableResolver(FacesContext.getCurrentInstance().getApplication().getVariableResolver());

    DynamicViewBean dynamicViewBean = (DynamicViewBean)macallaDelegatingVariableResolver.resolveVariable( context, "dynamicViewBean");

    return this.getWrapped().restoreView(context, dynamicViewBean.getViewId());

    }

    else {

    // Otherwise delegate to default.

    return this.getWrapped().restoreView(context, viewId);

    }

    }

    I then overloaded the getActionURL method to kind of Reverse the inbound mapping with an outbound mapping that could transform “/WEB-INF/views/htmls/{Path}” to “/virtual/{Path}” URI.

    @Override

    public String getActionURL(FacesContext ctx, String viewId) {

    /*

    * Normally JSF can work out the correct Action URL because it remains unchanged from a request URI.

    *

    * Because "pretty-faces" rewrited this URI JSF may start to calculate invalid URIs for actions because

    * The URI view root has changed. I.e JSF might calculate /mp/WEB-INF/xhtml/views/View.xhtml - which will

    * produce a 404.

    *

    * Solution is to use pretty faces to re-write an outbound URL if a "DynaView" is in use. PrettyFaces will tell

    * us if DynaView in use via its API.

    */

    String url = this.getWrapped().getActionURL( ctx, viewId );

    PrettyContext prettyContext = PrettyContext.getCurrentInstance(ctx);

    if ( prettyContext.isInNavigation() && prettyContext.isDynaviewProcessed() ) {

    String currentViewId = ctx.getViewRoot().getViewId();

    UrlMapping mapping = prettyContext.getCurrentMapping();

    /*

    * If the current View requested is not the same as the existing view, then we should dynamically

    * re-write the outbound URL using the same process as the inbound dynaview mechanism.

    */

    if (!StringUtils.equals( currentViewId, viewId ) ) {

    PrettyConfig config = prettyContext.getConfig();

    // todo: is this needed?

    ctx.getViewRoot().setViewId( viewId );

    // Get a explicit mapping, coudl detect via URL -

    // but probably not as obvious to others.

    mapping = config.getMappingById("simple-generic");

    injectPathParams(ctx, new URL(viewId), mapping );

    DynaviewEngine dynaview = new DynaviewEngine();

    url = prettyContext.getContextPath() + dynaview.calculateDynaviewId( ctx, mapping ) ;

    }

    else {

    // Rewrite the current DynaForm URL.

    ExtractedValuesURLBuilder builder = new ExtractedValuesURLBuilder();

    url = prettyContext.getContextPath() + builder.buildURL(mapping) + builder.buildQueryString(mapping);

    }

    }

    log.debug( "Action URL was " + viewId + " and is " + url );

    return url;

    }

    Lastly, Added a new Virtual URL mapping..

    <url-mapping id="generic">

    <pattern value="/#{dynamicViewBean.context}/#{dynamicViewBean.path}" />

    <view-id value="#{dynamicViewBean.getViewId }" />

    </url-mapping>

    <url-mapping id="simple-generic">

    <pattern value="/WEB-INF/xhtml/views/#{actionViewBean.viewName}" />

    <view-id value="#{actionViewBean.getViewId }" />

    </url-mapping>

    I think this is a bit of a hack which I may regret later – but I thikn for now ill use this to get started.

    Lincoln if you have any feedback/improvement R.e approach and code please do share. Im completely new to JSF 2.

    Thanks

    Colm

    #22764

    Hey Colm,

    Thanks for your detailed information. I’ll try to take a look at this asap and see if what you are doing can be streamlined. One thing that would help me inspect your setup is if you could attach a minimal example project that demonstrates what you’ve done. (You can attach files to the post below the reply form.)

    Thanks!

    Lincoln

    #22765

    Colm
    Participant

    Dear Lincon,

    Attached is a smaple project showing my implementation strategy for the DynaView solution m using. There is Source code also included, and a exploded WAR folder structure, with the root folder called “web”.

    I ran the project on Tomcat 7 and it worked OK. NOTE: You will need to populate the WEB-INF/lib folder with:

    Prettyfaces jsf2 3.3

    Myfaces 2.1 + dependencies

    Commons Lang 1.2+

    Bascially, if you start the applicaiton and navigate to URL: <your-context>/admin/Test.htm, Pretty faces (using DynaView method) will rewrite the URL to “/WEB-INF/xhtml/Test.xhtml” and then my “Custom View Handler” will convert and “Action URLS” back from “WEB-INF/xhtml/Test.xhtml” to “/admin/Tetst.htm” … Its a rough example only, with some code removed which I felt was not relavent.

    It does work for my specific usecase and means I dont need to configre each “view” I have in pretty-config.xml, but instead I can assume some sensible defaults.

    With JSF 2 implicit navigation, this is working well.

    Thanks.

    Colm

    #22766

    Colm
    Participant

    trying to upload zip instead…

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

You must be logged in to reply to this topic.

Comments are closed.