This blog has moved to
http://blog.matthias-reining.com

Die bestehenden Artikel bleiben vorerst alle bei blogspot. Neue Artikel veröffentliche ich allerdings nur noch auf http://blog.matthias-reining.com

Montag, 15. Dezember 2008

JBoss 5.0.0 GA und Eclipse

Endlich! JBoss 5 ist als GA Version erhältlich!

Wenn ich allerdings den JBoss Server in mein Eclipse WTP einbinden will bekomm ich folgende Fehlermeldung:

Missing classpath entry <server_path>\jboss-5.0.0.GA\server\default\lib\mail.jar

In der JBoss 5 CR2 Version hat dies noch alles funktioniert L

Der Grund hierfür liegt an der geänderten Verzeichnisstruktur des JBoss Servers. Es gibt nun ein common Verzeichnis, in dem gemeinsam genutzte libs (jar-Files) ausgelagert sind. Dafür ist nun bspw. das Verzeichnis jboss-5.0.0.GA\server\default\lib komplett leer.

Nach etwas googlen habe ich auch das Problem gefunden.

https://bugs.eclipse.org/bugs/show_bug.cgi?id=257823

Das Ticket in kürze zusammengefasst:

Durch die Änderungen in der JBoss Verzeichnisstruktur sind einige Anpassungen an der JBoss Einbindung notwendig:

In der Datei jboss5.serverdef im Eclipse plugin Verzeichnis (eclipse\plugins\org.eclipse.jst.server.generic.jboss_1.5.205.v200805140145\servers) müssen einige Änderungen vorgenommen werden.

Unter anderem muss zum Beispiel in der Zeile 129 folgendes

<exclude name="server/default/lib/mail.jar" /> mit <exclude name="common/lib/mail.jar" /> ausgewechselt werden.

Und so weiter…

Glücklicherweise gibt es das File auch schon komplett angepasst:

https://bugs.eclipse.org/bugs/attachment.cgi?id=120132

Achtung: immer nach dem neuesten Attachment Ausschau halten, vielleicht kommen ja noch ein paar Anpassungen…

Ansonsten wird dies sicherlich über einen Eclipse Patch reinkommen.

Bei mir hat dieser Workaround tadellos funktioniert. Vielen Dank an dieser Stelle an die fleißige Eclipse Community und vor allem an Wolfgang Knauf der das File hierfür erstellt hat!

Donnerstag, 4. Dezember 2008

Getter, Setter versus Domain Model/ Persistent Entities

Beim Anlegen meines Domain Models in Java stell ich mir mittlerweile schon die Frage: "Was sollen die ganzen getter- und setter Methoden?"

Selbstverständlich: die JavaBean Konvention!
http://java.sun.com/javase/technologies/desktop/javabeans/docs/spec.html

Anbei nun ein paar Gedanken hierzu:

Eigentlich sind die getter und setter Methoden bei Persistent Entities bzw. einem Domain Model zum Großteil nur eine Assistenten-Übungsaufgabe (zumindest innerhalb von Eclipse und allen anderen IDEs).

Es wäre zwar schon etwas schräg, aber eigentlich wäre hier mit public Variablen das Problem schon gelöst. Von einer Datenkapselung kann man eigentlich nicht mehr wirklich reden, da ja die getter und setter in der Regel direkten Zugriff auf die Variable geben. Gut: alle APIs die per Refelection die entsprechenden getter und setter Methoden aufrufen, müssten dann natürlich auch angepasst werden…

Soweit würde ich auch gar nicht gehen wollen.

Ich fände einen Ansatz per Annotations ganz nett:

@Entity
Public class Person {
    //…
    @GetterAccess
    private String firstname;
    //…
}

Die Annotation @GetterAccess würde hier nun ein entsprechendes public String getFirstname() automatisch generieren.

Eigentlich sollten alle Variablen, deren Klasse mit @Entity annotiert ist, automatisch getter und setter Methoden generiert bekommen. Die Methoden sollten dann einfach bei Bedarf "überschrieben" werden können.

Gibt es sowas vielleicht schon?

Soweit so gut erst mal mit meinen Gedanken zu Persitent Entities und deren unzählig vielen getter- und setter Methoden…

Montag, 1. Dezember 2008

ERROR [STDERR] javax.ejb.EJBException: java.lang.IllegalArgumentException: Wrong target.

Diese Fehlermeldung hat mich die letzten Stunden zur Verzweiflung gebracht, da ich auch im Web hierzu nichts Brauchbares finden konnte.

Szenario: Eclipse EJB3 Projekt(e) – 3 Projekte: <Project>, <Project>EJB, <Project>Web.

