Compare commits

...

49 Commits

Author SHA1 Message Date
7c98ce5545 v2.0.7-jersey pom 2023-06-12 17:19:39 -04:00
4c7e9da980 Merge branch 'develop-jersey' into stable-jersey 2023-06-12 17:19:21 -04:00
bb20a78224 Merge branch 'develop' into develop-jersey 2023-06-12 17:16:14 -04:00
f84279a148 better oauth token expiration handling 2023-06-12 17:16:03 -04:00
518be99d4e v2.0.6-jersey pom 2023-06-12 16:14:51 -04:00
2dd7de15a3 Merge branch 'develop-jersey' into stable-jersey 2023-06-12 16:14:16 -04:00
053b7a36ff removing javax.annotation conflict 2023-06-12 16:08:32 -04:00
fa96a8cfb8 Merge branch 'develop' into develop-jersey 2023-06-12 16:07:35 -04:00
a4a1b3a9e6 v2.0.4-jersey 2023-05-30 18:02:43 -04:00
9e3c749f9b Merge branch 'develop-jersey' into stable-jersey 2023-05-30 18:02:14 -04:00
2cbb08e9cc Merge branch 'develop' into develop-jersey 2023-05-30 17:58:54 -04:00
6844044395 Merge branch 'develop' into develop-jersey 2023-05-30 17:54:44 -04:00
722a04fe16 v2.0.3-jersey 2023-05-30 12:45:35 -04:00
1e5fcf6ab8 Merge branch 'develop-jersey' into stable-jersey 2023-05-30 12:45:13 -04:00
efcfab1795 Merge branch 'develop' into develop-jersey 2023-05-30 12:44:04 -04:00
5c242d88f1 v2.0.2-jersey pom 2023-05-29 10:17:17 -04:00
e56152c47b Merge branch 'develop-jersey' into stable-jersey 2023-05-29 10:16:34 -04:00
eb843c3cb6 upgraded jersey version 2023-05-29 10:07:46 -04:00
56aca63307 Merge branch 'develop' into develop-jersey 2023-05-29 09:58:48 -04:00
22ab063d4e Merge branch 'develop-jersey' into stable-jersey 2022-10-02 17:58:08 -04:00
6cb7afd9f1 Merge branch 'develop' into stable 2022-10-02 17:56:01 -04:00
5df5388bd2 Merge branch 'develop' into stable 2022-10-02 17:52:21 -04:00
df402790b4 added Jersey impl 2022-10-02 17:50:48 -04:00
5cda622082 v2.0.1 poms 2022-10-01 21:26:18 -04:00
e1191abaaa Merge branch 'develop' into stable 2022-10-01 21:25:26 -04:00
67ea4b72b3 POM release to v2.0.0 2022-10-01 00:53:17 -04:00
a574154c20 Merge branch 'develop' into stable 2022-10-01 00:52:53 -04:00
b1b19d8cb0 Merge branch 'develop' into stable 2022-05-02 18:33:27 -04:00
160766e0ef v1.1.6 pom 2022-04-07 23:36:39 -04:00
e167ac43cc Merge branch 'develop' into stable 2022-04-07 23:36:09 -04:00
a28fc13161 v1.1.5 pom 2022-03-08 13:32:45 -05:00
52c7e3cf6d Merge branch 'develop' into stable 2022-03-08 13:29:28 -05:00
bd7413bb48 v1.1.4 pom 2022-02-02 13:34:16 -05:00
75c518801f Merge branch 'develop' into stable 2022-02-02 13:33:47 -05:00
b3bc04467b v1.1.3 pom 2021-12-21 17:13:26 -05:00
88c8657a34 Merge branch 'develop' into stable 2021-12-21 17:12:58 -05:00
28b2478a08 v1.1.2 pom 2021-12-02 16:25:05 -05:00
dc63abc272 Merge branch 'develop' into stable 2021-12-02 16:20:39 -05:00
dd5d0f504a Merge branch 'develop' into stable 2021-10-27 14:54:54 -04:00
34d03a91e5 Merge branch 'develop' into stable 2021-09-03 12:42:25 -04:00
2584b8d668 v1.1.1 pom 2021-09-03 11:38:52 -04:00
a756b05f6c Merge branch 'develop' into stable 2021-09-03 11:38:22 -04:00
853b4e66cf Merge branch 'develop' into stable 2021-09-01 14:43:43 -04:00
f2032df0f3 Merge branch 'develop' into stable 2021-08-31 13:22:01 -04:00
8d3f4559bd v1.0.2 pom 2021-07-22 15:23:15 -04:00
3f476bba5c Merge branch 'develop' into stable 2021-07-22 15:22:49 -04:00
6292dde23b v1.0.1 pom 2021-07-07 11:07:04 -04:00
659f71c72c Merge branch 'develop' into stable 2021-07-07 11:06:35 -04:00
52f77dddbb v1.0.0 pom 2021-06-16 15:59:04 -04:00
5 changed files with 290 additions and 13 deletions

