Deutsch
Springe zu
Dein Feedback:
Hat die Seite Deine Erwartung erfüllt? vote3 Ja
vote2 Teilweise
vote1 Nein
Noch ein Kommentar?

Nur falls, Du eine Antwort erwartest, Deine E-Mailadresse

Gegebenenfalls noch Dein Name

Do not change this:
Feedback
Suchen

Java EE (J2EE)

Java EE ist eine Sammlung von Technologien / Schnittstellen.

KürzelNameLinks
JMSJava Message ServiceJava Message Service (JMS)
JavaMailJavaMail
CDIContext and Dependency InjectionContext and Dependency Injection (CDI)
JSSJava Servlet APIServlets
JSPJavaServer PagesJava Server Pages
JSFJava Server FacesJava Server Faces
JSTLJavaServer Pages Standard Tag Library
JPAJava Persistence APIDB Zugriff in Java EE
JTAJava Transaction API
EJBEnterprise Java BeansEnterprise JavaBeans
JNDIJava Naming and Directory InterfaceJNDI
JAXBJava Architecture for XML BindingJAXB
JAXPJava API for XML ProcessingJAXP
JAX-RPCJava API for XML-Based Remote Procedure Calls
JAXRJava API for XML Registries
StAXStreaming API for XMLStAX
WSWeb ServicesWebservices
-Web Service Metadata
JAX-WSJava API for XML Web ServicesJAX-WS
JACCJava Authorization Contract for Containers
JAASJava Authentication and Authorization ServiceJava Authentication and Authorization Service (JAAS)
JCAJ2EE Connector Architecture
JAFJavaBeans Activation Framework

Allgemeine Informationen

22-09-2012 12.35

Java Message Service (JMS)


Um JMS benutzen zu können, braucht man als erstes einen JMS Provider. Dafür stehen verschiedene Programme von verschiedenen Anbietern zur Auswahl, in diesem Beispiel wird der Glassfish Application Sever dafür benutzt.

JMS in GlassFish einrichten

Als erstes muss man zwei JMS Resourcen im Glassfish anlegen:
In der Admin Konsole des Glassfish anmelden, dann unter Resources / Verbindungsfactory eine Factory anlegen. In diesem Beispiel mit dem Namen jms/ConnectionFactory.
Danach unter Resources / Zielresourcen eine Queue und ein Topic anlegen. Hier mit den Namen jms/MyTopic sowie jms/MyQueue.
JMS GlassFish

JMS Beispiel

Das Beispiel soll folgendes leisten können:
  • Senden
  • Empfangen
  • Empfangen über eine CallBackMethode

Dabei kann entweder ein Topic benutzt werden (der Empfänger muss schon laufen wenn die Nachricht gesendet wird) oder aber eine Queue (die Nachrichten werden für den Empfänger aufbewahrt, wenn er während des Sendes noch nicht gestartet war).

Die Referenzen auf den Glassfish JMS werden über Annotationen automatisch eingefügt, wenn man die Anwendung über das appclient Tool startet:
appclient -client yourJMSClientJar.jar

package de.tgunkel.java.EJB;

import javax.annotation.Resource;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

public class MyJMSClass implements MessageListener
{
@Resource(mappedName="jms/ConnectionFactory")
private static ConnectionFactory connectionFactory;

@Resource(mappedName="jms/MyTopic")
private static Topic topic;

@Resource(mappedName = "jms/MyQueue")
private static Queue queue;

private void doSend(boolean useTopicInsteadOfQueue) throws JMSException
{
Connection connection;
Session session;
MessageProducer producer;

connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

if(useTopicInsteadOfQueue)
{
producer = session.createProducer(topic);
}
else
{
producer = session.createProducer(queue);
}

String[] toBeSend={"Hello World", "How are you", "Bye Bye"};

for(String s : toBeSend)
{
TextMessage message = session.createTextMessage();
message.setText(s);
producer.send(message);
}

producer.close();
session.close();
connection.close();
}

private String doReceive(boolean useTopicInsteadOfQueue) throws JMSException
{
final long TIMEOUT=2L*60L*1000L;

Connection connection;
Session session;

connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer;

if(useTopicInsteadOfQueue)
{
consumer=session.createConsumer(topic, null, false);
}
else
{
consumer=session.createConsumer(queue, null, false);
}

connection.start();

String result="";
TextMessage message;
do
{
message=(TextMessage) consumer.receive(TIMEOUT);
if(message!=null) result+="|"+message.getText();
} while(message!=null && !"Bye Bye".equals(message.getText()));

consumer.close();
session.close();
connection.close();
return result;
}

private static void doReceiverViaCallBackMethod(boolean useTopicInsteadOfQueue) throws JMSException
{
final long TIMEOUT=10L*60L*1000L;

Connection connection;
Session session;

connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer;

if(useTopicInsteadOfQueue)
{
consumer=session.createConsumer(topic, null, false);
}
else
{
consumer=session.createConsumer(queue, null, false);
}

consumer.setMessageListener(new MyJMSClass());

connection.start();
}

public void onMessage(Message message)
{
TextMessage textMessage=(TextMessage) message;
String stringFromTextMessage="";
try
{
stringFromTextMessage=textMessage.getText();
} catch (JMSException ex)
{
stringFromTextMessage="Error: "+ex;
}
System.out.println("CallBackMethod received: "+stringFromTextMessage);
}
}

Um die Klasse über ein Topic zu Kommunikation zu benutzen, erst den Empfänger im Hintergrund starten

MyJMSClass jmsClient=new MyJMSClass();
String messageForMe=jmsClient.doReceive(true);
System.out.println("Receiving Result: "+messageForMe);

Alternativ kann auch eine Callback Methode registriert werden:
MyJMSClass.doReceiverViaCallBackMethod(true);

Dann den Sender starten

MyJMSClass jmsSender=new MyJMSClass();
jmsSender.doSend(true);

Will man statt dessen eine Queue benutzen, übergibt man einfach false statt true an die Methoden, dann kann der Empfänger auch nach dem Sender gestartet werden.

Achtung: Der Empfänger darf nicht vergessen
connection.start();
aufzurufen, sonst kommen keine Nachrichten an!

Links


Context and Dependency Injection (CDI)

Damit in einer Java EE Applikation Beans in die Referenzen in den Beans auf andere Resourcen automatisch erzeugt und eingefügt werden können, ist normalerweise eine entsprechende Deklaration in einer XML Datei notwendig. Z.B.
faces-config.xml
applicationContext.xml
Mit CDI wird das über Annotationen erreicht
@Named(name="mySuperBean")
@RequestScoped
public class MyBean
{
...

@Inject
private OtherBean otherBean;

}

13-02-2012 00.26

Servlets

Ein Servlet ist ein ganz normale Java Klasse, die Interfaces aus javax.servlet implementiert. Ein Servlet Container wie z.B. Tomcat führt diesen Code dann aus, und liefert ihn (meist) als HTML an die Clients. Servlets sind sehr codelastig aber ungeeignet um umfangreiches HTML zu erzeugen.

Servlet Links


Servlet Beispiel

Die Imports sind nur verfügbar, wenn man das jar tomcatlibservlet-api.jar aus einer Tomcat Installation einbindet.
package foo;

import java.io.*;
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;

public class MyHelloWorldClass extends HttpServlet
{
private static final long serialVersionUID = 1L;

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();

out.println("<html>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>"+"Hello World!"+"</h1>");
out.println("Date: "+new Date().toString());
out.println("</body>");
out.println("</html>");
}
}

Wie man ein Servlet in Tomcat startet

Wann man das Servlet foo.MyHelloWorldClass in Tomcat ausführen will, sind folgende Schritte notwendig:

  1. Man denkt sich einen Namen für seine Web Application aus (die normalerweise mehrere Servlets enthalten wird). Beispielsweise "MyFirstServletApplication"
  2. Im Tomcat Installationsordner in den Ordner webapps wechseln. Darunter erzeugt man einen neuen Unterordner MyFirstServletApplication, darunter WEB-INF und darunter classes.
  3. Jetzt kompiliert man die Klasse foo.MyHelloWorldClass, die entstandene .class Datei kopiert man nach
    webapps/MyFirstServletApplication/WEB-INF/classes/foo/
  4. Jetzt fehlt noch eine web.xml (Deployment-Deskriptor) unter
    webapps/MyFirstServletApplication/WEB-INF/
    die so aussieht:
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <web-app
    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 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">

    <servlet>
    <servlet-name>My Super Servlet</servlet-name>
    <servlet-class>foo.MyHelloWorldClass</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>My Super Servlet</servlet-name>
    <url-pattern>/SayHelloWorld</url-pattern>
    </servlet-mapping>

    </web-app>
  5. Siehe auch jsptutorial.org: Verzeichnisstruktur von Java-Webanwendungen
  6. Nachdem man den Tomcat mit startup.bat oder startup.sh gestartet hat, kann man hier das Ergebnis bewundern
    http://localhost:8080/MyFirstServletApplication/SayHelloWorld
02-05-2010 21.50

Java Server Pages (JSP)

Wie wir schon gesehen haben, sind Servelets sind sehr codelastig und ungeeignet um größere HTML Ausgeben zu erzeugen. Viel besser sind dafür Java Server Pages (JSP). Diese haben selbst eine HTML Struktur, können HTML und Java Elemente enthalten und werden dann z.B. von Tomcat automatisch wieder in Servlets übersetzt. Siehe auch: Wie eine JSP in ein Servlet übersetzt wird.

JSP Links


JSP Syntax

Link zur Syntax von JSPs
Eine JSP Seite kann aus folgenden Sprachelementen bestehen

Skripting

Durch Skripting kann man in eine JSP Seite direkt Java Code integrieren.
Scripting Elemente:
  • Expressions
    Eine Expression wird ausgewertet, und in der JSP durch Ihre Ausgabe ersetzt. Beispiele:
    <%= request.getHeader("User-Agent") %>
    <%= request.getParameter("username") %>
    Achtung, kein ; am Ende der Expression verwenden.
  • Skriptlets
    Durch Skriptlets werden Bereiche in einer JSP markiert, in der ganz normaler Java Code verwendet werden kann. Das ähnlich wie <?PHP in einer HTML Datei.
    Beispiel:
    <%
    int x=5;
    %>

    <br />

    <%=
    x++;
    %>
    Zwischen den beiden Skriptlets steht normales HTML, die Änderungen am Wert der Variablen im ersten Skriptlet bestehen auch im zweiten noch fort.
  • Deklarationen
    Mit einer Deklaration kann man Variablen und komplette Methoden definiert, die dann z.B. von Skriptlets aufgerufen werden können.
    <%! int x = 7;
    Integer getAnswer() { return 42; } %>