Die Exception fliegt, wenn von der Webapplikation ein SLSB aufgerufen wird und als Parameter eine Persitent Entity übergeben wird. Die Persistent Entity ist im EJB Projekt heimisch und wird der Webapplikation per Eintrag im Manifest.MF bekannt gemacht.
Wird null als Objekt übergeben funktioniert der "entfernte" Methodenaufruf (es handelt sich in meinem Fall aber nur um einen Local Call). Liegt also an dem Objekt selbst…

Natürlich wurde an den Eclipse Projekteinstellungen "wild" herumgedreht um alles mal zu probieren…

Hierbei wurde in den Projekt Properties unter "Java EE Module Dependencies" im Tab "Web Libraries" das entsprechende Projekt EJB Module ausgewählt. à Böses Faul!!! Die Abhängigkeit zwischen Web-Projekt und EJB-Projekt innerhalb einer EJB Anwendung darf nur über die Einstellungen "Java EE Module Dependencies -> J2EE Modules" vorgenommen werden. Hier kann dann das EJB Projekt ausgewählt werden.

Anschließend neu kompilieren bzw. einfach das AppServer (JBoss) Arbeitsverzeichnis "cleanen" und happiness ;-)

Samstag, 29. November 2008

Web Frontend Entwicklung in einer JavaEE Umgebung | Der „langsame“ JBoss…

Vorab: beim JBoss handelt es sich natürlich nicht um einen langsamen Server! Allerdings das starten und Deployen dauert einfach etwas.

Während der Entwicklung kann dies leicht "nervend" sein. Hier will man bspw. nur mal kurz ein paar ActionKlassen (Struts Framework) ändern und die entsprechenden JSPs anpassen und natürlich gleich im Browser testen – jetzt muss allerdings erst das Paket auf dem Applicationserver deployed werden und … schnarch.

Um hier etwas mehr Schwung bei der Entwicklung unter Eclipse rein zu bekommen, gehe ich bei der Frontend Entwicklung vor allem bei GUI-Spielereien wie folgt vor:

Voraussetzung: Der JBoss Server ist in der Entwicklungsumgebung korrekt eingebunden und alles funktioniert.

In dem Beispiel gehe ich davon aus, dass eine Standard JEE Projektstruktur vorliegt. 3 Projekte: TestProject, TestProjectEJB, TestProjectWeb; die Standardprojekte bei der Anlage über den Eclipse Assistenten.

Die reine GUI Entwicklung sollte aber auch mit dem Tomcat Webserver Server alleine funktionieren. Der JBoss liefert seine Seiten ja ebenfalls über den Tomcat aus.

Apache Tomcat Server in der IDE einrichten
Hierzu muss neben dem JBoss im Eclipse ein zweiter Server eingerichtet werden. Hierzu unter Window->Preferences | Server->Runtime Environments mit Add einen Apache Tomcat Server auswählen. Entsprechende Pfade anpassen und gut – Dieser Server ist nun der Eclipse IDE bekannt.

Neue Runtime Umgebung einrichten
Im nächsten Schritt muss der Tomcat Server als Runtime Umgebung eingerichtet werden. Hier gibt es verschiede Wege, bspw über Run->Run Configurations… Ich bevorzuge allerdings einen anderen Weg.

Hierzu im Tab "Server", in dem bereits der JBoss Server registriert ist, mit der rechten Maustaste einen neuen Server anlegen.


Hier wie im Screenshot gezeigt die "Server runtime environment" auf Apache Tomcat stellen. Im nächsten Tab kann dann das EJB3 Webprojekt dem Tomcat Server hinzugefügt werden.


Mit ‚Add' hier das Beispielprojekt auf die rechte Seite bringen und den Vorgang mit Finish beenden.

Server starten (um diesen direkt eine "Runtime Configuration" zu geben)
Den Server jetzt einmal kurz starten mit click auf das grüne Server-Run Symbol.


Jetzt wurde eine Runtime Configuration automatisch angelegt die wir jetzt nur noch leicht bearbeiten müssen.

Classpath setzen
Die ganzen JBoss spezifischen Klassen sind dem Tomcat noch nicht bekannt. Dies ist aber notwendig, damit der Tomcat bei der Ausführung keine ClassNotFoundException wirft. Dies tritt auf wenn Klassen geladen werden, die APIs verwenden, die der JBoss als ApplicationServer zur Verfügung stellt (JPA Annotations beispielsweise). Die ganzen Container Managed EJBs funktionieren zwar nicht; sollte bei der GUI Entwicklung aber auch nicht unbedingt benötigt werden. Dagegen können alle Persistent Entities, da POJOs, problemlos verwendet werden (diese sind idealerweise auch direkt auf den JSPs eingebunden).

