Dieses Blog durchsuchen

Dienstag, 3. Februar 2026

Reduce hosting costs from €108 to €5 in 15 minutes

I recently experienced a DNS outage at my hosting provider, Host Europe. Support failed to react for two days—a service level that has definitely declined over the years. 

I was already dissatisfied with them because they had forced a migration of my emails to Microsoft Exchange without giving me a choice. 

Looking at my current expenses, I realized there had to be a more cost-effective way to host my setup.

Previous Costs:

Web Hosting: €84

Domain: €12

Mail: €12

Due to the outage, I prioritized the DNS migration first. 

I chose Cloudflare, a service I’ve used before and appreciate for its intuitive interface and generous free tier. I let Cloudflare automatically scan and import my existing DNS records before deleting them at Host Europe. 

Finally, I updated the nameserver entries to point to Cloudflare to complete the transition. 

My email traffic for my custom domain is now routed directly to Gmail. To handle outgoing emails, I configured Gmail to send via a custom SMTP relay. I also made sure to update my SPF and DKIM records within Cloudflare to ensure high deliverability and prevent my emails from landing in spam folders.

For my homepage, I decided to switch to GitHub Pages. 

Since it's free and Git-based, it allows me to manage my website just like any other software project—no more manual FTP uploads.

Finally, regarding the domain "jens-stahl.de" itself: unfortunately, Cloudflare does not support .de domains yet. 

After some research, I found netcup, which offers .de domains for only €5 per year. 

This transition has reduced my total costs to just €5 annually for my custom email, web hosting, and domain management. And I am pretty confident that this modern setup is more reliable than before.

Sonntag, 13. November 2022

Doing a JSF Login with the load testing tool Locust

Locust (locust.io) is an open source load testing tool which allows you to write load tests in Python. This is a nice approach because you can leverage the full possibilities of this nice programming language and are not restricted to complicated and overloaded UIs to configure your tests. (you probably have an idea about which tool I am referring to)

The starting point of each load test is a so called locust-file which contains the code that is executed in order to run the test. Locust can be used to test all kind of technology but is per default optimized for http-based systems like REST services or web-UIs.

Doing REST calls and simple GET-requests is easy and they are pretty much self-explanatory:

class WebTest(HttpUser):
    @task
    def index_page(self):
        self.client.get("/index.html")
    @task
    def login_page(self):
        self.client.get("/login.html")

In this example two simple get request is issued to the web sites "index.html" and "login.html". The task annotations define an action that a user is doing repeatedly.

When you have to support frameworks like JSF the challenges increase. For example when doing a JSF login with locust (python) you  have to keep two things in mind:

  1. JSF uses a JSESSIONID which is set to a cookie and identifies the session of a user on the server. Therefore the client needs to save the JSESSIONID after login in order to make sure the session is not lost between the requests.
  2. JSF uses a view state to keep track of the JSF component tree on the server side. The view state ID has to be sent to the server otherwise an exception is thrown.
Fortunately the first requirement is not a problem because locust's HttpUser keeps track of the session automatically by using cookies in the background. The View State ID on the other hand is something that needs to be sent to the server manually. In order to do so the first step is to call the pages that includes the login form with the view state is. 
The login procedure can be defined in the on_start method of the test class, which is executed only once before the tasks are performed:

  def on_start(self):
      response = client.get('/')
    
Afterwards the view state ID can be extracted from the response. Here is an example on how this is done with the "BeautifulSoup" library:

    bs = BeautifulSoup(response.content, features="html.parser")
    viewstate = bs.find("input", {"name": "javax.faces.ViewState"}).attrs['value']

Finally the login can be executed:

 data = {"loginForm": "loginForm",
     "user": "my_user",
     "password": "test123",
     "javax.faces.ViewState": viewstate,}
 
 response = client.post('/', data=data)

If you ask yourself which parameters you need to send in the post request for a successful login it can be helpful to take a look at the requests performed using the developer tools (i.e. from Chrome browser) to see the actual request done when using a web browser. 


Montag, 11. April 2022

Solution for long running tasks in Camunda: External tasks

One of my recent jobs was to design a bpmn-process with Camunda for one of my clients.

The requirements were clear and not too complicated. I had to struggle with some technical challenges regarding Camunda though. One of these challenges was that a task within the process can take some time. Standard synchronous Camunda tasks are not designed to do long lasting work by design.

At my client the Camunda engine runs within a Wildfly application server and therefore uses the transaction handling of Wildfly. The default transaction timeout for a Wildfly managed database transaction is 10 minutes. This means that your task's transaction will also timeout after 10 minutes which might not be enough in particular cases. Of course you could increase the transaction timeout, but this will affect all applications deployed within wildfly. And what if your transaction has to run for several days or weeks? Consulting the Camunda forum (Camunda has a great community) and the documentation I found a solution in using "external tasks".

External tasks enable you to provide a work load to an external worker. This way the task's transaction can finish right away and let some other service deal with the complexity of the long running job. This service can use its own transaction handling completely independent of the Camunda process.

The following steps are necessary to implement an external task:

  • Activate the Camunda REST-API (it is probably also possible to use external tasks with the Camunda Java API but I have found only examples with the REST-API). The REST-API is activated by extending the Application class and overriding the getClasses method: (only applicable for a Java EE application)

@ApplicationPath("/camunda-rest")
public class CamundaRestApi extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        // setting up Camunda REST service
        Set<Class<?>> classes = new HashSet<Class<?>>();
        // add camunda engine rest resources
        classes.addAll(CamundaRestResources.getResourceClasses());
        // add mandatory configuration classes
        classes.addAll(CamundaRestResources.getConfigurationClasses());
        return classes;
    }
}

Besides the file org.camunda.bpm.engine.rest.spi.ProcessEngineProvider with the content org.camunda.bpm.engine.rest.spi.ProcessEngineProvider has to be placed under the application's META-INF/services path: my-application\src\main\webapp\META-INF\services

  • Define your service task in your bpmn-file by setting the caunda:type attribute to external und providing a topic name. You can choose any topic name you want:
