Dieses Blog durchsuchen

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

Autocomplete 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.