Kommentare

So kann man einen Kommentar in eine JSP einfügen:
<%-- myComment --%>

Direktiven

Mit einer Direktive kann man Einfluss darauf nehmen, wie aus JSP eine Servlet wird.

<%@ include file="someFile" %>

<%@ page pageEncoding="UTF-8" %>
<%@ page contentType="text/html" %>
<%@ page import="java.math.BigInteger" %>

<%-- Import JSTL-Tags <c:tagname attribute="..." ... >
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

Fehlerbehandlung

Entweder so:
<%@ page errorPage="/WEB-INF/error.jsp" %>
oder in der web.xml zentral konfigurieren
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/jsp/errorHandler404.jsp</location>
</error-page>

Die Expression Language (EL)


Implizite Objekte

Spezielle Objekte, die implizit zur Verfügung stehen

requestjavax.servlet.http.HttpServletRequest
responsejavax.servlet.http.HttpServletResponse
sessionjavax.servlet.http.HttpSession(JSP Session)
pageContextjavax.servlet.jsp.PageContext
applicationjavax.servlet.ServletContext
configjavax.servlet.ServletConfig
outjavax.servlet.jsp.JspWriter
exceptionjava.lang.Throwable

Include

Man hat mehrere jsp Seiten, die im Prinzip das gleiche machen, nur mit anderen Beans.

Ein Lösung,

foo.jsp:
<t:aliasBean value="#{myFooBean}" alias="#{my_super_bean}">
<jsp:include ..../>
</t:aliasBean>

bar.jsp
<t:aliasBean value="#{myBarBean}" alias="#{my_super_bean}">
<jsp:include ..../>
</t:aliasBean>

Und über das jsp:include kann man dann eine gemeinsame Datei einlesen und dort my_super_bean benutzen.
22-09-2012 11.37

Tomcat

Tomcat ist ein Servletcontainer, also ein (Web-) Server, der Javacode ausführen kann.

Tomcat installieren

Nach der selbsterklärenden Installation, wird Tomcat einfach so gestartet und gestoppt (Windows / Linux)
startup.bat
startup.sh

Tomcat und Eclipse


  1. Nach der Tomcat Installation sollte dieser Link funktionieren, und auf den installieren Tomcat zeigen
    http://localhost:8080/

    Optional kann man den Tomcat Manager unter
    http://localhost:8080/manager/
    aktivieren. Nach der Installation funktioniert er vermutlich noch nicht:
    The requested resource (/manager/) is not available.

    Deshalb die conf/tomcat-users.xml editieren (SECRET durch ein sicheres Passwort ersetzen)
    <?xml version='1.0' encoding='utf-8'?>
    <tomcat-users>
    <role rolename="tomcat"/>
    <role rolename="manager"/>
    <role rolename="admin"/>
    <user username="myTomcatAdmin" password="SECRET" roles="tomcat,manager,admin"/>
    </tomcat-users>

    Tomcat neu starten, jetzt sollte er erreichbar sein (falls nicht, mal Browser Cache leeren / Laden ohne Cache erzwingen).
    Hier kann man sich jetzt mit myTomcatAdmin und dem ausgedachten Passwort anmelden
  2. Ein Eclipse Tomcat Plugin installieren, z.B. dieses Tomcat Plugin.
    Danach sollte Eclise das hier anbieten: Eclipse Tomcat Projekt erzeugen
  3. Optional: In Eclipse Preferences Tomcat einrichten, auch Tomcat Manager App einrichten:
    http://localhost:8080/manager
    myTomcatAdmin
    SECRET
  4. Das Plugin bietet sogar die Möglichkeit den Tomcat neu zu starten
    Tomcat neu starten
  5. Neues Tomcat Projekt einrichten, nennen wir es z.B. MyTomcatProject1
    Das Plugin erzeugt dann eine Konfiguration im Tomcat Installationsverzeichnis unter
    conf\Catalina\localhost\MyTomcatProject1.xml
    welches auf den Workspace des Projektes zeigt.
  6. JSP über das Plugin erstellen: Über das Kontextmentü des Projekts eine neue Datei erstellen und z.B. Test1.jsp nennen. Diese wird automatisch hochgeladen und ist sofort testbar:
    Struktur eines neuen Tomcat Projektes in Eclipse
    http://localhost:8080/MyTomcatProject1/Test1.jsp
  7. Servlet über das Plugin erstellen:
    Unter
    WEB-INF/src
    eine Klasse erzeugen:

    package invalid.test.mypackage;
    import javax.servlet.*;

    public class MyServerlet1 extends HttpServlet
    {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
    {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<html>");
    // ...
    }
    }

    Jetzt muss man sich noch eine URL ausdenken, die auf diese Klasse in diesem Package gemappt werden soll. Z.B.
    <servlet>
    <servlet-name>Foo</servlet-name>
    <servlet-class>invalid.test.mypackage.MyServerlet1</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>Foo</servlet-name>
    <url-pattern>/helloWorld</url-pattern>
    </servlet-mapping>

    Damit es unter
    http://localhost:8080/MyTomcatProject1/HelloWorld
    erreichbar ist. Man sogar in der Java Klasse einen Brakepoint setzen.
01-08-2011 01.17

Java Beans und JSP

Java Bean sind normale Java Objekte, die (mindestens) einen parameterlosen Konstruktor haben und für ihre Attribute Getter- und Settermethoden bereitstellen, deren Namen sich an gewissen Konventionen halten (damit man den Namen eines Getters für ein Attribut vorhersagen kann).

Java Beans und JSP Links


Java Beans und JSP Beispiel

Angenommen man hat so eine Bean MyPersonBean, die ein Attribut vorname hat und einen Status verheiratet. Wie schreiben eine jsp Seite, die testMyPersonBean.jsp heißt. In dieser zeigen wir ein Formular an, welches nach Vornamen und Verheiratet Ja/Nein fragt. Beim Absenden ruft die JSP sich selbst wieder auf und zeigt die Werte an, die abgesendet wurden. Über <jsp:useBean> wird dabei festgelegt, welche Java Bean benutzt werden soll und damit auch welche Getter und Setter aufgerufen werden sollen.
<jsp:useBean id="person" class="MyPersonBean" scope="page"/>
<jsp:setProperty name="person" property="*"/>

<html>
<body>
Vorname: <jsp:getProperty name="person" property="vorname"/><br/>
Verheiratet? <jsp:getProperty name="person" property="verheiratet"/><br/>
<br/>
<form name="beanTest" method="POST" action="testMyPersonBean.jsp">
Vorname?
<input type="text" name="vorname" size="50"><br/>
Verheiratet?
<select name="verheiratet">
<option value="false">No</option>
<option value="true">Ja</option>
</select>
<input type="submit" value="Absenden">
</form>
</body>
</html>
02-05-2010 21.50

JSP Tags / Taglib

In einer JSP stehen über Taglibs zusätzliche Elemente mit vielen zusätzlichen Funktionen zur Verfügung.

JSP Tags / Taglib Beispiel

Core JSTL-Tags:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:forEach items="${myBean.myList}" var="myTopic">

JSP Tags / Taglib Links

29-04-2010 01.01

Struts

Man möchte bei einer JSP Anwendung das Model View Controller Muster umsetzen. Dann müsste eigentlich jede JSP Seite umfangreiche Controller Funktionalität bereitstellen. Zum Beispiel müsste jede JSP Logik enthalten, bei welcher Eingabe welche andere JSP aufgerufen wird. Struts übernimmt viele diese Aufgaben für die JSPs.

Struts Links

29-04-2010 01.01

JavaServer Faces

Die JavaServer Faces, übernehmen wie Struts viele Controller Aufgaben, die sonst die jede JSP beinhalten müsste, um das MVC Muster umzusetzen. Hinzu kommen u.a. neue Tags

JavaServer Faces Links


