Archive

Archive for the ‘Development’ Category

Enterprise Architect – XML-Schema generieren

December 23rd, 2011 Comments off

Der Enterprise Architect (EA) bietet die Möglichkeit, aus einem bestehenden Klassenmodell ein XML-Schema zu erzeugen.
Zu finden ist diese Funktion im Menü unter Project –> XML Schema –> Generate XML Schema

Enterprise Architect - Generate XML Schema

Es erscheint ein Eingabedialog, in dem man Einstellung vornehmen kann, wie das XML-Schema generiert werden soll (z. B. Encoding) und unter welchem Pfad die XSD-Datei abgelegt werden soll.
Der Zielpfad für die XML-Schema-Datei lässt sich mit einem Doppelklick auf den Package Namen in der Tabelle Package-Filename ändern.

Enterprise Architect - Generate XML Schema Detail View

Create Java Classes from XML-Schema

December 23rd, 2011 1 comment

JAXB (Java Architecture for XML Binding) is a framework to marshal Java objects to XML and vice-versa.
It is part of the JDK and comes with a command-line compiler (xjc) that creates Java classes from an existing XML-Schema.

Location
The xjc it located at the /bin folder of the JDK:
../jdk1.6.0_18/bin/xjc.exe

Usage
xjc -d [target folder] -p [package name] [xml-schema file name]

Example
The classes will be written to the folder “src” (relative to the current location) in the package “de.myproject.somepackage”.
The schema file “my-schema.xsd” will be compiled.
xjc -d src -p de.myproject.somepackage my-schema.xsd

For more information see
xjc -help

CacheIgnoreHeaders Set-Cookie bei Apache mod_cache

November 3rd, 2011 Comments off

“Kleine Ursache, große Wirkung” passt bei dieser Konfigurationseinstellung ziemlich genau. Ist sie nicht gesetzt, werden bei allen gecachten Seiten die eventuell vorhandenen “set-cookie” Parameter in den Headern mit in den Cache abgelegt. Das kann fatale Folgen haben, wenn z.B. eine JSESSIONID als Cookie gesetzt wird. Wird dann eine Seite mit dem “set-cookie” aus dem Cache geladen, überschreibt diese JSESSIONID die aktuell vorhandene. Da die gecachte JSESSIONID aber einem ganz anderen Benutzer gehören könnte, ist es nun möglich, dessen Session zu übernehmen. Natürlich nur, falls dessen Session noch aktiv ist. Wenn nicht, dann gibt es zumindest einen ungewollten SessionTimedOut Fehler. Die Symptome treten allerdings auch nicht immer und nicht immer bei allen gecachten Inhalten auf, so dass der Fehler schwer reproduzierbar und auffindbar ist.

Apache mod_cache verursacht damit also ungewolltes “Session Hijacking”.

Abhilfe schafft das Setzen von “CacheIgnoreHeaders Set-Cookie”. Jeder, der mod_cache im Einsatz hat, sollte seine Konfiguration dahingehend prüfen wie diese Einstellung gesetzt ist und ob das so ausreichend ist.

Monitoring Apache HTTP Server

October 21st, 2011 Comments off

Für das Monitoring des Apache HTTP Servers stellt dieser eine Statusseite zur Verfügung: http://servername/server-status
Voraussetzung ist, dass am Webserver das Modul mod_status aktiviert ist.

Die Statusseite bietet folgende Informationen:

  • Anzahl der arbeitenden Prozesse (requests currently being processed)
  • Anzahl der Prozesse im Leerlauf (idle workers)
  • Status jedes Prozesses (Zahl der abgearbeiteten Anfragen; Gesamtzahl der Bytes)
  • Gesamtzahl der Zugriffe und Bytes
  • Startzeit und Laufzeit des Servers
  • Durchschnittliche Anzahl der Anfragen je Sekunde, Bytes je Sekunde und durchschnittliche Zahl von Bytes je Anfrage
  • Aktueller CPU-Ressourcenverbrauch je Prozess und Apache insgesamt
  • Aktuell bearbeitete Hostnamen und Anfragen

Sicherheit in Java-EE6-Webapplikationen

October 18th, 2011 Comments off

Java-EE-Webapplikation absichern

 

Authentifizierung und Autorisierung mit Servlet 3.0 und JDBCRealm

 

In diesem Weblog möchte ich in aller Kürze ;-) das Thema “Anwendungssicherheit” in
Java EE 6 erläutern. Als Applikationsserver habe ich Glassfish verwendet,
Infos zum JBoss sind weiter unten verlinkt.

