ANDROID_HOME unter macOS setzen

Beim Kompilieren eines Android-Projektes kann es zu folgender Meldung kommen:

Caused by: java.lang.RuntimeException: SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.

Diese Meldung besagt dass das SDK nicht gefunden werden kann, da die Eigenschaft sdk.dir bzw. die Umgebungsvariable ANDROID_HOME nicht gesetzt ist. Unter macOS befinden sich die Android-SDKs im Library-Pfad des angemeldeten Nutzers:

/Users/seeseekey/Library/Android/sdk/sources

Der Nutzername, in diesem Beispiel seeseekey, muss natürlich durch den eigenen Nutzernamen ersetzt werden. Um diesen Pfad als Umgebungsvariable einzutragen muss das Terminal geöffnet werden und mittels cd in das Nutzerverzeichnis gewechselt werden. Anschließend wird die Datei .bash_profile angelegt bzw. geöffnet:

cd
nano .bash_profile

In diese Datei wird nun folgende Zeile eingetragen:

export ANDROID_HOME=/Users/seeseekey/Library/Android/sdk

Damit wird die Umgebungsvariable ANDROID_HOME automatisch bei der Anmeldung gesetzt und kann anschließend auch vom betreffenden Android-Projekt genutzt werden.

Ausführbare jar-Datei mittels Maven generieren

Für bestimmte Java-Projekte ist es durchaus praktisch als Ergebnis eine jar-Datei inklusive der benötigten Abhängigkeiten zu generieren. So kann das Projekt in Form einer einzelnen Datei unkompliziert ausgeliefert werden. Mittels Maven lässt sich dies relativ einfach bewerkstelligen. Dabei wird eine Basis-POM (pom.xml) benötigt, in welcher das Plugin maven-assembly-plugin konfiguriert wird:

<plugin>
	<artifactId>maven-assembly-plugin</artifactId>
	<version>3.1.0</version>
	<configuration>
		<archive>
			<manifest>
				<mainClass>com.example.webservice.Webservice</mainClass>
			</manifest>
		</archive>
		<descriptorRefs>
			<descriptorRef>jar-with-dependencies</descriptorRef>
		</descriptorRefs>
	</configuration>
</plugin>

Soll nach erfolgter Entwicklung die jar-Datei mit den Abhängigkeiten erzeugt werden, muss das Kommando:

mvn clean compile assembly:single

genutzt werden. Damit das Ganze auch beim gewöhnlichen:

mvn package

funktioniert, muss die pom.xml angepasst werden. Dabei wird zur Plugin-Definition ein executions-Block hinzugefügt, welcher dafür sorgt das dass Ziel single auch während der Phase package ausgeführt wird. Die komplette pom.xml sieht dann wie folgt aus:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>webservice</artifactId>
    <version>1.0.0</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.example.webservice.Webservice</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Alle im dependencies-Block verwendeten Abhängigkeiten, werden somit in die jar-Datei übernommen.

REST-Service mittels Spring Boot aufsetzen

Sprint Boot vereinfacht das Setup eines Spring-Projektes und geht dabei nach der Methode Konvention vor Konfiguration vor. So lässt sich ein einfacher REST-Service mittels Spring Boot schnell und unkompliziert implementieren. Im ersten Schritt wird dazu mit dem Spring Initializr ein neues Projekt mit der Abhängigkeit Web angelegt.

Mit dem Spring Initializr wird ein Projekt mit der Abhängigkeit Web angelegt.

Nach dem Download des Projektes wird dieses in der IDE der Wahl (z.B. IntelliJ IDEA) geöffnet und eine Klasse mit dem Namen HelloWorldController angelegt. Die Klasse sollte im gleichen Package (in diesem Beispiel: com.example.demo) wie die DemoApplication angelegt werden und wie folgt aussehen:

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldController {

    @GetMapping("/hello")
    public String helloWorld(@RequestParam final String name) {
        return "Hello " + name + "!";
    }

    @GetMapping("/hello/{name}")
    public String helloWorld2(@PathVariable("name") final String name) {
        return "Hello " + name + "!";
    }
}

Über die Annotation @RestController wird dem Framework mitgeteilt das es sich bei dieser Klasse um einen Controller für die REST-API handelt. Anschließend wird die Ressource hello definiert. Diese kann über zwei Wege aufgerufen werden:

http://localhost:8080/hello?name=seeseekey
http://localhost:8080/hello/seeseekey

Bei beiden Aufrufen erscheint als Ergebnis der Text:

Hello seeseekey!

Der erste Aufruf wird auf die Methode helloWorld gemappt, während der zweite Aufruf mit dem Parameter in der URL an die Methode helloWorld2 gemappt wird. Mittels der Annotation @GetMapping wird der URL-Pfad festgelegt, auf welchen die Methode reagieren soll. Bei der ersten Methode kommt die Annotation @RequestParam zum Einsatz, welche einen gewöhnlich Parameter definiert, welcher von der eigentlichen URL abgetrennt ist. Dagegen wird mit der Annotation @PathVariable in der Methode helloWorld2 eine Variable im Pfad der eigentlichen URL definiert.

LoadingCache der Guava-Bibliothek nutzen

Für Java existiert eine Bibliothek mit dem Namen Guava. Diese, von Google entwickelte, Bibliothek bietet gewisse Basisfunktionalitäten, welche vom Java-Framework nicht abgedeckt werden. Eine dieser Funktionalitäten ist der LoadingCache. Bei diesem handelt es sich um einen Cache, welcher dazu genutzt werden kann die Ergebnisse zeitintensiver Operationen zwischenzuspeichern. Daneben verfügt er über die Informationen wie er die entsprechenden Daten bezogen werden. Um Guava zu nutzen muss die Bibliothek z.B. mittels Maven eingebunden werden. Dazu muss die pom.xml-Datei um eine Abhängigkeit erweitert werden:

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>27.0-jre</version>
</dependency>

Anschließend kann Guava in dem betreffenden Projekt genutzt werden. In diesem Beispiel soll der Cache dazu dienen Anfragen an einen Webservice zu cachen. Der Webservice liefert Daten zurück, welche in einem Objekt vom Typ BookData gespeichert werden:

class BookData {

	public final String title;
	public final String description;
	public final double price;

	public BookData(String title, String description, double price) {
		this.title = title;
		this.description = description;
		this.price = price;
	}
}

Innerhalb der Anwendung übernimmt die Methode getBookData die Aufgabe, diese Daten vom Webservice zu beziehen:

public BookData getBookData(String isbn) {
	return bookdataService.getData(isbn);
}

Der LoadingCache von Guava ist technisch betrachtet eine Map. Bei der Initialisierung ist dies ersichtlich:

LoadingCache<String, BookData> books = CacheBuilder.newBuilder()
		.maximumSize(1000)
		.expireAfterWrite(7, TimeUnit.DAYS)
		.build(
				new CacheLoader<String, BookData>() {
					public BookData load(String isbn) {
						return getBookData(isbn);
					}
				});

Beim Anlegen des Cache können unterschiedliche Parameter wie die maximale Größe, die Zeit ab der ein Eintrag erneuert wird und der CacheLoader definiert werden. Der CacheLoader implementiert die Methode load, welche die entsprechenden Daten vom Webservice bezieht. Nachdem der LoadingCache definiert ist, kann die Map normal genutzt werden:

BookData bookData = books.get("9783736889842");

Im Hintergrund wird der Wert, wenn vorhanden, aus dem Cache bezogen. Ist dies nicht der Fall wird stattdessen der CacheLoader genutzt, welcher in diesem Beispiel einen Request an den Webservice sendet und das Ergebnis wieder im Cache hinterlegt. Der LoadingCache selber beschränkt sich auf die Laufzeit der Applikation und wird nicht persistiert.

Spring Projekte einfach anlegen

Das Anlegen eines Projektes mit dem Spring-Framework (bzw. unter Zuhilfenahme von Spring Boot) kann von Hand erfolgen. Einfacher ist es den Spring Initializr zu nutzen. Über das Webinterface kann die Art der Projektes, die gewünschte Sprache und Spring Boot-Version definiert werden. Anschließend können gewünschte Abhängigkeiten ausgewählt und das Projekt generiert und heruntergeladen werden.

Mit dem Spring Initializr können Projekte schnell angelegt werden

Eine Instanz des Projektes ist unter start.spring.io zu finden. Der Quelltext des Projekts ist auf GitHub hinterlegt. Lizenziert ist Spring Initializr unter Apache License in der Version 2 und damit freie Software.