JavaServer Faces Einführung

  1. Ein neues Tomcat Projekt starten
  2. Die Java Server Faces jars herunterladen und nach WEB-INF/lib kopieren
    • jsf-impl.jar
    • jsf-api.jar
  3. Diese Datei anlegen: WEB-INF/faces-config.xml (hier kann man später z.B. konfigurieren welche Seite aufgerufen wird, wenn man eine andere Seite mit einem bestimmten Rückgabewert verlassen hat
    <?xml version="1.0"?>
    <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 http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
    version="2.0">

    </faces-config>
  4. Jetzt braucht man noch eine WEB-INF/web.xml Datei:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee <http://java.sun.com/xml/ns/javaee> " xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd <http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd> " xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

    <servlet>
    <servlet-name>MyJavaServerFacesServlets</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>MyJavaServerFacesServlets</servlet-name>
    <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>

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

    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    </web-app>
    Dieses web.xml verknüpft alle URLs, die der Client aufruft und die mit .jsf enden mit Java Server Faces. Das bedeutet aber nicht;, dass die Dateien auf dem Server auf .jsf enden, sondern nur die URLs (z.B. im Browser). Zwischen URL- und Dateiname findet noch einmal eine Abbildung statt!


JavaServer Faces Beispiel

Dieses Beispiel zeigt, wie man über JavaServer Faces konfiguriert, welche Seite aufgerufen wird, je nachdem mit welchem Rückgabewerte eine Seite verlassen wird.

Als erstes erzeugen wir drei JavaServer Faces Dateien. Alle sollten parallel zum WEB-INF Ordner abgelegt werden

test.xhtml:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>JSF 2.0: Server Test</title>
</h:head>
<h:body>
Hello World
<h:graphicImage url="helloworld.gif" />

<h:form>

<fieldset>
<legend>Good</legend>
Click button. You should get success page.<br/>
<h:commandButton value="Click the good button" action="oh-yes" />
</fieldset>

<fieldset>
<legend>Bad</legend>
Click button. You should get error message about not being able to find matching navigation case.<br/>
<h:commandButton value="Do not click the bad button" action="no-way" />
</fieldset>

</h:form>

</h:body>
</html>

page-for-bad-case.xhtml:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html">
<h:head><title>OK</title></h:head>
<h:body>OK</h:body>
</html>

page-for-good-case.xhtml (wie bad case nur statt OK mit einem anderen Text)

Alle drei Dateien kann man sich jetzt schon direkt im Browser ansehen
http://localhost:8080/myJSFProject1/test.xhtml
Allerdings werden dann die JSF Tags wie z.B. h:graphicImage noch nicht "übersetzt", sie werden also im Browser nicht das gewünschte Ergebnis erzielen.

Ruft man allerdings diese URL auf:
http://localhost:8080/myJSFProject1/test.jsf
greift das Mapping in der web.xml und die JSF Tags werden ersetzt, es wird versucht das Bild anzuzeigen.

Java Server Faces verknüpfen
In der WEB_INF/faces-config.xml eine Navigation definieren:
<navigation-rule>
<from-view-id>/test.xhtml</from-view-id>
<navigation-case>
<from-outcome>oh-yes</from-outcome>
<to-view-id>/page-for-good-case.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>no-way</from-outcome>
<to-view-id>/page-for-bad-case.jsp</to-view-id>
</navigation-case>
</navigation-rule>

Je nachdem ob die test.xhtml mit "oh-yes" oder mit "no-way" verlassen wird, rufen wir die page-for-good-case.jsp oder page-for-bad-case.jsp auf.

Pfad zum webapps Verzeichnis finden

In welchem Ordner auf der Serverfestplatte befindet sich das webapps Verzeichnis?
FacesContext facesContext=FacesContext.getCurrentInstance();

ServletContext servletContext = (ServletContext) facesContext.getExternalContext().getContext();
String lPath=servletContext.getRealPath("/");
01-08-2011 01.17

Java Server Faces und Beans

Auch in einer JSF Seite kann man auf eine Bean zugreifen, und z.B. den Inhalt eines Attributes der Bean oder den Rückgabewert eine Methode der Bean ganz einfach auslesen.

Attribute auslesen:
Hit Message: <h:outputText value="#{myFirstBean.yourMessage}" />
Hit Counter: <h:outputText value="#{myFirstBean.hitCounter}" />
DB Connection: <h:outputText value="#{myFirstBean.myDB.link}" />

Der Client schreibt Werte in einer Bean:
<h:inputText value="#{myFirstBean.yourMessage}" />

Der Rückgabewerte einer Bean entscheidet, welche Seite als nächstes aufgerufen wird:
<h:commandButton value="Click the bean" action="#{myFirstBean.doIt}" />

Damit das die Zuordnung automatisch funktioniert, muss der erste Buchstabe der Beans und Attribute in der JSF klein geschrieben werden, in der Bean selbst muss es aber einen Getter und Setter mit Großen Buchstaben am Anfang geben. Man kann den Namen der Bean aber auch in der @ManagedBean Annotation manuell angeben.

Die Bean sieht dann so aus:
package invalid.test.mypackage;

import java.io.Serializable;
import javax.faces.bean.*;

@ManagedBean(name="myFirstBean")
@SessionScoped
public class MyFirstBean implements Serializable
{
private Integer hitCounter=0;

public Integer getHitCounter() { return this.hitCounter; }
public void setHitCounter(Integer pNew) { this.hitCounter=pNew; }

public String doIt()
{
this.hitCounter++;
return "reload";
}
}

Die Lebenszeit einer Java Server Faces Bean

Die Lebenszeit der Bean lässt sich über eine Annotation steuern:
@RequestScopedDie Bean wird verworfen, sobald die nächste Seite komplett geladen wurde (Default)
@ViewScopedDie Bean wird verworfen, sobald eine Seite verlassen wird
@SessionScopedJeder User hat seine eigene Bean, auf allen Seiten verfügbar ist
@ApplicationScopedEs gibt nur eine einzige Bean für alle User, die sich diese Bean teilen

Java Server Faces ManagedProperty

Man kann sich auch Attribute per Annoation (oder Konfigurationsdatei) belegen lassen

@ManagedProperty(value="#{myDatabaseConnectionBean}")
private MyDatabaseConnectionBean myDB;

Dann wird dieses Attribut mit einer Bean gefüllt, die myDatabaseConnectionBean heisst.

Das macht vor allem bei der Nutzung der faces-config.xml Sinn:
<managed-bean>
<managed-bean-name>myDatabaseConnectionBean</managed-bean-name>
<managed-bean-class>foo.bar.myDatabaseConnectionBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>hitCounter</property-name>
<value>42</value>
</managed-property>
</managed-bean>
29-04-2010 01.01

JavaServer Faces Daten ausgeben und bearbeiten


JavaServer Faces DataTable Ausgeben und Filtern

So kann man Listen von Beans ausgeben und filtern:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
>

<h:head>
<title>JSF 2.0: Server Test</title>
</h:head>

<h:body>

Neuen Filter setzen:

<h:form>
<h:selectOneMenu value="#{myPersonHolderBean.myActiveFilter}">
<f:selectItems value="#{myPersonHolderBean.myAvailableFilterEntries}"/>
<f:ajax render="MyFirstDataTable"/>
</h:selectOneMenu>

<h:dataTable
var="aPerson"
value="#{myPersonHolderBean.myFilteredList}"
border="1"
id="MyFirstDataTable"
>

<f:facet name="caption">
Meine tolle Tabelle. Aktiver Filter: "#{myPersonHolderBean.myActiveFilter}"
</f:facet>

<h:column>
<f:facet name="header">Name der Person</f:facet>
#{aPerson.name}
</h:column>

<h:column>
<f:facet name="header">Alter der Person</f:facet>
#{aPerson.age}
</h:column>

</h:dataTable>
</h:form>

</h:body>
</html>

Das ist die Bean die die Liste enthält und sich merkt was gefiltert ist:
package invalid.test.mypackage;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.model.SelectItem;

@ManagedBean(name="myPersonHolderBean")
@RequestScoped
public class MyPersonHolder implements Serializable
{
private static final long serialVersionUID = 1L;
private List<MyPerson> myList;
private String myActiveFilter;
private List<SelectItem> myAvailableFilterEntries;

public MyPersonHolder()
{
myList=new ArrayList<MyPerson>();

for(int i=0; i<100; i++)
{
myList.add(new MyPerson("Person"+i, i, ((i % 5)==0)));
}

this.myAvailableFilterEntries=new ArrayList<SelectItem>();
this.myAvailableFilterEntries.add(new SelectItem("ALL", "Alle Personen"));
this.myAvailableFilterEntries.add(new SelectItem("Young", "Die jungen"));
this.myAvailableFilterEntries.add(new SelectItem("Old", "Die alten" ));
this.myActiveFilter="ALL";
}

public String getMyActiveFilter() {
return myActiveFilter;
}

public void setMyActiveFilter(String myActiveFilter) {
this.myActiveFilter = myActiveFilter;
}

public List<SelectItem> getMyAvailableFilterEntries() {
return myAvailableFilterEntries;
}

public List<MyPerson> getMyList() {
return myList;
}

public List<MyPerson> getMyFilteredList() {
List<MyPerson> lResult=new ArrayList<MyPerson>();

for(MyPerson p : getMyList())
{
if(myActiveFilter==null || ("ALL".equals(myActiveFilter)) ||
("Young".equals(myActiveFilter) && p.getAge().compareTo(15)<0) ||
("Old".equals(myActiveFilter) && p.getAge().compareTo(60)>0)
)
lResult.add(p);
}
return lResult;
}

}

Das ist die Bean, die in der Liste ausgegeben wird:
package invalid.test.mypackage;

import java.io.Serializable;
import javax.faces.bean.ManagedBean;

@ManagedBean(name="myPersonBean")
public class MyPerson implements Serializable
{
private static final long serialVersionUID = 1L;
private String name;
private Integer age;

public MyPerson()
{
}

public MyPerson(String pName, Integer pAge, Boolean pSelected)
{
this.setName(pName);
this.setAge(pAge);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

}

JavaServer Faces DataTable Editieren

Das ist eine Datentabelle, die man auch noch editieren kann:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
>

<h:head>
<title>JSF 2.0: Server Test</title>
</h:head>

<h:body>
Neuen Filter setzen:
<h:form>
<h:selectOneMenu value="#{myPersonHolderBean.myActiveFilter}">
<f:selectItems value="#{myPersonHolderBean.myAvailableFilterEntries}"/>
<f:ajax render="MyFirstDataTable"/>
</h:selectOneMenu>

<h:dataTable
var="aPerson"
value="#{myPersonHolderBean.myFilteredList}"
border="1"
id="MyFirstDataTable"
>

<f:facet name="caption">Meine tolle Tabelle. Aktiver Filter: "#{myPersonHolderBean.myActiveFilter}"</f:facet>

<h:column>
<f:facet name="header">Editierbar?</f:facet>
<h:selectBooleanCheckbox value="#{aPerson.updateable}">
<f:ajax render="@form"/>
</h:selectBooleanCheckbox>
</h:column>

<h:column>
<f:facet name="header">Name der Person</f:facet>
#{aPerson.name}
</h:column>

<h:column>
<f:facet name="header">Alter der Person</f:facet>
<h:outputText value="#{aPerson.age}" rendered="#{!aPerson.updateable}" />
<h:inputText value="#{aPerson.age}" size="12" rendered="#{aPerson.updateable}" />
</h:column>

<h:column>
<h:commandButton value="Update" rendered="#{aPerson.updateable}">
<f:ajax render="@form" execute="@form"/>
</h:commandButton>
</h:column>


</h:dataTable>

</h:form>
</h:body>
</html>

Geändert hat sich gegenüber dem vorangehenden Beispiel nur noch die Person Bean, die sich jetzt merkt, ob man sie editieren kann oder nicht:
package invalid.test.mypackage;

import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean(name="myPersonBean")
@SessionScoped
public class MyPerson implements Serializable
{
//....

public Boolean getUpdateable() {
return updateable;
}

public void setUpdateable(Boolean updateable) {
this.updateable = updateable;
}

}

JSF DataTable

Änderungen in einer Data Table erkennen

Wenn man in einem Data Table alle Daten editierbar anbietet, hat man das Problem, dass man nicht erkennen kann, welche Daten der User verändert hat. Dafür gibt es demm valueChangeListener

<h:inputText value="#{row.data.myvalue}" valueChangeListener="#{row.myValueChangeListener}" />

public void myValueChangeListener(ValueChangeEvent event) { ... }

Aus dem Event läßt sich leider nicht erkennen, welche Zeile im Data Table diesen Event ausgelöst hat. Eine Möglichkeit wäre, diese Methode in die Bean zu packen, die einer Zeile im Data Table entspricht. Dabei vermischt man allerdings GUI und Datenanspekte (unelegant). Besser: Eine Box Klasse, die die Bean enthält wird benutzt. Die Box Klasse beinhaltet neben der Bean die valueChangeListener Methode (und bei Bedarf andere GUI relevanten Attribute z.B. ob eine Bean in der GUI gerade markiert ist).

Ausgabe formatieren und validieren

Wenn man ein Datumsfeld ausgeben möchte:

<h:outputText value="#{row.myDate}" />
Wird normalerweise nur Jahr, Monat und Tag ausgegeben. Mit Filtern kann man das beeinflussen:

<h:outputText value="#{row.myDate}">
<f:convertDateTime pattern="HH:mm:ss dd.MM.yyyy" />
<f:convertDateTime timeZone="Europe/Berlin" pattern="HH:mm:ss dd.MM.yyyy" />
</h:outputText>

Andere vorgefertigte Filter

<f:convertDateTime pattern="MM/yyyy"/>
<f:converter id="javax.faces.Short"/>
<f:convertNumber maxFractionDigits="2" currencySymbol="$"/>
<f:validateLongRange maximum="150" minimum="42"/>

Man kann aber auch seine eigenen Converter und Validator schreiben:

<h:inputText  value="#{myBean.foo}"
converter="FooConverter"
validator="FooValidator"
valueChangeListener="#{myBean.valueChanged}" />

Und in der faces-config.xml:

<converter>
<converter-id>FooConverter</converter-id>
<converter-class>de.tgunkel.de.bar.gui.converter.FooConverter</converter-class>
</converter>
<validator>
<validator-id>FooValidator</validator-id>
<validator-class>de.tgunkel.de.bar.gui.converter.FooValidator</validator-class>
</validator>


Der Java Code sieht dann so aus
public class FooConverter implements Converter
{
...
}

public class FooValidator implements Validator
{
...
}
01-08-2011 01.17

Spring


Spring Eclipse Plugin

Hier findet man ein Spring Eclipse Plugin.

Spring beziehen


Hier gibt es die Spring Libs. In älteren Spring Versionen benötigte man
  • dist/spring.jar
  • lib/jakarta-commons/commons-logging.jar
In neueren Spring Versionen benötigt man den kompletten "dist" Ordner und das "commons-logging.jar".

Kleines Spring Tutorial

Eine Klasse für einen (Buch-) Leser. Erst ein Interface
package invalid.mypackage;

public interface BookReader
{
public void setCreditStatus(String creditStatus);

@Override
public String toString();
}

dann die eigentliche Implementierung

package invalid.mypackage;

public class BookReaderImpl implements BookReader
{
private BookStore buysBooksAt;

private String creditStatus;

public BookReaderImpl()
{
this.buysBooksAt = new BookStoreOnline();
}

public BookReaderImpl(BookStore pBookLibrary)
{
this.buysBooksAt = pBookLibrary;
}

public void setCreditStatus(String creditStatus)
{
this.creditStatus = creditStatus;
}

@Override
public String toString()
{
return "BookReader [buysBooksAt=" + buysBooksAt + ", creditStatus=" creditStatus + "]";
}

}

Und noch eine Buchverkäufer Klasse. Wieder erste eine Schnittstelle

package invalid.mypackage;

public interface BookStore
{
public boolean buy(String pTitle);
}

dann die eigentliche Implementierung

package invalid.mypackage;

public class BookStoreOnline implements BookStore
{
public BookStoreOnline()
{
}

public boolean buy(String pTitle)
{
// ...
}

@Override
public String toString()
{
return "Online Store";
}
}

Jetzt eine XML Datei, kann man z.B. "beans.xml" nennen, oder (vor allem für Webapplikationen) "applicationContext.xml"
Den Ordner unbedingt in den Klassenpfad übernehmen.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<bean id="myBookStore" scope="singleton" class="invalid.mypackage.BookStoreOnline" />

<bean id="bookReader" scope="singleton" class="invalid.mypackage.BookReaderImpl">
<constructor-arg ref="myBookStore"/>
<property name="creditStatus" value="BBB" />
</bean>

</beans>

Das Ganze wird dann so benutzt

package invalid.mypackage;

import org.springframework.beans.factory.*;
import org.springframework.context.support.*;

public class MyMainClass
{
public static void main(String[] args)
{
BeanFactory beanFactory =new ClassPathXmlApplicationContext("beans.xml");
BookReader lBookReader = (BookReader) beanFactory.getBean("bookReader");
System.out.println(lBookReader);
}

}

Autowire

Normalerweise setzt man mit Spring so eine Referenz von buch auf buchLaden:
<bean id="buchLaden" class="BuchLaden" />

<bean id="buch" class="Buch">
<property name="buchLaden" ref="buchLaden" />
</bean>

public class Buch
{

private BuchLaden buchLaden;

public BuchLaden getBuchLaden() {
return buchLaden;
}

public void setBuchLaden(BuchLaden buchLaden)
{
this.buchLaden = buchLaden;
}

//...
}

Mit Autowire kann man diese Referenz automatisch setzen lassen. Und das ohne entsprechend Einträge in der XML Datei und der Code benötigt nicht einmal einen entsprechenden Setter:

<context:annotation-config />

<bean id="buch" class="autowiredexample.Buch" />
<bean id="buchLaden" class="autowiredexample.BuchLaden" />

import org.springframework.beans.factory.annotation.Autowired;

public class Buch
{
@Autowired
private BuchLaden buchLaden;
}

Der Einsatz von Autowire ist aber wohl nicht unumstritten.

scope

Spring Einführung, die auch den Scope behandelt
Wenn man von Spring eine Bean geliefert bekommt, ist es noch wichtig, zu wissen, wie lange diese Bean gültig ist, und unter welchen Bedingungen man eine neue Bean erhält.
singleton(default) Man bekommt immer wieder genau die selbe Bean
prototypeMan bekommt eine neue Bean
requestweb
sessionweb
globalSessionweb

Spring für Webanwendungen

Einführung: Spring für Webanwendungen
Ein normale Webapplikation starten. Per Contextmenü dem Projekt (mithilfe des Eclipse Spring Plugins) Spring Unterstützung hinzufügen.
Adding Spring support
Eine kleine Bean erstellen:

package foo;

import java.io.Serializable;

public class MyHelloWorldBean implements Serializable
{
private String message;

public MyHelloWorldBean()
{
}

public MyHelloWorldBean(String pMessage)
{
setMessage(pMessage);
}

public String getMessage()
{
return message;
}

public void setMessage(String message)
{
this.message = message;
}

}

WEB-INF/applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
>

<bean id="sayHi" scope="singleton" class="foo.MyHelloWorldBean">
<constructor-arg value="Say hello World" />
</bean>

</beans>

WEB-INF/web.xml:

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

<!-- Get spring reference -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>foo.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/SayHiServlet</url-pattern>
</servlet-mapping>

</web-app>

Und kann das Servlet dann über Spring auf den Wert in der Bean zugreifen:

package foo;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class MyServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");

// get bean via Spring
ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext((getServletContext()));
MyHelloWorldBean b=(MyHelloWorldBean) context.getBean("sayHi");
out.println("<h1>"+b.getMessage()+"</h1>");

out.println("</body>");
out.println("</html>");
}

}