View File

@@ -1,7 +1,7 @@
# Common ReST Client Library
This project provides a library for Spring and POJO-based REST client instantiation.
This project provides a library for Spring and POJO-based REST client instantiation. It includes special classes for the Jersey JAXRS-based client frameworks.
## Usage
@@ -15,7 +15,7 @@ First, you will need to include the library in your project.
<dependency>
<groupId>com.inteligr8</groupId>
<artifactId>common-rest-client</artifactId>
<version>...</version>
<version>...-jersey</version>
</dependency>
...
</dependencies>
@@ -23,4 +23,65 @@ First, you will need to include the library in your project.
</project>
```
See the `cxf` and `jersey` branches for examples and more documentation.
### Spring Framework
#### Single Client
If you will only be declaring a single client in your Spring context, this is easy. You will just need two things. First, inject the single client into any of your Spring beans. You may do inject it into more than one. An example is below:
```java
@Component
public class ... {
@Autowired
@Qualifier("client.jersey") // may be optional
private Client client;
}
```
Next, you need to configure that client. You can do that by providing a single implementation of the `ClientConfiguration` (or `ClientJerseyConfiguration`) interface.
```java
@Configuration
public class ... implements ClientJerseyConfiguration {
...
}
```
#### Multiple Clients
If you will or may have multiple clients in your Spring context, there is an extra step. You will still need to define a `ClientConfiguration` for each. On top of that, you will need to create specialized implementations of each client. That special implementation will reference the configuration directly. An example is below.
```java
@Component("my.client")
public class MyClient extends ClientJerseyImpl {
@Autowired
public MyClient(MyClientConfiguration config) {
super(config);
}
}
```
You can then inject your client(s) into your Spring beans. Like the example below:
```java
@Component
public class ... {
@Autowired
private MyClient client;
@PostConstruct
public void init() {
MyJaxRsApi api = this.client.getApi(MyJaxRsApi.class);
}
}
### POJO
You do not have to use the Spring framework to use these classes. You can instantiate them directly. But you wil still need to create a `ClientConfiguration` as mentioned above.
```java
MyClientConfiguration config = new MyClientConfiguration();
...
ClientJerseyImpl client = new ClientJerseyImpl(config);
MyJaxRsApi api = client.getApi(MyJaxRsApi.class);
```

64
pom.xml
View File

@@ -6,11 +6,11 @@
<groupId>com.inteligr8</groupId>
<artifactId>common-rest-client</artifactId>
<version>2.0-SNAPSHOT</version>
<version>2.0.7-jersey</version>
<packaging>jar</packaging>
<name>ReST API Client for Java</name>
<description>A common library for building REST API clients</description>
<description>A common library for building Jersey REST API clients</description>
<url>https://bitbucket.org/inteligr8/common-rest-client</url>
<licenses>
@@ -46,14 +46,10 @@
<junit.version>5.7.2</junit.version>
<spring.version>5.3.27</spring.version>
<jackson.version>2.15.1</jackson.version>
<jersey.version>2.39.1</jersey.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
@@ -102,10 +98,64 @@
<version>4.5.14</version>
<scope>test</scope>
</dependency>
<!-- Jersey libraries -->
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-proxy-client</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<id>add-jaxrs-src</id>
<goals><goal>add-source</goal></goals>
<configuration>
<sources>
<source>src/main/jersey</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-test-src</id>
<goals><goal>add-test-source</goal></goals>
<configuration>
<sources>
<source>src/test/jersey</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.0</version>

View File

