Schlagwort-Archive: Java

Wildfly: JDBC-Treiber und Datasource einrichten

Ich hatte hier beschrieben, wie man eine Datasource inklusive neuem JDBC-Treiber per CLI im JBoss einrichten kann. Heute wollte ich mit einer Wildfly 8.2 Installation diese Schritte durchführen, leider funktionierte es nicht, es gibt einen Bug, der das verhindert. Eine Variante, die funktioniert, ist über die Web Management Console. Eine Anleitung habe ich hier gefunden. Nachfolgend habe ich das nochmal bei nachgestellt und Screenshots davon erstellt.

JDBC-Treiber installieren

Zuerst muss ein passender JDBC-Treiber installiert werden, falls noch nicht vorhanden. Dazu öffnet man die Managementconsole, in der Regel ist sie unter Port 9990 zu finden, bei mir lokal unter http://localhost:9990/. Falls noch kein Managementuser vorhanden ist, muss man noch einen anlegen und sich dann mit diesem anmelden. Der Applikationsserver muss natürlich laufen. Dann öffnet man den Reiter Deployments.

screenshot-localhost 9990 2015-01-04 17-26-28

Ein Klick auf den Button Add öffnet den Wizard zum hinzufügen eines neuen Deployments. Hier wählt man die JAR-Datei des entsprechenden JDBC-Treibers aus und bestätigt mit Next.

screenshot-localhost 9990 2015-01-04 17-28-17

Im folgenden Fenster kann man den Namen und den Runtime-Namen noch anpassen. Zusätzlich kann man durch Auswählen von Enable den Treiber gleich aktivieren. Die Eingaben bestätigt man durch Save.

screenshot-localhost 9990 2015-01-04 17-28-55

Der installierte JDBC-Treiber sollte jetzt angezeigt werden.

screenshot-localhost 9990 2015-01-04 17-29-24

Nun wechselt man auf den Reiter Configuration. Hier wählt man bei den Subsystems -> Connector -> Datasources aus. Falls man die Konfiguration noch nicht angepasst hat, sollte die Beispieldatasource ExampleDS noch aufgeführt werden. Um jetzt eine eigene hinzuzufügen wählt man Add aus.

screenshot-localhost 9990 2015-01-05 00-04-29

Name und JNDI Adresse der neuen Datasource werden jetzt abgefragt. Der JNDI-Name hat der Adresse zu entsprechen, die man später in der persistence.xml unter dem Element jta-data-source einträgt. Die Eingaben sind mit Next zu bestätigen.

screenshot-localhost 9990 2015-01-05 01-20-30

Hier wählt man den passenden JDBC-Treiber aus und bestätigt die Eingabe wieder mit Next.

screenshot-localhost 9990 2015-01-05 00-05-17

In diesem Fenster werden die Verbindungdaten zur Datenbank eingetragen. Mit Test Connection können die Eingaben überprüft werden. Das Anlegen der Datasource ist mit Done abzuschliessen.

screenshot-localhost 9990 2015-01-05 00-06-58

screenshot-localhost 9990 2015-01-05 00-07-16

Da die Datasource noch nicht aktiviert ist, wählt man sie aus und klickt auf Enable.

screenshot-localhost 9990 2015-01-05 00-07-39

Das Fenster mit Confirm schliessen.

screenshot-localhost 9990 2015-01-05 00-08-03

Nun steht die Datasource mit dem passenden Treiber im Wildfly zur Verfügung.

Die passenden persistence.xml, die im META-INF des WARs liegen muss, sieht bei mir so aus:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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_2_0.xsd">
   <persistence-unit name="primary">
      <jta-data-source>java:jboss/datasources/dblocator</jta-data-source>
      <properties>
         <!-- Properties for Hibernate -->
         <property name="hibernate.hbm2ddl.auto" value="update" />
         <property name="hibernate.show_sql" value="false" />
      </properties>
   </persistence-unit>
</persistence>

Update 25.01.2015

Neuer Rechner, neues Glück. Wildfly 8.2 auf einem Ubuntu-Rechner hat jetzt noch ein anderes Verhalten an den Tag gelegt. Das Deployen eines JDBC-Treibers war dort über den oben beschriebenen Weg nicht persistent möglich. Nach jedem Neustart des Wildfly war der JDBC-Treiber weg und musste neu deployt werden.

