com.jk.services.client.JKServiceClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jk-framework-service-client Show documentation
Show all versions of jk-framework-service-client Show documentation
A simple API to call Microservices from Java
/*
* Copyright 2002-2021 Dr. Jalal Kiswani.
* Email: [email protected]
* Check out https://smart-api.com for more details
*
* All the opensource projects of Dr. Jalal Kiswani are free for personal and academic use only,
* for commercial usage and support, please contact the author.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jk.services.client;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.JerseyClient;
import org.glassfish.jersey.media.multipart.MultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
import com.jk.core.config.JKConfig;
import com.jk.core.config.JKConstants;
import com.jk.core.context.JKContextFactory;
import com.jk.core.http.JKHttpStatus;
import com.jk.core.logging.JKLogger;
import com.jk.core.logging.JKLoggerFactory;
import com.jk.core.security.JKSecurityUtil;
import com.jk.core.util.JK;
import com.jk.core.util.JKIOUtil;
import com.jk.core.util.JKObjectUtil;
import com.jk.services.client.logging.JKLogServiceClient;
import com.jk.services.client.workflow.models.SystemModel;
/**
* The Class JKServiceClient. Refactor me
*
* TODO: Tune for load testing
*
* @param the generic type
*/
public class JKServiceClient {
/** The log service client. */
JKLogServiceClient logServiceClient;
/** The logger. */
protected JKLogger logger = JKLoggerFactory.getLogger(getClass());
/** The base. */
private String base;
/** The model class. */
private Class modelClass;
/** The service name. */
private String serviceName;
/** The headers. */
Map headers = new HashMap();
/** The publish error. */
private boolean enableRemoteLogging = true;
/** The request media type. */
private String requestMediaType = MediaType.APPLICATION_JSON;
/** The accept media type. */
private String acceptMediaType = MediaType.APPLICATION_JSON;
/**
* The status for the last call
*/
private JKHttpStatus lastStatus;
private Client client;
/**
* Instantiates a new JK service client.
*/
public JKServiceClient() {
this.modelClass = JKObjectUtil.getGenericClassFromParent(this);
}
/**
* Instantiates a new JK service client.
*
* @param base the base
*/
public JKServiceClient(String base) {
this.base = base;
this.modelClass = JKObjectUtil.getGenericClassFromParent(this);
}
/**
* Instantiates a new JK service client.
*
* @param base the base
* @param clas the clas
*/
public JKServiceClient(String base, Class clas) {
this.base = base;
modelClass = clas;
}
/**
* Call single json.
*
* @param url the url
* @return the t
*/
public T callSingleJson(String url) {
logger.debug("callSingleJson({})", url);
String json = callJsonAsString(url);
return toObject(json);
}
/**
*
* @param
* @param clas
* @param url
* @return
*/
public T callSingleJson(Class clas, String url) {
logger.debug("callSingleJson(clas={},url={})", clas.getName(), url);
String json = callJsonAsString(url);
return toObject(clas, json);
}
/**
*
* @param json
* @return
*/
public T toObject(String json) {
logger.trace("toObject(json={})", json);
if (modelClass == null) {
JK.exception("Model Class is not provided, please prvodei throught the constructor or setter");
}
if (json == null) {
return null;
}
return toObject(modelClass, json);
}
/**
*
* @param
* @param clas
* @param json
* @return
*/
public E toObject(Class clas, String json) {
if (clas == null) {
JK.exception("Model Class is not provided, please prvodei throught the constructor or setter");
}
logger.trace("toObject(json={},class={})", json, clas.getName());
if (json == null) {
return null;
}
return JKObjectUtil.jsonToObject(json, clas);
}
/**
* Call json as list of objects.
*
* @return the list
*/
public List callJsonAsListOfObjects() {
logger.debug("callJsonAsListOfObjects()");
return callJsonAsListOfObjects(getBase());
}
/**
* To objects.
*
* @param json the json
* @return the list
*/
public List toObjects(String json) {
if (modelClass == null) {
JK.exception("Model Class type is not set");
}
return JKObjectUtil.jsonToObjectList(json, modelClass);
}
/**
* Call json as list of objects.
*
* @param path the path
* @return the list
*/
public List callJsonAsListOfObjects(String path) {
logger.debug("callJsonAsListOfObjects(path={})", path);
String json = callJsonAsString(getFullUrl(path));
return toObjects(json);
}
/**
* Call json as string.
*
* @return the string
*/
public String callJsonAsString() {
logger.debug("callJsonAsString()");
return callJsonAsString(getBase());
}
/**
* Call json as string.
*
* @param path the path
* @return the string
*/
public String callJsonAsString(String path) {
logger.debug("callJsonAsString(path={}) ", path);
return (String) callService(path, builder -> builder.get());
}
/**
* Validate reponse.
*
* @param url the url
* @param response the response
*/
protected void validateReponse(String url, Response response) {
JKHttpStatus status = JKHttpStatus.valueOf(response.getStatus());
this.lastStatus=status;
logger.debug("validateReponse(url={},status={})", url, status);
if (!status.is2xxSuccessful()) {
String message= response.readEntity(String.class);
JK.throww(new JKServiceClientException(getServiceName(), getFullUrl(url), status, message));
}
}
/**
* Call json with post.
*
* @param object the object
* @return the string
*/
public String callJsonWithPost(Object object) {
logger.debug("callJsonWithPost()");
return callJsonWithPost(getBase(), object);
}
/**
* Call json with post.
*
* @param path the path
* @param object the object
* @return the string
*/
public String callJsonWithPost(String path, Object object) {
logger.debug("callJsonWithPost(path={}, payload={}) ", path, JK.buildToString(object));
return (String) callService(path, (builder) -> builder.post(Entity.entity(object, MediaType.APPLICATION_JSON)));
}
/**
* Call json with put.
*
* @param object the object
* @return the string
*/
public String callJsonWithPut(Object object) {
logger.debug("callJsonWithPut");
return callJsonWithPut(getBase(), object);
}
/**
* Call json with put.
*
* @param path the path
* @param object the object
* @return the string
*/
public String callJsonWithPut(String path, Object object) {
logger.debug("calling JsonWithPut(path={}, payload={}) ", path, object);
String mediaType = MediaType.APPLICATION_JSON;
return callPut(path, object, mediaType);
}
/**
* Call put.
*
* @param path the path
* @param object the object
* @param mediaType the media type
* @return the string
*/
public String callPut(String path, Object object, String mediaType) {
logger.debug("calling callPut(path={},mediaType={},object={}) ", path, mediaType, object);
return (String) callService(path, builder -> {
return builder.put(Entity.entity(object == null ? "" : object, mediaType));
});
}
/**
* Call json with delete.
*
* @param path the path
* @return the string
*/
public String callJsonWithDelete(String path) {
logger.debug("calling JsonWithDelete(path={}) ", path);
return (String) callService(path, builder -> builder.delete());
}
/**
* Creates the client.
*
* @return the client
*/
protected Client createClient() {
// This is very important to commnicate with HTTPS server
// https://stackoverflow.com/questions/6047996/ignore-self-signed-ssl-cert-using-jersey-client
if (client == null || ((JerseyClient) client).isClosed()) {
try {
SSLContext sslcontext = createSslContext();
// @formatter:off
client = ClientBuilder.newBuilder().
sslContext(sslcontext).
hostnameVerifier((s1, s2) -> true).
register(new JKClientRequestFilter()).
connectTimeout(getConnectTimeout(), TimeUnit.SECONDS).
readTimeout(getReadTimeout(), TimeUnit.SECONDS).
build();
// @formatter:on
client.register(MultiPartFeature.class);
} catch (Exception e) {
JK.throww(e);
return null;// unreachable
}
}
return client;
}
/**
* Gets the read timeout.
*
* @return the read timeout
*/
protected int getReadTimeout() {
return JKConfig.get().getPropertyAsInteger(JKConstants.Microservices.READ_TIMEOUT, 20);
}
/**
* Gets the connect timeout.
*
* @return the connect timeout
*/
protected int getConnectTimeout() {
return JKConfig.get().getPropertyAsInteger(JKConstants.Microservices.CONNECT_TIMEOUT, 10);
}
/**
* Creates the ssl context.
*
* @return the SSL context
* @throws NoSuchAlgorithmException the no such algorithm exception
* @throws KeyManagementException the key management exception
*/
protected SSLContext createSslContext() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslcontext;
sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[] { new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
} }, new java.security.SecureRandom());
return sslcontext;
}
/**
* Gets the full url.
*
* @param path the path
* @return the full url
*/
protected String getFullUrl(String path) {
if (path.startsWith("http:") || path.startsWith("https://")) {
return path;
}
if (!path.startsWith("/")) {
path = "/".concat(path);
}
return getBase().concat(path);
}
/**
* Gets the base.
*
* @return the base
*/
public String getBase() {
return base;
}
/**
* Sets the base.
*
* @param base the new base
*/
public void setBase(String base) {
this.base = base;
}
/**
* Gets the model class.
*
* @return the model class
*/
public Class getModelClass() {
return modelClass;
}
/**
* Sets the model class.
*
* @param modelClass the new model class
*/
public void setModelClass(Class modelClass) {
this.modelClass = modelClass;
}
/**
* Call service.
*
* @param path the path
* @param caller the caller
* @return the string
*/
public Object callService(String path, JKServiceCaller caller) {
logger.debug("calling service ({}) ", path);
String fullUrl = getFullUrl(path);
logger.debug("Full url ({})", fullUrl);
lastStatus = null;
try {
logger.debug("create client");
Client client = createClient();
logger.debug("create target({})", fullUrl);
WebTarget target = client.target(fullUrl);
logger.debug("call target.request()");
Builder request = target.request();
logger.debug("call accept media type to ({})", caller.getAcceptMedia());
Builder builder = request.accept(caller.getAcceptMedia());
logger.debug("call setHeadersOnRequest");
setHeadersOnRequest(builder);
logger.debug("call on the actual caller ({}) ", caller.getClass().getName());
try (Response response = caller.call(builder)) {
logger.debug("validate response...");
validateReponse(path, response);
logger.debug("reading entity");
Object responseObject = caller.readResponse(response);
if (isEnableRemoteLogging()) {
getLogServiceClient().callInfo("Service called successfully :".concat(fullUrl));
}
return responseObject;
}
} catch (Exception e) {
// logger.error(e.getMessage());
if (isEnableRemoteLogging()) {
getLogServiceClient()
.callError("Service :".concat(fullUrl).concat(". failed with error ".concat(e.getMessage())));
}
if (e instanceof JKServiceClientException) {
throw e;
} else {
JK.throww(new JKServiceClientException(getServiceName(), fullUrl, JKHttpStatus.SERVICE_UNAVAILABLE,
null, e));
}
return null;
}
}
/**
* Checks if is enable remote logging.
*
* @return true, if is enable remote logging
*/
public boolean isEnableRemoteLogging() {
return enableRemoteLogging;
}
/**
* Sets the enable remote logging.
*
* @param enableRemoteLogging the new enable remote logging
*/
public void setEnableRemoteLogging(boolean enableRemoteLogging) {
this.enableRemoteLogging = enableRemoteLogging;
}
/**
* Upload file.
*
* @param path the path
* @param file the file
* @return the string
*/
public String uploadFile(String path, File file) {
MultiPart multiPart = new MultiPart();
multiPart.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE);
FileDataBodyPart fileDataBodyPart = new FileDataBodyPart("file", file, MediaType.APPLICATION_OCTET_STREAM_TYPE);
multiPart.bodyPart(fileDataBodyPart);
return (String) callService(path, builder -> builder.post(Entity.entity(multiPart, multiPart.getMediaType())));
}
/**
* Sets the headers on request.
*
* @param builder the new headers on request
*/
protected void setHeadersOnRequest(Builder builder) {
Set keySet = headers.keySet();
for (String key : keySet) {
builder.header(key, headers.get(key));
}
}
/**
* Gets the service name.
*
* @return the service name
*/
public String getServiceName() {
return serviceName;
}
/**
* Sets the service name.
*
* @param serviceName the new service name
*/
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
/**
* Gets the headers.
*
* @return the headers
*/
public Map getHeaders() {
return headers;
}
/**
* Sets the headers.
*
* @param headers the headers
*/
public void setHeaders(Map headers) {
this.headers = headers;
}
/**
* Call download file with post.
*
* @param path the path
* @param object the object
* @return the file
*/
public File callDownloadFileWithPost(String path, Object object) {
logger.debug("callDownloadFileWithPost({},{}) ", path, object);
return (File) callService(path, new JKFileDownloadServiceCaller(object));
}
/**
* Call get.
*
* @param path the path
* @return the string
*/
public String callGet(String path) {
logger.debug("calling String({}) ", path);
return (String) callService(path, new JKServiceCaller() {
@Override
public Response call(Builder builder) {
return builder.get();
}
@Override
public String getAcceptMedia() {
return getAcceptMediaType();
}
});
}
/**
* Call post.
*
* @param path the path
* @return the string
*/
public String callPost(String path) {
logger.debug("callPost String({}) ", path);
return (String) callService(path, new JKServiceCaller() {
@Override
public Response call(Builder builder) {
return builder.post(null);
}
@Override
public String getAcceptMedia() {
return getAcceptMediaType();
}
});
}
/**
* Gets the log service client.
*
* @return the log service client
*/
protected JKLogServiceClient getLogServiceClient() {
if (logServiceClient == null) {
logServiceClient = new JKLogServiceClient();
}
return logServiceClient;
}
/**
* Gets the request media type.
*
* @return the request media type
*/
public String getRequestMediaType() {
return requestMediaType;
}
/**
* Sets the request media type.
*
* @param requestMediaType the new request media type
*/
public void setRequestMediaType(String requestMediaType) {
this.requestMediaType = requestMediaType;
}
/**
* Gets the accept media type.
*
* @return the accept media type
*/
public String getAcceptMediaType() {
return acceptMediaType;
}
/**
* Sets the accept media type.
*
* @param acceptMediaType the new accept media type
*/
public void setAcceptMediaType(String acceptMediaType) {
this.acceptMediaType = acceptMediaType;
}
/**
* @param
* @param clas
* @param path
* @return
*/
public List callJsonAsListOfObjects(Class clas, String path) {
String json = callJsonAsString(getFullUrl(path));
return toObjects(clas, json);
}
/**
*
* @param
* @param clas
* @param json
* @return
*/
public List toObjects(Class clas, String json) {
return JKObjectUtil.jsonToObjectList(json, clas);
}
/**
*
* @return
*/
public JKHttpStatus getLastStatus() {
return lastStatus;
}
}