Mittwoch, 28. Juli 2010

JAXB und das @XmlRootElement

JAXB wird verwendet um aus einem XML Schema Java Klassen zu erstellen. Diese können dann mit Daten gefüllt und als XML - Datei abgespeichert werden. Andersrum können auch bestehende XML - Dateien in Java Klassen umgewandelt werden.
Je nach Schemadefinition ergibt sich dabei allerdings ein Problem.

Der folgende Ausschnitt beschreibt ein einfaches Schema:

<xs:schema targetnamespace="http://test.kirchner.de/jaxb"            xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="start">
        <xs:complextype>
       </xs:complextype>
    </xs:element>
</xs:schema>


JAXB erstellt daraus diese Klasse, die im Package test.kirchner.de.jaxb untergebracht wird:

@XmlRootElement(name = "start")
public class Start {
}

Das folgende Schema beschreibt dieselbe XML Struktur, nur das dem start Element jetzt ein Typ über das type Attribut zugeordnet wird und dieser nicht innerhalb des Elements deklariert wurde:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"            xmlns:mk="http://test.kirchner.de/jaxb"            targetNamespace="http://test.kirchner.de/jaxb">
   <xs:element name="start" type="mk:startType"/>
   <xs:complexType name="startType"/>
</xs:schema>

Hieraus wird von JABX jedoch folgendes erstellt:

@XmlType(name = "startType")
public class StartType {
}

In der zugehörigen ObjectFactory wird zum Erzeugen eines start Elements die folgende Methode zur Verfügung gestellt:

@XmlRegistry
public class ObjectFactory {
  private final static QName _Start_QNAME =
      new QName("http://test.kirchner.de/jaxb", "start");
  ...
  @XmlElementDecl(namespace = "http://test.kirchner.de/jaxb",
      name = "start")
  public JAXBElement createStart(StartType value) {
    return new JAXBElement(_Start_QNAME, StartType.class, null,         value);
  }
}

Daraus ergibt sich nun aber das Problem, dass kein XmlRootElement definiert ist und der Versuch die Java Klassen mit dem Marshaller als XML Datei zu speichern schlägt fehl. Die hervorgehobene Zeile führt zu einer javax.xml.bind.MarshalException

JAXBElement list = (new ObjectFactory()).createStart(new StartType());
StartType root = list.getValue();
JAXBContext jaxbContext = JAXBContext.newInstance("test.kirchner.de.jaxb");
Marshaller m = jaxbContext.createMarshaller();
m.marshal(root, new File("d:/start.xml"));

Ausschnitt aus dem Stacktrace:

#javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: unable to marshal type "test.kirchner.de.jaxb.StartType" as an element because it is missing an @XmlRootElement annotation]

Abhilfe schaffen lässt sich, indem man im Schema folgendes hinzufügt:

<jaxb:globalBindings>
   <xjc:simple/>
</jaxb:globalBindings>


Das komplette XSD sieht somit wie folgt aus:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mk="http://test.kirchner.de/jaxb" targetNamespace="http://test.kirchner.de/jaxb" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="1.0" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:extensionBindingPrefixes="xjc">
    <xs:annotation>
        <xs:appinfo>
            <jaxb:globalBindings>
                <xjc:simple/>
            </jaxb:globalBindings>
        </xs:appinfo>
    </xs:annotation>
    <xs:element name="start" type="mk:startType"/>
    <xs:complexType name="startType"/>
</xs:schema>


Durch die xjc:simple wird von JAXB wieder die Start Klasse mit der @XmlRootElement Notation erzeugt.

Siehe auch: den Blog von Kohsuke Kawaguchi

Donnerstag, 22. Juli 2010

Webanwendung mit JSF (MyFaces), Facelets und Richfaces

Im folgenden wird beschrieben welche Bibliotheken und Einstellungen notwendig sind, um eine JSF - fähig Webanwendung für einen JBoss Server 5.1.0 zu erstellen. Als JSF Implementierung wird dabei MyFaces gewählt. Zusätzlich sollen noch Facelets und Richfaces unterstützt werden.

Folgende Versionen werden dabei verwendet: (In späteren Auflistungen von Bibliotheken werden alle Versionsnummern weggelassen)
  • MyFaces 1.2.8
  • Facelets 1.1.15.B1
  • Richfaces 3.3.2