<bpmn:serviceTask id="lotsOfWorkId" name="do a long running task" camunda:type="external" camunda:topic="lotsOfWorkTopic">
  • Create and an external task client and subscripe to the topic:
public static ExternalTaskClient getExternalTaskClient() {
    return externalTaskClientBuilder()
        .baseUrl("https://myHost/MyWebapp/camunda-rest")
        .workerId("lotsOfWorkWorkerId")
        .build();
}

getExternalTaskClient()
    .subscribe("lotsOfWorkTopic")
    .handler(lotsOfWorkHandler)
    .open();
  • The external task handler is the place where the actual business logic is defined that will be executed when a task is written to the topic. This handler is not bound to any transactions and can define on its own how the work should be executed. The handler implements the ExternalTaskHandler interface.
public class LotsOfWorkHandler implements ExternalTaskHandler {
    @Override
    public void execute(final ExternalTask externaltask, final ExternalTaskService service) {
    //do some long lasting work...
    //...
    //complete the task
    externalTaskService.complete(externaltask);
}

By using external tasks I was able to let a task run for a very long time and leave the default transaction timeout to 10 minutes. One disadvantage of this approach is, that all variables that are passed to and from the external task handler have to be serializable because they are transferred over http through the REST API. I was not able to use JSON directly (Camunda uses the Spin API for this) but had to pass a List<String> and convert it to JSON within the task itself.


Freitag, 19. März 2021

CRaSH Console - start your long running process in an extra thread!

The CRaSH console is a nice tool to do imports or batch updates that should not become part of your main application.

Why should you use the CRaSH console?

  • your code runs within your application context and you have access to your runtime environment (business services like spring beans, database access to your DAOs etc)
  • your code is deployed through all stages and it is impossible to accidentally run test code on your production environment
  • you can use light weight scripting languages like groovy to implement your requirements that can be changed without redeployment
There is one important thing to consider when you are doing long running batch updates or imports: For some reason database connection-timeouts occur when running the code synchronously and the execution times takes more than a few hours. 
In order to fix this situation our team has come up with the solution to start the actual long running code in an extra thread:

    public void performBatchJob() {
        new Thread(() -> {
            //do some long running updates or imports
        }).start();
    }

Doing the execution this way will protect you from DB timeouts and the code runs savely for a long time. We have done batch updates that run several weeks without problems in the CRaSH console this way.

To learn more about the CRaSH console visit https://www.crashub.org/



Sonntag, 18. Oktober 2020

JSF: Keep data in Flash Scope on browser refresh and browser back

JSF supports different data scopes like the session scope to store data in the user session and the request scope to keep data for the lifespan of one request. Of course the goal should always be to keep the data in the most narrow scope possible because this approach will save server resources.

An interesting JSF scope is the flash scope. The flash scope expands the request scope to survive redirects. Redirects are an important part of the PRG-pattern which is commonly used in JSF applications. (for details on the PRG-pattern please see this post-redirect-get-and-jsf-20 blog post for details)

One problem I have encountered recently using the flash scope is that data is lost on a browser refresh and on a browser back. Some developers approach this problem by telling the users not to use this browser functionality (i.e. by java script checks) but in my opinion an application should support this basic functionality. Fortunately there is a suprisingly easy solution to this problem: By invoking the code:

FacesContext.getCurrentInstance().getExternalContext().getFlash().keep("context");

JSF is instructed to keep the flash data (in this example the variable "context") even when the user hits F5 (refresh) or navigates back to a previous page with the browser's navigation buttons.

Mittwoch, 8. Juli 2020

Understanding Java Keystores for private key authentication

When your application needs to communicate over https (SSL) a KeyStore and a TrustStore may be involved.

The TrustStore usually holds the public keys of the servers that the client wants to establish a connection to. This store is located in the [jdk_home]\lib\security\cacerts file.

Usually it is sufficient to import a server certificate into this file in order to trust the issuing server with the help of the keytool command.

A KeyStore on the other hand usually holds private keys that can be used for authentication. The file is often in the PKCS12 format and has the file ending "pfx".

There are two ways to configure a TrustStore and a KeyStore. The easiest way is to use these JVM parameters:

 a) by configuration

-Djavax.net.ssl.keyStore=/var/datamyKeyStore.pfx
-Djavax.net.ssl.keyStorePassword=myPassword
-Djavax.net.ssl.trustStore=/java/jdk11/lib/security/cacerts
-Djavax.net.ssl.trustStorePassword=changeit

 Obviously these variables need to be evaluated when connecting to a server. The apache http client (https://hc.apache.org/httpcomponents-client-5.0.x/index.html) and the jax-rs jersey client (https://mvnrepository.com/artifact/com.sun.jersey/jersey-client) do not read values from these variables. Therefore a different approach is necessary:

 b) programmatically load the TrustStore and KeyStore

This example uses the popular Apache HttpClient.
The following code shows how a SSL context is created with a truststore and a keystore which is needed when creating the client:

private SSLContext getSslContext() throws Exception {
    //load truststore (cacerts file)
    KeyStore serverKeystore = KeyStore.getInstance(KeyStore.getDefaultType());
    serverKeystore.load(new FileInputStream(trustStorePath), trustStorePassword.toCharArray());
    TrustManagerFactory serverTrustManager = TrustManagerFactory.getInstance("X509");
    serverTrustManager.init(serverKeystore);
    //load keystore (pfx file)
    KeyStore userKeystore = KeyStore.getInstance("JKS");
    userKeystore.load(new FileInputStream(keystorePath), keyStorePassword.toCharArray());
    KeyManagerFactory userKeyFactory =
            KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    userKeyFactory.init(userKeystore, keyStorePassword.toCharArray());
    SSLContext sslContext = SSLContext.getInstance("TLS");
    //init the SSL context with truststore and keystore
    sslContext.init(userKeyFactory.getKeyManagers(),
            serverTrustManager.getTrustManagers(), null);
    return sslContext;
}

This HttpClient is then created on basis of the SSLContext:

private CloseableHttpClient secureConnection() throws Exception {
    SSLContext sslContext = getSslContext();
    SSLConnectionSocketFactory sslConSocFactory = new SSLConnectionSocketFactory(sslContext);
    CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslConSocFactory).build();
    return (httpclient);
}

Finally the client can be used to perform a https request with private key authentication:

private void performRequest(String url) throws Exception {
    CloseableHttpClient closeableHttpClient = secureConnection();
    HttpHead httpHead = new HttpHead(url);
    CloseableHttpResponse response = closeableHttpClient.execute(httpHead);
}

Sonntag, 29. März 2020

Obfuscating a jar file with yGuard and maven

yGuard is a nice tool that obfuscates java sources. Unfortunately there is no maven support.
It is possible to combine yGuard with maven but you have to make sure obfuscation is called in the correct step of the maven build process.

In order to run yGuard in a maven build process the following antrun plugin configuration in the maven pom.xml file of a jar artifact can be used:

<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
 <execution>
  <phase>integration-test</phase>
  <configuration>
   <tasks>
    <taskdef name="yguard"
       classname="com.yworks.yguard.YGuardTask"
       classpath="build-resources/obfuscator/yguard.jar"/>
    <copy todir="target">
     <fileset dir="target" />
     <globmapper from="*.jar" to="backup/*_backup.jar" />
    </copy>
    <yguard>
     <inoutpairs>
      <fileset dir="target" includes="myApp*.jar"/>
     </inoutpairs>
     <property name="naming-scheme" value="best"/>
     <rename logfile="renamelog.xml">
      <adjust replaceContent="true">
       <include
         name="web.xml"/>
      </adjust>
      <keep>
       <class methods="private" fields="private">
        <patternset>
         <include name="de/myProject/unchanged/**/*"/>
        </patternset>
       </class>
       <class classes="none" methods="none" fields="none">
        <patternset>
         <include name="de/myProject/obfuscate/**/*"/>
        </patternset>
       </class>
      </keep>
     </rename>
    </yguard>
    <copy todir="target">
     <fileset dir="target" />
     <globmapper from="*_obf.jar" to="*.jar" />
    </copy>
    <delete>
     <fileset dir="target">
      <include name="*_obf.jar"/>
     </fileset>
    </delete>
   </tasks>
  </configuration>
  <goals>
   <goal>run</goal>
  </goals>
 </execution>
</executions>
</plugin>
Note the following aspects:
  1. the antrun plugin is executed in the "integration-test" phase. During this phase the original jar file that needs to be obfuscated is already built and we have a chance to obfuscate it before it is installed into the local maven repo.
  2. before obfuscation a backup of the original jar file is done 
  3. after obfuscation (running the yguard-task) the obfuscated jar is renamed to its original name (with the help of copy and delete)
  4. Afterwards the obfuscated jar is installed into the local maven repository and can be referenced from other maven modules

Donnerstag, 5. Dezember 2019

Tagging an AWS Cloud Watch alarm

Tagging an AWS Cloud Watch Alarm

Recently tried to tag an alarm in AWS Cloud Watch. First I took a look at their REST-API documentation:


There are several things I found remarkable when trying to add a tag via the TagResource "action".

First of all there is no example included in the documentation. Not too bad because there is google with a lot of examples out there? Wrong, I couldn't find a single sample of someone doing such a REST call. After a lot of try and error I found a working solution:

The tag has to be included as a query parameter and a GET-request has to be issued in order to create a tag. (Please do not ask me why they are not implementing this as a post or put request like everyone else is doing). A key-value pair of tags has to be provided in this format as query parameters:

...&Tags.member.1.Key=myKey&Tags.member.1.Value=myValue

I am wondering how anyone is able to come up with this solution after reading the API documentation (see link above).

Since I found several other issues when implementing plain REST calls I continued with the Java SDK:


This API is well documented with a lot of examples and it is also easy to use. 

Conclusion: I think Amazon doesn't really want you to use their REST API directly. It is complicated, not well documented and has a strange architecture (GET-request to create data). Unfortunately I have found no hint that you are way better off using the SDKs that are available in multiple languages (C#, Go, JavaScript, Python, PHP, etc)

Freitag, 17. Mai 2019

Positioning of a primefaces dialog (p:dialog)

When using the primefaces dialog on a large page that has a vertical scrollbar, the dialog might not be visible because it is displayed at the top of the page and your current scroll position is too far down.

In order to make the dialog nicely centered on the visible part of the page I have used a small java script function that positions the dialog for me after it is rendered by primefaces:

function positionDialog(dialogId,anchorId) {
    var anchor = $(anchorId);
    PF(dialogId).getJQ().position({
        "my": "center",
        "at": "center",
        "of": $(anchor)
    })
}

In your xhtml page all you have to do is to define the dialog and the anchor to which the dialog has to be moved to. The anchor should be put on the place of the page where the dialog should appear:


<div id="myAnchorId"></div>
<p:dialog id="myId"
          header="My dialog"
          widgetVar="myId"
          onShow="positionDialog(myId','#'+'myAnchorId')"
          modal="true">
          This is the content of my dialog
</p:dialog>

This way the dialog is always nicely centered no matter where your vertical scollbar is positioned at the moment.

Sonntag, 22. Oktober 2017

Autocompile im IntelliJ

Diese Woche bekam ich eine interessante Mail vom JRebel Produkt-Manager Sander Sõnajalg. Darin wird beschrieben, wie man im Intellij autocompile nutzen kann. Dies war bisher meines Wissens nicht möglich, man musste entweder Strg-F9 (Projekt bauen) oder Strg-Shift-F9 (aktuelle Klasse compilieren) nutzen, um Änderungen wirksam werden zu lassen.

