Results for tag "java"

12 Articles

Doing integration tests with Arquillian and real mocked EJBs

Our current project uses JSF and CDI for the presentation layer. The business logic is encapsulated inside EJB with no-interface view as proposed by Adam Bien and others. I evaluated different alternatives for integration testing and ended up with Arquillian. For JSF/CDI based applications Arquillian is the best fit.

As I digged a little bit deeper into Arquillian one big problem occured: The usage of no-interface EJBs did not allow me to inject some CDI alternatives through the @Alternative annotation. @Alternative expects an interface which I did not have. In addition I had to use the @EJB annotation in the JSF backing bean because our target application server was WebSphere. Since the EJB container, for example JBoss for integration testing, expects all fields annotated with @EJB to be resolved and deployed, I would have to deploy the EJB with all its dependencies. In the end the whole application had to be deployed including database access and without being able to manipulate the result of the EJB methods.
Our data access layer uses JPA/Hibernate but can not make use of “plain” JQL because we had to access legacy stored procedures of an already existing Oracle database – in-memory testing with H2 or Derby was not possible. Another problem would have been the total duration of the integration tests. Our application has a certain complexity and with proceeding project progress the integration tests could not be executed any longer in an acceptable time span.

The only option would have been to switch from no-interface EJBs back to traditional @Local EJBs/interfaces. In the integration tests I would define a stub which implements the interface and deploy the stub with Arquillian. Nevertheless, dynamically controlling the behavior of this stub is not directly possible and I had to write a lot of stubs.

The whole situation did not make me happy. Doing integration tests with Arquillian should force me to change the architecture and introduce more complexity? This was an option I was unwilled to choose and so I searched for alternatives. Surprisingly, Google did not provided any solution. I thought about the problem again and had an idea: I could modify the Java bytecode of the EJB class before it is deployed. The modified EJB would only act as a facade and delegates every method call to an inner mock which has the same class methods as the facade.
After doing some research Javassist seemed to be the best tool for doing the bytecode manipulation. During the implementation of the desired bytecode modifier I struggled with some odd behavior of the application container but in the end I suceeded.

EjbMocker allows you to deploy a bytecode modified version of your EJB to be injected by Arquillian into your application server. You can completely control the behavior of the EJB with help of Mockito. Every method of the EJB is forwarded to an internal mocked instance with the same class signature.

An example project can be found at https://github.com/schakko/arquillian-warp-mocked-ejb. The EjbMocker contains usage instructions so I won’t repeat it here.

Hibernate uses wrong schema during schema validation

Recently I struggled upon the same problem, this guy described. Our Oracle database instance contains multiple schematics with almost the same structure. Every developer has it’s own schema for unit and integration tests. On application startup the Hibernate schema validator calls the DatabaseMetaData.getTables() for every linked entity. The method returns the first table which could be found in any schema. The returned tables are by default ordered by schema name. Side node: I would expect that the home schema of the current user would be prefered. This leads to situation that sometimes the validation fails: a user has already migrated his own schema (schema name app_user_unittest) but the schema for the build server (schema name app_build_unittest) still has the old schema version.

Overwriting DatabaseMetaData.getTables() method is not possible as it resides in the Oracle JDBC driver. Instead, you can use the environment variable hibernate.default_schema which points to to prefered schema. Depending on your development environment, the variable could be set during application startup by the application itself or by a system property through your application server.

Unit tests inside Eclipse succeed, unit tests in Maven fail. WTF?

Our new project makes use of Maven as build management tool. Eclipse (STS edition) is used for the development process. A part of the project consists of a transformation process which converts XML files to Java POJOs. Because of the given XML structure we used JAXB in combination with EclipseLink MOXy for this.

After a few weeks of initial development, mainly architectural decisions I prepared our TeamCity instance. The first TeamCity build failed because some of the unit tests throw unexpected errors. I must admit that until this time I had only executed the unit tests through Eclipse and every test case had passed without any problem. My local command line Maven builds were triggered with -DskipTests=true and succeeded too.

The failed build in TeamCity occured through the following JAXB error:

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
org.springframework.integration.Message is an interface, and JAXB can't handle interfaces.
	this problem is related to the following location:
		at org.springframework.integration.Message
		at public org.springframework.integration.Message ...

I repeated the test suite on my local machine (mvn test) and the first time it ran it succeeded. Eclipse passed the unit tests, too. At first I suspected different Java/JDK versions on my local machine and the build server, but the versions were the same. So I started with a fresh mvn clean test on my machine and the build failed, too. WTF? Now running the compiled unit test and the source code in Eclipse although resulted in the error above. Re-compiling the code with Eclipse fixed the errors. Eclipse uses Eclipse Compiler for Java (ECJ) during compilation and not javac of the JDK. Could it be a compiler bug? The byte code of both .class files (Maven compiled vs. Eclipse compiled) were more or less the same so this was not the answer.

During debugging the Maven compiled artifacts I noticed that the MOXy compiler was not hit, instead the default implementation was used. Could it be that the jaxb.properties file was not copied to the class path? jaxb.properties is read by JAXB for initializing/overwriting the default XML context factory. And indeed, the jaxb.properties was missing. ECJ copied the .properties file to the target directory but Maven ignored the file.

What did I learned from that?

  1. Fail early – Set up the build infrastructure on the first day of your project and don’t wait until a first prototype is available.
  2. Fail everywhere – Running the tests only in one environment (Eclipse) does not mean that it succeeds in other environments (pure Maven).
  3. Don’t skip tests – Waiting for a local build to succeed sucks. Skipping the unit tests makes it better, but a failed build in another environment (see 2.) although sucks.

.NET aus der Sicht eines Java-Entwicklers

Die letzten beiden Tage war ich damit beschäftigt, ein paar Evaluierungen für die .NET-Plattform zu machen. Eines unserer Projekte greift mit Hilfe eines (zugegebenermaßen ziemlich coolen) WPF-Frontends über WCF auf einen SOAP-Service zu, der die Verbindung zu einer MS SQL-Datenbank herstellt.

Logging auf die Konsole

Unter Java bzw. innerhalb eines Application-Servers ist es kein Problem, ein simples System.out.println() oder eben log.debug() zu benutzen. Die Ausgaben erscheinen dann jeweils in der Konsole der Entwicklungsumgebung. Wer jetzt denkt: so etwas geht doch sicherlich auch mit ASP.NET, der irrt gewaltig. Wenn innerhalb einer ASP.NET-Anwendung – hier zähle ich unseren WCF-Service einfach mal dazu – auf die Konsole geloggt wird (per log4nets Log.Debug oder aber Console.Write()), verschwinden die Ausgaben im Nirvana. Grund dafür ist, dass der IIS keine Konsole zur Verfügung stellt, in die überhaupt geloggt werden kann.

Entweder man benutzt nun die Trace-Methoden oder aber man schreibt die Logging-Ausgaben von log4net in eine Datei und setzt auf diese dann ein tail ab.

Lebenszyklus eines WCF-Service

Als JEE-Entwickler weiß ich, dass ein Servlet mit dem Start der Anwendung einmalig initalisiert wird und das Servlet dann so lange im Arbeitsspeicher gehalten wird, bis die Applikation heruntergefahren wird.

Bei WCF wird das Attribut InstanceContextMode benutzt, um die Gültigkeit eines Objekts zu beschreiben. Für einen WCF-Service gilt die Standardeinstellung, dass das Objekt (der Service) so lange vorgehalten wird, bis die gegenwärtige Session beendet wird (Einstellung PerSession).

Reihenfolge der Initalisierung von Servlets / Initialkonfiguration

In Anwendungen, die ich nicht mit Spring geschrieben habe, habe ich in meiner web.xml immer ein Servlet definiert, dass nichts weiter macht, als das die Standardeinstellungen gesetzt und z.B. der Logger initalisiert wird.

Bei .NET gibt es so etwas in der Art nicht (sollte ich falsch liegen, bitte ich um baldige Korrektur). Stattdessen muss man über eine selbst definierte ServiceHostFactory diese Konfiguration durchführen. Möglich wäre so etwas:

namespace My.Namespace.Service
{
    public class CustomServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
    {
        private Boolean isInitialized = false;

        public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
        {
            initalize();
            return base.CreateServiceHost(constructorString, baseAddresses);
        }

        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            initalize();
            return base.CreateServiceHost(serviceType, baseAddresses);
        }

        private void initalize()
        {
            if (isInitialized)
            {
                return;
            }

            log4net.Config.BasicConfigurator.Configure();
           // Weitere Initialisierungen
        }
    }
}

In der jeweiligen .svc-Datei muss dann die Instanzierung des Service über die CustomServiceHostFactory geschehen:

<%@ ServiceHost Language="C#" Debug="true" Factory="My.Namespace.Service.CustomServiceHostFactory" Service="My.Namespace.Service" CodeBehind="Service.svc.cs" %>

Service-Verbindungen im Client

Hinten über bin ich gefallen, als ich in der app.config des Clients probierte, das Suffix des Service-Endpoints einmalig über

<add key="httpBaseAddress" value="http://localhost" />

zu definieren. Das Attribut httpBaseAddress ist nur auf der Serverseite verfügbar und führt dazu, dass Redundanzen durch doppelte URIs entstehen.
Korrekt schaut das Beispiel nun so aus:

<configuration>
    <appSettings>
    </appSettings>
    <system.serviceModel>
      <!-- CusomtBinding benutzen, da mit wsHttpBinding NTLM nicht korrekt funktioniert -->
        <bindings>
          <customBinding>
            <binding name="Binding_Service">
              <mtomMessageEncoding />
              <security authenticationMode="SecureConversation">
                <secureConversationBootstrap authenticationMode="SspiNegotiated" />
              </security>
              <httpTransport authenticationScheme="Ntlm"/>
            </binding>
          </customBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:52264/Service.svc"
              binding="customBinding" bindingConfiguration="Binding_Service"
              contract="My.Namespace.Service.IService" name="Binding_IService">
            <identity>
              <dns value="localhost" />
            </identity>
          </endpoint>
        </client>
    </system.serviceModel>
</configuration>

Im Client wird dann über

ChannelFactory ServiceFactory = new ChannelFactory("Binding_IService");

der Proxy des Services geladen.

Die obige Konfiguration (customBinding) zeigt übrigens den Zugriff auf den Web-Service via NTLM. Mit dem Binding wsHttpBinding funktioniert NTLM nämlich nicht.

Apache Tomcat für Azubis

Nachdem ich vor ein paar Wochen den Artikel über Spring für Azubis veröffentlicht habe, wird es heute Zeit für “Apache Tomcat für Azubis”.
Dieser Artikel hat nicht ansatzweise den Anspruch, die komplette Konfiguration eines Tomcat-Servers zu beschreiben – dafür ist die offizielle Dokumentation gedacht. Hier geht es darum die grobe Funktionsweise zu erklären. Im Folgenden definiere ich Web-Applikation als eine Anwendung, die in Java geschrieben ist und in einem Applikationsserver läuft.

Was ist der Apache Tomcat?

Spätestens ein paar Stunden nach dem Einstieg in die Web-Programmierung mit Java stellt sich die Frage: “Wie bekomme ich den Mist überhaupt zum Laufen?”. Von der Entwicklung mit PHP ist man gewöhnt, dass die Applikation nur in eines der Verzeichnisse des Web-Servers geschoben werden muss und das war es dann schon – klar, PHP muss vorher noch in den Webserver als CGI oder Modul eingebunden sein.

Der Apache Webserver liefert bei einer Anfrage über HTTP oder HTTPS die jeweiligen (geparsten) Dateien zurück.
Nichts Anderes macht der Apache Tomcat auch. Tomcat ist ein Webserver, der speziell auf die Anforderungen von Web-Applikationen unter Java zugeschnitten ist.

Spezifikationen

Einige Hintergrundinfos sind nötig: Über den Java Community Process (JCP) werden in regelmäßigen Abständen so genannte Java Specification Requests (JSR) veröffentlicht. Diese beinhalten Standardisierungen für die Java-Umgebung. Oracle, damals Sun, hat 1997 mit der Version 1.0 die erste Version der Servlet-API definiert und diese mit der Version 2.3 im August 2001 dem JCP zugängig gemacht. Seit dieser Zeit werden Änderungen in der Servlet-API gemeinsam von der Java-Community bestimmt.