JavaServerFaces und Spring

In der faces-config.xml konfigurieren, dass Beans auch über Spring aufgelöst werden
<?xml version="1.0"?>
<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
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
version="2.0">

<application>
<!-- For Spring -->
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
<!-- For Spring -->
</application>

</faces-config>

In der web.xml muss, wie beim Servlet Beispiel, auch eine Referenz auf Spring vorhanden sein:
<!-- Get spring reference -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

In der JSF kann man dann so die Inhalte der Bean ausgeben:
<h:outputText value="#{sayHi.message}" />
Ist ist dann unerheblich, ob die Bean jetzt über Spring aufgelöst wird, ober durch eine mit
@ManagedBean
annotierten Klasse.

Aspektorientierte Programmierung (AOP) mit Spring

Aspektorientierte Programmierung

Man hat eine Menge von Methoden. Als erstes schränkt man ein, welche dieser Methoden man beinflussen möchte. Diese Filterbedingung nennt man Pointcut.
Eine Methode, die man dabei auswählt hat, nennen man Join-Point.
Jetzt definiert man eine neue Methode, die z.B. beim Verlassen der Join-Point Methode aufgerufen werden soll. Diese neue Methode nenne man dann Advice.

Kleines Beispiel für aspektorientierte Programmierung mit Spring

Folgende jars werden benötigt (Spring 3.x)

Aus dem "dist" Order:
  • org.springframework.aop-3*
  • org.springframework.asm-3*
  • org.springframework.beans-3*
  • org.springframework.context-3*
  • org.springframework.core-3*
  • org.springframework.expression-3*

Plus diese hier aus dem großen Spring Paket (mit Dependencies)
  • com.springsource.org.aopalliance-1*.jar
  • com.springsource.org.aspectj.weaver-1*.jar

beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:aop=http://www.springframework.org/schema/aop
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop> http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
>

<bean id="myBookStore" scope="singleton" class="invalid.mypackage.BookStoreOnline" />
<bean id="bookReader" scope="singleton" class="invalid.mypackage.BookReaderImpl">
<constructor-arg ref="myBookStore"/>
<property name="creditStatus" value="BBB" />
</bean>

<bean id="myBookStoreBuyAdvice" class="invalid.mypackage.BookStoreBuyAdvice" />
<aop:config>
<!-- <aop:pointcut id="myQueryPointcut" expression="target(invalid.mypackage.BookStore)" /> -->
<aop:pointcut id="myQueryPointcut" expression="execution(Integer buy(java.lang.String))" />
<aop:advisor advice-ref="myBookStoreBuyAdvice" pointcut-ref="myQueryPointcut" />
</aop:config>

</beans>

Neue Bean:

package invalid.mypackage;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class BookStoreBuyAdvice implements MethodInterceptor
{

public Object invoke(MethodInvocation invocation) throws Throwable
{
System.out.println("Advice started.");
return invocation.proceed();
}

}

Das in eine Main:

public static void main(String[] args)
{
BeanFactory beanFactory =new ClassPathXmlApplicationContext("beans.xml");
BookReader lBookReader = (BookReader) beanFactory.getBean("bookReader");
System.out.println(lBookReader);
}

Siehe auch:


Spring & JDBC


Spring & Hibernate

Man braucht schon mal folgende jars (Version kann natürlich abweichen)
  • com.springsource.antlr-2.7.7.jar
  • com.springsource.javax.ejb-3.0.0.jar
  • com.springsource.net.sf.cglib-2.2.0.jar
  • com.springsource.org.apache.commons.pool-1.3.0.jar
  • com.springsource.org.apache.openjpa-1.1.0.jar
  • com.springsource.org.hibernate.annotations-3.4.0.GA.jar
  • com.springsource.org.hibernate.annotations.common-3.3.0.ga.jar
  • com.springsource.org.hibernate.ejb-3.4.0.GA.jar
  • com.springsource.slf4j.api-1.5.3.jar
  • commons-collections-3.2.jar
  • commons-dbcp-1.2.1.jar
  • dom4j-1.4.jar
  • ejb3-persistence.jar
  • hibernate-annotations.jar
  • hibernate-commons-annotations.jar
  • hibernate3.jar
  • jta.jar
  • log4j-1.2.14.jar
  • ojdbc14.jar
  • slf4j-api.jar
  • slf4j-log4j12.jar