Anhand eines Beispiels gehen wir die einzelnen Schritte durch, um den Server
abzusichern und um unsere Web-Anwendung vor unberechtigtem Zugriff zu schützen.
Die Web-Anwendung wird drei voneinander abgegrenzte Bereiche (admin, employee,
manager) haben, die nur ein Nutzer mit der richtigen Rolle betreten darf.

 

Ganz konkret werden wir an einer bestehenden Webapplikation folgende Dinge tun:

1. Wir sichern das Umfeld des Web-Servers ab. [Link]

2. Wir erzeugen eine Datenbanktabelle, die User, Passwort und Rollen enthält. [Link]

3. JSF Login: Wir bauen eine JSF-Login-Seite, die EJB nutzt, um Nutzer einzulassen oder
auszusperren. Die Zugriffsberechtigungen liegen in einer SQL-Datenbank. [Link]

4. Deployment Deskriptor web.xml: Wir setzen deklarativ SecurityConstraints in der
web.xml, um Seiten vor unberechtigtem Zugriff zu schützen. [Link]

5. EJBs und Servlets: Per Annotation setzen wir erlaubte und unerlaubte Rollen
und können in EJBs sogar auf Methodenebene Nutzer ausschließen. [Link]

 

Unsere Web-Anwendung besteht aus folgenden Schichten:
JSF 2.0 und Servlets 3.0 (z.B. Glassfish 3, JBoss 6+, Tomcat 7)
EJB 3.1 (mind. JavaEE6)
MySQL-Datenbank

 

1. Server und Betriebssystem absichern

Bevor wir uns daranmachen, unsere Web-Anwendung zu sichern, müssen wir erstmal
daran, den Server und das Umfeld zu sichern. Wir legen hier die Basis für die Sicherheit
unserer Web-Anwendung. Was hilft es, wenn unsere Web-Anwendung perfekt geschützt
ist, aber der Server und das Betriebssystem angreifbar sind?

In diesem Artikel möchte ich nur einige Stichpunkte geben, da das Thema zu weitreichend
ist, um es hier auch nur annähernd vollständig behandeln zu können.

1.1 Absichern des Betriebssystems:

  • User mit eingeschränkten Rechten einrichten, nicht als Administrator auf dem BS arbeiten
  • Firewall, Virenscanner und gesunden Menschenverstand einschalten
  • “Sichere”, d.h. aktuelle und gepatchte Software verwenden
  • und vieles mehr

1.2 Absichern des (Java Application) Servers:

  • Nur Ports öffnen, die vom Server genutzt werden (s. %serverdir%/config/server.xml auf
    den meisten Java-Applikationsservern und Servlet-Containern)
  • Die Admin-Konsole per Passwort absichern. Hier einige Beispiele:
    Tomcat (ungetestet): Die Datei %tomcat%/conf/tomcat-users.xml muss angepasst werden
    Glassfish: %glassfish%/bin/asadmin change-master-password bzw.
    %glassfish%/bin/asadmin change-admin-password auf der Kommandozeile ausführen
    JBoss 7 (ungetestet)%jboss7%/standalone/configuration/mgmt-users.properties editieren
  • und vieles mehr

2. Authentifizierung mit einer Datenbanktabelle

Für die Login-Seite müssen wir ein Realm anlegen.

Was ist ein Realm? Lt. Definition im offiziellen Tutorial von Sun ist ein Realm eine Sicherheits-
domäne, die für einen Webserver definiert wird. Den Satz verstehe ich selber nur zur Hälfte,
deshalb habe ich ein Bild gemalt, das diesen Sachverhalt klarer macht:

In Worten: Wir haben in der Datenbank einen User ‘holger’, der in der Gruppe ‘manager’ ist,
einen User ‘bernhard’, der in der Gruppe ‘employee’ ist usw. Unsere Web-Anwendung definiert
Rollen, die zufällig genauso heißen – aber auch anders heißen könnten. Nun müssen wir noch
die in der Datenbank definierten Gruppen unserer Web-Anwendung bekannt machen: das
geht über eine Mapping-Tabelle, die besagt, dass bspw. der Datenbankeintrag ‘manager’ auf
die Rolle ‘manager’ unserer Web-Anwendung passt.

Ziel: Der “Manager” darf nur in den “Manager”-Bereich unserer Web-Anwendung und nur mit
“manager” bezeichnete Geschäftslogik aufrufen, das Gleiche gilt für den Nutzer mit der Rolle
“employee” und Nutzer der Rolle “admin”.

 