Das Autocompile Feature lässt sich wie folgt nutzen:

  1. Im Intellij die Einstellungen öffnen, Compiler in den Filter eingeben, "Build project automatically" anwählen (obwohl hier die Warnung steht, dass dies nicht beim laufenden oder im Debug-Modus befindlichen Projekt funktioniert)
  2. Strg-Schift-A drücken im Intellij, "Registry..." in den Filter tippen, auswählen und "compiler.automake.allow.when.app.running" Checkbox aktivieren

Mit diesen Einstellungen werden Änderungen am Quelltext sofort im laufenden Programm wirksam, ohne dass eine zusätzliche Eingabe erfolgen muss. Die bisherigen Tests sahen sehr gut aus, sodass ich mich weiterhin über das geniale, wenn auch relativ teure JRebel freue. (gibt es Leute, die nach Nutzung von JRebel noch ohne dieses Tool leben können?)

Freitag, 6. Januar 2017

REST Service Exception Handling


Bei der Implementierung von REST-Services stellt sich der Entwickler früher oder später die Frage, wie eigentlich mit Exceptions umgegangen werden soll. Denn anders als bei der Implementierung von SOAP Services muss sich der Entwickler hier eine eigene Strategie überlegen, wie aufgetretene Fehler dem nutzenden System übermittelt werden sollen.

An dieser Stelle möchte ich eine Variante aus der Praxis vorstellen, die sich bewährt hat. Und zwar wird dazu die Klasse javax.ws.rs.ext.ExceptionMapper implementiert und mit der @Provider Annotation versehen. Eine weitergehende Konfiguration oder Aktivierung des Mappers ist nicht notwendig. Indem nun die Methode toResponse überschrieben wird, kann definiert werden, wie die Antwort des Servers im Falle einer Exception konkret aussehen soll.

Das Beispiel zeigt den Aufbau einer Nachricht bestehend aus einleitendem Text "An Error occured!", dem aktuellen Datum, dem Namen der Exception-Klasse und der Nachricht. Falls die Exception einen root cause hat, wird auch dieser noch mit ausgegeben.
Zusätzlich ist es sinnvoll, einen passenden HTTP-Status-Code mitzugeben, in diesem Fall Status Code 500 für "Internal Server Error", denn viele Clients fragen diesen Status-Code ab, um entsprechend reagieren zu können.

@Provider
public class RestThrowableExceptionMapper implements ExceptionMapper<Throwable> {

    @Context
    private HttpHeaders headers;

    @Override
    public Response toResponse(Throwable throwable) {
        int status = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(); //defaults to internal server error 500;
        StringBuilder messageBuilder = new StringBuilder();
        messageBuilder.append("An Error occured! ");
        messageBuilder.append(new Date());
        messageBuilder.append(": ");
        messageBuilder.append("Cause -> ");
        messageBuilder.append(throwable.getClass().getName());
        messageBuilder.append(": ");
        messageBuilder.append(throwable.getMessage());
        // also append root cause of exception if present
        Throwable rootCause = ExceptionUtils.getRootCause(throwable);
        if (rootCause != null) {
            messageBuilder.append("; Root Cause -> ");
            messageBuilder.append(rootCause.getClass().getName());
            messageBuilder.append(": ");
            messageBuilder.append(rootCause.getMessage());
        }
        return Response.status(status).
               entity(messageBuilder.toString()).
               type(headers.getMediaType()).build();
    }
}

Dienstag, 27. Dezember 2016

Eigener Authenticator für Basic Authentication in Kombination mit Proxy-Authentifizierung

Sofern ein REST-Client (oder auch SOAP-Client) sich zunächst über einen Proxy authentifizieren soll und anschließend eine Basic-Authentifizierung durchführen muss, bietet es sich an, einen eigenen Authenticator zu schreiben. 
Dieser Authenticator muss von der Klasse java.net.Authenticator ableiten und die Methode getPasswordAuthentication() überschreiben. Erwähnenswert ist nun, wie die implementierung dieser Methode konkret aussieht:

@Override
protected PasswordAuthentication getPasswordAuthentication() {
    String requestingHost = getRequestingHost();
    if (proxyEnabled && requestingHost.equals(proxyHost))    {
        return new PasswordAuthentication(proxyUserName, proxyPassword);
    }   else    {
        return new PasswordAuthentication(serverUserName, serverPassword);
    }
}

Zu sehen ist, wie mithilfe der geerbten Methode getRequestingHost()zunächst der Host ermittelt wird, der eine Authentifizierungsanfrage stellt. Sofern dies der Proxy ist, wird entsprechend der Nutzer und das Passwort des Proxys gesetzt. Handelt es sich jedoch um den Zielserver, werden die Credentials entsprechend für diesen verwendet.

Über die statische Methode Authenticator.setDefault(new MyAuthenticator()); wird der Authenticator schließlich JVM-weit gesetzt und beginnt mit der Arbeit, sobald die ersten Authentifizierungsanfragen eintreffen.

Mittwoch, 15. Juni 2016

REST Service Dokumentation mit Swagger


REST Services erfreuen sich seit einigen Jahren immer größerer Beliebtheit. Während SOAP Services als unflexibel und unnötig kompliziert gelten, sind REST Services schlank und bestehende Schnittstellen sind nicht so "empflindlich" gegenüber Änderungen. Z. B. lassen sich Services und Query-Parameter hinzufügen, ohne dass der Schnittstellenvertrag gebrochen wird.

