Skip to content

Commit 630b892

Browse files
committed
update 3rd party dependencies and refactoring
1 parent 558f9ed commit 630b892

14 files changed

Lines changed: 398 additions & 486 deletions

README.md

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11

22
## Dependencies
33

4-
| group | artifact | version |
5-
| ------------------------- | ---------------- | ------- |
6-
| org.apache.httpcomponents | httpclient | 4.5.3 |
7-
| com.google.code.gson | gson | 2.8.1 |
8-
| com.google.guava | guava | 23.0 |
9-
| org.slf4j | slf4j-api | 1.7.25 |
10-
| org.slf4j | jcl-over-slf4j | 1.7.25 |
4+
| group | artifact | version |
5+
| ------------------------- | ---------------- | -------- |
6+
| com.squareup.okhttp3 | okhttp | 3.12.3 |
7+
| com.google.code.gson | gson | 2.8.5 |
8+
| com.google.guava | guava | 27.1-jre |
9+
| org.slf4j | jcl-over-slf4j | 1.7.26 |
1110

1211
## Installation
1312

14-
Die Library ist als Maven Projekt ausgelegt und sollte als Abhängigkeit hinzugefügt werden.
13+
Die Library ist als Maven POM Projekt ausgelegt und kann als Abhängigkeit hinzugefügt werden.
1514
```xml
1615
<dependencies>
1716
<dependency>
1817
<groupId>de.pfadfinden</groupId>
1918
<artifactId>ica-services</artifactId>
20-
<version>1.3-SNAPSHOT</version>
19+
<version>2.0.0</version>
2120
</dependency>
2221
<dependencies>
2322
```
@@ -28,30 +27,28 @@ Die Library ist als Maven Projekt ausgelegt und sollte als Abhängigkeit hinzuge
2827
## Dokumentation
2928

3029
### Verbindungsaufbau und Authentifizierung
31-
Mit Instanzierung eines IcaConnectors wird eine HTTP Session aufgebaut und die Authentifizierung an der ICA API
32-
vorgenommen.
33-
34-
Eine Instanz des IcaConnectors kann für ein oder mehrere Serviceaufrufe verwendet werden. Dadurch entfällt der Overhead
35-
für erneuten Aufbau der Session. Bei längerer Nicht-Benutzung kann die Session jedoch auslaufen.
30+
Mit Instanzierung einer `IcaConnection` wird eine HTTP Session aufgebaut und die Authentifizierung an der ICA API
31+
vorgenommen. Eine Instanz kann für ein oder mehrere Serviceaufrufe verwendet werden. Dadurch entfällt der Overhead
32+
für erneute Authentifizierung und Aufbau der Session. Bei längerer Nicht-Benutzung kann die Session jedoch verfallen.
3633

3734
```java
38-
IcaConnector icaConnector = new IcaConnector(IcaServer.BDP_QA,"username","password");
35+
IcaConnection icaConnection = new IcaConnection(IcaServer.BDP_QA,"username","password");
3936
```
4037

41-
Um die API Session sicher zu beenden und die Ressourcen des HTTP Clients freizugeben, muss der IcaConnector nach
38+
Um die API Session sicher zu beenden und die Ressourcen des HTTP Clients freizugeben, sollte die IcaConnection nach
4239
Verwendung geschlossen werden.
4340

4441
```java
4542
// Nutzung für ein oder mehrere Serviceaufrufe
46-
icaConnector.close();
43+
icaConnection.close();
4744
```
4845

4946
Da `java.io.Closeable` implementiert wird, kann alternativ zum manuellen `close` auch das try-with-resources
5047
Statement verwendet werden.
5148