Das muss in die beans.xml

<!-- Hibernate -->
<bean id="myHibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@MyDBServerName:1521:MyDBName" />
<property name="username" value="yourDBAUserName" />
<property name="password" value="yourSuperSecretPasswort" />
</bean>

<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="annotatedClasses">
<list>
<value>invalid.mypackage.MyDataObject</value>
</list>
</property>

<!--
<property name="mappingResources">
<list>
<value>invalid/mypackage/MyDataObject.hbm.xml</value>
</list>
</property>
-->

<property name="hibernateProperties" ref="myHibernateProperties" />
</bean>

<bean id="myUserDAO" class="invalid.mypackage.UserDAOImpl">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<!-- Hibernate -->

Eine kleine Klasse, die per Annotations gemappt wird:

package invalid.mypackage;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

@Entity(name="MyDataObject")
@Table(name="schemaFoo.TableBar")
public class MyDataObject
{
private Integer id;

private String name;

private Integer age;

@Id
public Integer getId()
{
return id;
}

public void setId(Integer id)
{
this.id = id;
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

public Integer getAge()
{
return age;
}

public void setAge(Integer age)
{
this.age = age;
}

@Transient
@Override
public String toString()
{
return "MyDataObject [id=" + id + ", name=" + name + ", age=" + age + "]";
}

}

Eine Komponente, um auf die Datenbank zuzugreifen:

package invalid.mypackage;

import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateTemplate;

public class UserDAOImpl
{
private HibernateTemplate hibernateTemplate;

public void setSessionFactory(SessionFactory sessionFactory)
{
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}

@SuppressWarnings("unchecked")
public List<MyDataObject> listMyDataObjects()
{
return hibernateTemplate.find("from MyDataObject");
}

}

Und so greift man dann z.B. zu:

UserDAOImpl lDB=(UserDAOImpl) beanFactory.getBean("myUserDAO");

for(MyDataObject d : lDB.listMyDataObjects())
{
System.out.println(d.toString());
}
29-04-2010 01.01

DB Zugriff in Java EE

DAO (Data Access Object)

Damit die Webanwendung von der verwendeten Persistenslösung isoliert ist, kapselt man den Zugriff der Webanwendung auf die Persistenzschicht über sogenannte DAO.

public Interface BestellungDAO
{
public Bestellung findById(Long pID);
public void deleteBestellung(Bestellung pBestellung);
...
}

Konkrete Umsetzungen für eine Persistenzschicht (z.B. Hibernate) implementieren dann das Interface

public class BestellungDAOHibernate implements BestellungDAO
{
...
}

Solange wir im Code nur das Interface benutzen ist die Persistenzschicht leicht austauschbar.

Achtung: Da es in einer Webanwendung leicht passieren kann, dass mehrere Benutzer gleichzeitig versuchen ein Objekt zu verändern, sollte man Schritte unternehmen, um konkurierende Änderungen zu bemerken. Mit Hibernate z.B. eine Spalte für die Version des Objektes benutzen.

In den DAO sollte in der Regel keine Verwaltung der Transaktionen stattfinden, da Transaktionen DAO häufig übergreifen durchgeführt werden sollen.

Aus einer DAO Factory kann man sich dann neue DAO liefern lassen, die dann z.B. gleich eine Referenz auf eine Hibernate Session Factor enthalten.

public class DAOFactory
{
...
public static getBestellungDAO()
{
return new BestellungDAOHibernate(this.hibernateSessionFactory);
}
...
}

Wenn man Spring benutzt kann man die DAO als Beans konfigurieren und die Referenz auf die Hibernate Session Factory per Spring Konfiguriation setzen lassen. Man braucht hier also keine solche Factory.

Da Methoden wie findById(Long pID) für alle DAO nützlich sind, macht es Sinn eine generische Klasse zu schreiben in die diese Methoden ausgelagert werden. Dafür kann man dann Generics benutzen.

Transaktionsarten

Es gibt globale und lokale Transaktionen. Lokale umschließen nur eine Resource (z.B. eine Datenbankverbindung) globale umschließen ein oder mehrere Resourcen (z.B. eine Datenbankverbindung und eine Netzwerkverbindung).

Transaktionen mit Spring

Dabei muss es sich nicht zwangsweise um eine Hibernate oder eine Datenbank Transaktion handeln.
Spring Transaktionen
Es gibt ein Interface
PlatformTransactionManager
TransactionStatus getTransaction(TransactionDefinition definition)
void commit(TransactionStatus status)
void rollback(TransactionStatus status)

dazu das passende TransactionStatus Interface
TransactionStatus
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();

Man definiert sich eine DataSource und einen TransactionManager je nach Anforderungen und benutzter Resource

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

Einschub: So kann das z.B. fertig mit Hibernate aussehen
<!-- Hibernate Property -->
<bean id="oracleHibernateProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.Oracle9Dialect
</prop>
<prop key="hibernate.cache.provider_class">
org.hibernate.cache.NoCacheProvider
</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>

<!-- contains DB username and passwort -->
<bean id="hsqlDataSource"
class="de.allianzgi.ic.data.AppADSDataSource">
<!-- public class AppADSDataSource extends BasicDataSource implements BeanFactoryPostProcessor -->
</bean>

<!-- Hibernate SessionFactory -->
<bean id="hsqlSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

<property name="dataSource" ref="hsqlDataSource" />
<property name="hibernateProperties" ref="oracleHibernateProperties" />

<property name="annotatedClasses">
<list>
<value>de.foo.bar</value>
</list>
</property>

<property name="mappingResources">
<list>
<value>de/foo/blub.hbm.xml</value>
</list>
</property>
</bean>

<!-- Transaction Manager for Hibernate SessionFactory -->
<bean id="hibernateTransactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="hsqlSessionFactory" />
</bean>

<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="hibernateTransactionManager" mode="proxy" />

Spring und Transaktionsverwaltung

High-level approach
Das ist der empfohlene Weg. Man benutzt Template Klassen, wie JdbcTemplate, HibernateTemplate, JdoTemplate und ruft die dortigen Methoden auf, die sich selbst um die Verwaltung der Resourcen und der Transaktionen auf diesen Resourcen kümmern.

Low-level approach
Man kann aber auch DataSourceUtils (for JDBC), SessionFactoryUtils (for Hibernate), PersistenceManagerFactoryUtils (for JDO) benutzen. Man holt sich z.B. so eine JDBC Verbindung:

Connection conn = DataSourceUtils.getConnection(dataSource);

Vorteil: Man kann direkter auf die Resourcen zugreifen.

TransactionAwareDataSourceProxy
Man benutzt einen TransactionAwareDataSourceProxy um auf die DataSource zuzugreifen. Nicht empfohlen.

Declarative transaction management
Auch eine empfohlene Variante, die Konfiguration findet über Aspekt-orientierte Programmierung statt, der Code muss kaum oder nicht angepasst werden.

Man hat eine Interface mit den gewünschten Methoden:
interface FooService
und ein oder mehrere Klassen, die diese Methoden implementieren:

class DefaultFooService implements FooService

Und per config werden dann die Transaktionen definiert:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
...
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
...
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="Foo.service.DefaultFooService"/>

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- what methods should be treated and how: -->
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<!-- Connect the txAdvice with the Service class -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* Foo.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>

<bean id="dataSource" class="..." destroy-method="close">
...
</bean>

<bean id="txManager" class="....DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

</beans>

Man kann natürlich auch gleich alle Methoden aller Klassen in einer bestimmten Klassenhierachie per Spring bearbeiten lassen:

<aop:pointcut id="fooServiceMethods" expression="execution(* Foo.service.*.*(..))"/>

Rollback wird per default nur für ungefangene Runtime Exceptions und Errors ausgelöst. Man kann aber auch nicht Runtime Exceptions konfigurieren:

<tx:method name="get*" read-only="true" rollback-for="MyException" no-rollback-for="MyOtherException"/>

Nachteil: Der Einsatz von AOP muss gut dokumentiert werden, wird sonst leicht vergessen.

@Transactional
Statt AOP zu benutzen, kann man die Methoden, für die die Transaktionen verwaltet werden sollen, auch per Annotation markiert werden

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
...
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
...
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
...
<tx:annotation-driven transaction-manager="txManager"/>
...
@Transactional sollte vor die konkrete Klasse oder Methode gesetzt werden, die die Transaktionen benötigt (nicht an deren Interface)

Achtung, die Methoden einer Klasse, die von außen zu erreichen sind, müssen direkt mit der Annotation markiert werden. Macht man das nicht können innerhalb der Klasse keine Transaktionen verwendet werden, selbst wenn man andere Methoden der eigenen Klasse aufruft, die die Annotation hat.

Data Access Object (DAO) support in Spring

Spring DAO
Vereinfachen Transaktionen wenn man z.B. JDBC oder Hibernate benutzt. Spring bietet abstrakte Klassen an (JdbcDaoSupport für JDBC bietet eine JdbcTemplate an, HibernateDaoSupport für Hibernate bietet ein HibernateTemplate an), von denen man entsprechend erben kann.

DAO JDBC
Spring Data access using JDBC
Object Relational Mapping (ORM) data access
Spring Object Relational Mapping (ORM) data access
Object Relational Mapping (ORM) data access Hibernate

So kann man sich ansehen wann in Hibernate welche Transactionen erzeugt werden:
log4j.logger.org.hibernate.transaction=debug

Man definiert neben der DataSource noch eine Hibernate Session Factory

<beans>

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
<property name="username" value="foo"/>
<property name="password" value="secret"/>
</bean>

<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="mappingResources">
<list>
<value>product.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
</value>
</property>
</bean>

</beans>

Eine Bean, die einen DB Hibernate benutzen möchte bekommt eine Referenz auf die Session Factory

<beans>

<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>

</beans>

Die Session Factor speichert sie dann aber nicht direkt ab, sondern packt sie in ein HibernateTemplate

public class ProductDaoImpl implements ProductDao {

private HibernateTemplate hibernateTemplate;

public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
}

Hat die Klasse solch ein HibernateTemplate, wird es z.B. so benutzt:

this.hibernateTemplate.find("from test.Product product where product.category=?", category);

Aber Achtung: Diese Methode haben offenbar seltsame Default Einstellungen z.B. bezüglich der maximalen Anzahl an Zeilen, die zurückgeliefert werden.

Falls man direkt mit der Session arbeiten möchte geht das so:

this.hibernateTemplate.execute(new HibernateCallback()
{
public Object doInHibernate(Session session)
{
Query q=session.createQuery(...);
return q.list();
}
};


Achtung: Auch wenn man Zugriff auf die Session erhalten hat, die Transaktionen sollten weiterhin von Spring kontrolliert werden. Also NICHT
session.beginTransaction();
benutzen.

Damit man den Setter nicht immer wieder neu implementieren muss, kann man statt dessen aber auch von der Spring Klasse HibernateDaoSupport erben, diese beinhaltet bereits diesen Setter.

Zusätzlich bietet sie auch gleich eine Methode an, um an die Session zu kommen:
Session session = getSession(false);

Mit stellt man sicher dass die Session autoamtisch erstellt und geschlossen wird? -> Auch mit @Transactional

Ab Hibernate 3.x geht es auch ohne HibernateTemplate:

Session sessionFactory.getCurrentSession();

Damit sichergestellt wird, dass in einer Methode auch wirklich eine Transaktion vorhanden ist, kann man dies per Annotation erzwingen
@Transactional(propagation = Propagation.REQUIRED)

oder einfach nur
@Transactional
Dabei ist
REQUIRED
der Default)

Vielleicht auch noch interessant:
Propagation.REQUIRES_NEW
hier wird nicht nur sichergestellt dass möglicherweise bereitsvorhande Transaktionen beendet werden und eine neue zur Verfügung gestellt wird

Fehler:
javax.el.PropertyNotFoundException: javax.el.PropertyNotFoundException: Property 'foo' not found on type $Proxy24

Man hat @Transactional in einer Klasse verwendet

Besser: Ein Interface Bar und eine implementierende Klasse BarImpl entwerfen, die nur dafür benutzt werden zu persisitieren.

Fehlermeldung:

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

applicationContext.xml:
...
<bean id="hibernateTransactionManager"

class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="hsqlSessionFactory" />
</bean>

<!-- enable the configuration of transactional behavior based on
annotations -->
<tx:annotation-driven
transaction-manager="hibernateTransactionManager" mode="proxy" />
...

JUnit Tests Spring
Fehlermeldung:
java.lang.IllegalStateException: No Scope registered for scope 'session' at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:295)
Dann muss man der BeanFactory Scopes unterschieben