Einen großen Nachteil haben REST Services jedoch gegenüber SOAP mit WSDL: die WSDL definiert die Schnittstelle und ermöglicht es einem Client so, das Interface sehr einfach mit wenigen Zusatzinformationen anzusprechen. Es gibt Tools die anhand einer WSDL Stubs (Client Klassen) erstellen können, sodass schnell klar wird, wie die Schnittstelle zu bedienen ist. Zwar gibt es auch bei REST vergleichbare Ansätze (siehe z. B. WADL), diese konnten sich jedoch in der Praxis nicht wirklich durchsetzen.

Umso wichtiger ist es daher, die REST Services umfassend zu dokumentieren und die Doku auch aktuell zu halten. Genau hier setzt Swagger an. Mit Swagger lassen sich die Services sehr komfortabel über Annotations beschreiben. Dadurch dass zur Erstellung der Doku Informationen direkt aus dem Quelltext zu Rate gezogen werden, ist stets Aktualität gewährleistet. Des weiteren bietet Swagger eine HTML Oberfläche, um die Doku zu präsentieren und die Services direkt auszuprobieren.

Im Folgenden werden die wenigen notwendigen Schritte beschrieben, um Swagger im eigenen Projekt nutzen zu können. (basierend auf einem JAX-RS und Maven Projekt)

Zunächst muss die Swagger-Lib als Dependency aufgenommen werden: 

<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-jaxrs
    <version>1.5.9</version>
    <scope>compile</scope>
</dependency>

Anschließend muss die Rest- "Application" Klasse um folgenden Konstruktor ergänzt werden (falls JAX-RS verwendet wird): 

//Swagger initialization
public Application() {
    BeanConfig beanConfig = new BeanConfig();
    beanConfig.setVersion("1.0");
    beanConfig.setTitle("Meine REST services");
    beanConfig.setBasePath("/anwendung/service");
    //Hier befinden sich die Rest-Services
    beanConfig.setResourcePackage
    ("de.stahl.restservice");
    beanConfig.setScan(true);
}

