views:

347

answers:

1

I have a War and Jar project in my Eclipse-based JSF project. I have decided to use annotations to declare my FacesConverter, (among a myriad other things), rather than declare it using my faces-config.xml.

@FacesConverter(value="passwordFieldStringConverter")
public class PasswordFieldStringConverter implements Converter {

 public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) throws ConverterException {
  try {
   return arg2.getBytes("UTF-16BE");
  }
  catch(UnsupportedEncodingException uee) {
   Assert.impossibleException(uee);
  }

  return(null);
 }

 public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) throws ConverterException {
  try {
   return new String((byte[]) arg2, "UTF-16BE");
  }
  catch(UnsupportedEncodingException uee) {
   Assert.impossibleException(uee);
  }

  return(null);  
 }

}

And then I use passwordFieldStringConverter directly in my .xhtml:

<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core" 
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:sec="http://www.springframework.org/security/facelets/tags"&gt;
 <ui:composition>
  <f:view>
      <f:loadBundle basename="landingPage.bundle" var="bundle" /> 

   <ui:decorate template="/WEB-INF/jsf_helpers/htmlShell.xhtml">
    <ui:param name="PageTitleParam" value="#{bundle.pageTitle}" />

    <h:form>
      <h:dataTable var="rowVar" value="#{userListContainer.users}">
       <f:facet name="header"><h:outputText value="Users you are currently managing:" /></f:facet>
       <h:column>
        <f:facet name="header">
         <h:outputText value="Screen Name" />
        </f:facet>
        <h:outputText value="#{rowVar.screenName}" />
    </h:column>
       <h:column>
        <f:facet name="header">
         <h:outputText value="Password" />
        </f:facet>
        <h:outputText value="#{rowVar.password}">
         <f:converter converterId="passwordFieldStringConverter" />
        </h:outputText>
       </h:column>
      </h:dataTable>
    </h:form>
   </ui:decorate>
  </f:view>
 </ui:composition>
</html>

JSF is supposed to scan the jars in my War at deployment-time and detect which classes have annotations on them (and auto-configure the application accordingly). My problem is that JSF is apparently not detecting the classes I have which sport annotations.

The War project has all of my .xhtml files as well as the project's faces-config.xml, my Jar project has all of my faces related Java code (action beans, managed beans, custom converters, etc.)

+5  A: 

Yes, I'm immediately answering my own question because I already spent a week banging my head on the table and I only figured out my problem after debugging through the JSF 2.0 RI (Mojarra) to see what it was doing.

Basically, the annotation scanner only consults the WAR's /WEB-INF/classes .class files for annotations (assuming you have a faces-config.xml in /WEB-INF). If you keep your code in separate Jar files and want your .class files you have in /WEB-INF/lib .jar files to be scanned for annotations, you must have a faces-config.xml in that Jar's META-INF folder. The faces-config.xml you plop into your jar can be empty, it just needs to be there or else the annotation scanner will passover your jar like it was leftover meatloaf.

Empty faces-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee /WEB-INF/schema/web-facesconfig_2_0.xsd" 
              version="2.0" metadata-complete="false">
    <!-- This file must be present with our jar, even if it is empty.  Otherwise, our Java annotations won't get scanned! -->
</faces-config>

I know a threw out a lot of -INF's there. So, just to recap. Your faces-config.xml in your War goes in WEB-INF. Your potentially empty, annotation scanner enabling, faces-config.xml in each of your Jar files goes in that Jar's META-INF.

If you're wondering why the behavior has to be so nutty, it's because the JSF configs can be decentralized and scattered across 1st and 3rd party libraries this way. And if you're wondering about the whole presence of the faces-config.xml in a jar being a factor, well, my opinion is that that is what marks a Jar as interesting to the engine - and the absence of a faces-config.xml means the annotation scanner can avoid that jar and save on processing at deployment time. It just would have been nice if those scanner semantics were a little more clearly explained somewhere, <expletives deleted>!

The following blog post was very useful in my understanding of what the code was doing:

http://one-size-doesnt-fit-all.blogspot.com/2007/01/using-multiple-faces-configxml-files-in.html

I really hope this saves someone from a week of pain like I had.

DWoldrich
@DWoldrich - I believe the rules are covered in section _11.5.1 Requirements for scanning of classes for annotations_ in the JSF 2.0 spec. http://jcp.org/en/jsr/detail?id=314
McDowell
@McDowell - Yes, I found the exact section you cite after I had already divined the solution through tracing the source while debugging. This was one of those cases where I did not know what questions to ask in order to get the answer I wanted. Here's a bullet from that section relevant to me.... ■ For every jar in the application's WEB-INF/lib directory, if the jar contains a “META-INF/faces-config.xml”file or a file that matches the regular expression “.*\.faces-config.xml” (even an empty one), all classes inthat jar must be scanned.
DWoldrich
@McDowell - That said, it would be useful if the spec coordinators could explain or justify that design. Why is the faces-config.xml being used to declare both faces configuration as well as 'tainting' a jar as being of interest to the annotation scanner? Shouldn't there also be a way of declaring with a web.xml context-param which jars should be scanned as well? I spent a great deal of time trying to coax the com.sun.faces.annotationScanPackages context-param to scan my jar to no avail. (I realize I'm conflating the RI with the JSF spec here, two different things, but still...)
DWoldrich
@DWoldrich - Ed Burns ( http://www.java.net/blogs/edburns/ ) is probably the man to ask to understand the logic of this decision.
McDowell