Ggf. kann man sich die Serviceaufrufe der SLSB auch "wegmocken".

Der Classpath muss hier dann wie folgt erweitert werden:

Run->Run Configurations… und dort die neuangelegte Tomcat Runtime Environment selektieren und im Abschnitt "Classpath" unter "User Entries" "Add external JARs" ausfühen


Hier das entsprechende JBoss Verzeichnis lokal auswählen (bei mir D:\jboss\jboss-4.2.3.GA-jdk6\jboss-4.2.3.GA\server\default\lib) und alle jar Files hinzufügen.

Und nochmal!

Anschließend diesen Schritt nochmal mit dem lib Verzeichnis direkt unterhalb des Servers durchführen! (bei mir D:\jboss\jboss-4.2.3.GA-jdk6\jboss-4.2.3.GA\lib).

Und nun das Wichtigste…

Das EJB Projekt selbst muss natürlich auch noch hinzugefügt werden.

Hierzu den Schritt ein weiteres Mal wiederholen, allerdings anstelle eines jar Files "Add Projects…" auswählen


Hier dann das entsprechende EJP Projekt wählen und dann sollten auch alle Pfade gesetzt sein.

Und Los…
Anschließend sollte das Projekt sich über den "schnellen" Tomcat starten lassen. Hier geht nun auch das "schnelle" Hot Code Replacement und ein Serverdurchstarten ist auch in 3 Sekunden erledigt.

In meinem Struts2 Projekt in Verbindung mit EJB3 auf einem JBoss spart das Vorgehen "GUI Entwicklung mit dem Apache Tomcat Server" viel Wartezeit….


 


 

Donnerstag, 30. Oktober 2008

EJB3 und ServiceLocator

Leider benötige ich, anders als hier im Web oft beschreiben, im Zusammenspiel zwischen Stateless SessionBeans (SLSB) und meinem Web Frontend das ServiceLocator Pattern (Plattform JBoss5). Aus einem Servlet (managed container) kann man zwar ganz komfortable die Annotation @EJB aufrufen, aus einem einfachen POJO allerdings leider nicht… (wäre mal ein ganz nettes Feature für die nächste Generation ;-) )

Problem: Aufruf meiner SLSB aus meiner Struts2 Action.

Nachdem die Action nicht vom Webcontainer verwaltet wird kann mit @EJB auch die SLSB nicht injiziert werden. Also muss doch das gute alte ServiceLocator Pattern her. Seit EJB2 hat sich hier allerdings einiges getan (Generics, HomeInterface, …), so dass ein einfaches C&P von der Pattern Page nicht wirklich toll passt (http://java.sun.com/blueprints/corej2eepatterns/Patterns/ServiceLocator.html).

Klar, jetzt kommt eigentlich Spring zum Einsatz. Aber… ich will in diesem Projekt einfach kein Spring benutzen ;-)

Mit folgendem Code komme ich ganz gut zu recht:

ServiceLocator Klasse:

import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class ServiceLocator {

    private static ServiceLocator instance;
    InitialContext context;
    private ServiceLocator() {
        try {
            Properties p = new Properties();
            p.put(Context.INITIAL_CONTEXT_FACTORY,
                    "org.jnp.interfaces.NamingContextFactory");
            p.put(Context.URL_PKG_PREFIXES,
                    "org.jboss.naming:org.jnp.interfaces");
            p.put(Context.PROVIDER_URL, "jnp://localhost:1099");
            context = new InitialContext(p);
        } catch (NamingException e) {
            throw new ServiceLocatorException(e);
        }
    }

    public static ServiceLocator getInstance() {
        if (instance == null)
            instance = new ServiceLocator();
        return instance;
    }

    @SuppressWarnings("unchecked")
    public <T> T getService(Class<T> clazz) {
        try {
            // TODO Name des ear-Files (MrBlue) zur Laufzeit ermitteln
            String lookupName = "MrBlue/" + clazz.getSimpleName()
                    + "Bean/local";
            T lookup = (T) context.lookup(lookupName);
            return lookup;
        } catch (ClassCastException e) {
            throw new ServiceLocatorException(e);
        } catch (NamingException e) {
            throw new ServiceLocatorException(e);
        }
    }

}

Aufruf aus der Struts Action:

public String execute() {        
        AccountService accountService = ServiceLocator.getInstance()
                .getService(AccountService.class);
        accountService.ping();

        return SUCCESS;
    }

Caching wird hier Clientseitig nicht verwendet. Übernimmt hier ja quasi der EJB-Container.