import static org.junit.Assert.*;

import org.hibernate.Query;
import org.hibernate.classic.Session;
import org.hibernate.impl.SessionFactoryImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import
org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.RequestScope;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.SessionScope;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="/applicationContext.xml")
@TransactionConfiguration(transactionManager="hibernateTransactionManage
r", defaultRollback=false)
@Transactional
public class MyDBTestClass
{
private static ConfigurableListableBeanFactory beanFactory;
private static SessionFactoryImpl sessionFactory;

@BeforeClass
public static void beforeClass()
{
System.out.println("This is before the class is started");
XmlBeanFactory lBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

/* Scope does not work well within non web applications like for example unit tests
* Add empty scopes to make Spring happy, might have strange effects
*/
lBeanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope() );

lBeanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope() );
MockHttpServletRequest request = new
MockHttpServletRequest();
ServletRequestAttributes attributes = new
ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attributes);
beanFactory=lBeanFactory;
System.out.println("Bean Factory created");
....
sessionFactory=(SessionFactoryImpl)
beanFactory.getBean("hsqlSessionFactory");
}

@BeforeTransaction
public void beforeTransactionMethod()
{
System.out.println("This is before the transaction is started");
}

@Before
public void beforeMethod()
{
System.out.println("This is before the test method is called");
}

@Test
@Rollback(true)
public void modifyDatabaseWithinTransaction()
{
System.out.println("This is a DB test");
HibernateTemplate hibernateTemplate=new HibernateTemplate(sessionFactory);
Session s=hibernateTemplate.getSessionFactory().getCurrentSession();
Query q=s.createSQLQuery("SELECT * FROM dual");
q.list();
System.out.println("Finished DB test");
}

@Test
public void failMe()
{
fail("Fail me");
}

@After
public void afterMethod
()
{
System.out.println("This is after the test method is called");
}

@AfterTransaction
public void afterTransactionMethod()
{
System.out.println("This is before the transaction is started");
}
}

DB Zugriff über Applikation Server (Glassfish)

Damit man über den Applikation Server überhaupt auf eine Datenbank zugreifen kann, muss man diese im Applikation Server erst mal konfigurieren. Hier am Beispiel von Glassfish.

Als erstes ist es notwendig, Programmcode vom Datenbankanbieter in den lib Ordner der Domäne zu kopieren. Z.B. für eine Oracle Datenbank
cp ojdbc6.jar glassfish/domains/MyDomain/lib/databases

Damit Glassfish das neue jar anzieht, muss mindestens die Domaine neu gestartet werden.

Dann muss man im Glassfish einen Connection Pool einrichten

Glassfish Connection Pool Glassfish Connection Pool
Über die GUI kann man einen Ping auf die Datenbank absetzten, der sollte fehlerfrei funktionieren.

Jetzt fehlt nur noch eine DataSource, die auf den neuen ConnectionPool verweist.
Glassfish Data Source

JPA

Ab jetzt können Java EE Anwendungen auf die Datenbank zugreifen. Dafür müssen sie eine Datei persistence.xml beinhalten. Diese sieht minimal etwa so aus

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="MyPersistenceUnit" transaction-type="JTA">
<jta-data-source>jdbc/MyOracleDB</jta-data-source>
<properties>
</properties>
</persistence-unit>
</persistence>

und beinhaltet eine Referenz auf die gerade angelegte DataSource.

Angenommen man hat in der Datenbank solch eine Tabelle:
create table foo.product (id number, name varchar2(255), descr varchar2(255));

Dann sieht ein entsprechendes Mapping in Java dazu z.B. so aus (im Mapping wird absichtlich der Spaltenname descr auf description umgebogen, um zu zeigen wie das geht):

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "foo.product")
public class Product {

private Long id;

private String name;

private String description;

public Product() {
}

public Product(Long id, String name, String desc) {
this.id=id;
this.name=name;
this.description=desc;
}

@Id
@Column(name = "ID")
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Column(name = "descr")
public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}
}

An anderer Stelle im Code kann dann so aus der Tabelle in das Java Objekt gelesen werden

import javax.persistence.PersistenceUnit;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityManager;