Swagger UI wird benötigt, um die Dokumentation als Web-App bereitzustellen. Dazu muss die Web-Anwendung heruntergeladen werden (http://swagger.io/swagger-ui/) und der Inhalt des "dist" Ordners (swagger-ui/dist) in den Ordner "src/main/webapp" bereitgestellt werden. 

Damit die Dokumentation schließlich auch erstellt wird, müssen die Services noch mit den passenden Annotations versehen werden, die eine Beschreibung der Funktionalität beinhalten, zum Beispiel:


  •  Auf Klassenebene: 
@Api(value="Mein REST Service")
  •  Auf Methodenebene: 
@ApiOperation(value = "Beschreibung der API Operation") @ApiParam(value = "ein wichtiger Parameter", required = true) 


Diese Schritt reichen aus, um die Dokumentation zu erstellen. Detailliertere Informationen zu den verfügbaren Annotations gibt es hier: https://github.com/swagger-api/swagger-core/wiki/Annotations

Offline-Nutzung

Es könnte notwendig sein, eine Schnittstellendokumentation zu erstellen, obwohl der Quelltext noch gar nicht fertiggestellt wurde, z.B. wenn ein externe Komponente frühzeitig Informationen über eine sich in der Entwicklung befindliche Schnittstelle benötigt. Auch dies ist mit Swagger möglich.

Dazu wird zunächst die Swagger-Definition (JSON-File) erstellt. Dies kann mit Hilfe des Swagger Editors geschehen: http://editor.swagger.io/#/
Dann wird das resultierende JSON in die index.html der Swagger-Web-Anwendung eingefügt und das SwaggerUi-Objekt initialisiert: 

var spec = {"json":"test"};

window.swaggerUi = new SwaggerUi({
   url:url,
   spec: spec,
   ...
Da die Swagger Web-App ausschließlich auf HTML und Javascript basiert, kann diese anschließend, z.B. als Zip-Archiv and die potenziellen Clients verteilt werden.

JRebel Support

Als JRebel Fan möchte ich noch erwähnen, dass Swagger nun auch von JRebel wunderbar unterstützt wird. (siehe https://zeroturnaround.com/forums/topic/support-for-swagger/) Dies ist sehr hilfreich, um die Änderungen an der Dokumentation und den Annotations direkt in der Oberfläche nachvollziehen zu können.

Sonntag, 21. Juni 2015

Responsive Web Design

Anfang des Jahres bekam ich eine Nachricht von Google, dass meine Web Seite Probleme mit der mobilen Nutzerfreundlichkeit aufweist. Dies hat mich nicht sonderlich überrascht, da ich die Seite gar nicht für die Smartphone-Nutzung optimiert hatte. Da ich die Problematik aber sehr spannend finde und die mobile Nutzung von Web-Seiten auch beruflich immer mehr in den Vordergrund rückt, habe ich die Mail zum Anlass genommen, mich näher mit der Thematik zu befassen.

Also habe ich mir das Buch "The responsive web" meines Lieblingsverlags Manning bestellt (The responsive web) und mir das erste Kapitel durchgelesen.

Kurze Zusammenfassung:

Man sollte seine Web-Seite zunächst für die mobile Nutzung optimieren (leider zu spät für meine Seite) und dann Schritt für Schritt für größere Screens optimieren. Da meine Seite nun einmal schon da war, habe ich angefangen, sie für kleinere Auflösungen anzupassen. Das zentrale Element für das Design auf Basis von CSS sind die sogenannten Media queries und Breakpoints. Ich habe mich dazu entschlossen, die Seite sowohl für Screens ab 1200px zu optimieren, als auch für alles darunter bis zu einer minimalen Größe von 200px. Die zentrale Anweisung im CSS sieht so aus:

@media only screen and (min-width: 1200px) {
     div#Mainpage {
         width: 1024px;
     }
}

Dies bedeutet, dass das div-Element mit der ID "Mainpage" eine breite von 1024px haben soll; aber nur dann, wenn der Screen 1200px breit ist. (Bei den 1200px handelt es sich um einen sogenannten Breakpoint) Für alle Bildschirme unter 1200px soll die Web-Seite die komplette Bildschirmbreite einnehmen:

div#Mainpage {
    width: 100%;
}

Auf diese Weise nimmt die Seite auf kleineren Bildschirmen die größtmögliche Bildschirmfläche ein, während sie auf großen Wide-Screens zentriert auf 1024px begrenzt wird. Die Media queries sind sehr mächtig, denn nun ist es möglich, alle möglichen Designs auf die entsprechenden Bildschirmgrößen hin zu optimieren. So ist es z.B. auf einem Smartphone sinnvoll, die Navigation mit großen Links zu präsentieren, die nicht zu nahe zusammenstehen. Auch horizontales Scrollen sollte möglichst vermieden werden.

In nächster Zeit gibt es noch einiges zu tun, bis sich meine Web-Seite vollständig "responsive" verhält. Leider ist es nämlich so, dass die CSS Optimierungen viel Zeit kosten und ein perfektes Ergebnis extrem aufwändig ist. Daher ist es durchaus eine Überlegung wert, Content-Management Systeme wie Joomla einzusetzen, die diesen Mechanismus über Plugins bereits mitbringen - dies kann eine Menge Arbeit ersparen.

Abschließend möchte ich noch auf die Google Webmaster Tools hinweisen. Sie geben viele wertvolle Tipps und analysieren die Web-Seite auf Knopfdruck.
Im Hinblick auf die mobile Optimierung der Seite gibt es unter folgendem Link konkrete Hinweise und Lösungsvorschläge:
Mobile Usability 

Wer sich stärker auf die Performance der eigenen Web-Seite konzentrieren möchte, ist hier gut aufgehoben:
Pagespeed insight 

Die Tipps sind umfassend und wertvoll, sodass man sich eine teure, intellektuelle Analyse der eigenen Website sparen kann und sich sofort auf die Beseitigung der Problemfelder konzentrieren kann. Google vergibt einen Score für die Seite, bei 100 Punkten ist die Seite optimal implementiert.

Zulange sollte man die Thematik "Responsive Design" auf die lange Bank schieben, denn Google könnte nicht optimierte Seiten abstrafen. In der Hinweismail von Google klingt das so: "Diese Seiten werden von der Google-Suche als nicht für Mobilgeräte optimiert eingestuft, und werden entsprechend in den Suchergebnissen für Smartphone-Nutzer dargestellt."

 
 

Dienstag, 30. Dezember 2014

Debugging Ausgabe bei Richfaces erhöhen

In den letzten Monaten war ich mit der Migration einer Anwendung von Richfaces 3.3 auf Richfaces 4.5 beschäftigt. Diese Migration war dringend notwendig, da neuere Browser die alte Richfaces Version nicht mehr unterstützt haben (z. B. gingen keine Klappboxen mehr auf oder Ajax-Requests liefen ins Leere) Die Migration hat mich dann einiges an Nerven gekostet, vor allem deshalb, weil sich die Richfaces-Entwickler dazu entschlossen haben, sämtliche CSS-Benennungen zu ändern und auch viele Tag-Namen und Attribute zum Teil völlig unnötigerweise abzuändern. Dies führte dazu, dass nach Umstellung auf die neue Richfaces Version zunächst einmal nichts mehr funktionierte und auch das Design kaum noch wiederzuerkennen war.

Im Rahmen dieser Migration gab es auch immer wieder den Fall, dass eine in der Managed-Bean definierte Action-Methode nicht aufgerufen wurde. Hier war ich dann komplett ratlos, da es weder auf dem Server (JBoss-Log) noch auf dem Client (Java-Script Konsole) eine Fehlermeldung oder Warnung gab. Hier zeigt sich sehr schön der Nachteil einer komplexen Komponentenbibliothek: Es ist im Prinzip eine Black-Box und wenn die Standard-Komponenten nicht funktionieren, hat man erstmal Pech gehabt. Ein Kollege gab mir dann den Tipp, wie das Logging (sehr schön zu sehen in der Firebug-Konsole) auf Debug-Level erhöht werden kann, um Fehler leichter identifizieren zu können:

<a4j:log mode="console" level="DEBUG" />

Platziert man dieses Tag auf die XHTML-Seite, werden detaillierte Informationen in die Browser-Console gegeben, z. B. wie folgt:

RichFaces: New request added to queue. Queue requestGroupingId changed to Form:subviewTable:infoTable:6:checkbox
RichFaces: Queue will wait 0ms before submit
RichFaces: richfaces.queue: will submit request NOW
RichFaces: Received 'begin' event from <input id=form:subviewTable:infoTable:6:checkbox class=rowCheckbox ...>
POST http://localhost:8080/app/views/shortInfo/shortInfo.jsf
200 OK
RichFaces: Received 'beforedomupdate' event from <input id=form:subviewTable:InfoTable:6:checkbox class=rowCheckbox ...>
RichFaces: [object Object]
RichFaces: [object Object]
RichFaces: richfaces.queue: ajax submit successfull
RichFaces: richfaces.queue: Nothing to submit
RichFaces: Received 'success' event from <input id=form:subviewTable:infoTable:6:checkbox class=rowCheckbox ...>
RichFaces: Received 'complete' event from <input id=form:subviewTable:infoTable:6:checkbox class=eventRowCheckbox ...> 


Mit Hilfe dieser Ausgabe lässt sich besser erkennen, was RichFaces Java-Script-seitig tut und wo Probleme (z.B. beim nicht-Aufruf einer Action-Methode) herrühren können.
Weitere Informationen zum Thema Debugging gibt es hier:


https://github.com/richfaces/richfaces/wiki/Debugging-RichFaces

Freitag, 28. Februar 2014

Rückgabewerte bei einem JAX-WS Client-Aufruf werden nicht gesetzt

Die letzten Tage hat mich ein Problem mit Jax-WS sehr beschäftigt (=fast in den Wahnsinn getrieben)

Ich habe einen Jax-WS Client (Metro-Implementierung) geschrieben, der einen Web Service aufruft und Rückgabeparameter erwartet. Leider waren diese Rückgabewerte allesamt auf "Null" gesetzt. Ich hatte jedoch mit SoapUI die Soap-Antwort des Servers überprüft und habe gesehen, dass die Rückgabewerte korrekt übertragen werden. Auch mit Axis2 hat es wie gewünscht funktioniert.

Die Schwierigkeit war, dass es keinerlei Fehlermeldung oder Warnhinweis gab. Von daher half nur stundenlanges googeln und sich von Kollegen inspirieren zu lassen.

Nach und nach konnte ich das Problem auf JAXB eingrenzen. JAXB ist dafür verantwortlich, die SOAP Nachricht zu parsen und daraus Java Objekte zu machen und genau das ging ja offensichtlich schief.

Im Endeffekt hat sich dann (nach vielen weiteren Tests) herausgestellt, dass es ein Problem mit den Namensräumen gab. In de WSDL (Schema-Abschnitt) war folgendes definiert:

elementFormDefault="qualified"

In den Client-Stubs, die über wsimport erstellt wurden, war jedoch in der Datei package-info.java kein entsprechender Eintrag zu finden. Und dies bedeutet, dass der Default, nämlich unqualified verwendet wird. Das ganze lässt sich über folgende Annotation richtigstellen:

@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.namespace.de",elementFormDefault = XmlNsForm.QUALIFIED)

-und schon wurden die Rückgabewerte wie erwartet gefüllt.

Ob dies nun ein Fehler im JAXB ist (was mich bei der Verbreitung von JAXB wundern würde) oder ob irgendein Detail in der WSDL bzw. in der Schema Datei fehlerhaft ist, kann ich nicht mit Sicherheit sagen.

Freitag, 4. Oktober 2013

Multi-Thread Programmierung: Einfache Regeln

Die Multi-Thread Programmierung ist nicht einfach. Fehlerhafter Code kann in sogenannten "Race Conditions" münden. Dies sind Fehler, die aufgrund von konkurrierendem Zugriff von Threads auf gemeinsam genutzte Ressourcen entstehen. Race Conditions sind deshalb schwer zu erkennen, da sie nicht reproduzierbar, sondern meist nur unter hoher Last (also im produktiven Betrieb) auftreten. Alle Unit-Tests sind grün, die Anwender haben stundenlang getestet und dann treten in Produktion massenweise, unvorhergesehene Fehler auf. Keine schöne Situation.

Die Materie der Multi-Thread Programmierung ist schwierig und umfangreich. Wer Software entwickelt, die von vielen Threads parallel durchlaufen wird, sollte sich unbedingt damit auseinandersetzen. Gerade in Zeiten von Prozessoren mit vielen Kernen wäre es eine Verschwendung von Prozessor-Ressourcen den Code vor Nebenläufigkeit abzuschirmen.

Aber auch beim Einsetzen von Frameworks und insbesondere auch in der JavaEE Entwicklung sollte man sich der o. g. Problematik stets bewusst sein. Zwar nehmen einem Application-Server viel Arbeit ab, aber man sollte trotzdem wissen, wie diese funktionieren und worauf zu achten ist. Der Tomcat z. B. (sowie auch der JBoss) verwenden Servlets, um (HTTP-)Requests abzuarbeiten. Diese Servlets werden u. U. von vielen Threads parallel durchlaufen, sodass es hier durchaus zu Multi-Thread Fehlern kommen kann.

Um die gröbsten Fehler zu vermeiden, folgen drei Tipps. Wenn diese beachtet werden, ist die Software im Hinblick auf Thread-Sicherheit schon auf einem guten Weg!

  • Wenn möglich auf Instanz-Variablen verzichten
Variablen, die lokal im Scope einer Methode definiert werden, können niemals zu Race Conditions führen, da sie nicht zwischen Threads geteilt werden.

  • Möglichst auf statische Klassenvariablen verzichten
Diese sind besonders gefährlich, da sie potenziell (je nach Sichtbarkeit) in der kompletten Anwendung und nur ein einziges Mal vorkommen und von allen Threads geteilt werden.
  • Falls Variablen geteilt werden müssen, diese mit synchronized schützen
Eine (Instanz-)Variable, die zwischen verschiedenen Threads geteilt werden soll, sollte mit Hilfe des Schlüsselworts synchronized geschützt werden. Dies bedeutet, dass jeweils nur ein Thread die Variable lesen bzw. schreiben kann. Wichtig: Unbedingt sowohl das Schreiben als auch das Lesen synchronisieren! Dies erfolgt üblicherweise über das Versehen der getter und setter Methode mit dem  Schlüsselwort synchronized.

Schließlich sollte man sich auch immer darüber informieren, ob die verwendeten APIs Thread-sicher sind. Heißer Kandidat sind Connection-Objekte, die applikationsweit wiederverwendet werden. Lassen sich hierüber keine Informatinen finden, sollten diese Objekte sicherheitshalber auch synchronisiert werden.

Sonntag, 2. Juni 2013

Hot-Deployment mit JRebel

Einer der großen Nachteile der Software-Entwicklung mit Java EE ist es, dass das Deployment von Anwendungen oft sehr viel Zeit in Anspruch nimmt. Dies ist bei Interpreter-Sprachen wie Java-Script oder PHP anders, ein Refresh der Seite genügt, um die Änderungen am Quelltext direkt testen zu können.

Das Deployment im Java EE Bereich dagegen kann sehr lange dauern. Zwar gibt es die Möglichkeit des Hot-Deployments bei Java EE Servern, allerdings funktioniert dies nicht immer und das Neuladen der Anwendung dauert trotzdem seine Zeit. Außerdem geht die aktuelle Session (üblicherweise) verloren, sodass man, um ein Feature testen zu können, den Ausgangspunkt innerhalb der Anwendung wiederherstellen muss. Gerade im Bereich von Web-Anwendungen sind zum Teil sehr viele Deployments pro Tag notwendig. (da können schon mal 5-10 oder mehr Deployments pro Stunde zusammenkommen) Hier geht viel Zeit verloren. Besonders nachteilig ist, dass man während des Server-Neustarts eine andere Aufgabe wahrnehmen muss. Dieser Context-Switch kostet zusätzlich Zeit, da man sich ständig in neue Problemstellungen hineindenken muss.

Der o.b. Problematik hat sich die Firma zeroturnaround angenommen. Das Produkt "JRebel" ermöglicht es, dass Source-Code Anpassungen innerhalb von JEE-Anwendungen sofort sichtbar werden. Das betrifft sowohl kompilierte Klassen, als auch Resource-Daten (xhtml) oder auch properties-Dateien. Sogar Spring-Beans und Hibernate-Entity-Klassen werden zur Laufzeit ausgetauscht und ermöglichen strukturelle Anpassungen der Anwendung "on-the-fly". Unterstützt werden gängige Entwicklungsumgebungen und Applikationsserver.

JRebel ist sehr schnell eingebunden und konfiguriert. Damit das Ganze funktioniert, muss der Applikationsserver mit folgendem Parameter gestartet werden:

-javaagent:c:\Java\jrebel\jrebel.jar

Jrebel.jar ist die Bibliothek, die das Austauschen von Klassen zur Laufzeit ermöglicht.  Außerdem wird noch eine XML-Datei (rebel.xml) im Classpath der Anwendung benötigt, welche Informationen darüber beinhaltet, wo sich die Dateien befinden, die auszutauschen sind.

 <classpath>
    <dir name="C:\PfadZuDenKompiliertenKlassen"/>
    <dir name="C:\PfadZuDenResources"/>
  </classpath>


Ändert sich nun eine Datei innerhalb o.g. Pfade, wird diese automatisch im Hintergrund auf dem Applikationsserver ausgetauscht.

Nach einigen Stunden des Testens stellte sich heraus, dass es wirklich eine enorme Verbesserung der täglichen Arbeit ist. Man konzentriert sich automatisch auf die eigentliche Arbeit (das Programmieren) und kann die Infrastruktur-Aufgaben vernachlässigen. Die eingesparte Zeit ist gerade für größere Projekte enorm und zusätzlich macht das Entwickeln einfach mehr Spaß.

Das Produkt ist leider nicht ganz billig (zur Zeit 265$ pro Jahr für eine auf den Entwickler zugelassene Lizenz), amortisiert sich aber meist sehr schnell. Ein weiterer Nachteil ist, dass der JRebel-Vertrieb sehr penetrant ist und man mit Anrufen überhäuft wird. Interessant ist die Möglichkeit, eine komplett freie Lizenz von JRebel nutzen zu können; dafür postet JRebel allerdings auf dem eigenen Twitter-Account und die Lizenz ist nur für nicht kommerzielle Projekte nutzbar.


Freitag, 25. Januar 2013

Hibernate SQL-Statements loggen

Kürzlich hat mich ein Kollege auf eine sehr schöne Möglichkeit hingewiesen SQL-Statements, die von Hibernate erstellt werden, vernünftig lesbar (und damit direkt gegen eine Datenbank ausführbar) zu loggen.

Zwar kannte ich die Möglichkeit, über die üblichen Logging-Einstellungen

<logger name="org.hibernate.SQL">
    <level value="debug"/>
</logger>

<logger name="org.hibernate.type">
    <level value="debug"/>
</logger>
 
Statements sichtbar zu machen. Allerdings wurden die Query Parameter mit Fragezeichen versehen und anschließend separat aufgelistet. Möchte man die Statements vollständig und gut lesbar loggen, sind drei Schritte durchzuführen:

  • In der log4j.xml folgende Einstellung vornehmen: 
<logger name="jdbc.sqltiming">
    <level value="INFO" />
</logger>
  • Die Datenquelle anpassen (z. B. im JBoss im "deploy"-Verzeichnis)
<connection-url>jdbc:log4jdbc:oracle:thin:@localhost:1521:XE</connection-url> <driver-class>net.sf.log4jdbc.DriverSpy</driver-class>
  • Den Proxy-Treiber log4jdbc4-1.2.jar ins lib-Verzeichnis des Applikations-Servers (oder der Anwendung) kopieren
Dieser lässt sich über folgenden Link beziehen: http://log4jdbc.googlecode.com/files/log4jdbc4-1.2.jar

Und schon werden wunderbar lesbare SQL-Statements rausgeloggt. Dies ist ein Feature, das ich schon viele Jahre gesucht habe und meiner Meinung nach sehr nützlich sein kann.

Freitag, 9. März 2012

Eclipse und Encoding

Gestern sind mein Kollege und ich auf eine "interessante" Einstellung in Eclipse gestoßen.

Bisher dachte ich immer, das Encoding wird in Eclipse unter Window->Preferences->General->Workspace Text file encoding eingestellt. Zusätzlich dann in dem jeweiligen Projekt unter Properties->Resource Text file encoding.

Beides war vorbildlich auf UTF-8 gestellt. Nichtsdestotrotz gab es das Problem, dass Umlaute in der Properties Datei falsch dargestellt wurden. Nach einiger Suche haben wir denn die dritte Stelle gefunden, die man sich anschauen sollte:

Unter Window->Preferences->General->Content Types kann das Encoding noch einmal für jeden einzelnen Datei-Typen eingestellt werden. Hier war ein ISO-Zeichensatz für .properties Dateien definiert, sodass es Probleme mit Umlauten zur Folge hatte.

Meiner Meinung nach alles ziemlich unübersichtlich und eine böse Falle, denn keiner konnte sich daran erinnern, das Encoding für die Properties Datei bewusst umgestellt zu haben...