Der ServiceLocator hier geht davon aus, dass bei der Klassenbezeichnung der SessionBeans eine Konvention eingehalten wird (wie eigentlich üblich…)

Local Interface: <name>

Remote Interface: <name>Remote ß hier im ServiceLocator noch nicht implementiert (geht ja bei einer Webapplikation hier im ersten Schritt ja auch nur um local Calls).

Session Bean: <name>Bean


 

Montag, 20. Oktober 2008

Ladezeiten von Google Analytics & Co

An einer Website, an der ich im Moment arbeite, tritt öfters folgendes Problem auf:

Kundenaussage: Die Page funktioniert nicht!
Zur korrekten Darstellung und unter anderem zur Aktivierung einiger Buttons muss Initial etwas JavaScript Code ausgeführt werden. Doch leider wird der Code, der im onload Event liegt, nicht ausgeführt, da die Seite nicht fertig geladen wird. Dies wiederum liegt an der Anbindung von Google Analytics per JavaScript.

var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");

document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));

//…

var pageTracker = _gat._getTracker("UA-2915107-1");

pageTracker._setDomainName("yyy.xxx.de");

pageTracker._initData();

//…

pageTracker._trackPageview("/xxx/page10.html");


 

Ist das ga.js File bei google-analytics nicht problemlos erreichbar, stockt die gesamte Verarbeitung (Rendering) der Page.

Nachdem das Tracking der einzelnen Seiten mit der Funktion _trackPageview( <pagename> ) passiert, kann der Code auch über ein IFRAME auf die Page eingebunden werden. Hierdurch finden die Tracking-Aktivitäten in einem extra Thread statt und blockieren die eigentliche Seite nicht.

Samstag, 4. Oktober 2008

Javascript und Mathematik (oder Fließkommaarithmetik)

Ausgerechnet heute, am Feiertag (03. Oktober), hat doch in einem Projekt der Kunde ein böses Problem entdeckt… Die Applikation funktioniert nicht mehr! (zumindest ein ganz spezieller Fall) normal ;-)

In der entsprechenden Applikation (Webapplikation) werden im Frontend in Javascript einige Berechnungen durchgeführt (eher trivial, dafür allerdings ziemlich viel). Berechnungen in Javascript? Spart den Serverrroundtrip, der per AJAX notwendig wäre… Zur Kontrolle wird aber natürlich nochmal alles im Backend in Java mit BigDecimal nachgerechnet. …und da passen die Ergebnisse jetzt leider nicht zusammen. Die Applikation wirft einen Fehler und funktioniert nicht mehr.

Die Berechnung

function roundNumber(value, maxDecimalPlaces ) {
var e = Math.pow(10, maxDecimalPlaces);
return (Math.round(value * e) / e);
}

var x = 234.7;
var factor = 0.95;
var result = roundNumber( x * factor, 2 );
alert(result);

Das Ergebnis ist 222,96
Eigentlich hätte man aber ein leicht anderes Ergebnis erwartet… sauber gerechnet ist x*factor=222,965. Kaufmännische gerundet sollte das Ergebnis 222,97 sein.

Das Problem liegt natürlich an Fließkommazahlen und deren Rundungsfehler. Vergisst man nur mal schnell wenn man ein "bischen" in JavaScript hackt.

Das Ergebnis in Javascript für x * factor:

234.7 * 0.95 = 222.96499999999997

Und somit wird hier natürlich anschließend auch falsch gerundet.

Abhilfe schafft eine modifizierte round Methode:

function roundNumber(value, maxDecimalPlaces ) {
var e = Math.pow(10, maxDecimalPlaces);
//fliesskommaaritmetic
value = value + 0.0000000000001;
value = (Math.round(value * e) / e);
return value;
}

http://de.selfhtml.org/javascript/objekte/math.htm#round

http://www.galileocomputing.de/openbook/javainsel7/javainsel_02_004.htm#mjf9723d99079baea563298aa74cd42cb9

Donnerstag, 25. September 2008

Blog-Eintrag aus Microsoft Word erstellen und bearbeiten

Bin mal gespannt ob das wirklich so einfach geht…

Nachdem das schreiben in dem HTML Editor bzw. direkt in HTML bei google Blogger zwar ok ist, Word allerdings beim Schreiben von Text einfach unschlagbar ist, probier ich mal aus ob das wirklich alles so funktioniert (mit Microsoft Word 2007).

Nach der ersten Neuanlage mit anschließender Bearbeitung: tolle Sache!

Anschließend noch etwas Finetuning um Source Code schön strukturiert anzuzeigen….