Die Servlet-API beschreibt anhand von Interface-Definitionen, welche Schnittstellen ein Applikations-Server implementieren bzw. bereitstellen muss.

Warum die Spezifikation?

Für verschiedene Anwendungsfälle gibt es verschiedene (Java-)Applikationsserver. Der bekannteste unter ihnen ist sicherlich Apache Tomcat. Daneben gibt es noch Jetty (freie Implementierung, ist relativ klein und kann in Embedded-Umgebungen gut eingesetzt werden), Websphere (kommerzielle Lösung von IBM) und einige andere.

Damit nun eine Applikation unter allen Applikationsserver funktionieren kann, muss jeder Server die Servlet-API bereitstellen. Oracle/Sun sichert über Test-Suiten ab, dass die Applikationsserver auch mit der Servlet-API kompatibel sind und den Anforderungen in dem JSR entsprechen.

Der Entwickler einer Webapplikation kann sich nun sicher sein, dass die Anwendung unter jedem Applikationsserver lauffähig ist. Die Entscheidung, welcher Server einzusetzen ist, hängt von den Anforderungen des Kunden oder des persönlichen Geschmacks ab.

Versions-Wirr-War(r)

Mit jeder neuen Version der Servlet-API werden neue Funktionalitäten aufgenommen, die die aktuellen Anforderungen im Web-Bereich widerspiegeln.

Wenn man sich den Eintrag in der englischen Wikipedia zu Servlets anschaut sieht man, dass einige Versionen existieren. Aktuell ist die Version 3.0, die im JSR 315 definiert ist. Eine der größten Erweiterungen ist die Definition von asynchronen Servlets, die durch die starke Verbreitung von AJAX-Funktionalitäten im Internet längst überfällig gewesen ist. Der Apache Tomcat 7 implementiert die Version 3.0 der Servlet-API und kann somit auch die neuen Funktionaliten bereitstellen.

Allerdings wird bei vielen Projekten weiterhin die letzte Version 2.5 eingesetzt – hierfür wird der Apache Tomcat 6 eingesetzt. Da Version 3.0 aber i.a.R. abwärtskompatibel ist, können für die Servlet-API 2.5 auch der Tomcat 7 eingesetzt werden.

Übersicht über die Servlet-API

Die Spezifikation für JSR 153/Servlet 2.5 ist knapp 350 Seiten lang, diese hier alle zusammenzufassen wäre unsinnig. Statt dessen will ich kurz die wichtigsten Themen behandeln.

Jede Web-Applikation basiert auf einem oder mehreren Servlets. Servlets sind Klassen, die von der abstrakten Klassen HttpServlet erben.Diese stellt Methoden bereit, die beim Aufruf des Servlets aufgerufen werden. doGet(…) wird bei einem HTTP-GET-Request, doPost(…) bei einem HTTP-POST-Request u.s.w.

Der Entwickler implementiert nun diese Methoden und kann über die Parameter vom Typ HttpServletRequest (eingehender HTTP-Request) und HttpServletResponse (zu erzeugende HTTP-Antwort) die Ein- und Ausgabe des HTTP-Streams steuern.

Klingt einfach, ist es an sich auch. Frameworks wie Spring kapseln all ihre Methoden um diese drei wichtigsten Klassen – und natürlich noch einigen anderen.

JavaServer Pages / JSP

JSP? WTF? Von PHP sind sicherlich Dateien bekannt, die wild PHP-Quellcode und HTML-Ausgabe mischen. JSP-Dateien sind das Gegenstück zu eben diesen PHP-Dateien. In ihnen kann HTML- und Java-Code parallel stehen.

Intern passiert beim Aufruf einer JSP-Datei nichts weiter, als dass bei ihrem ersten Aufruf die Datei in ein Servlet umgewandelt wird. Alle HTML-Fragemente werden über out.println() ausgegeben. Bei erneutem Aufruf der Datei wird nun das cachete Servlet ausgeführt. Sollte sich die JSP-Datei ändern, wird sie beim nächsten Aufruf neu kompiliert.

Datei web.xml und Ordner WEB-INF

