13
votes

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">
 <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.)

2

2 Answers

30
votes

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.

5
votes

A JAR contains both EJB & FacesBeans:

In the case that the JAR that contains Faces Beans is already bundled in an ear (because it contains EJBs), then you also have to put the same JAR in the WEB-INF/lib of the war (on top of adding the faces-config.xml in the META-INF directory), so that the container recognizes the JSF 2.0 Annotations.

In this case, it is probably better to create two JARS, one for the EJBs and one for the FacesBeans.