NullPointerException beim Starten von JSF 2

Die Exception

Im Zuge der Umstellung auf JSF 2.x sind mir einige Exceptions begegnet. Eine davon ist relativ trickreich zu beheben. Sie äußert sich dass beim Deployment der Anwendung auf dem Server folgende Exception auftritt:

java.lang.NullPointerException
  at com.sun.faces.config.InitFacesContext.cleanupInitMaps(InitFacesContext.java:278)
  at com.sun.faces.config.InitFacesContext.(InitFacesContext.java:102)
  at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:156)
  ...

Die Analyse

Dieser Fehler verhindert dann auch den Start der Anwendung. Woran liegt das? Dazu sehen wir uns mal eine Funktion der JSF-Referenz-Implementierung Mojarra an:

static Map getInitContextServletContextMap() {
  ConcurrentHashMap initContextServletContext = null;
  try {
    Field initContextMap = FacesContext.class.getDeclaredField("initContextServletContext");
    initContextMap.setAccessible(true);
    initContextServletContext = (ConcurrentHashMap)initContextMap.get(null);
  } catch (Exception e) {}
  return initContextServletContext;
}

Hierbei fällt auf, dass beim Zugriff mittels Reflection auf ein Feld der Fehlerfall einfach ignoriert wird und null zurück gegeben wird. Die Methode besitzt keine Dokumentation oder einen Hinweis darauf, was der Rückgabewert ist. Also muss der zugreifende Code den Fehler abfangen können. Aber tut er das? Sehen wir uns das mal an:

Map <InitFacesContext, ServletContext>initContextServletContext = InitFacesContext.getInitContextServletContextMap();
Set<Map.Entry<InitFacesContext, ServletContext>> entries = initContextServletContext.entrySet();

Damit wird schnell klar: Es wird NullPointerException geben und man sieht an der auftretenden Exception nicht den eigentlichen Grund. Dieses Fehlverhalten ist damit nicht ganz einfach zu beheben. Grundsätzlich ist es schlechter Stil eine Exception einfach zu schlucken. Besser ist es diese zumindest zu loggen, vielleicht sogar mit hilfreicher Zusatzinformation wie:

...
} catch (Exception e) {
  log.error("Could not access FacesContext#initContextServletContext, " +
    "probably JSF-API and implementation mismatch!", e);
}

Die Lösung

Jetzt ist offensichtlich, woher der Fehler kommt: die JSF-API und die Implementierung im Classpath passen nicht zusammen. In meinem Fall wurde eine weitere JSF-API über Maven als Abhängigkeit hinzugefügt, die dann „falsche“ Klassen in den Classpath lädt.

Es reicht also in den meisten Fällen den Classpath der Java-Anwendung auf folgende Probleme zu prüfen:

  • Passt die JSF-Implementierung zur JSF-API?
  • Gibt es mehr als eine JSF-Implementierung im Classpath (z.B. JSF 1.x und 2.x)?
  • Gibt es mehr als eine JSF-API im Classpath?

Copyright © christophbrill.de, 2002-2017.