@@ -48,6 +48,7 @@ public abstract class OAuthAuthorizationFilter implements AuthorizationFilter {
private String accessToken;
private long expiration;
private String refreshToken;
private Long refreshTokenExpiration;
/**
* This constructor creates an OAuth-based authorization filter using the
@@ -99,8 +100,29 @@ public abstract class OAuthAuthorizationFilter implements AuthorizationFilter {
*/
@Override
public void filter(ClientRequestContext requestContext) {
if (this.accessToken == null || System.currentTimeMillis() > this.expiration)
this.requestToken();
if (this.accessToken == null) {
this.requestToken();
} else if (System.currentTimeMillis() >= this.expiration) {
this.logger.trace("Access token expired; retrieving new one with refresh token");
if (this.refreshTokenExpiration != null && System.currentTimeMillis() >= this.refreshTokenExpiration.longValue()) {
this.logger.debug("Refresh token expired; performing full authentication");
this.refreshToken = null;
this.requestToken();
} else {
try {
this.requestToken();
} catch (WebApplicationException wae) {
if (wae.getResponse().getStatusInfo().getFamily() == Family.CLIENT_ERROR) {
this.logger.debug("Received OAuth response {} using refresh token; performing full authentication", wae.getResponse().getStatus());
this.refreshToken = null;
this.requestToken();
} else {
throw wae;
}
}
}
}
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + this.accessToken);
}
@@ -139,6 +161,8 @@ public abstract class OAuthAuthorizationFilter implements AuthorizationFilter {
.register(new JacksonJaxbJsonProvider())
.build();
WebTarget target = client.target(this.tokenUrl);
long requestSendTime = System.currentTimeMillis();
Response response = target.request().post(entity);
@@ -160,8 +184,10 @@ public abstract class OAuthAuthorizationFilter implements AuthorizationFilter {
}
this.accessToken = (String)responseMap.get("access_token");
this.expiration = System.currentTimeMillis() + ((Number)responseMap.get("expires_in")).longValue() * 1000L;
this.expiration = requestSendTime + ((Number)responseMap.get("expires_in")).longValue() * 1000L;
this.refreshToken = (String)responseMap.get("refresh_token");
if (responseMap.containsKey("refresh_token_expires_in"))
this.refreshTokenExpiration = requestSendTime + ((Number)responseMap.get("refresh_token_expires_in")).longValue() * 1000L;
}
protected Form createRefreshForm() {

View File

@@ -0,0 +1,35 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.rs;
/**
* This interface defines additional configurations specific to the Jersey
* JAX-RS library and its nuances.
*
* @author brian@inteligr8.com
*/
public interface ClientJerseyConfiguration extends ClientConfiguration {
/**
* Jersey is automatically strict in its adherence to the ReST API
* specifications. It requires a body to PUT calls by default.
*
* @return true to require body in PUT calls; false to make it optional
*/
default boolean isPutBodyRequired() {
return true;
}
}

View File

@@ -0,0 +1,105 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.rs;
import javax.annotation.PostConstruct;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.ext.RuntimeDelegate;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.proxy.WebResourceFactory;
import org.glassfish.jersey.internal.RuntimeDelegateImpl;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* A class that provides pre-configured JAX-RS Client &amp; WebTarget objects
* for Jersey.
*
* @author brian@inteligr8.com
*/
@Component("client.jersey")
public class ClientJerseyImpl extends Client {
private final Logger logger = LoggerFactory.getLogger(ClientJerseyImpl.class);
private ClientJerseyConfiguration config;
/**
* This constructor is for Spring or POJO use.
* @param config The client configuration.
*/
@Autowired
public ClientJerseyImpl(ClientJerseyConfiguration config) {
this.config = config;
}
/**
* This method registers the Jersey library as the default provider for the
* JAX-RS specification.
*/
@PostConstruct
public void register() {
if (RuntimeDelegate.getInstance() == null) {
this.logger.info("Setting JAX-RS runtime delegate to the Jersey library");
RuntimeDelegate.setInstance(new RuntimeDelegateImpl());
} else if (RuntimeDelegate.getInstance() instanceof RuntimeDelegateImpl) {
this.logger.info("JAX-RS runtime delegate already the Jersey library");
} else {
this.logger.warn("Setting JAX-RS runtime delegate to the Jersey library; was: " + RuntimeDelegate.getInstance().getClass().getName());
RuntimeDelegate.setInstance(new RuntimeDelegateImpl());
}
if (this.logger.isInfoEnabled())
this.logger.info("API Base URL: " + this.getConfig().getBaseUrl());
}
/**
* @param clientBuilder A client builder.
*/
@Override
public void buildClient(ClientBuilder clientBuilder) {
clientBuilder.register(MultiPartFeature.class);
if (!this.getConfig().isPutBodyRequired()) {
// allow PUT operations without body data
clientBuilder.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
}
}
/**
* @return The client configuration.
*/
public ClientJerseyConfiguration getConfig() {
return this.config;
}
/**
* This method retrieves a JAX-RS implementation of the specified API with
* the specified authorization.
*
* @param authFilter A dynamic authorization filter.
* @param apiClass A JAX-RS annotation API class.
* @return An instance of the API class.
*/
@Override
public <T> T getApi(AuthorizationFilter authFilter, Class<T> apiClass) {
return WebResourceFactory.newResource(apiClass, this.getTarget(authFilter));
}
}