Jede Web-Applikation muss im Hauptverzeichnis den Ordner WEB-INF und darin die Datei WEB-INF/web.xml besitzen. Der Inhalt des Ordners WEB-INF kann von außerhalb, d.h. vom Benutzer nicht aufgerufen werden. Er enthält für die Applikation alle wichtigen Dateien, dazu zählen die kompilierten Java-Dateien (Ordner WEB-INF/classes) und eben die Datei web.xml. Diese beschreibt die grundlegende Konfiguration der Applikation. Die web.xml ist ein so genannter Deployment Descriptor.

web.xml

Kommen wir nun zum simplen Aufbau des Deployment Descriptors

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
        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">
       <env-entry>
                <env-entry-name>environmentVariable</env-entry-name>
                <env-entry-value>true</env-entry-value>
                <env-entry-type>java.lang.Boolean</env-entry-type>
        </env-entry>
        <servlet>
                <servlet-name>myServlet</servlet-name>
                <servlet-class>de.ckl.servlet.MyServlet</servlet-class>
                <description>Serlvet</description>
                <init-param>
                        <param-name>debug</param-name>
                        <param-value>true</param-value>
                </init-param>
                <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet>
                <servlet-name>myLaterServlet</servlet-name>
                <servlet-class>de.ckl.servlet.MyLaterServlet</servlet-class>
                <description>Spaeter geladenes Servlet</description>
                <init-param>
                        <param-name>debug</param-name>
                        <param-value>false</param-value>
                </init-param>
                <load-on-startup>10</load-on-startup>
        </servlet>
        <servlet-mapping>
                <servlet-name>myServlet</servlet-name>
                <url-pattern>/servlet/*</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
                <servlet-name>myLaterServlet</servlet-name>
                <url-pattern>/later/*</url-pattern>
        </servlet-mapping>
</web-app>

Das Root-Tag web-app enthält den zu benutzenden Namespace und die dahinter liegende XSD. Wenn die Zuordnung nicht stimmt, kann vom Applikationsserver die Anwendung nicht gestartet werden. Also Achtung!

Das Tag env-entry legt anhand von Key-Value-Paaren applikationsweite Einstellungen fest, die für alle Servlets der Applikation gültig sind.

Über servlet werden nun die einzelnen Servlets definiert. servlet-name muss für jedes Servlet eindeutig sein, servlet-class verweist auf die Klasse, die HttpServlet implementiert. Mit init-param können Servlet-spezifische Einstellungen festgelegt werden. Im obigen Beispiel wird das Debugging für myServlet aktiviert, bei myLaterServlet hingegen deaktiviert.

Damit eine Reihenfolge zum Starten festgelegt werden kann – z.B. um Ressourcen wie Datenbankverbindungen zu initalisieren, wurde das Tag load-on-startup eingeführt. Je kleiner der Wert, desto früher wird das Servlet gestartet.

Wir haben nun also die Servlets und deren Einstellungen konfiguriert. Es geht nun darum, dass die Servlets auch im Browser aufgerufen werden. Im obigen Beispiel werden alle HTTP-Anfragen (das Verb – also GET, POST etc. ist dabei egal) auf die URL http://…/mein_context/servlet/* auf das Servlet myServlet gemappt (siehe erstes servlet-mapping-Tag), die URLs unter http://…/mein_context/later/* hingegen auf auf myLaterServlet.

Deployment

Das Einspielen der Webapplikation in den Tomcat nennt man Deployen. Entweder man hat die Applikation bereits manuell eingerichtet und kopiert die neuen Dateien einfach in das Applikationsverzeichnis oder aber man spielt ein .war ein. Ein WAR (Web Archive) ist nichts weiter als eine ZIP-Datei, die im Verzeichnis webapps des Tomcats kopiert wird. Der Tomcat überwacht den Inhalt des Verzeichnisses und extrahiert bei einer neuen .war-Datei diese in das Verzeichnis webapps/war-datei. Der Vorgang wird Hot-Deployment genannt.

Wir haben beispielsweise die Datei my-test-applikation.war. Diese wird nach webapps kopiert. Der Tomcat entpackt den Inhalt des Archivs automatisch nach webapps/my-test-applikation.

Das Archiv enthält alle Dateien, die von der Web-Applikation benötigt werden. Dazu gehören kompilierte Klassen (/WEB-INF/classes), abhängige Libraries (/WEB-INF/lib), der Deployment-Deskriptor (/WEB-INF/web.xml) und JSP-Dateien, Templates u.s.w.
Als Entwickler muss man immer im Hinterkopf behalten, dass die Anpassung der globalen conf/server.xml bzw. conf/web.xml des Tomcats während des Deployments nichts angetastet werden soll. Dadurch wird sichergestellt, dass die Applikationen zum einen einfach zu deployen und zum anderen unabhängig von dem eingesetzten Application-Server sind. In einem Jetty existiert z.B. keine server.xml!

Context Path

Die Applikation an sich ist dann unter http://localhost/my-test-applikation erreichbar. Den Pfad hinter http://localhost/ wird Context Path genannt und ist unabhängig von dem realen Ordner!

In der Datei conf/server.xml wird automatisch beim Hot Deployment ein neuer Konfigurationseintrag erzeugt:

<Host name="localhost" debug="0" appBase="webapps"
	      unpackWARs="true" autoDeploy="true">
    <Context path="my-test-applikation" docBase="webapps/my-test-applikation" debug="0"/>
</Host>

Über das path-Attribut wird festgelegt, dass alle Anfragen auf die URL http://localhost/my-test-applikation an die Applikation geht, die im Odner (docbase) webapps/my-test-applikation liegt.

Lifecycle

Beim Starten des Tomcats werden alle Applikationen, d.h. jeder definierte Context gestartet. Dazu überprüft Tomcat die Datei web.xml und startet jedes Servlet in der angegebenen Reihenfolge.

Sobald nun eine HTTP-Anfrage an den Tomcat gerichtet ist, wird zuerst überprüft, ob der Context Path definiert ist. Bei Existenz wird an das Servlet übergeben, auf dem das Mapping der URL zutrifft.

Beim Beenden des Tomcats werden alle laufenden Servlets beendet.

Wichtigster Unterschied zu PHP-Webapplikationen

Beim Umstieg von PHP-Webapplikationen zu Java gibt es neben den sprachlichen Unterschieden folgende, gravierende Differenz, die man als Entwickler immer im Hinterkopf haben muss: Unter PHP wird bei jedem Seitenaufruf das Script geparst – bei Java hingegen wird eine Applikation zu Beginn ihres Lebenszyklus gestartet und läuft die ganze Zeit!

Dafür ist es wichtig, dass man die verschiedenen Scopes kennt:

  • Web Context / Application – javax.servlet.ServletContext
    Dieser Kontext umfasst die komplette Applikation und existiert vom Start des Servlets bis zum Herunterfahren
  • Session – javax.servlet.http.HttpSession
    Eine Session unter PHP ist bekannt? Dieser Scope ist identisch dazu.
  • Request – javax.servlet.HttpServletRequest
    Der Request-Scope ist so lange verfügbar, wie eine HTTP-Anfrage dauert.
  • Page – javax.servlet.jsp.PageContext
    Bei der Arbeit mit JSP-Dateien existiert dieser Scope. In ihm sind nur Objekte verfügbar, die innerhalb der Seite definiert worden sind.

Objekte im Scope Application können von allen Servlets gesehen werden, es existiert immer nur ein Application-Scope innerhalb einer Anwendung. Session-Scopes existieren für jeden Benutzer, solange er mit der Applikation kommuniziert. Ein Benutzer kann nicht auf die Session eines anderen zugreifen (zumindest nicht ohne Tricks).
Bei 10 HTTP-Anfragen existieren auch 10 Request-Scopes, die i.a.R. jeweils einer Session zugeordnet sind.

Entwicklung mit Eclipse

Eclipse bzw. WTP bindet den Applikationsserver direkt in die IDE mit ein.

Integration

Zuerst muss unter Preferences -> Server -> Runtime Environments ein neuer Server hinterlegt werden. Dazu muss über den Button Add zuerst die verwendete Tomcat-Version ausgewählt und danach das Installationsverzeichnis angegeben werden. Unter File -> New -> Other -> Server -> Server muss danach ein neuer lokaler Server eingerichtet werden. Im folgenden Dialog muss unter Apache die verwendete Version ausgewählt werden. Die letzte Seite des Wizards mit dem Namen Add and Remove wird dazu verwendet, um Projekte vom Typ Dynamic Web Project automatisch der Tomcat-Instanz zuzuweisen.

Nachträgliche Änderung eines Projekts in Dynamic Web Project

Falls ein Projekt nachträglich in ein Dynamic Web Project umgewandelt werden soll, geschieht das über einen Rechtsklick auf den Projektordner im Project Explorer. Dort muss unter Project Facets der Eintrag Dynamic Web Module ausgewählt werden.
Die Aktivierung sorgt dafür, dass das Projekt von Eclipse als Web-Projekt erkannt wird.

Ordnerstruktur

Eclipse benutzt standardmäßig den Ordner WebContent des Projekts als Hauptverzeichnis für die Dateien, die automatisch im Tomcat deployt werden. Wenn das Verzeichnis nicht WebContent lauten soll, muss im Projektordner die Datei .settings/org.eclipse.wst.common.component angepasst werden. Dort lässt sich definieren, welche Dateien in den Tomcat deployt werden sollen.

Automatisches Deployment

Was macht nun Eclipse beim Starten der lokalen Tomcat-Instanz?

  • Zuerst werden alle Klassen kompiliert, die in einem Source-Ordner liegen.
  • Danach wird, falls noch nicht vorhanden, unter workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmpN/wtpwebapps der Ordner des Context-Paths erstellt – das N bei tmpN steht dabei für die Nummer der jeweiligen Serverinstanz
  • Anhand der XML-Datei .settings/org.eclipse.wst.common.component werden nun alle dort definierten Dateien in den Context-Path kopiert
  • Eclipse startet nun eine neue Tomcat-Instanz, bei der das Verzeichnis mit den Konfigurationsdateien auf Servers -> Apache Tomcat x.x at localhost-config gemappt wird. Somit wird sichergestellt, dass die lokale Tomcat-Installation autark von der Entwicklerinstanz betrieben werden kann.
  • Tomcat lädt die Apache Tomcat x.x at localhost-config/server.xml und erstellt einen neuen Context-Path in der Laufzeitumgebung des Tomcat
  • Alle HTTP(S)-Anfragen des Context-Paths werden somit auf den realen Pfad workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmpN/wtpwebapps/my-test-applikation gemappt

Rautiges 2010-06-28

Perle IOLAN (Trueport) und RXTXcomm (Java) unter Linux

Für unser aktuelles Projekt verwenden wir eine Perle IOLAN-Box. Die Box stellt serielle Ports – in unserem Fall sind das zwei Stück – über Ethernet zur Verfügung.
Der Einsatzbereich ist z.B. nötig, wenn man GSM-Modems in abgeschirmten Räumen (Rechenzentren, Bunkern) betreiben will. Die IOLAN-Box bildet eine Bridge zwischen Seriell- und Ethernet ab. So lässt sich das GSM-Modem (Seriell) an die IOLAN-Box (Seriell) anschließen, die wiederum über Ethernet an das Netzwerk angebunden ist. Für Linux und Windows existieren passende Programme, die nun lokal wiederum die Verbindung über das Netzwerk als serielles Interface abbilden.

Gestern stieß ich bei der Einrichtung der lokalen Schnittstellen auf ein Problem: RXTXComm – das Java-Framework zur Kommunikation mit seriellen/parallelen Schnittstellen – listete beim Start die /dev/tx00*-Devices nicht auf. Die FAQ von RXTX weist daraufhin, dass man per
System.setProperty(“gnu.io.rxtx.SerialPorts”, portFileNames) zusätzliche Ports definieren kann. Der Lösungsansatz brachte keinen Erfolg.
Zweite Idee war dann, dass ich – wie in der FAQ beschrieben – die tx*-Devices manuell in die RXTXCommDriver.java eintrage. Auch das war nicht von Erfolg gekrönt.

Der ausschlaggebende Punkt war schließlich, dass die /dev/tx00*-Devices die falschen Berechtigungen besaßen: RXTXComm erkennt die seriellen Schnittstellen nur wenn /dev/tx00* der Gruppe uucp angehört (chown uucp /dev/tx*) und die Gruppe Schreib- und Leserechte besitzt (chmod g+rw /dev/tx*).

Update 2016-05-02

Wir standen mit einem OpenSuSE 13.2 32 Bit wieder vor dem selben Problem. Diesmal half aber auch nicht das Setzen der Permissions. Beim Start konnte der CommPortIdentifier nur die /dev/ttyS*-Devices erkennen, obwohl die Berechtigungen korrekt gewesen sind. Letzendlich half nur, Symlinks mit den selben Berechtigungen von /dev/tx00* nach /dev/ttyS* zu setzen.

Let the ants feed the bird: ant-twitter, a Twitter task for Ant

I have learned the last 5 day for business studies and was so bored that I really got happy when I saw the article about a Twitter-Maven-Plugin on entwickler.com yesterday morning.
We still use Ant as building system in our development team so I searched for an Ant task which enables Twitter support. As I did not found any plugin I started to write my own simple task called ant-twitter.

Features

  • Support for URL shortening service bit.ly via bitlyj
  • Support for messages more than 140 characters – the message will be splitted into two or more messages. A small source snippet is taken  from jtwitter – thanks a lot guys!
  • Open source – Apache license
  • It is simple 🙂

Installation

I assume you have already installed Ant-1.70 (1.6x should work although) and configured your Ant working environment. I refer to $ANT_LIB$ which is a directory where your Ant installation can found further JARs (should be appended to your classpath).

  • Download needed packages and extract them to $ANT_LIB$:
  • Create a file twitter4j.properties in your classpath ($ANT_LIB$) and paste the following code into it:
    twitter4j.http.useSSL=true
    twitter4j.debug=false
    

Usage

After you have installed the needed dependencies and upset your configuration you have to edit your build.xml. Put the following taskdef in top of your build file:

<project name="TwitterTestTask" default="main" basedir=".">
  <taskdef name="twitter" classname="de.ecw.ant.twitter.AntTwitterTask"/>

You are now able to use the twitter task in form of

    <twitter message="Hello World from Ant!" username="twitter-username" password="twitter-password" bitlyUsername="bitly-username" bitlyApiKey="bitly-key" enableBitly="false" />

ant-twitter has the following options:

  • message (required, String): Your Twitter tweet
  • username (required, String): Your Twitter screenname
  • password (required, String): Your Twitter password
  • bitlyUsername (optional, required if enableBitly=true, String): Your bit.ly username
  • bitlyApiKey (optional, required if enableBitly=true, String): Your bit.ly API key
  • enableBitly (optional, true|false): Enable bit.ly support

Bug unter Linux mit Java 1.5.0

Gestern war ich damit beschäftigt, unser Deployment-Tool im Server-Modus in einer Ubuntu-VM zu installieren. DT läuft als J2EE-Applikation und wurde von mir in einem Tomcat 6.0 unter Java 1.6 deployt. Die Kombination von Tomcat 6.0 und Java 1.6 führte aber -mal wieder aus unerfindlichen Gründen – dazu, dass weder die Logs unter /var/log/tomcat6 gefüllt wurden, noch mein eigenes DT-Log. Die Dateien wurden zwar angelegt, aber die Log-Messages wurden nicht geschrieben. Laut ps aux und /var/log/syslog lief Tomcat aber. Der Verdacht, dass die Festplatte vollgelaufen wäre, bestätigte sich nicht.

Ich führte also ein Downgrade auf Java 5 und Tomcat 5.5 durch. Nach dem Start des Tomcats bekam ich ein Segmentation Fault der Hotspot VM mit dem Status SIGBUS (0x7). Im Internet wurde ich dann im Bugtracker von Sun auch schnell fündig: Sobald das /tmp-Verzeichnis voll ist, schmiert die Hotspot VM ab. Der Fehler traf auf mein Problem genau zu, da ich vorher mit strace die Aufrufe der VM nachgegangen bin und diese nach dem Zugriff auf /tmp/hsperfdata abstürzte. Radikale Lösung war: rm -rf /tmp/*.