public class Foo
{

@PersistenceUnit
private EntityManagerFactory emf;

@Resource
private UserTransaction transaction;

...
EntityManager em = emf.createEntityManager();
List<Product> products = em.createQuery("select p from Product p").getResultList();
...
Product product = new Product(id, name, descr);
transaction.begin();
{
em.persist(product);
}
transaction.commit();
...
22-09-2012 12.35

EJB3

Glassfish herunterladen und installieren.
Und / oder Eclipse mit eingebauten Glassfish (Eclipse IDE for Java EE Developers) herunterladen und installieren.

Glassfish Tutorial
Eclipse mit eingebautem Glassfish starten. Dann Window -> Open Perspecitve -> Java EE
Unten erscheint ein Server Reiter, Rechtsklick, New Server, Download additional server adapters, Glassfish Java EE Server, die gewünschte Version aussuchen. Auf Nachfrage auf das bereits installierte Glassfish verweisen.
Achtung, der Server braucht ein jdk kein jre!

Sobald der Server läuft:

Glassfish Java Webandwendung

File -> New -> Dynamic Web Project

Rechtsklick auf dem neuen Projekt, Run as -> Run on server und den konfigurierten Server auswählen

EJB Glassfish

EJB Links


EJB Glassfish Beispiel

File -> New -> EJB Project

Als Name z.B. "MyEJBProjectNr1" wählen,
x Add project to an EAR, als Namen z.B. "MyEJBProjectNr1EAR" nehmen.
x Create an EJB Client JAR module to hold the interfaces and classes

Dann per Rechtsklick auf das neue Projekt eine Session Bean hinzuügen. Als Typ Stateless, Remote und Local Interface erzeugen.
Eclipse sollte die eigentliche Bean im neuen Projekt anzeigen, die beiden Interfaces allerdings nur im Client Teil.

Im Server Reiter jetzt über Add das Projekt dem Server hinzufügen.

Source Code:

package de.tgunkel.java.MyEJBProjectNr1;
import javax.ejb.Local;
@Local
public interface MySessionStatelessNr1Local
{
public String getMessage();

public void setMessage(String message);

public String getInfoString();
}

package de.tgunkel.java.MyEJBProjectNr1;
import javax.ejb.Remote;

@Remote
public interface MySessionStatelessNr1Remote
{
public String getMessage();

public void setMessage(String message);

public String getInfoString();
}

package de.tgunkel.java.MyEJBProjectNr1;

import java.util.Date;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class MySessionStatelessNr1 implements MySessionStatelessNr1Remote, MySessionStatelessNr1Local
{
private String message;
private Date startdate;


public MySessionStatelessNr1()
{
startdate=new Date();
}

public String getMessage()
{
return message;
}

public void setMessage(String message)
{
this.message = message;
}

public String getInfoString()
{
return "MySessionStatelessNr1 [Created on startdate=" + startdate + ", with message="+ message + "]";
}
}

EJB Client zum Testen

Jetzt zum Testen einen standalone Client schreiben. Ein neues normales Java Project starten, nennen es z.B. "MyEJBProjectNr1StandAloneClient". Wichtig ist, dass wir unbedingt die "glassfish\modules\gf-client.jar" aus dem Glassfish Installationsordner im Buildpfad (Classpath sollte auch reichen?) haben. Zusätzlich binden wir den Client Teil des EJB Projects als Teil des Buildpfades ein. Entweder über Eclipse Java Build Path -> Projects -> Add oder indem man das EJB Client JAR einbindet.

Weitere Informationen: EJB Standalone Client
package de.tgunkel.java.MyEJBProjectNrStandAloneClient;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import de.tgunkel.java.MyEJBProjectNr1.MySessionStatelessNr1Remote;

public class MyMainClient
{

public static void main(String[] args)
{
MySessionStatelessNr1Remote myBean=null;

InitialContext ic;
try
{
ic = new InitialContext();
myBean = (MySessionStatelessNr1Remote) ic.lookup("de.tgunkel.java.MyEJBProjectNr1.MySessionStatelessNr1Remote");
} catch (NamingException e)
{
e.printStackTrace();
}

if(myBean!=null)
{
myBean.setMessage("Hallo World");
System.out.println("Bean message: "+myBean.getInfoString());
}
}
}
01-08-2011 01.17

Java Naming and Directory Interface (JNDI)


Ein Beispiel für JNDI über einen LDAP Server

package de.tgunkel.java.JNDIDemo;

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.NamingException;

class MyJNDIDemo
{
DirContext ctx;

public MyJNDIDemo(String pURL, String pUser, String pPassword) throws NamingException
{
final String SUN_LDAP="com.sun.jndi.ldap.LdapCtxFactory";
Hashtable<String, String> lHashTable = new Hashtable<String, String>();
lHashTable.put(Context.INITIAL_CONTEXT_FACTORY, SUN_LDAP);
lHashTable.put(Context.PROVIDER_URL, pURL);
lHashTable.put( Context.PROVIDER_URL, pURL);
lHashTable.put( Context.SECURITY_PRINCIPAL, pUser);
lHashTable.put( Context.SECURITY_CREDENTIALS, pPassword);
ctx=new InitialDirContext(lHashTable);
}

public String searchPerson(String pSearchString) throws NamingException
{
Attributes attrs = ctx.getAttributes(pSearchString);
return attrs.get("sn").get().toString();
}

public Integer getMyInteger(String pID) throws NamingException
{
return (Integer) ctx.lookup( pID );
}

public void putMyInteger(Integer pInteger, String pID) throws NamingException
{
ctx.bind( pID, pInteger );
}

public void closeConnection() throws NamingException
{
ctx.close();
}
}

Und so sucht man nach einem Text oder schreibt ein Objekt und liest es dann wieder aus (man braucht natürlich einen funktionierenden LDAP Server dafür)

final String lURL="ldap://myldapserver.example.com:389/o=MyJNDIDemo";
final String lSearchTerm="cn=Mr Foo, ou=People";

MyJNDIDemo demo = new MyJNDIDemo(lURL, "cn=DemoUser, o=MyJNDIDemo", "secret");

String result=demo.searchPerson(lSearchTerm);

demo.putMyInteger(new Integer(42), "cn=MySuperInteger");
Integer y=demo.getMyInteger("cn=MySuperInteger");
01-08-2011 01.17

XML

In diesem Abschnitt werden XML relevante Teile von Java EE behandelt. Um die Technologien an einem einfachen Beispiel ausprobieren zu können, ist hier eine XML Datei, die ein sehr einfaches Adressbuch abbildet:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MyContatcs>
<ContactsOwner>John Doe</ContactsOwner>
<People>
<Person>
<Name>Bill</Name>
<Age>39</Age>
</Person>
<Person>
<Name>Jane</Name>
<Age>56</Age>
</Person>
<Person>
<Name>Frank</Name>
<Age>33</Age>
</Person>
</People>
</MyContatcs>

Hier noch ein XML Schema:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" jxb:version="2.0">

<xsd:element name="MyContatcs" type="MyContactsType" />

<xsd:complexType name="MyContactsType">
<xsd:sequence>
<xsd:element name="ContactsOwner" type="xsd:string" maxOccurs="1" minOccurs="1"/>
<xsd:element name="People" type="PeopleListType" maxOccurs="1" minOccurs="1"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="PeopleListType">
<xsd:sequence>
<xsd:element name="Person" type="PersonType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="PersonType">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" maxOccurs="1"
minOccurs="1" />
<xsd:element name="Age" type="xsd:integer" maxOccurs="1"
minOccurs="1" />
</xsd:sequence>
</xsd:complexType>

JAXB

Die Java Architecture for XML Binding (JAXB) ermöglicht es, JAVA Klassen mit XML Dateien zu verknüpfen. Die Klassen werden mit den Inhalten des XML gefüllt oder umgekehrt.

Als erstes eine xsd Definition für die XML Datei schreiben und im Dateisystem ablegen.
Aus der xsd Definition kann dann mit dem Tool xjc (im JDK enthalten) Code für entsprechende Java Klassen erzeugt werden

# xjc -d src/ -p de.tgunkel.JavaXMLDemo contacts.xsd
parsing a schema...
compiling a schema...
de\tgunkel\JavaXMLDemo\ObjectFactory.java
de\tgunkel\JavaXMLDemo\PeopleListType.java
de\tgunkel\JavaXMLDemo\PersonType.java

Jetzt kann man entsprechende Java Objekte erzeugen lassen und mit Inhalten füllen. Aus diesen Objekten kann dann jederzeit das entsprechende XML generiert werden

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import de.tgunkel.JavaXMLDemo.MyContactsType;
import de.tgunkel.JavaXMLDemo.ObjectFactory;
import de.tgunkel.JavaXMLDemo.PeopleListType;
import de.tgunkel.JavaXMLDemo.PersonType;


public class XMLTest
{
...
ObjectFactory objFact=new ObjectFactory();

MyContactsType myContacts=objFact.createMyContactsType();
JAXBElement<MyContactsType> myContactsRootNode = objFact.createMyContatcs(myContacts);

myContacts.setContactsOwner("John Doe");
myContacts.setPeople(objFact.createPeopleListType());
PeopleListType peopleList=myContacts.getPeople();

for(int i=0; i<3; i++)
{
PersonType person=objFact.createPersonType();
person.setName(names.get(i));
person.setAge(ages.get(i));
peopleList.getPerson().add(person);
}

JAXBContext jaxbContext = JAXBContext.newInstance( "de.tgunkel.JavaXMLDemo" );
Marshaller marshall = jaxbContext.createMarshaller();
marshall.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );

FileOutputStream foStream = new FileOutputStream("xml/output.xml");

marshall.marshal( myContactsRootNode, foStream );
// marshall.marshal( myContactsRootNode, System.out );
foStream.close();
...
}

Und auch dem umgekehrte Weg, wenn man schon XML hat und möchte die verknüpften Java Objekte erhalten ist sehr leicht:

import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import de.tgunkel.JavaXMLDemo.MyContactsType;
import de.tgunkel.JavaXMLDemo.ObjectFactory;
import de.tgunkel.JavaXMLDemo.PeopleListType;
import de.tgunkel.JavaXMLDemo.PersonType;


public class XMLTest
{
...
FileInputStream fiStream=new FileInputStream("xml/output.xml");
Unmarshaller unmarshaller=jaxbContext.createUnmarshaller();
JAXBElement<MyContactsType> unMarshalledContactsRootNode =(JAXBElement<MyContactsType>) unmarshaller.unmarshal(fiStream);

String owner=unMarshalledContactsRootNode.getValue().getContactsOwner();
System.out.println("Owner: "+owner);
for(PersonType p : unMarshalledContactsRootNode.getValue().getPeople().getPerson())
{
String name=p.getName();
BigInteger age=p.getAge();
System.out.println("Name: "+name+" Age: "+age);
}
}

}

Owner: John Doe
Name: Bill Age: 39
Name: Jane Age: 56
Name: Frank Age: 33

JAXP

In JAXP gibt es verschiedene Ansätze, XML zu parsen.

JAXP DOM

Man kann das komplette XML in ein DOM Dokument laden

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XMLTest2
{
private void echoNodes(Node pNode)
{
if(pNode!=null)
{
System.out.println(pNode.getNodeName()+": "+pNode.getNodeValue());
NodeList children=pNode.getChildNodes();
for(int i=0; i<children.getLength(); i++)
{
Node child=children.item(i);
echoNodes(child);
}
}
}

public void XMLParser() throws ParserConfigurationException, SAXException, IOException
{
String fileName="xml/output.xml";
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File(fileName));
Node n=doc.getFirstChild();
echoNodes(n);
}

}

JAXP Callback

Der Callback Ansatz über SAX

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;

...

String fileName="xml/output.xml";
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
factory.setNamespaceAware(false);
SAXParser parser = factory.newSAXParser();
parser.parse(new File(fileName), new MyHandler());

...