So, jetzt müssen wir die User anlegen, die unsere Web-Anwendung im Login erkennen soll.
Hier will ich die Erzeugung von Usern auf zwei Wegen behandeln – per FileRealm und per
JDBCRealm. FileRealm ist einfacher, da wir dafür keine Datenbanktabelle und kein
Mapping brauchen, aber wir können damit nicht dynamisch User anlegen, wie wir es mit
dem datenbankbasierten JDBCRealm können, sondern müssen händisch User anlegen.

  • FileRealm (optional): wir legen die User mitsamt Gruppe per Hand auf dem Server (nicht per DB)
    an. Wenn nur der JDBC-Realm interessant ist, der überspringt diesen Abschnitt und
    klickt hier.
    Der Server legt dafür eine Datei auf dem Server an, die Username, gehashtes Passwort
    und Rollen enthält.
    Glassfish: In der Web-Oberfläche gehen wir auf den Punkt
    Konfigurationen -> server-config ->Sicherheit -> Bereiche -> file (s. Screenshot).


    Hier im Beispiel habe ich die User ‘fileBernhard’ mit Gruppenzugehörigkeit ‘employee’
    angelegt, den ‘fileAdmin’ mit Gruppe ‘admin’ und ‘fileHolger’ mit Gruppe ‘manager’.
    Wer das sofort mal testen will, kann den kommenden Abschnitt ‘JDBCRealm’
    überspringen und geht gleich zum Login unserer Web-Applikation [Link].
    JBoss
    : http://docs.jboss.org/jbossweb/3.0.x/realm-howto.html (ungetestet)
  • JDBCRealm: die Definition der User liegt in einer DB-Tabelle. Legen wir also eine
    DB-Tabelle an:

    CREATE TABLE `myuser` (
      `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
      `username` VARCHAR( 30 ) NOT NULL ,
      `password` VARCHAR( 30 ) NOT NULL ,
      `group` VARCHAR( 30 ) NOT NULL
    )

    und legen ein paar Datensätze an:

    INSERT INTO `myuser` ( `username` , `password` , `group`)
    VALUES
      ('admin', 'adminpw', 'admin'),
      ('bernhard', 'bernhardpw', 'employee'),
      ('holger', 'holgerpw', 'manager'),
      ('karlheinz', 'karlheinzpw', 'employee')

    Ok, wir haben jetzt 4 Datensätze. Die müssen wir jetzt unserer Web-Anwendung bekannt
    machen (s. Abb. “Realm”).

    Nächster Schritt: Im Glassfish müssen wir die Datei WEB-INF/glassfish-web.xml anlegen, die
    unsere “Gruppen” in unserer Datenbank-Tabelle auf die “Rollen” auf dem Server überträgt.

    glassfish-web.xml

      <security-role-mapping>
        <role-name>admin</role-name>
        <group-name>admin</group-name>
      </security-role-mapping>
      <security-role-mapping>
        <role-name>employee</role-name>
        <group-name>employee</group-name>
      </security-role-mapping>
      <security-role-mapping>
        <role-name>manager</role-name>
        <group-name>manager</group-name>
    </security-role-mapping>

    Im JBoss gibt es ebenfalls die Security Roles mit leicht unterschiedlicher Notation:
    JBoss-Docs zum Mapping

    Wir müssen die Datenbank dem Server über JNDI bekannt machen: wie das geht, hat
    Kollege Joachim beschrieben: [Link zu JNDI]

    Jetzt legen wir das JDBCRealm an:
    bei Glassfish geht das über die Weboberfläche,
    bei JBoss über die Konfigurationsdatei $CATALINA_HOME/conf/server.xml [Link]
    JBoss-Docs zu JDBCRealm

 

3. JSF Login-Seite

Wir nähern uns langsam dem ersten Erfolg. Erstellen wir also die Login-Seite als JSF-Facelet:
login.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">
	<head>
		<title>Login</title>
	</head>
	<h:body>
		<h:form id="j_security_check">
			<center>
				<h:messages errorClass="errorMessage" infoClass="infoMessage"
							warnClass="warnMessage"></h:messages>
				<h:panelGrid columns="2">
					<h:outputText value="Username : "/>
					<h:inputText id="j_username" value="#{loginBean.username}"/>

					<h:outputText value="Password : "/>
					<h:inputSecret id="j_password" value="#{loginBean.password}"/>

					<h:commandButton value="Submit" action="#{loginBean.login}"
									 type="submit"/>
				</h:panelGrid>
			</center>
		</h:form>
	</h:body>
</html>

Und die logout.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">

	<head>
		<title></title>
	</head>
	<h:body styleClass="body">
		<h:messages></h:messages>
		<h:form>
			<h:commandLink value="Logout" action="#{loginBean.logout}"></h:commandLink>
		</h:form>
	</h:body>
</html>

Wir man sieht, greift das Facelet auf eine loginBean zu.
Diese wollen wir jetzt definieren:
LoginBean.java

package de.triona.ejb;
 
import java.io.Serializable;
import java.security.Principal;
import java.util.logging.Logger;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
 
@ManagedBean
public class LoginBean implements Serializable {
 
	private String username;
	private String password;
 
	public String login() {
		FacesContext fc = FacesContext.getCurrentInstance();
		HttpServletRequest request = (HttpServletRequest) fc.getExternalContext().getRequest();
		try {
			//Login per Servlet 3.0
			request.login(username, password);
 
			// Der Principal entspricht dem Usernamen
			Principal principal = request.getUserPrincipal();
 
			// Wir können hier nur abfragen, ob der User eine Rolle hat (isUserInRole('whatever')),
			// aber wir können NICHT die Rolle aktiv erfragen (z.B. mit getUserRole(...))
			if (request.isUserInRole("admin")) {
				String msg = "User: " + principal.getName() + ", Role: admin";
				fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null));
				return "admin/startrek";
			} else if (request.isUserInRole("manager")) {
				String msg = "User: " + principal.getName() + ", Role: manager";
				fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null));
				return "manager/timesheet";
			} else if (request.isUserInRole("employee")) {
				String msg = "User: " + principal.getName() + ", Role: employee";
				fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null));
				return "employee/work";
			}
			return "du_musst_die_rollen_noch_definieren";	// hier sollte etwas sinnvolles passieren ;-)
		} catch (ServletException e) {
			fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "An Error Occured: Login failed", null));
			e.printStackTrace();
		}
		return "loginFailed";
	}
 
	public void logout() {
		FacesContext fc = FacesContext.getCurrentInstance();
		HttpSession session = (HttpSession) fc.getExternalContext().getSession(false);
		if (session != null) {
			session.invalidate();
		}
		fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "/login.xhtml");
	}
 
	public String getUsername() {
		return username;
	}
 
	public void setUsername(String username) {
		this.username = username;
	}
 
	public String getPassword() {
		return password;
	}
 
	public void setPassword(String password) {
		this.password = password;
	}
}

Die Besonderheit an diesem Code ist die Methode HttpServletRequest.login(username,password)
aus der Servlet-Spezifikation 3.0 und HttpServletRequest.isUserInRole(String role).
Mit login(username,password)  wird username und password des Realms (hier: JDBCRealm oder
FileRealm) validiert. Die Methode HttpServletRequest.isUserInRole(String role) ist selbst-
erklärend: hier wird die Rolle abgefragt, wir können aber aus Sicherheitsgründen nicht – und das ist
Absicht der Java EE 6 Spezifikation – aktiv die Rolle abfragen.

 

Zwischenstand: 

  • wir haben eine DB-Tabelle myusers (mit Name, Passwort und Gruppe) angelegt und befüllt
  • wir haben einen FileRealm auf dem Server angelegt (dieser Schritt ist optional)
  • wir haben einen JDBCRealm angelegt
  • wir haben eine JSF-Seite login.xhtml angelegt, die auf die EJB-Klasse LoginBean zugreift
  • wir haben auf dem Glassfish die XML-Datei glassfish-web.xml bzw. auf dem JBoss die SecurityRoles
    angelegt, die das Datenbankfeld myuser.group auf die Rollen in der Web-Applikation abbildet

Was fehlt noch?

 

4. web.xml bearbeiten

Wir müssen unserem Deployment-Deskriptor (WEB-INF/)web.xml mitteilen, das er eine Authentifizierung
anhand unseres Formulars login.xhtml durchführt und wir müssen unsere Rollen definieren.
In der Netbeans-Oberfläche können wir die web.xml grafisch bearbeiten.

Wer direkt die web.xml bearbeiten will, kann das auch tun:
web.xml

    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>loginRealm</realm-name>
        <form-login-config>
            <form-login-page>/login.xhtml</form-login-page>
            <form-error-page>/loginfailed.xhtml</form-error-page>
        </form-login-config>
    </login-config>
    <security-role>
        <description/>
        <role-name>manager</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>employee</role-name>
    </security-role>

Probieren wir das Ganze aus: wir starten die login.xhtml im Browser.
Loggen wir uns erfolgreich als admin ein, dann sollten wir zur Seite /admin/startrek.xhtml
weitergeleitet werden, der manager wird auf /manager/timesheet.xhtml und der employee auf
/employee/work.xhtml weitergeleitet.

So weit, so gut: wir können uns einloggen und unsere Rolle ist dem Server bekannt.
Ein Problem haben wir noch: wer zufällig den Link für den Admin-Bereich kennt, kann
diese Seiten aufrufen, dito für den Employee- und den Manager-Bereich.

Das können wir so ebenfalls in der web.xml fixen, indem wir URLs explizit für eine Rolle
erlauben. Der Admin darf nur URLs mit dem Pattern /admin/* aufrufen, dito für employee
und Manager:
Hier via Netbeans:

Und hier der XML-Codeausschnitt aus der web.xml:

    <security-constraint>
        <display-name>AdminConstraint</display-name>
        <web-resource-collection>
            <web-resource-name>AdminArea</web-resource-name>
            <description/>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>admin</role-name>
        </auth-constraint>
        <user-data-constraint>
            <description/>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-constraint>
        <display-name>EmployeeConstraint</display-name>
        <web-resource-collection>
            <web-resource-name>EmployeeArea</web-resource-name>
            <description/>
            <url-pattern>/employee/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>employee</role-name>
        </auth-constraint>
    </security-constraint>
    <security-constraint>
        <display-name>ManagerConstraint</display-name>
        <web-resource-collection>
            <web-resource-name>ManagerArea</web-resource-name>
            <description/>
            <url-pattern>/manager/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>manager</role-name>
        </auth-constraint>
    </security-constraint>

In diesem Beispiel habe ich den Admin-Bereich via SSL-geschützt

<user-data-constraint><transport-guarantee>CONFIDENTIAL</transport-guarantee></user-data-constraint>

Der Browser leitet direkt von http auf https um.

Langsam geht es auf das Finale zu:

 

5. Annotationsbasierter Schutz von Servlets und EJBs

Wir haben das Schlimmste hinter uns. Jetzt folgt die Kür und die wunderbare Welt
der Annotationen.

Fangen wir mit dem Servlet an. Wir wollen ein Servlet schreiben, das nur User mit den Rollen
manager und admin besuchen können. Nichts leichter als das:

ManagerAndAdminServlet.java

@ServletSecurity(@HttpConstraint(rolesAllowed={"manager", "admin"}))
public class ManagerAndAdminServlet extends HttpServlet {
	private static final long serialVersionUID = 1046L;
 
	protected void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
//manager und admin dürfen rein, der employee bleibt draußen mit einer SecurityException
        }
}

Mit der Annotation

@ServletSecurity(@HttpConstraint(rolesAllowed={"manager", "admin"}))

ist schon alles fertig.

 

Genauso einfach ist eine EJB vor unautorisiertem Zugriff geschützt:

WorkBean.java

@DeclareRoles({"manager", "employee", "admin"})
@Stateless
@LocalBean
public class WorkBean {
 
	/** Nur für Nutzer mit Rolle 'employee' */
	@RolesAllowed("employee")
	public void doWork(String employee) {
		System.out.println("work");
	}
 
	/** Nur für Nutzer mit Rolle 'manager' */
	@RolesAllowed("manager")
	public void delegateWork() {
		System.out.println("delegateWork");
	}
 
	/** Nur für den 'admin' */
	@RolesAllowed("admin")
	public void administrateWhatever() {
		System.out.println("administrateWhatever");
	}
 
	/** Alle Nutzer ('manager', 'employee' und 'admin') dürfen diese Methode aufrufen */
	public void lookOutOfTheWindow() {
		System.out.println("it rains");
	}
}

 

Unser Web-Anwendung könnte bspw. jetzt so aussehen:


Fazit: Wir haben unsere Web-Anwendung deklarativ und programmativ schützen können.
Natürlich haben wir zum Thema Anwendungssicherheit nur die Spitze des Eisbergs gesehen.
Dies ist nur ein erster Einblick.

Liebe Leser, vielen Dank für eure Aufmerksamkeit.

JBoss7: Oracle 10g und das Leid mit Oracles JDBC-Treiber

September 29th, 2011 Comments off

Heute wollte ich den neuen superschnellen JBoss 7 mit unserer
Oracle-DB 10g (10.2.0.1) verheiraten. Der Server soll eine JNDI-Verbindung
zur Oracle-DB aufbauen und unsere Web-Anwendung greift per JNDI
darauf zu. Ich hole mir vorher noch auf der Oracle-Seite den passenden
JDBC-Treiber für die Version 10.2.0.1.

Nichts leichter als das, denke ich mir, google ein wenig und finde einen
schön kurzen, knackigen Artikel, wie das denn funktioniert:

http://blog.foos-bar.com/2011/08/jboss-as-7-and-oracle-datasource.html

Alles haarklein ausgeführt (Jar auf Server kopieren, XMLs anpassen), 3 mal
gegengecheckt, und es kommt, wie es kommen soll: klappt nicht! Aaaarrgh!
Hin und her überlegt, was der Fehler ist, aber es hilft nichts.
Der Kollege probiert den obigen Blog-Eintrag mit JBoss 7 auch aus und bei
ihm klappt’s tadellos. Na super!

Wo war der Fehler?

Der JDBC-Treiber für Oracle 10.x funktioniert im JBoss 7 nicht mit Oracle 10.x.

Lösung: Wir nehmen den JDBC-Treiber mit der Version 11 !

 

Was war das Problem?
Ich mutmaße: Der Treiber ist nicht JDBC4-konform. Bei älteren Treibern (hier:
JDBC 3) empfiehlt die JBoss-Doku die jdbc.jar händisch um einen Eintrag in der
META-INF zu erweitern, aber diese Maßnahme greift nicht.
Aber es kann auch einfach sein, dass der Jboss 7 schlichtweg mit dem 6 Jahre
alten Treiber ein “internes” Problem hat. Wie dem auch sei, Oracle 10g freut sich
über den JDBC-Treiber v11.

Enterprise Architect – Teamarbeit mit Versionskontrolle

September 26th, 2011 Comments off

Wer nicht alleine an einem Projekt in Enterprise Architect (EA) arbeitet, wird schnell feststellen, dass der EA nicht für die Gruppenarbeit geschaffen wurde: Es kann immer nur eine Person gleichzeitig an einem EA-Projekt arbeiten! Zumindest in der Standardeinstellung.

Prinzipiell hat man im Team drei Möglichkeiten mit dem EA zu arbeiten:

  • Man wechselt sich bei der Bearbeitung ab oder tauscht Packages per Im-/Export aus
  • Man verwendet eine zentrale relationale Datenbank
  • Man verwendet eines der vom EA unterstützen Versionskontrollsysteme

Die erste Variante ist unpraktisch und zeitintensiv, da man hier nicht parallel arbeiten kann und der Im- und Export es auch nicht praktikabel, da man beim Import vorhandene Packages nicht einfach überschreiben kann. Die zweite Variante ist relativ aufwändig in der Konfiguration und verlangt nach einem relationalen Datenbanksystem. Die dritte Variante ist dagegen relativ einfach in der Konfiguration und benötigt “nur” ein Repository in einem der folgenden Versionskontollsysteme:

  • SCC
  • CVS – Concurrent Versions System
  • SVN – Subversion
  • TFS – Team Foundation Server

Dieser Artikel wird sich mit der Integration von Subversion in den EA beschäftigen. Neben der Möglichkeit effizient im Team zu arbeiten erhält man natürlich die von einer Versionskontrolle gewohnte Historisierung seiner Arbeit und kann somit jederzeit zu einem früheren Entwicklungsstand zurückkehren.

Funktionsweise und Begriffe

Der EA verwendet den Kommandozeilen-Client von Subversion zur Versionierung und keine integrierte Lösung. Die EA-Projektdatei ist technisch betrachtet eine Access-Datenbank die vom Versionskontrollsystem nicht verwaltet werden kann. Sie kann zwar im Repository liegen, Änderungen von verschiedenen Personen können aber nicht vom Versionskontrollsystem automatisch eingearbeitet werden (Merge). Es macht dennoch Sinn sie ins Repository zu stellen, da in ihr sämtliche Konfigurationen gespeichert sind, sowohl die SVN-Grundkonfiguration, als auch welche Packages versioniert sind (s.u.). Für die Aktualisierung der Projektdatei sollte aber nur eine Person verantwortlich sein und jeder holt sich die aktuelle Version und speichert sie außerhalb des Repository-Verzeichnisses.

Packages, die unter Versionskontrolle gestellt sind, werden vom EA automatisch importiert und exportiert und liegen dann als XML-Dateien im Repository.

Hier einige Beriffe wie sie bei EA verwendet werden und im Kontextmenü der versionierten Packages zu finden sind (Package Control).

Begriff Bedeutung
Check Out Exklusiven Zugriff erlangen
(SVN: get lock)
Undo Check Out Exklusiven Zugriff freigeben und Änderungen verwerfen
(SVN: revert + release lock)
Check In Neue Version im Repository speichern
(SVN: commit)
Check In Branch Neue Version mehrerer Packages im Repository speichern
(SVN: commit) für mehrere Dateien
Get Latest / Get All Latest Aktuelle Version aus dem Repository (bestimmtes / alle Packages) holen
(SVN: update)

Voraussetzungen

Folgende Voraussetzungen müssen erfüllt sein und werden im Folgenden vorausgesetzt:

  • Der Enterprise Architect ist auf den Rechnern der Teammitglieder installiert und eingerichtet.
  • Ein zentraler Subversion-Server mit dem Projekt-Repository ist installiert und so eingerichtet, dass die Teammitglieder darauf zugreifen können.
  • Der Kommandozeilen Subversion-Client  ist auf den Rechnern der Teammitglieder installiert.
  • Optional: Es empfiehlt sich den Windows Subversion-Client Tortoise zu installieren.

Bevor die eigentliche Konfiguration des EA beginnen kann, muss noch der initiale Checkout des Projekt-Repositories in ein lokales Verzeichnis erfolgen. Dieses Verzeichnis ist dann die Arbeitskopie (im EA: “Working Copy”).

Subversion im EA einrichten

Die Konfiguration von Versionskontrollsystemen erfolgt im EA Projektbezogen. Zuerst wird also die Projektdatei (*.eap) aufgerufen, anschließend werden über

Project >Version Control > Version Control Settings…

die Grundeinstellungen zur Subversion-Einbindung vorgenommen.

"Version Control Settings" Dialog

Feld Wert
Working Copy path  Die ist der Pfad zur lokalen Arbeitskopie des Repositories, in dem das Projekt versioniert werden soll.
Subversion Exe path  Programmpfad zum SVN Kommandozeilen-Client.

Sind alle Einstellungen getroffen, wird die Konfiguration mit einem Klick auf “Save” gesichert.

Bisher haben wir nur die Grundeinstellungen für die Versionskontrolle vorgenommen, verwendet wird sie aber noch nicht.

Ein EA-Projekt ist meist in mehrere Packages aufgeteilt und jedes dieser Packages kann einzeln (auch verschachtelt) unter Versionskontrolle gestellt werden.

Im Vorfeld sollte man sich über eine sinnvolle Unterteilung des Projektes Gedanken machen. Es können zwar mehrere Personen parallel an verschiedenen Packages arbeiten, an einem Package aber immer nur einer zur Zeit. Nur das Root-Package zu versionieren macht daher wenig Sinn, das andere Extrem (jedes Package) aber ebenfalls nicht, da man dann erst mehrere Packages zur Arbeit Freischalten muss (“auschecken”) bevor man loslegen kann. Wie das Projekt sinnvoll unterteilt werden kann, ist aber Projektabhängig und kann hier nicht allgemein beschrieben werden.

Packages werden folgendermaßen unter Versionskontrolle gestellt: im Project Browser mit der rechten Maustaste auf das zu versionierende Package klicken und im Kontextmenü

Package Control > Configure…

auswählen.

"Package Control Options" Dialog

Feld Wert
Control Package Diese Checkbox muss angehakt sein. Hiermit wird ein Packages unter Versionskontrolle gestellt, oder dort wieder herausgenommen.
Version Control Das Pulldown-Menü bietet eine Auswahl vorhandener Konfigurationen für Versionskontrollsysteme. Hier sollte mindestens die von uns oben im “Version Control Settings”-Dialog erzeugte Konfiguration SVN_PROJECT auftauchen. Diese wird auch ausgewählt.
XMI Filename Pfad an dem das Package im Repository gespeichert werden soll.
ACHTUNG: Der XMI-Dateiname darf keine Umlaute enthalten, ebensowenig das Verzeichnis in dem die Datei gespeichert werden soll. Wenn der Packagename also Umlaute enthält (was kein Problem ist) muss man dies beim Dateinamen ändern.

Es ist wichtig, dass alle Teammitglieder nach dieser Änderung die neue, aktualisierte EA-Projektdatei übernehmen.

Nach dieser Konfiguration kann man nach einem “Check Out” das Package bearbeiten und mit einem “Check In” im Repository aktualisieren und für andere freigeben.

Fazit

Die Grundkonfiguration ist schnell erledigt und je nach Anzahl der Packages auch deren Versionierung. Der Gewinn, den man durch die Möglichkeit des parallelen Arbeitens erhält, ist jedoch enorm, auch schon ab zwei Personen, besonders wenn man das Projekt geschickt unterteilt hat. Dann hört man auch nur selten: “Wer hat Package X schon wieder ausgecheckt?

SOA Light – Usage of services in your application – Part #2

September 25th, 2011 Comments off

Welcome to Part #2 of the SOA Light  – Usage of services in your application.

In the 2nd part I want to introduce a sample project, which reflects the architecture described in part one. All the projects are prepared to be compiled with Maven 2.

The project itself consists of the following Maven modules:

  1. sampleApplication: This is the application that uses the services.
  2.  service-base: This module contains the service interface and a class which describes a DTO (Data Transfer Object).
  3. service-client: This module contains a so called façade class. This façade implements the interface from the base module. This façade also contains a property of type SampleService.So this class, the façade, is actually the bridge between the sample application and the service itself. This façade will be injected into the sampleApplication.
  4. service-server: This module offers the service itself. The service has implemented the interface from service-base. This is the place where the actual work is done. This can be communication with database or whatever comes to your mind.

A simple way to use the application and the service is to deploy them to a Jetty server. The service and the application contain a jetty plug in which can be started via
“mvn jetty:run -Djetty.port=9002.

See the attachment for the example implementations:

Service Implementation and Sample Application

 

JAX-WS Webservice Endpoint URL zur Laufzeit ändern

September 19th, 2011 Comments off

Lässt man sich über das kleine JAX-WS Tool “wsimport” automatischen Java-Clientcode für einen WebService generieren, sind sowohl die WSDL-URL, als auch die Endpoint-URL im Clientcode fest hinterlegt. Das Erzeugen des Clients kann dann über einen parameterlosen Konstruktor erfolgen, was im ersten Moment verlockend einfach klingt.

Ändert sich nun die WSDL- oder gar die Endpoint-URL, so möchte man natürlich ungern den generierten Code per Hand ändern oder den Import ständig wiederholen. Noch spannender ist die Frage, wie man beide Parameter zur Laufzeit ändern kann. JAX-WS bietet glücklicherweise für diese Fälle entsprechende Lösungen.

WSDL-URL ändern
Die WSDL-URL ist der erste Anlaufpunkt des Clients. Hier holt er sich Informationen über die vom Service bereitgestellten Methoden und die Information, mit welcher Endpoint-URL der eigentliche Service anzusprechen ist.

Beim Erzeugen des Client über den parameterlosen Konstruktor wird die beim wsimport genutzte URL als Standard genommen. Es gibt aber auch einen Konstruktor, der die WSDL-URL und den Servicenamen übernimmt.

WebService webService = new WebService(wsdlLocation, serviceName);

Endpoint-URL ändern
In der WSDL-Datei wird die Endpoint-URL des Webservice propagiert. Also die URL, an die man die Methodenaufrufe dann schlussendlich richtet. Soll auch diese von außen überschrieben werden, lässt sich das Service-Binding nachträglich ändern.

((BindingProvider) webServicePort).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, newEndPointUrlAsString);

Enterprise Architect – Reverse Engineering: Klassendiagramme aus Quellcode erzeugen

September 19th, 2011 Comments off

Wer bereits fertigen Sourcecode vor sich hat und daraus für den Enterprise Architect (hier in Version 7.5) Klassendiagramme erstellen möchte, der kommt mit der Funktion “Code Engineering” zum Ziel.

Man erstellt einfach ein leeres Package und hat dann im Project-Browser im Kontextmenü den entsprechenden Eintrag “Code Enigneering” -> “Import Source Directory”.

Nun kann man zum einen die entsprechende Codesprache auswählen, zum Anderen kann man hier einige Parameter des Import bestimmen.

Mit einem abschließenden Klick auf “Ok” werden alle Klassen in den Enterprise Architect importiert. Feintuning ist dann natürlich noch nötig, aber das automatische Importieren erspart Unmengen an Tipparbeit.