Von JBoss gibt es hier eine gute Anleitung, wie man einen JDBC-Treiber permanent deployt. Anschliessend kann man eine Datasource einrichten, wie oben beschrieben über die webbasierte Managementconsole.

Für PostgreSQL habe ich unter wildfly/modules/org/postgres/main/modules.xml folgendes eingetragen:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="org.postgres">
  <resources>
    <resource-root path="postgresql-jdbc4-9.2.jar"/>
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.transaction.api"/>
  </dependencies>
</module>

Unter wildfly/modules/org/postgres/main/ liegt in diesem Fall der JDBC-Treiber für Postgres mit dem Dateinamen postgresql-jdbc4-9.2.jar.

Zum Aktivieren des JDBC-Treibers für Postgres habe ich folgende Zeile in der JBoss CLI genutzt:

/subsystem=datasources/jdbc-driver=postgres:add(driver-name=postgres,driver-module-name=org.postgres,driver-xa-datasource-class-name=org.postgresql.xa.PGXADataSource)

Das JBoss CLI nutzt man, in dem man den Wildfly startet, im wildfly/bin Verzeichnis jboss-cli.sh startet und sich dann mit connect mit der Wildfly-Managementconsole verbindet.

Maven und Jetty Embedded

Ich war auf der Suche nach einem Applikationsserver oder Servletcontainer, den ich embedded laufen lassen kann. Ziel ist es, eine quasi Standalone-Webanwendung hinzubekommen, die keinen installierten Applikationsserver benötigt. Am Ende sollte ein java -jar anwendungs.jar reichen, um eine Webanwendung zu starten. Ich hab Jetty ausprobiert.

Um Jetty als embedded Servletcontainer laufen lassen zu können, habe ich folgende pom.xml zusammengestellt:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>de.yellowshoes.jettyembedded</groupId>
    <artifactId>test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>test</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>9.1.5.v20140505</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>de.yellowshoes.jettyembedded.test.HelloWorld</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>de.yellowshoes.jettyembedded.test.HelloWorld</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Noch kurz die wichtigsten Teile der pom.xml erklärt.

Damit werden die notwendigen Jar-Dateien von Jetty als Abhängigkeiten für das Projekt definiert:

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>9.1.5.v20140505</version>
        </dependency>

Mit dem exec-maven-plugin kann man per Maven eine beliebige Javaklasse (wenn sie denn eine main-Methode implementiert) über Maven Kommandozeile aufrufen. Im Beispiel wird versucht eine Klasse de.yellowshoes.jettyembedded.test.HelloWorld aufzurufen. Der Aufruf über die Kommandozeile sieht dann beispielsweise so aus: mvn clean compile exec:java

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>de.yellowshoes.jettyembedded.test.HelloWorld</mainClass>
                </configuration>
            </plugin>

Um ein Standalone-Jar zu erzeugen, nutze ich hier das Share-Plugin. Damit wird das Jar gebaut und alle definierten Abhängigkeiten entpackt und mit ins erzeugte Jar eingefügt. Der Aufruf zum Bauen des Jar sieht so aus: mvn clean install

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>de.yellowshoes.jettyembedded.test.HelloWorld</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

Die erzeugte Jar-Datei sollte jetzt test-0.0.1-SNAPSHOT.jar heissen und mittels java -jar test-0.0.1-SNAPSHOT.jar gestartet werden können….wenn man die fehlende Klasse HelloWorld hätte 🙂

package de.yellowshoes.jettyembedded.test;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;

public class HelloWorld extends AbstractHandler {

    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        response.setContentType("text/html;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        baseRequest.setHandled(true);
        response.getWriter().println("<h1>Hello World</h1>");
    }

    public static void main(String[] args) throws Exception {
        Server server = new Server(8080);
        server.setHandler(new HelloWorld());

        server.start();
        server.join();
    }
}

Jboss: Logging einrichten

Über die JBoss CLI kann man für seine Applikationen Loggingkonfigurationen definieren, die in der Applikation genutzt werden können.

Zuerst richtet man einen Handler ein, der für das Schreiben in einer Datei zuständig ist:

/subsystem=logging/periodic-rotating-file-handler="DBLOCATOR":add(formatter="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n", append=true, file={"relative-to"=>"jboss.server.log.dir", "path"=>"dblocator.log"}, suffix=".yyyy-MM-dd", name="DBLOCATOR")

Anschliessend definiert man einen Logger, den man später in der Applikation ansprechen kann:

/subsystem=logging/logger=de.sauerspace.dblocator:add(level=INFO, handlers=["DBLOCATOR"], use-parent-handlers=false)

In der Applikation bekommt man den passenden Log4J Logger dann über folgende Anweisung:

Logger log = Logger.getLogger( "de.sauerspace.dblocator" );

 

JBoss: Datasources einrichten

Update:

Nach diversen Problemen mit dem Deployment von Treibern und Einrichten von Datasources hier noch eine andere Variante, die jetzt wohl weniger Probleme macht. Die folgenden Befehle werden im JBoss CLI ausgeführt. Dazu im bin-Verzeichnis der JBoss Installation jboss-cli.sh ausführen. Anschliessend mittels connect mit dem laufenden JBoss verbinden. Funktioniert auch mit dem Wildfly.

module add --name=org.postgresql --resources=postgresql-9.3-1100.jdbc41.jar --dependencies=javax.api,javax.transaction.api
/subsystem=datasources/jdbc-driver=postgres:add(driver-module-name=org.postgresql,driver-name=postgres,driver-class-name=org.postgresql.Driver)
/subsystem=datasources/data-source="BeispielDS":add(jndi-name="java:jboss/datasources/BeispielDS",max-pool-size=10,min-pool-size=5,driver-name="postgres",connection-url="jdbc:postgresql://localhost:5432/beispieldb",user-name="beispieluser",password="geheim")

Die Datasource kann dann unter java:jboss/datasources/beispieldb angesprochen werden.


 

Alter Text:

Um eine Datasource im JBoss einzurichten, kann man dies per JBoss CLI umsetzen.

Falls der passende Treiber für die Datenbank noch nicht installiert ist, macht man dies mit folgendem Befehl. Anschliessend ist der Treiber unter dem Namen postgres referenzierbar.

deploy postgresql-9.3-1100-jdbc4.jar --name=postgres

Eine Datasource legt man mit folgendem Befehl an:

/subsystem=datasources/data-source="DBLocator":add(jndi-name="java:jboss/datasources/dblocator",max-pool-size=10,min-pool-size=5,driver-name="postgres",connection-url="jdbc:postgresql://localhost:5432/dblocator",user-name="username",password="geheim")

 

jCIFS und OutOfMemoryExceptions

Aktuell arbeite ich mit jCIFS, einer Java-Libary für den Zugriff auf Windows-Shares. Dabei wurde in bestimmten Situationen reproduzierbar eine OutOfMemoryException geworfen. Der Stacktrace deutete auf Probleme in SmbFile.listFiles() hin. In einer Mailingliste zu Samba gab es den Hinweis, dass es ein Problem mit „Name lookup threads“ geben könnte. Da mir aufgefallen war, dass bei Ausführung des Codes die Threadanzahl von ca. 60 auf über 1000 stieg, war das ein Hinweis, der eine heisse Spur sein könnte.

Über die statische Config-Klasse wurde dann per setProperty die Reihenfolge der aufzurufenden Nameresolver geändert. Und siehe da, dann gab es keine OutOfMemoryExceptions mehr und die Threadanzahl blieb bei ca. 60. So setzt/verändert man die Reihenfolge der Nameresolver:

Config.setProperty( "jcifs.resolveOrder", "DNS,LMHOSTS,WINS" );

Wichtig war DNS an den Anfang zu setzen. Defaultmässig steht es ganz hinten.

[Android] RESTEasy und Android