Der interessante Teil ist hier in der Klasse MyHandler. Die dort enthaltenten Methoden werden immer dann aufgerufen, wenn bestimmte Ergebnisse eintreten (z.B. ein neues Element wird im eingelesenen XML geöffnet)

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class MyHandler extends DefaultHandler
{
@Override
public void startDocument() throws SAXException
{
super.startDocument();
System.out.println("Starting document ...");
}

@Override
public void endDocument() throws SAXException
{
super.endDocument();
System.out.println("Ending document.");
}

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
{
super.startElement(uri, localName, qName, attributes);
System.out.println("<"+qName+">");
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException
{
super.endElement(uri, localName, qName);
System.out.println("</"+qName+">");
}

@Override
public void characters(char[] ch, int start, int length) throws SAXException
{
super.characters(ch, start, length);
String content=new String(ch, start, length);
System.out.println(content);
}
}

StAX

Eine Mischung zwischen dem Event basierten JAXP Ansatz und der DOM Lösung. Es gibt weiterhin Events (z.B. wenn ein neues Element gelesen wird) aber die Abarbeitung der Elemente kann selbst gesteuert werden

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.XMLEvent;

public class XMLTest3
{
private void echoElement(XMLStreamReader x) throws XMLStreamException
{
switch(x.getEventType())
{
case XMLEvent.START_ELEMENT:
System.out.println("<"+x.getName()+">");
break;
case XMLEvent.END_ELEMENT:
System.out.println("</"+x.getName()+">");
break;
case XMLEvent.CHARACTERS:
System.out.println(x.getText());
break;
default:
break;
}
}

public void readXML() throws FileNotFoundException, XMLStreamException
{
String filename="xml/output.xml";
XMLInputFactory xmlif = XMLInputFactory.newInstance();
XMLStreamReader xmlr = xmlif.createXMLStreamReader(filename, new FileInputStream(filename));

while(xmlr.hasNext())
{
xmlr.next();
echoElement(xmlr);
}
}
}
09-08-2011 00.16

Webservices


Webservices sind für Programme etwa das, was Webseiten für menschliche Benutzer sind. Über Webservices könnnen Programme plattformübergreifen kommunizieren. Webservices basieren auf XML und häuft HTTP. Trotzdem kann man im Allgemeinen Webservices über einen Browser nicht benutzen.

Anwendungsbeispiele: Eine Fluggesellschaft bietet über Webservices Flüge an, in Reisebüros läuft ein Programm, welches die Fluginformationen ausliest, einem Benutzer anzeigt, der die Flüge darüber dann auch kaufen kann.

Damit der Klient weiß, welche Schnittstellen der Webservice anbietet, gibt es die Web Services Description Language (WSDL) http://de.wikipedia.org/wiki/WSDL. Der Webservice stellt dazu eine WSDL Datei bereit, in der über XML die Schnittstellen definiert sind.

Die eigentliche Kommunikation findet zwischen Klient und dem Server findet dann über das Simple Object Access Protocol (SOAP) http://de.wikipedia.org/wiki/SOAP statt. SOAP besteht selbst ebenfalls aus XML welche z.B. über HTTP übertragen werden.

Weder die die WSDL Datei noch die SOAP Nachrichten muss man selbst erstellen, das leisten entsprechenden Frameworks transparent im Hintergrund.

Java API für XML WebServics (JAX-WS)

JAX-WS ist ein solches Framework für Java. Damit wird die Bereitstellung eines Webservices sehr einfach.

So erstellt man z.B. einen Webservice der "Hallo Welt" ausgeben und zwei Zahlen addieren kann:

package de.tgunkel.JAVAEE.WS.calculator.server;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService
public class MyCalculatorWS
{

@WebMethod(operationName = "sayHelloWorld")
public String mySayHelloWorldMethod()
{
return "Hello World";
}

@WebMethod(operationName = "addTwoNumber")
public int myAddMethod(@WebParam(name = "i") int pNumber1,
@WebParam(name = "j") int pNumber2)
{
return (pNumber1 + pNumber2);
}

}

Die Annoationen WebMethod und WebParam sind optional, per Default werden alle public Methoden mit ihrem Namen in den Webservice aufgenommen.

Daraus eine WAR Datei erstellen und in den Application Server laden. In Glassfish kann man den Webservice jetzt schon ausprobieren. In die Admin Console, Anwendungen, unsere Webservice Anwendung auswählen, Endpunkt anzeigen.

Java EE Webservice Glassfish Java EE Webservice Glassfish
Dort kann man auch die erzeugte WSDL herunterladen.

Wenn man jetzt einen Client für den Webservice schreiben möchte, kann man mit dem Tool wsimport (bzw. dem in Glassfish enthaltenen ant Task) aus der WSDL direkt entsprechenden Code erzeugen. In unserem Beispiel wurde dann unter anderem die beiden Klassen
de.tgunkel.JAVAEE.WS.client.autogenerated.MyCalculatorWS;
de.tgunkel.JAVAEE.WS.client.autogenerated.MyCalculatorWSService;
erzeugt.

Und so kann der Client dann über die automatisch aus der WSDL Datei erzeugten Klassen auf dem Webservice zugreifen

package de.tgunkel.JAVAEE.WS.calculator.client;

import de.tgunkel.JAVAEE.WS.client.autogenerated.MyCalculatorWS;
import de.tgunkel.JAVAEE.WS.client.autogenerated.MyCalculatorWSService;
import java.io.*;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import javax.xml.ws.WebServiceRef;

@WebServlet(name="ClientServlet", urlPatterns={"/ClientServlet"})
public class ClientServlet extends HttpServlet
{
@WebServiceRef(wsdlLocation = "http://localhost:8080/CalculatorApp/MyCalculatorWSService?wsdl")
public MyCalculatorWSService service;

public void client()
{
try
{
MyCalculatorWS port = service.getMyCalculatorWSPort();

int i = 5;
int j = 42;

String message = port.sayHelloWorld();
int result = port.addTwoNumber(i, j);

((Closeable)port).close();
}
finally
{
out.close();
}
}
...
}

Damit das Einfügen der Resourcen in den Client auch außerhalb des Application Servers funktioniert, muss er mit appclient aufgrufen werden.

JAX-RS / RESTful Webservice

Representational State Transfer (REST) Eine REST Anfrage besteht aus einer URI und einer Operation (GET, POST, PUT und DELETE).
Die URI besteht aus dem Protokoll (http://), dem Servernamen (www.example.com) dem Port (:80), context root (/myrestapplication) und dem REST resource path (/mydata) und schließlich noch dem Pfad zur eigentlichen Resource (/item).

Den REST resource path kann man für JAX-RS auf zwei Arten angeben.
1. Über die web.xml, indem man den Pfad (/mydata) auf ein Servlet mappt, das auf com.sun.jersey.spi.container.servlet.ServletContainer zeigt.
2. Über eine leere Klasse, die eine bestimmte Annotation trägt und von javax.ws.rs.core.Application erbt.

import javax.ws.rs.core.Application;

@ApplicationPath("mydata")
public class AnyRandomName extends Application {
}

Jetzt kann man eine Klasse erstellen, die unter einer solchen URI die verschiedenen Operationen anbietet:

@Path("item")
public class Shop
{

@GET
@Produces("text/xml")
public String getItem()
{
...
return ...
}

@PUT
@Consumes("text/xml")
public void createItem(String pItemXML)
{
...
}

@POST
@Consumes("text/xml")
public void updateItem(String pItemXML)
{
...
}

@DELETE
@Consumes("text/xml")
public void deleteItem(String pItemXML)
{
...
}

}

Es ist noch etwas unhandlich dabei immer mit XML Strings zu hantieren. Aber auch dafür gibt es über JAXB eine praktische Lösung.

@XmlRootElement
public class Item implements Serializable
{

...

private String itemName;

...

public String getItemName()
{
return itemName;
}

...

}

Jetzt kann die Shop Klasse umgeschrieben werden und statt XML Strings die Item Klasse benutzen

@Path("item")
public class Shop
{

@GET
@Produces("text/xml")
public Item getItem()
{
...
return ...
}

@PUT
@Consumes("text/xml")
public void createItem(Item pItemObject)
{
...
}

...
}
22-09-2012 11.37

Java Authentication and Authorization Service (JAAS)

In der Glassfish Admin Console legt man unter Security in der File Realm einen neuen User an. Der User wird mindestens einer Gruppe zugefügt. Hier z.B. der User "MyDemoUserInGlassfish" und die Gruppe "MyVIPGroupInGlassfish".
Glassfish security new user
In der web.xml des Projektes definiert man jetzt eine Reihe von Sicherheitseinstellungen.

<security-role>
<role-name>myRole</role-name>
</security-role>

<security-constraint>
<display-name>My Security Constraint</display-name>
<web-resource-collection>
<web-resource-name>My Protected Area</web-resource-name>
<url-pattern>/topsecret/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>myRole</role-name>
</auth-constraint>
</security-constraint>

<login-config>
<auth-method>FORM</auth-method>
<realm-name>Example Form-Based Authentication Area</realm-name>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/error.jsp</form-error-page>
</form-login-config>
</login-config>


Damit legt man fest, dass URLs, die mit /topsecret/* anfangen, nur von Benutzern besucht werden dürfen, die die Rolle "myRole" inne haben. Falls sich der Besucher noch nicht als User ausgewiesen hat, wird er auf die Seite login.jsp umgeleitet.

Damit das funktioniert, muss es noch eine Abbildung zwischen den Usern und Gruppen in Glassfish und den Rollen geben. Das kann über die Datei glassfish-web.xml erreicht werden (angeblich auch über sun-web.xml, hat aber nicht funktioniert).

<security-role-mapping>
<role-name>myRole</role-name>
<group-name>MyVIPGroupInGlassfish</group-name>
</security-role-mapping>

In der login.jsp muss es mindestens diesen beiden Felder geben

<input type="text"     name="j_username"></td>
<input type="password" name="j_password"></td>

So kann man einen User wieder ausloggen
session.invalidate();

Damit man HTTPS benutzt, muss man in der web.xml
<security-constraint>
...
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</security-constraint>
setzen. Noch strenger ist
<transport-guarantee>INTEGRAL</transport-guarantee>
damit werden nicht HTTPS Anfragen nicht nur umgeleitet, sondern abgelehnt.

Statt dem File Realm kann man auch den Certificate Realm benutzen, dort authentifizieren sich die User über ein Client Zertifikat. Die Zertifikate können mit diesem Tool erzeugt werden
keytool
13-02-2012 00.26

Truststore kaputt

Ich hatte eine Zeit lange immer mal wieder diese Fehlermeldung:
java.io.IOException: Keystore was tampered with, or password was incorrect
Ursache: Eine Anwendung hat den Truststore und Keystore von Glassfish benutzt und dabei beschädigt.

Lösung: Diese Datei aus einer Sicherung wiederherstellen:
glassfish\domains\domain1\config\cacerts.jks
Und in der bösen Anwendung den Truststore und Keystrore selbst festlegen:

System.setProperty("javax.net.ssl.trustStore",        "mystore.jks");
System.setProperty("javax.net.ssl.trustStorePassword","supersecret");
System.setProperty("javax.net.ssl.keyStore", "mystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "supersecret");

Oder über Parameter der Java Anwendung
-Djavax.net.ssl.keyStore=mystore.jks
-Djavax.net.ssl.keyStorePassword=supersecret
-Djavax.net.ssl.trustStore=mykeys.jks
-Djavax.net.ssl.trustStorePassword=supersecret

Hier noch mal der komplette Stacktrace für Google
 at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:553)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:544)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:362)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
at org.apache.catalina.core.StandardContext.contextListenerStart(StandardContext.java:4664)
at com.sun.enterprise.web.WebModule.contextListenerStart(WebModule.java:535)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:5266)
at com.sun.enterprise.web.WebModule.start(WebModule.java:499)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:928)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:912)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:694)
at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1947)
at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1619)
at com.sun.enterprise.web.WebApplication.start(WebApplication.java:90)
at org.glassfish.internal.data.EngineRef.start(EngineRef.java:126)
at org.glassfish.internal.data.ModuleInfo.start(ModuleInfo.java:241)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:236)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:339)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:183)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:272)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:305)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:320)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1176)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$900(CommandRunnerImpl.java:83)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1235)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1224)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:365)
at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:204)
at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:166)
at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:100)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:245)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
at java.lang.Thread.run(Thread.java:662)
Caused by: java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: com.sun.net.ssl.internal.ssl.DefaultSSLContextImpl)
at javax.net.ssl.DefaultSSLSocketFactory.throwException(SSLSocketFactory.java:179)
at javax.net.ssl.DefaultSSLSocketFactory.createSocket(SSLSocketFactory.java:192)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.jndi.ldap.Connection.createSocket(Connection.java:317)
at com.sun.jndi.ldap.Connection.<init>(Connection.java:187)
... 70 more
Caused by: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: com.sun.net.ssl.internal.ssl.DefaultSSLContextImpl)
at java.security.Provider$Service.newInstance(Provider.java:1245)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:220)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:147)
at javax.net.ssl.SSLContext.getInstance(SSLContext.java:125)
at javax.net.ssl.SSLContext.getDefault(SSLContext.java:68)
at javax.net.ssl.SSLSocketFactory.getDefault(SSLSocketFactory.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.jndi.ldap.Connection.createSocket(Connection.java:273)
... 71 more
Caused by: java.io.IOException: Keystore was tampered with, or password was incorrect
at sun.security.provider.JavaKeyStore.|#]
22-09-2012 11.37
Powered by PHP Created with Xemacs Valid XHTML 1.0! Valid CSS!