5249
```java
5350
try(
54-
IcaConnector icaConnector = new IcaConnector(icaServer,icaCredentials);
51+
IcaConnection icaConnection = new IcaConnection(icaServer,icaCredentials);
5552
){
5653
// Nutzung für ein oder mehrere Serviceaufrufe
5754
}
@@ -61,12 +58,12 @@ Sollte bereits über einen anderen Weg die Authentifizierung an der MV erfolgt s
6158
mit einer SessionId erfolgen.
6259

6360
```java
64-
IcaConnector icaConnector = new IcaConnector(IcaServer.BDP_QA,"XXX_SESSION_STRING_XXX");
61+
IcaConnection icaConnection = new IcaConnection(IcaServer.BDP_QA,"XXX_SESSION_STRING_XXX");
6562
```
6663

6764
### API Zugriffslimit
6865
Die Vereinbarung zur Nutzung der BdP MV API sieht eine Beschränkung der Anzahl von maschinellen Zugriffe je Sekunde
69-
vor. Die Klasse `IcaConnector` sieht in den Zugriffsmethoden ein entsprechendes Ratelimitung vor. Bei Überschreitung
66+
vor. Die Klasse `IcaConnection` sieht in den Zugriffsmethoden ein entsprechendes Ratelimitung vor. Bei Überschreitung
7067
der zulässigen Zugriffe werden API Anfragen verzögert, um ein temporäre Sperrung der genutzten IP Adresse zu vermeiden.
7168

7269
### Logging
@@ -75,7 +72,7 @@ Library sollte deshalb ein SLF4J kompatibles Logging Framework (z.B. Logback) od
7572
einbinden.
7673

7774
### Serviceaufruf
78-
Sämtliche Serviceaufrufe benötigen eine Instanz des `IcaConnector` im Konstruktor.
75+
Sämtliche Serviceaufrufe benötigen eine Instanz des `IcaConnection` im Konstruktor.
7976

8077
#### MitgliedService
8178
Der einfachste Anwendungsfall des Mitgliedservice ist die Abfrage der Mitgliedsdaten zu einer eindeutigen
@@ -111,11 +108,7 @@ Kind-Gruppierungen in diesem Baum gespeichert.
111108

112109

113110
```java
114-
MitgliedService mitgliedService = new MitgliedService(icaConnector);
115-
Optional<IcaMitglied> mitglied = mitgliedService.getMitgliedById(11111);
116-
mitglied.ifPresent(
117-
icaMitglied -> System.out.println(icaMitglied.getNachname())
118-
);
111+
119112
```
120113

121114

pom.xml

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
<groupId>de.pfadfinden</groupId>
88
<artifactId>ica-services</artifactId>
9-
<version>1.11-SNAPSHOT</version>
9+
<version>2.0.0</version>
1010

11-
<name>BdP ICA Services</name>
11+
<name>ica-services</name>
1212
<description>Java Library zur BdP Mitgliederverwaltung ICA.</description>
1313
<url>https://github.com/pfadfinden/ica-services</url>
1414

@@ -17,6 +17,19 @@
1717
<url>http://www.pfadfinden.de</url>
1818
</organization>
1919

20+
<licenses>
21+
<license>
22+
<name>The Apache License, Version 2.0</name>
23+
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
24+
</license>
25+
</licenses>
26+
27+
<scm>
28+
<connection>scm:git:git://github.com/pfadfinden/ica-services.git</connection>
29+
<developerConnection>scm:git:ssh://github.com:pfadfinden/ica-services.git</developerConnection>
30+
<url>http://github.com/pfadfinden/ica-services/tree/master</url>
31+
</scm>
32+
2033
<developers>
2134
<developer>
2235
<id>philippst</id>
@@ -31,53 +44,41 @@
3144
<java.version>1.8</java.version>
3245
<maven.compiler.source>1.8</maven.compiler.source>
3346
<maven.compiler.target>1.8</maven.compiler.target>
34-
<maven.test.skip>true</maven.test.skip>
3547
</properties>
3648

3749
<dependencies>
3850
<dependency>
39-
<groupId>org.apache.httpcomponents</groupId>
40-
<artifactId>httpclient</artifactId>
41-
<version>4.5.6</version>
42-
<exclusions>
43-
<exclusion>
44-
<groupId>commons-logging</groupId>
45-
<artifactId>commons-logging</artifactId>
46-
</exclusion>
47-
</exclusions>
51+
<groupId>com.squareup.okhttp3</groupId>
52+
<artifactId>okhttp</artifactId>
53+
<version>3.12.3</version>
4854
</dependency>
4955
<dependency>
5056
<groupId>com.google.code.gson</groupId>
5157
<artifactId>gson</artifactId>
5258
<version>2.8.5</version>
5359
</dependency>
54-
<dependency>
55-
<groupId>org.slf4j</groupId>
56-
<artifactId>slf4j-api</artifactId>
57-
<version>1.7.25</version>
58-
</dependency>
5960
<dependency>
6061
<groupId>org.slf4j</groupId>
6162
<artifactId>jcl-over-slf4j</artifactId>
62-
<version>1.7.25</version>
63+
<version>1.7.26</version>
6364
</dependency>
6465
<dependency>
6566
<groupId>com.google.guava</groupId>
6667
<artifactId>guava</artifactId>
67-
<version>26.0-jre</version>
68+
<version>27.1-jre</version>
6869
</dependency>
6970

7071
<!-- SCOPE: TEST -->
7172
<dependency>
7273
<groupId>org.junit.jupiter</groupId>
7374
<artifactId>junit-jupiter-api</artifactId>
74-
<version>5.3.1</version>
75+
<version>5.4.2</version>
7576
<scope>test</scope>
7677
</dependency>
7778
<dependency>
7879
<groupId>org.slf4j</groupId>
7980
<artifactId>slf4j-simple</artifactId>
80-
<version>1.7.25</version>
81+
<version>1.7.26</version>
8182
<scope>test</scope>
8283
</dependency>
8384
</dependencies>
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package de.pfadfinden.ica;
2+
3+
import com.google.common.util.concurrent.RateLimiter;
4+
import com.google.gson.Gson;
5+
import com.google.gson.GsonBuilder;
6+
import com.google.gson.reflect.TypeToken;
7+
import de.pfadfinden.ica.converter.LocalDateConverter;
8+
import de.pfadfinden.ica.converter.LocalDateTimeConverter;
9+
import de.pfadfinden.ica.converter.LocalTimeConverter;
10+
import de.pfadfinden.ica.execption.IcaApiException;
11+
import de.pfadfinden.ica.execption.IcaAuthenticationException;
12+
import de.pfadfinden.ica.model.IcaApiResponse;
13+
import de.pfadfinden.ica.model.IcaResponse;
14+
import okhttp3.*;
15+
import org.slf4j.Logger;
16+
import org.slf4j.LoggerFactory;
17+
18+
import java.io.Closeable;
19+
import java.io.IOException;
20+
import java.lang.reflect.Type;
21+
import java.time.LocalDate;
22+
import java.time.LocalDateTime;
23+
import java.time.LocalTime;
24+
import java.util.Collections;
25+
26+
import static com.google.common.base.Preconditions.checkNotNull;
27+
import static com.google.common.base.Strings.emptyToNull;
28+
29+
public class IcaConnection implements Closeable {
30+
31+
private OkHttpClient okHttpClient;
32+
33+
private Gson gson;
34+
35+
private final Logger logger = LoggerFactory.getLogger(IcaConnection.class);
36+
private final RateLimiter apiRateLimiter = RateLimiter.create(3.0); // rate is "3 permits per second"
37+
38+
private CookieJar cookieJar = new MemoryCookieJar();
39+
40+
private IcaServer icaServer;
41+
42+
private static final String URL_STARTUP = "rest/nami/auth/manual/sessionStartup";
43+
44+
public IcaConnection(IcaServer icaServer, String username, String password) throws IcaAuthenticationException {
45+
this.init(icaServer);
46+
47+
try {
48+
authenticate(username, password);
49+
} catch (IOException e) {
50+
e.printStackTrace();
51+
}
52+
}
53+
54+
public IcaConnection(IcaServer icaServer, String session) {
55+
this.init(icaServer);
56+
57+
Cookie cookie = new Cookie.Builder()
58+
.domain(icaServer.getHost())
59+
.path("/ica")
60+
.name("JSESSIONID")
61+
.value(session)
62+
.secure()
63+
.build();
64+
65+
// ToDo: httpURL!
66+
this.cookieJar.saveFromResponse(null, Collections.singletonList(cookie));
67+
}
68+
69+
private void init(IcaServer icaServer){
70+
gson = new GsonBuilder()
71+
.registerTypeAdapter(LocalDate.class, new LocalDateConverter())
72+
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeConverter())
73+
.registerTypeAdapter(LocalTime.class, new LocalTimeConverter())
74+
.create();
75+
76+
this.icaServer = icaServer;
77+
78+
this.okHttpClient = new OkHttpClient.Builder()
79+
.cookieJar(this.cookieJar)
80+
.build();
81+
}
82+
83+
private void authenticate(String username, String password) throws IOException, IcaAuthenticationException {
84+
85+
checkNotNull(emptyToNull(username),"username null or empty");
86+
checkNotNull(emptyToNull(password),"password null or empty");
87+
88+
this.apiRateLimiter.acquire(); // may wait
89+
90+
HttpUrl httpUrl = this.getUrlBuilder(false)
91+
.addPathSegments(URL_STARTUP)
92+
.build();
93+
94+
RequestBody formBody = new FormBody.Builder()
95+
.add("username", username)
96+
.add("password", password)
97+
.add("Login", "API")
98+
.add("redirectTo", "./pages/loggedin.jsp")
99+
.build();
100+
101+
Request request = new Request.Builder()
102+
.url(httpUrl)
103+
.post(formBody)
104+
.build();
105+
106+
logger.debug("Authenticating on ICA via URI '{}'.",httpUrl);
107+
108+
try (
109+
Response response = okHttpClient.newCall(request).execute()
110+
) {
111+
if (response.code() == 200 && response.body() != null) {
112+
String resultData = response.body().string();
113+
IcaApiResponse<Object> resp = gson.fromJson(resultData, new TypeToken<IcaApiResponse<Object>>() {}.getType());
114+
logger.debug("Security: Authenticated to ICA using API token: {}.",resp.getApiSessionToken());
115+
} else {
116+
logger.warn("Security: Authentication on ICA failed with response code {}.",response.code());
117+
throw new IcaAuthenticationException("Authentication on ICA failed.");
118+
}
119+
}
120+
}
121+
122+
public <T> T executeApiRequest(HttpUrl httpUrl, Type resultType) throws IcaApiException {
123+
Request request = new Request.Builder()
124+
.url(httpUrl)
125+
.build();
126+
return this.executeApiRequest(request,resultType);
127+
}
128+
129+
public <T> T executeApiRequest(Request request, Type resultType) throws IcaApiException {
130+
131+
this.apiRateLimiter.acquire(); // may wait
132+
133+
logger.info("Executing ICA API request. URI: '{}' Method: '{}' ResultType: '{}'",
134+
request.url(),
135+
request.method(),
136+
resultType.getTypeName()
137+
);
138+
139+
try (
140+
Response response = okHttpClient.newCall(request).execute()
141+
) {
142+
if(response.code() != 200){
143+
logger.error("ICA API returns error: RequestURI {} RequestType {} API ResultStatusCode {}",
144+
request.url(),
145+
request.method(),
146+
response.code()
147+
);
148+
}
149+
150+
Type typeWithResponse = IcaResponse.getType(resultType);
151+
Type typeWithApiResponse = IcaApiResponse.getType(typeWithResponse);
152+
153+
IcaApiResponse<IcaResponse<T>> result = gson.fromJson(response.body().string(), typeWithApiResponse);
154+
155+
if (result == null || result.getResponse() == null || result.getStatusCode() != 0) {
156+
logger.error("Ica API returns error: RequestURI {} RequestType {} API ResultStatusCode {}",
157+
request.url(),
158+
request.method(),
159+
response.code()
160+
);
161+
throw new IcaApiException(result);
162+
}
163+
164+
return result.getResponse().getData();
165+
} catch (IOException e) {
166+
throw new IcaApiException(e);
167+
}
168+
}
169+
170+
public HttpUrl.Builder getUrlBuilder(){
171+
return this.getUrlBuilder(true);
172+
}
173+
174+
public HttpUrl.Builder getUrlBuilder(Boolean apiEndpoint){
175+
HttpUrl.Builder builder = new HttpUrl.Builder()
176+
.host(icaServer.getHost())
177+
.addPathSegment(icaServer.getDeployment())
178+
.scheme("https");
179+
if(apiEndpoint) builder.addPathSegments("rest/api/1/2/service");
180+
return builder;
181+
}
182+
183+
public OkHttpClient getCloseableHttpClient() {
184+
return this.okHttpClient;
185+
}
186+
187+
public String toJson(Object o) {
188+
return gson.toJson(o);
189+
}
190+
191+
@Override
192+
public void close() {
193+
194+
}
195+
}

0 commit comments

Comments
 (0)