Um eine leere Webanwendung zu Erstellen wird Eclipse verwendet. Dort kann über
File --> New --> Dynamic Web Project (oder File --> New --> Other und dann unter Web/Dynamic Web Project) eine entsprechende Anwendung erstellt werden. (Als Servlet Version 2.5 angeben)
Die JSF Unterstützung kann ebenfalls über Eclipse hinzugefügt werden, da ich jedoch lieber selber eine Übersicht habe, welche Dateien wo erstellt werden, wird hier beschrieben, wie sie per Hand hinzugefügt wird.

JSF Unterstützung hinzufügen

Die wichtigste Konfigurationsdatei für JSF ist die faces-config-xml. Diese Datei wird im Ordner WEB-INF erstellt.In der einfachsten Form sieht diese wie folgt aus:

<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee" xi="http://www.w3.org/2001/XInclude" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
</faces-config>

Nachdem dies geschehen ist, muss die web.xml noch angepasst werden:

Um JSF zu verwenden wird das Faces Servlet eingebunden. Zusätzlich werden noch die passenden Mappings für das Servlet hinzugefügt.

<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

Soll in der Webanwendung z.B. mit xhtml Seiten gearbeitet werden, wird als Mapping *.xhtml angegeben.

Das Mapping von /faces/* ist wichtig, da einige Bibliotheken, die ich einbinde (z.B. Richfaces), bestimmte Resourcen über den Kontextpfad .../faces/... schickt.

Diese Angaben reichen aus, um der Webanwendung JSF beizubringen. Allerdings wird dann die Default - Implementierung des Servers für JSF verwendet, was im Falle von JBoss die SUN-RI Implemetierung ist.

MyFaces Implementierung verwenden

Um die MyFaces Implementierung von JSF zu verwenden, müssen folgende Dateien in den WEB-INF/lib Ordner kopiert werden (Download auf MyFaces).
  • myfaces-impl
  • myfaces-api
  • commons-beanutils
  • commons-codec
  • commons-collections
  • commons-digester
  • commons-discovery
  • commons-logging
Nachdem diese Dateien in den Ordner kopiert wurden, müssen noch folgende Angaben der web.xml hinzugefügt werden:

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

Der Context Parameter sorgt dafür, dass der JBos Server für diese Web Anwendung die JSF Implementierung nimmt, die im WEB-INF/lib Ordner liegt.
Zusätzlich muss noch der folgende Listener eingebunden werden:

<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>

Mit diesen Einstellungen verwendet die Webanwendung ab sofoert die Myfaces Implementierung.

Facelets hinzufügen

Die jar Datei zu Facelets kann hier runtergeladen werden. Um Facelets einzubinden, muss nur die folgende Datei in den WEB-INF/lib ordner kopiert werden:
  • jsf-facelets.jar
In der web.xml muss der folgende Context-Parameter hinzugefügt werden:

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

Als param-value wird dabei dieselbe Endung angegeben, die zuvor bereits für das Mapping des Faces Servlets verwendet wurde.

Des Weiteres muss in der faces-config.xml ein ViewHandler für facelets eingetragen werden. dazu wird unter dem faces-config Tag folgendes eingefügt:

<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>

Achtung: Wird vergessen der ViewHandler hinzuzufügen, kommt es beim Aufrufen einer *.xhtml Seite aus der Webanwendung zu einem Stackoverflow:

Servlet.service() for servlet Faces Servlet threw exception
java.lang.StackOverflowError
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:216)
...
--> View Handler in faces-config.xml angeben!

Richfaces hinzufügen

Die Richfaces Bibliotheken können hier runtergeladen werden. Folgende Dateien müssen in den WEB-INF/lib Ordner kopiert werden:
  • richfaces-api
  • richfaces-impl
  • richfaces-ui
Um Richfaces in die Anwendung einzubinden, müssen noch folgende Einträge in der web.xml gemacht werden:

<filter>
<display-name>RichFaces Filter</display-name>
<filter-name>richfaces</filter-name>
<filter-class>org.ajax4jsf.Filter</filter-class>
</filter>

<filter-mapping>
<filter-name>richfaces</filter-name>
<servlet-name>Faces Servlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>

Fertig ist die Webapplikation !

Buchtip für Anwendungen mit JSF 2.0:
JavaServer Faces 2.0: Grundlagen und erweiterte Konzepte