h:link not rendered

Splash Forums PrettyFaces Users h:link not rendered

This topic contains 20 replies, has 3 voices, and was last updated by  Lincoln Baxter III 4 years, 7 months ago.

Viewing 15 posts - 1 through 15 (of 21 total)
  • Author
    Posts
  • #18799

    DusegrEsque
    Member

    When <h:link outcome="pretty:send-password" value="Send password" /> get’s rendered I receive the following exception:

    Log-file: 2012-10-09 09:23:57,396 WARNING [javax.enterprise.resource.webcontainer.jsf.renderkit] (http-0.0.0.0-8080-3) jsf.outcometarget.navigation.case.not.resolved

    GUI: Unable to find matching navigation case from view ID ‘/portal/login.xhtml’ for outcome ‘/portal/send-password.xhtml?com.ocpsoft.mappingId=send-password’

    Versions of servers and relevant libraries

    • JBoss AS 5.1.0GA
    • JSF 2.0.9 Mojarra
    • PrettyFaces jsf2-3.3.3
    • PrimeFaces 3.4.1
    • PrimeFaces-Extensions 0.6.0
    • Apache Shiro 1.2.1

    Your web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
    <display-name>Centerware Portal</display-name>

    <!-- ============================================================== -->
    <!-- Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- ============================================================== -->
    <welcome-file-list>
    <welcome-file>faces/portal/welcome.xhtml</welcome-file>
    </welcome-file-list>

    <session-config>
    <!-- web.xml expects the session timeout in minutes -->
    <session-timeout>20</session-timeout>
    </session-config>

    <context-param>
    <param-name>org.jboss.jbossfaces.WAR_BUNDLES_JSF_IMPL</param-name>
    <param-value>true</param-value>
    </context-param>

    <context-param>
    <param-name>com.sun.faces.expressionFactory</param-name>
    <param-value>org.jboss.el.ExpressionFactoryImpl</param-value>
    </context-param>

    <context-param>
    <param-name>com.ocpsoft.pretty.BASE_PACKAGES</param-name>
    <param-value>none</param-value>
    </context-param>

    <context-param>
    <param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
    <param-value>de.mobilexag.portal.util.FaceletsResourceResolver</param-value>
    </context-param>

    <context-param>
    <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
    <param-value>.xhtml</param-value>
    </context-param>

    <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
    </context-param>

    <context-param>
    <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
    <param-value>2</param-value>
    </context-param>

    <context-param>
    <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
    <param-value>true</param-value>
    </context-param>

    <context-param>
    <param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
    <param-value>auto</param-value>
    </context-param>

    <context-param>
    <param-name>de.mobilexag.portal.login.LOGIN_URL</param-name>
    <param-value>/login</param-value>
    </context-param>

    <context-param>
    <param-name>de.mobilexag.portal.login.FALLBACK_URL</param-name>
    <param-value>/welcome</param-value>
    </context-param>

    <!-- ============================================================== -->
    <!-- Listener ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- ============================================================== -->
    <listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>

    <listener>
    <listener-class>de.mobilexag.portal.util.MenuLoaderListener</listener-class>
    </listener>

    <!-- ============================================================== -->
    <!-- Filter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- ============================================================== -->
    <filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>

    <filter>
    <filter-name>PrettyFilter</filter-name>
    <filter-class>com.ocpsoft.pretty.PrettyFilter</filter-class>
    </filter>

    <filter>
    <filter-name>FileUploadFilter</filter-name>
    <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
    </filter>

    <filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
    </filter-mapping>

    <filter-mapping>
    <filter-name>PrettyFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>ERROR</dispatcher>
    </filter-mapping>

    <filter-mapping>
    <filter-name>FileUploadFilter</filter-name>
    <servlet-name>FacesServlet</servlet-name>
    <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    <!-- ============================================================== -->
    <!-- Servlets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- ============================================================== -->
    <servlet>
    <servlet-name>FacesServlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>FacesServlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>

    <!-- ============================================================== -->
    <!-- Resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- ============================================================== -->
    <ejb-local-ref>
    <ejb-ref-name>de.mobilexag.mip.security.realm.CenterwareRealm</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <local>de.mobilexag.mip.security.realm.CenterwareRealm</local>
    <ejb-link>CenterwareRealmBean</ejb-link>
    </ejb-local-ref>

    <ejb-local-ref>
    <ejb-ref-name>de.mobilexag.portal.usermanager.Delegator</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <local>de.mobilexag.portal.usermanager.Delegator</local>
    <ejb-link>Delegator</ejb-link>
    </ejb-local-ref>

    <ejb-local-ref>
    <ejb-ref-name>de.mobilexag.portal.news.service.NewsDao</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <local>de.mobilexag.portal.news.service.NewsDao</local>
    <ejb-link>NewsDaoBean</ejb-link>
    </ejb-local-ref>

    <ejb-local-ref>
    <ejb-ref-name>de.mobilexag.portal.config.service.ConfigDao</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <local>de.mobilexag.portal.config.service.ConfigDao</local>
    <ejb-link>ConfigDao</ejb-link>
    </ejb-local-ref>

    <ejb-local-ref>
    <ejb-ref-name>de.mobilexag.portal.masterdata.service.MasterDataDao</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <local>de.mobilexag.portal.masterdata.service.MasterDataDao</local>
    <ejb-link>MasterDataDao</ejb-link>
    </ejb-local-ref>

    <ejb-local-ref>
    <ejb-ref-name>de.mobilexag.portal.jobprotocol.service.JobProtocolDao</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <local>de.mobilexag.portal.jobprotocol.service.JobProtocolDao</local>
    <ejb-link>JobProtocolDao</ejb-link>
    </ejb-local-ref>

    <ejb-local-ref>
    <ejb-ref-name>de.mobilexag.portal.admin.service.AdminDao</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <local>de.mobilexag.portal.admin.service.AdminDao</local>
    <ejb-link>AdminDao</ejb-link>
    </ejb-local-ref>
    </web-app>

    Your pretty-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <pretty-config xmlns="http://ocpsoft.com/prettyfaces/3.3.2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://ocpsoft.com/prettyfaces/3.3.2 http://ocpsoft.com/xml/ns/prettyfaces/ocpsoft-pretty-faces-3.3.2.xsd">

    <!-- See also http://ocpsoft.org/docs/prettyfaces/3.3.2/en-US/html/ -->

    <!-- ============================================================== -->
    <!-- Rewrite Rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- ============================================================== -->

    <!-- ============================================================== -->
    <!-- URL Mappings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- ============================================================== -->
    <url-mapping id="portal-welcome">
    <pattern value="/welcome" />
    <view-id value="/faces/portal/welcome.xhtml" />
    </url-mapping>

    <url-mapping id="portal-message">
    <pattern value="/message" />
    <view-id value="/faces/portal/message.xhtml" />
    </url-mapping>

    <url-mapping id="portal-login">
    <pattern value="/login" />
    <view-id value="/faces/portal/login.xhtml" />
    </url-mapping>

    <url-mapping id="change-password">
    <pattern value="/change-password" />
    <view-id value="/faces/portal/change-password.xhtml" />
    </url-mapping>

    <url-mapping id="send-password">
    <pattern value="/send-password" />
    <view-id value="/faces/portal/send-password.xhtml" />
    </url-mapping>

    </pretty-config>

    The expected behavior

    render prettified link on UI

    The actual behavior

    exception

    #23064

    Could you try using this instead:

    <h:link outcome="/faces/portal/send-password.xhtml" value="Send password" />

    or:

    <h:link outcome="/portal/send-password.xhtml" value="Send password" />

    #23065

    DusegrEsque
    Member

    Does not work either. What works is

    <pretty:link mappingId="send-password">Send password</pretty:link>

    but the pretty-docs say h:link must work also and it would be nice to use it.

    #23066

    Out of curiosity. What happens if you change the mappingId to be sendPassword instead?

    So…

    <h:link outcome="pretty:sendPassword" value="Send password" />

    and…

    <url-mapping id="sendPassword">
    <pattern value="/send-password" />
    <view-id value="/faces/portal/send-password.xhtml" />
    </url-mapping>

    #23067

    Though this would be better:

    <h:link outcome="/faces/portal/send-password.xhtml" value="Send password" />

    #23068

    DusegrEsque
    Member

    Leaving out the hyphen results in the same error:

    Unable to find matching navigation case from view ID '/portal/login.xhtml' for outcome '/portal/send-password.xhtml?com.ocpsoft.mappingId=sendPassword'

    It seems that normal JSF navigation takes place. It searches the faces-config.xml for outbound URL rewriting, but there is nothing left than the faces-config root node since navigation is configured only for pretty-faces. Mmh, am I missing something in the pretty-faces configuration?

    Could somebody give me a hint for debugging? From which class should I start to check what’s going on behind the scene?

    #23069

    You could try debugging PrettyNavigationHandler to see why it is not matching the view-id with the mapping. That would be a good place to start. Thanks for digging!

    #23070

    DusegrEsque
    Member

    As far as I can see the method

    public NavigationCase getNavigationCase(final FacesContext context, final String fromAction, final String outcome)

    is called on the PrettyNavigationHandler. This method calls in turn

    public abstract NavigationCase getNavigationCase(FacesContext context, String fromAction, String outcome)

    of the underlying faces implementation. An there the error comes up: Unable to find matching navigation case …

    #23071

    DusegrEsque
    Member

    Finally I found out what causes the issue. My development is based on a modular web application. Many *.xhtml pages live in the META-INF folder of a jar inside the war. To make them accessible I included a custom faclets resource resolver. This works fine, except for pretty-faces outbound URL rewriting. When getNavigationCase is called the case is matched implicitly. In the very end the existence of the resource is checked via servletContext.getResource(path) but the resource /portal/login.xhtml is not found (because it is not located beneath the context root, but inside a jar). Any help would be greatly appreciated.

    #23072

    DusegrEsque
    Member

    I have played around a little and got it working with a slightly modified PrettyViewHandler class. When overriding method deriveViewId with this

    public class PortalViewHandler extends PrettyViewHandler {

    public PortalViewHandler(final ViewHandler viewHandler) {
    super(viewHandler);
    }

    @Override
    public String deriveViewId(final FacesContext facesContext, final String rawViewId) {
    String viewId = super.deriveViewId(facesContext, rawViewId);
    if (viewId == null) {
    viewId = derivePhysicalViewId(facesContext, rawViewId, true);
    }
    return viewId;
    }

    protected String derivePhysicalViewId(FacesContext facesContext, String rawViewId, boolean checkPhysical) {
    String canonicalViewId = new URLDuplicatePathCanonicalizer().canonicalize(rawViewId);
    if (checkPhysical) {
    FaceletsResourceResolver resourceResolver = new FaceletsResourceResolver(null);
    return (resourceResolver.resolveUrl(canonicalViewId) != null) ? canonicalViewId : null;
    }
    return canonicalViewId;
    }

    }

    everything works fine. The FacletsResourceResolver class resolves from the META-INF directory directly. The code is

    public class FaceletsResourceResolver extends ResourceResolver {

    private static final String MODULE_BASE_PATH = "/META-INF";

    private ResourceResolver resolver;

    public FaceletsResourceResolver(ResourceResolver resolver) {
    this.resolver = resolver;
    }

    @Override
    public URL resolveUrl(String path) {
    if (path == null || path.isEmpty()) {
    return null;
    }
    URL url = null;
    // resolve from war
    if (resolver != null) {
    url = resolver.resolveUrl(path);
    }
    // resolve from jar
    if (url == null) {
    url = getClass().getResource(MODULE_BASE_PATH + path);
    }
    return url;
    }

    }

    Would it be possible to provide a patch?

    #23073

    Thanks for debugging this. Of cause you can provide a patch. PrettyFaces is open source and we are always happy to receive contributions. :)

    However, I think your current fix is very specific to your problem. Especially because you hard-coded the FaceletsResourceResolver implementation. The standard way of providing a custom ResourceResolver is to use a servlet context parameter. PrettyFaces could also use this parameter to dynamically lookup the resolver to use.

    I’m not sure deriveViewId() is the correct place to do this. Could you tell us a bit more what happens there?

    #23074

    DusegrEsque
    Member

    In PrettyViewHandler deriveViewId() delegates to the parent ViewHandler provided by the faces implementation. It checks whether the *.xhtml file exits in the file system. In case it is not found null is returned and an error is printed out.

    However the starting point is the PrettyNavigationHandler. It checks for the navigation case by delegating to

    parent.getNavigationCase(context, fromAction, viewId)

    In my case this condition is meet:

    if ((outcome != null) && outcome.startsWith(PrettyContext.PRETTY_PREFIX) && config.isMappingId(outcome))

    Of course my solution is not very generic. It just links to my own resource resolver which I have to use anyway since I am tied to servlet 2.5. Others may not run into this problem. But it would be nice to have a configuration switch for this.

    Currently I does not know the pretty faces internals very well. Perhaps there is a better place for this.

    #23075

    Are you sure the default ViewHandler checks whether the xhtml file exists? I didn’t check the code for myself, but the API docs say:

    The default implementation of this method simply returns rawViewId unchanged.

    See:

    http://docs.oracle.com/javaee/6/api/javax/faces/application/ViewHandler.html

    #23076

    Is it possible that the argument rawViewId of deriveViewId() is already null when PrettyViewHandler is called? That would explain why super.deriveViewId() returns null.

    #23077

    DusegrEsque
    Member

    This is definitely not the case. From the sources of my jsf-impl.jar:

    package com.sun.faces.application.view;

    public class MultiViewHandler extends ViewHandler {

    @Override
    public String deriveViewId(FacesContext context, String rawViewId) {
    return derivePhysicalViewId(context, rawViewId, true);
    }

    protected String derivePhysicalViewId(FacesContext ctx,
    String rawViewId,
    boolean checkPhysical) {
    if (rawViewId != null) {
    String mapping = Util.getFacesMapping(ctx);
    String viewId;
    if (mapping != null) {
    if (!Util.isPrefixMapped(mapping)) {
    viewId = convertViewId(ctx, rawViewId);
    } else {
    viewId = normalizeRequestURI(rawViewId, mapping);
    if (viewId.equals(mapping)) {
    // The request was to the FacesServlet only - no
    // path info
    // on some containers this causes a recursion in the
    // RequestDispatcher and the request appears to hang.
    // If this is detected, return status 404
    send404Error(ctx);
    }
    }
    try {
    if (checkPhysical) {
    return ((ctx.getExternalContext().getResource(viewId) != null) ? viewId : null);
    } else {
    return viewId;
    }
    } catch (MalformedURLException mue) {
    if (logger.isLoggable(Level.SEVERE)) {
    logger.log(Level.SEVERE,
    mue.toString(),
    mue);
    }
    return null;
    }
    }
    }
    return rawViewId;
    }
    }

    The method deriveViewId is called with /portal/send-password.xhtml. Since I have a modular webapp it is not found and null is returned from MultiViewHandler ctx.getExternalContext().getResource(viewId) != null) ? viewId : null.

    It would be nice if pretty-faces could check the web.xml for a faclets resource resolver <param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>. If one is defined PrettyViewHandler should give it a try.

Viewing 15 posts - 1 through 15 (of 21 total)

You must be logged in to reply to this topic.

Comments are closed.