Wurde eine REST-Schnittstelle auf Server-Seite mit RESTEasy implementiert, gab es bisher keine wirkliche Möglichkeit, mit RESTEasy den vorhandenen Code auch auf Android-Seite zu nutzen. RESTEasy funktionierte bisher nicht unter Android. Thomas Diesler hat glücklicherweise ein Projekt auf github (https://github.com/tdiesler/resteasy-mobile) gestartet, welches diesen Teil nachimplementiert. Er hat hierzu auch einen Blogpost verfasst.

Nachfolgende die Kurzform der notwendigen Schritte. Ich habe den Code in drei Projekte aufgeteilt:

  • REST-Interface und Transferobjekte
    In diesem Projekt werden die Transferobjekte und das REST-Interface implementiert. Transferobjekte sind die Klassen, die die Daten zwischen Server und Client kapseln. Wie der Name REST-Interface schon vermuten lässt, wird damit die Schnittstelle zwischen Client und Server definiert (aber noch nicht implementiert). Nutzt Maven.
  • REST-Server-Implementierung
    Die Implementierung der Backend-Logik. Benutzt das REST-Interface und die Transferobjekte. Nutzt Maven.
  • REST für Android
    Die Implementierung der Android-Client-Seite. Benutzt das REST-Interface und die Transferobjekte.

Für das REST-Interface und Transferobjekte wird folgende Dependency in der pom.xml eingetragen:

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxrs</artifactId>
 <version>1.2.1.GA</version>
</dependency>

Für die REST-Server-Implementierung werden folgende Dependencies in der pom.xml eingetragen:

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxrs</artifactId>
 <version>1.2.1.GA</version>
</dependency>

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxb-provider</artifactId>
 <version>1.2.1.GA</version>
 </dependency>

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>jaxrs-api</artifactId>
 <version>1.2.1.GA</version>
</dependency>

<dependency>
 <groupId>net.sf.scannotation</groupId>
 <artifactId>scannotation</artifactId>
 <version>1.0.2</version>
</dependency>

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jackson-provider</artifactId>
 <version>2.3.1.GA</version>
</dependency>

<!-- Reference to REST-Interface-Project -->
<dependency>
 <groupId>de.yellowshoes.rest</groupId>
 <artifactId>restinterface</artifactId>
 <version>0.0.1-SNAPSHOT</version>
</dependency>

Auf Android-Seite ist kein Maven im Einsatz. Folgende Libraries werden im Buildpath mit aufgenommen:

  • Jackson Core ASL 1.6.3
  • Jackson JAXRS 1.6.3
  • Jackson Mapper ASL 1.6.3
  • Jackson XC 1.6.3
  • JAXRS API 2.2.1 GA von RESTEasy
  • RESTEasy Mobile 1.0.0

Nachfolgend ein einfaches Beispiel, wie eine konkrete Implementierungen aussehen kann:

REST-Interface

Zuerst die Transferklasse. Hier ein Objekt Book. Ein simples POJO, mehr nicht.

public class Book
 {
 private String titel;
 private String author;

 public String getTitel()
 {
 return titel;
 }

public void setTitel( String titel )
 {
 this.titel = titel;
 }

public String getAuthor()
 {
 return author;
 }

public void setAuthor( String author )
 {
 this.author = author;
 }
}

Nun das REST-Interface. Die @Path-Annotation definiert jeweils, an welche Url die Klasse/die Methode gebunden wird. Hier wird das komplette Interface an /api gebunden, die Methode (durch die Vererbung) an /api/book. Mit den Annotations @Consumes und @Produces wird festgelegt, was für MediaTypes verarbeitet werden können und was zurückgeschickt werden kann. Hier wird jeweils JSON definiert.

@Path( "/api" )
@Consumes( MediaType.APPLICATION_JSON )
@Produces( MediaType.APPLICATION_JSON )
public interface RestResourceInterface
 {
 @GET
 @Path( "/book" )
 public Book getBook();
 }

REST-Implementierung

Auf Server-Seite wird nur eine Klasse benötigt.

public class RestResource implements RestResourceInterface
 {
 public Book getBook()
  {
  Book book = new Book();
  book.setAuthor( "Hans Müller" );
  book.setTitel( "REST gut" );
  return book;
  }
}

REST-Android

Auch auf Android-Seite muss nicht mehr viel gemacht werden. Hier eine einfache Client-Implementierung. Das wichtigste ist die Methode getClient(), die über die ProxyFactory die Schnittstellen-Implementierung initialisiert. Der Code sollte ansonsten selbsterklärend sein.

public class ResteasyClient implements RestResourceInterface
 {
 RestResourceInterface client = null;

 @Override
 public Book getBook()
  {
  RestResourceInterface client = getClient();
  Book book = client.getBook();
  return book;
  }

 private RestResourceInterface getClient()
  {
  String requestURI = "http://192.168.11.86:8080/restest/";

  if( client == null )
   {
   BasicHttpParams params = new BasicHttpParams();
   HttpProtocolParams.setVersion( params, HttpVersion.HTTP_1_1 );
   HttpProtocolParams.setContentCharset( params, HTTP.DEFAULT_CONTENT_CHARSET );
   HttpProtocolParams.setUseExpectContinue( params, false );
   client = ProxyFactory.create( RestResourceInterface.class, requestURI, new ApacheHttpClient4Executor( params ) );
   }
  return client;
  }
 }