commit d7ac94c69db90595a6b3981ac0a80c989dc8769d Author: Brian Long Date: Mon Jun 7 23:03:24 2021 -0400 initial checkin diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..47da827 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Maven +target +pom.xml.versionsBackup + +# Eclipse +.project +.classpath +.settings + +# Visual Studio Code +.vscode diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d48615e --- /dev/null +++ b/pom.xml @@ -0,0 +1,142 @@ + + 4.0.0 + com.inteligr8 + common-rest-api + 1.0-SNAPSHOT + ReST API Client for Java + + + utf-8 + 8 + 8 + + 5.7.2 + 5.2.14.RELEASE + 2.34 + 3.3.2 + + + + + org.springframework + spring-context + ${spring.version} + + + com.fasterxml.jackson.core + jackson-databind + 2.12.2 + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + 2.12.2 + + + org.slf4j + slf4j-api + 1.7.29 + + + jakarta.ws.rs + jakarta.ws.rs-api + 2.1.6 + + + org.glassfish.jersey.ext + jersey-proxy-client + ${jersey.version} + provided + + + org.apache.cxf + cxf-rt-rs-client + ${cxf.version} + provided + + + org.glassfish.jersey.core + jersey-client + ${jersey.version} + test + + + org.glassfish.jersey.inject + jersey-hk2 + ${jersey.version} + test + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${jersey.version} + test + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.springframework + spring-test + ${spring.version} + test + + + org.apache.httpcomponents + httpclient + 4.5.9 + test + + + + + + + maven-surefire-plugin + 3.0.0-M5 + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + + + + + maven-failsafe-plugin + 3.0.0-M5 + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + + + + + io.repaint.maven + tiles-maven-plugin + 2.21 + true + + true + + com.inteligr8:maven-public-deploy-tile:[1.0.0,2.0.0) + + + + + + + + + inteligr8-releases + http://repos.inteligr8.com/nexus/repository/inteligr8-public + + + \ No newline at end of file diff --git a/src/main/java/com/inteligr8/rs/AccessTokenRequestFilter.java b/src/main/java/com/inteligr8/rs/AccessTokenRequestFilter.java new file mode 100644 index 0000000..630cb9c --- /dev/null +++ b/src/main/java/com/inteligr8/rs/AccessTokenRequestFilter.java @@ -0,0 +1,20 @@ +package com.inteligr8.rs; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.core.HttpHeaders; + +public class AccessTokenRequestFilter implements ClientRequestFilter { + + private final String token; + + public AccessTokenRequestFilter(String token) { + this.token = token; + } + + @Override + public void filter(ClientRequestContext requestContext) { + requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + this.token); + } + +} diff --git a/src/main/java/com/inteligr8/rs/BasicAuthRequestFilter.java b/src/main/java/com/inteligr8/rs/BasicAuthRequestFilter.java new file mode 100644 index 0000000..8cd2e30 --- /dev/null +++ b/src/main/java/com/inteligr8/rs/BasicAuthRequestFilter.java @@ -0,0 +1,28 @@ +package com.inteligr8.rs; + +import java.io.UnsupportedEncodingException; +import java.util.Base64; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.core.HttpHeaders; + +public class BasicAuthRequestFilter implements ClientRequestFilter { + + private final String username; + private final String password; + + public BasicAuthRequestFilter(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public void filter(ClientRequestContext requestContext) throws UnsupportedEncodingException { + String userAndPass = this.username + ":" + this.password; + String userAndPassEncoded = Base64.getEncoder().encodeToString(userAndPass.getBytes("utf-8")); + + requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Basic " + userAndPassEncoded); + } + +} diff --git a/src/main/java/com/inteligr8/rs/Client.java b/src/main/java/com/inteligr8/rs/Client.java new file mode 100644 index 0000000..1752f82 --- /dev/null +++ b/src/main/java/com/inteligr8/rs/Client.java @@ -0,0 +1,34 @@ +package com.inteligr8.rs; + +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.client.WebTarget; + +import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; + +/** + * Configured JAX-RS Client & WebTarget + */ +public abstract class Client { + + protected abstract ClientConfiguration getConfig(); + + public javax.ws.rs.client.Client getClient() { + ClientRequestFilter authFilter = this.getConfig().getAuthorizationFilter(); + + ClientBuilder clientBuilder = ClientBuilder.newBuilder() + .register(new JacksonJaxbJsonProvider()); + if (authFilter != null) + clientBuilder.register(authFilter); + + return clientBuilder.build(); + } + + public WebTarget getTarget() { + return this.getClient() + .target(this.getConfig().getBaseUrl()); + } + + public abstract T getApi(Class apiClass); + +} diff --git a/src/main/java/com/inteligr8/rs/ClientConfiguration.java b/src/main/java/com/inteligr8/rs/ClientConfiguration.java new file mode 100644 index 0000000..5a5e784 --- /dev/null +++ b/src/main/java/com/inteligr8/rs/ClientConfiguration.java @@ -0,0 +1,54 @@ +package com.inteligr8.rs; + +import java.net.URI; + +import javax.ws.rs.client.ClientRequestFilter; + +public abstract class ClientConfiguration { + + public abstract String getBaseUrl(); + + public abstract String getBasicAuthUsername(); + + public abstract String getBasicAuthPassword(); + + public abstract String getAccessToken(); + + public abstract String getOAuthTokenUrl(); + + public abstract String getOAuthClientId(); + + public abstract String getOAuthClientSecret(); + + public abstract String getOAuthAuthCode(); + + public abstract String getOAuthAuthRedirectUri(); + + public abstract String getOAuthUsername(); + + public abstract String getOAuthPassword(); + + public ClientRequestFilter getAuthorizationFilter() { + if (this.getAccessToken() != null) { + return new AccessTokenRequestFilter(this.getAccessToken()); + } else if (this.getOAuthTokenUrl() != null) { + if (this.getOAuthAuthCode() != null) { + return new OAuthAuthorizationCodeRequestFilter(this.getOAuthTokenUrl(), + this.getOAuthClientId(), this.getOAuthClientSecret(), + this.getOAuthAuthCode(), URI.create(this.getOAuthAuthRedirectUri())); + } else if (this.getOAuthUsername() != null) { + return new OAuthPasswordGrantRequestFilter(this.getOAuthTokenUrl(), + this.getOAuthClientId(), this.getOAuthClientSecret(), + this.getOAuthUsername(), this.getOAuthPassword()); + } else { + return new OAuthClientCredentialRequestFilter(this.getOAuthTokenUrl(), + this.getOAuthClientId(), this.getOAuthClientSecret()); + } + } else if (this.getBasicAuthUsername() != null) { + return new BasicAuthRequestFilter(this.getBasicAuthUsername(), this.getBasicAuthPassword()); + } else { + return null; + } + } + +} diff --git a/src/main/java/com/inteligr8/rs/ClientCxfImpl.java b/src/main/java/com/inteligr8/rs/ClientCxfImpl.java new file mode 100644 index 0000000..6229208 --- /dev/null +++ b/src/main/java/com/inteligr8/rs/ClientCxfImpl.java @@ -0,0 +1,61 @@ +package com.inteligr8.rs; + +import java.util.LinkedList; +import java.util.List; + +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.ext.RuntimeDelegate; + +import org.apache.cxf.jaxrs.client.JAXRSClientFactory; +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.jaxrs.impl.RuntimeDelegateImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; + +import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; + +/** + * Configured JAX-RS Client & WebTarget & CXF WebClient for CXF + */ +public abstract class ClientCxfImpl extends Client implements InitializingBean { + + private final Logger logger = LoggerFactory.getLogger(ClientCxfImpl.class); + + @Override + public void afterPropertiesSet() { + if (RuntimeDelegate.getInstance() == null) { + this.logger.info("Setting JAX-RS runtime delegate to the CXF library"); + RuntimeDelegate.setInstance(new RuntimeDelegateImpl()); + } else if (RuntimeDelegate.getInstance() instanceof RuntimeDelegateImpl) { + this.logger.info("JAX-RS runtime delegate already the CXF library"); + } else { + this.logger.warn("Setting JAX-RS runtime delegate to the CXF library; was: " + RuntimeDelegate.getInstance().getClass().getName()); + RuntimeDelegate.setInstance(new RuntimeDelegateImpl()); + } + + if (this.logger.isInfoEnabled()) + this.logger.info("API Base URL: " + this.getConfig().getBaseUrl()); + } + + public WebClient getCxfClient() { + List providersAndFilters = new LinkedList(); + providersAndFilters.add(new JacksonJaxbJsonProvider()); + + ClientRequestFilter authFilter = this.getConfig().getAuthorizationFilter(); + if (authFilter != null) + providersAndFilters.add(authFilter); + + // we can't use JAXRSClientFactory with a JAXRS client (duh!) + // so we need to create a CXF client + WebClient client = WebClient.create(this.getConfig().getBaseUrl(), providersAndFilters); + + return client; + } + + @Override + public T getApi(Class apiClass) { + return JAXRSClientFactory.fromClient(this.getCxfClient(), apiClass); + } + +} diff --git a/src/main/java/com/inteligr8/rs/ClientJerseyImpl.java b/src/main/java/com/inteligr8/rs/ClientJerseyImpl.java new file mode 100644 index 0000000..3adaaae --- /dev/null +++ b/src/main/java/com/inteligr8/rs/ClientJerseyImpl.java @@ -0,0 +1,39 @@ +package com.inteligr8.rs; + +import javax.ws.rs.ext.RuntimeDelegate; + +import org.glassfish.jersey.client.proxy.WebResourceFactory; +import org.glassfish.jersey.internal.RuntimeDelegateImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; + +/** + * Configured JAX-RS Client & WebTarget for Jersey + */ +public abstract class ClientJerseyImpl extends Client implements InitializingBean { + + private final Logger logger = LoggerFactory.getLogger(ClientJerseyImpl.class); + + @Override + public void afterPropertiesSet() { + 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()); + } + + @Override + public T getApi(Class apiClass) { + return WebResourceFactory.newResource(apiClass, this.getTarget()); + } + +} diff --git a/src/main/java/com/inteligr8/rs/OAuthAuthorizationCodeRequestFilter.java b/src/main/java/com/inteligr8/rs/OAuthAuthorizationCodeRequestFilter.java new file mode 100644 index 0000000..bf06e27 --- /dev/null +++ b/src/main/java/com/inteligr8/rs/OAuthAuthorizationCodeRequestFilter.java @@ -0,0 +1,40 @@ +package com.inteligr8.rs; + +import java.net.URI; + +import javax.ws.rs.core.Form; + +public class OAuthAuthorizationCodeRequestFilter extends OAuthRequestFilter { + + private final String code; + private final URI redirectUri; + + public OAuthAuthorizationCodeRequestFilter(String tokenUrl, String clientId, String code) { + this(tokenUrl, clientId, null, code); + } + + public OAuthAuthorizationCodeRequestFilter(String tokenUrl, String clientId, String code, URI redirectUri) { + this(tokenUrl, clientId, null, code, redirectUri); + } + + public OAuthAuthorizationCodeRequestFilter(String tokenUrl, String clientId, String clientSecret, String code) { + this(tokenUrl, clientId, clientSecret, code, null); + } + + public OAuthAuthorizationCodeRequestFilter(String tokenUrl, String clientId, String clientSecret, String code, URI redirectUri) { + super(tokenUrl, clientId, clientSecret); + + this.code = code; + this.redirectUri = redirectUri; + } + + @Override + protected Form createForm() { + Form form = new Form().param("grant_type", "authorization_code") + .param("code", this.code); + if (this.redirectUri != null) + form.param("redirect_uri", this.redirectUri.toString()); + return form; + } + +} diff --git a/src/main/java/com/inteligr8/rs/OAuthClientCredentialRequestFilter.java b/src/main/java/com/inteligr8/rs/OAuthClientCredentialRequestFilter.java new file mode 100644 index 0000000..222ea31 --- /dev/null +++ b/src/main/java/com/inteligr8/rs/OAuthClientCredentialRequestFilter.java @@ -0,0 +1,16 @@ +package com.inteligr8.rs; + +import javax.ws.rs.core.Form; + +public class OAuthClientCredentialRequestFilter extends OAuthRequestFilter { + + public OAuthClientCredentialRequestFilter(String tokenUrl, String clientId, String clientSecret) { + super(tokenUrl, clientId, clientSecret); + } + + @Override + protected Form createForm() { + return new Form().param("grant_type", "client_credentials"); + } + +} diff --git a/src/main/java/com/inteligr8/rs/OAuthPasswordGrantRequestFilter.java b/src/main/java/com/inteligr8/rs/OAuthPasswordGrantRequestFilter.java new file mode 100644 index 0000000..545812b --- /dev/null +++ b/src/main/java/com/inteligr8/rs/OAuthPasswordGrantRequestFilter.java @@ -0,0 +1,27 @@ +package com.inteligr8.rs; + +import javax.ws.rs.core.Form; + +public class OAuthPasswordGrantRequestFilter extends OAuthRequestFilter { + + private final String username; + private final String password; + + public OAuthPasswordGrantRequestFilter(String tokenUrl, String clientId, String username, String password) { + this(tokenUrl, clientId, null, username, password); + } + + public OAuthPasswordGrantRequestFilter(String tokenUrl, String clientId, String clientSecret, String username, String password) { + super(tokenUrl, clientId, clientSecret); + this.username = username; + this.password = password; + } + + @Override + protected Form createForm() { + return new Form().param("grant_type", "password") + .param("username", this.username) + .param("password", this.password); + } + +} diff --git a/src/main/java/com/inteligr8/rs/OAuthRequestFilter.java b/src/main/java/com/inteligr8/rs/OAuthRequestFilter.java new file mode 100644 index 0000000..392d09a --- /dev/null +++ b/src/main/java/com/inteligr8/rs/OAuthRequestFilter.java @@ -0,0 +1,85 @@ +package com.inteligr8.rs; + +import java.util.Map; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.HttpHeaders; + +import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; + +public abstract class OAuthRequestFilter implements ClientRequestFilter { + + private final String tokenUrl; + private final String clientId; + private final String clientSecret; + private final String scope; + private String accessToken; + private long expiration; + private String refreshToken; + + public OAuthRequestFilter(String tokenUrl, String clientId) { + this(tokenUrl, clientId, null); + } + + public OAuthRequestFilter(String tokenUrl, String clientId, String clientSecret) { + this(tokenUrl, clientId, clientSecret, null); + } + + public OAuthRequestFilter(String tokenUrl, String clientId, String clientSecret, String scope) { + this.tokenUrl = tokenUrl; + this.clientId = clientId; + this.clientSecret = clientSecret; + this.scope = scope; + } + + @Override + public void filter(ClientRequestContext requestContext) { + if (this.accessToken == null || System.currentTimeMillis() > this.expiration) + this.requestToken(); + + requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + this.accessToken); + } + + private void requestToken() { + Form form; + + if (this.refreshToken != null) { + form = new Form().param("grant_type", "refresh_token") + .param("refresh_token", this.refreshToken); + } else { + form = this.createForm(); + } + + form.param("clientId", this.clientId); + if (this.clientSecret != null) + form.param("clientSecret", this.clientSecret); + if (this.scope != null) + form.param("scope", this.scope); + + Entity
entity = Entity.form(form); + + WebTarget target = ClientBuilder.newBuilder() + .register(new JacksonJaxbJsonProvider()) + .build() + .target(this.tokenUrl); + + @SuppressWarnings("unchecked") + Map response = target.request().post(entity, Map.class); + + if (response.containsKey("error")) + throw new WebApplicationException((String)response.get("error"), 400); + + this.accessToken = (String)response.get("access_token"); + this.expiration = System.currentTimeMillis() + ((Number)response.get("expires_in")).longValue() * 1000L; + this.refreshToken = (String)response.get("refresh_token"); + } + + protected abstract Form createForm(); + +}