Mit der Word Formatvorlage "HTML Vorformatiert" geht dann auch das Einrücken mit Space/ Leerzeichen. Dies wird übersetzt in den HTML-Tag <code>.

Zeile 1
zwei Space eingeruckt
6x Leerzeichen

Damit der Sourcecode nicht in Orange dargestellt wird, habe ich die entsprechende CSS Klasse noch in google Blogger angepasst. Fertig.

Mittwoch, 24. September 2008

Java Logging

Über Logging in Java ist bereits viel geschrieben worden. Das wieso-weshalb-warum erspar ich mir deshalb an dieser Stelle. Im Folgenden eine kurze Einführung wie man in Java loggen kann.

Um die Implementierungen des Loggers später austauschen zu können und um eine Abstraktionsschicht fürs Logging zu integrieren bietet sich die Commons Logging API der Apache Software Foundation an (http://commons.apache.org/logging).

Der Einsatz ist ganz einfach und eigentlich ziemlich ähnlich zu der direkten Implementierung von log4j (http://logging.apache.org/log4j)

package com.mre678.blog.jconsole;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class LoadBuilder {

private static final Log logger = LogFactory.getLog(LoadBuilder.class);

/**
* Load duration in minutes.
*/
private static final short LOAD_DURATION = 60;

public static void main(String... args) {

long startLoad = System.currentTimeMillis();
long endLoad = startLoad + (LOAD_DURATION * 60L * 1000L);

long loops = 0;

if (logger.isInfoEnabled())
logger.info("Starts load builder " + startLoad + "; end " + endLoad);

while (System.currentTimeMillis() < endLoad) {
loops++;
if (logger.isDebugEnabled()) logger.debug("Debug Message");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.error(e);
}
}
if (logger.isInfoEnabled()) logger.info(loops + " loops was executed.");
}
}



Die konkrete Implementierung wird im File commons-logging.properties konfiguriert. Hier beispielsweise log4j.

org.apache.commons.logging.Log = org.apache.commons.logging.impl.Log4JLogger


In diesem kleinen Stand-Alone Beispielprogramm wird 60 Minuten lang jede Sekunde eine log Info Meldung ausgegeben.

Änderung des Loglevels zur Laufzeit

Wäre toll wenn man das Loglevel der Applikation zur Laufzeit ändern kann... Klar geht ganz einfach indem man einfach den Inhalt von log4j.xml während das Java Programm läuft anpasst. Hierzu muss natürlich die log4j Konfiguration mit DOMConfigurator.configureAndWatch eingelesen worden sein (http://logging.apache.org/log4j/1.2/apidocs/index.html?org/apache/log4j/xml/DOMConfigurator.html). Ist aber ja irgendwie uncool. Wäre mit der jconsole von bspw. einem entfernten Rechner schon etwas schicker. Hierzu gibt es bspw. von log4j den JMX Support. Allerdings funktioniert dieser ohne Zutun bei Stand-Alone Applikationen nicht (falls doch würde ich mich über einen kleinen Hinweis freuen ;-); das gleiche gilt auch für den Tomcat, im JBoss dagegen funktioniert dies. Hintergrund dafür ist, das der VM bei dem obigen Beispiel kein MBean (Managed Bean) bekannt ist.

MBean schreiben und registrieren: auch doof...

Wenn man nun die Log Implementierung austauscht auf bspw. Jdk14Logger, der Log-Implementierung von Java.

Austauchen geht ganz einfach: Änderung der commons-logging.properties

org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger

Mit dieser Implementierung kann nun bspw. mit der jconsole direkt das Loglevel zur Laufzeit geändert werden. Doch Achtung: die Loglevel bei java.util.logging lauten anders als bei log4j (siehe http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/Level.html).

Nachdem ich log4j gegenüber java.util.logging bevorzuge ist dies natürlich auch keine wirkliche Alternative. Aber: funzt!

Sonntag, 14. September 2008

Mein erster Blogeintrag

Mein erster Blogeintrag...

Nach ein klein wenig googlen und ein paar interessanten (allerdings meist veralteten) Artikeln hab ich mich dazu entschieden hier ein Blog einzurichten.

Auf bspw. der Seite http://sw-guide.de/webdienste-blogging/wie-starte-ich-ein-blog-und-mache-dieses-bekannt/ konnte ich mich erstmal ein bischen allg. über das Bloggen bzw. das Blogschreiben informieren. Nachdem ich jetzt sehe wie easy das ganze hier ist, bin ich fast froh, dass ich mich, für die ersten Gehversuche, nicht gleich am Selberhosten versucht habe (technisch zwar bestimmt interessant, allerdings hätte es dann vermutlich bis zum ersten Eintrag noch etwas gedauert).