All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jk.services.client.JKServiceClient Maven / Gradle / Ivy

There is a newer version: 7.0.0-M7
Show newest version
/*
 * 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;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy