Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.appops.service.invocation.OperationInvoker Maven / Gradle / Ivy
/*
* AppOps is a Java framework to develop, deploy microservices with ease and is available for free
* and common use developed by AinoSoft ( www.ainosoft.com )
*
* AppOps and AinoSoft are registered trademarks of Aino Softwares private limited, India.
*
* Copyright (C) <2016>
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version along with applicable additional terms as
* provisioned by GPL 3.
*
* 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 and applicable additional terms
* along with this program.
*
* If not, see and
*/
package org.appops.service.invocation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.appops.configuration.store.CurrentRunningServiceContext;
import org.appops.core.service.OpParameterMap;
import org.appops.core.service.Parameter;
import org.appops.core.service.meta.MappedOpMeta;
import org.appops.core.service.meta.ServiceOpMeta;
import org.appops.log.service.slim.service.Level;
import org.appops.logging.impl.LogManager;
import org.appops.logging.logger.Logger;
import org.appops.marshaller.DescriptorType;
import org.appops.marshaller.Marshaller;
import org.appops.service.exception.InvocationException;
import org.appops.service.lifecycle.CallStack;
import org.appops.service.store.RequestParameterStore;
import org.appops.slim.base.api.ServiceMetaManager;
import org.appops.slim.base.invocation.UrlEncodeUtil;
import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.servlet.RequestParameters;
/**
* Invoker class which locates and invokers mapped code based method for an operation definition
* provided.
*
* @author deba
* @version $Id: $Id
*/
public class OperationInvoker {
private Provider callStackProvider;
private Provider methodLocator;
private Provider serviceMetaManager;
private Marshaller marshaller;
private Provider paramStoreProvider;
private static final String LOG_SERVICE_NAME = "LogService";
private Logger rootLogger;
private CurrentRunningServiceContext currentRunningServiceContext;
@Inject
private Injector injector;
@Inject
@RequestParameters
private Provider> requestParametersProvider;
/**
*
* Constructor for OperationInvoker.
*
*
* @param serviceMetaManager a {@link com.google.inject.Provider} object.
* @param logManager a {@link org.appops.logging.impl.LogManager} object.
*/
@Inject
public OperationInvoker(Provider serviceMetaManager, LogManager logManager) {
this.serviceMetaManager = serviceMetaManager;
this.rootLogger = logManager.getRootLogger();
}
/**
* Locates and invokes method for an operation definition passed.
*
* @param service Name of service.
* @param opPathOrSignature Operation path. Can be signature or a full path to reach the method.
* @return Result obtained from actual method invocation.
*/
public Object invoke(String service, String opPathOrSignature) {
return invoke(service, opPathOrSignature, null);
}
/**
* Locates and invokes method for an operation definition passed.
*
* @param serviceName Name of the service.
* @param opPathOrSignature Operation path. Can be signature or a full path to reach the method.
* @param postData Operation parameters received as post request data.
* @return Result obtained from actual method invocation.
*/
public Object invoke(String serviceName, String opPathOrSignature, OpParameterMap postData) {
ServiceOpMeta opMeta = getServiceStore().getOpMeta(serviceName, opPathOrSignature);
ServiceOpMeta finalOperation = getMappedOperation(opMeta);
printOpLog(Level.INFO, serviceName, finalOperation.getName(), "Starting operation execution.");
String apiQualifiedName = finalOperation.getParent().getQualifiedClassName();
Class> apiClass;
List params = getParameters(postData, opPathOrSignature, finalOperation);
this.paramStoreProvider.get().setParameters(params);
try {
apiClass = Class.forName(apiQualifiedName);
} catch (ClassNotFoundException e1) {
printOpLog(Level.ERROR, serviceName, finalOperation.getName(), "Api/Operation not found!!!");
throw new InvocationException("Api not found, name ->" + apiQualifiedName);
}
Method method = getMethodLocator().findMethod(apiClass, finalOperation);
callStackProvider.get().addOperation(finalOperation);
currentRunningServiceContext.setCurrentRunningService(serviceName);
FindAyncImplementation findAyncImplementation = new FindAyncImplementation();
Class> asyncImplClass = findAyncImplementation.getAsyncImplementation(apiClass);
Object result = null;
Object[] paramRaw = params.toArray();
try {
if (asyncImplClass != null) {
try {
Object service = injector.getInstance(asyncImplClass);
result = invokeAsyncImplementation(asyncImplClass, method, paramRaw, service);
} catch (ConfigurationException e) {
rootLogger.withMessage("Implementation not found for " + asyncImplClass);
result = invokeSyncImplementation(apiClass, method, paramRaw);
}
} else {
result = invokeSyncImplementation(apiClass, method, paramRaw);
}
} catch (Exception e) {
throw new InvocationException(e.getCause());
} finally {
callStackProvider.get().callFinished();
}
printOpLog(Level.INFO, serviceName, finalOperation.getName(), "Completed operation execution.");
return result;
}
/**
* It invokes the sync implementation using given api class, method and parameters.
*
* @param apiClass an api class
* @param method method to invoke
* @param paramRaw method parameters
* @return result after method invocation
*/
private Object invokeSyncImplementation(Class> apiClass, Method method, Object[] paramRaw) {
Object service = null;
try {
service = injector.getInstance(apiClass);
if (method.getParameterTypes().length > 0 && method.isVarArgs()) {
Object argumentArray = new Object[] {paramRaw};
return method.invoke(service, argumentArray);
} else {
return method.invoke(service, paramRaw);
}
} catch (Exception e) {
throw new InvocationException(e.getCause());
}
}
/**
* It invokes the async implementation using given async impl class, method and parameters.
*
* @param asyncImplClass an async api class
* @param method method to invoke
* @param paramRaw method parameters
* @param service service name
* @return result after method invocation
*/
private Object invokeAsyncImplementation(Class> asyncImplClass, Method method,
Object[] paramRaw, Object service) {
try {
Method asyncMethod = asyncImplClass.getMethod(method.getName(), method.getParameterTypes());
if (asyncMethod.getParameterTypes().length > 0 && asyncMethod.isVarArgs()) {
Object argumentArray = new Object[] {paramRaw};
CompletableFuture> completableFuture =
(CompletableFuture>) asyncMethod.invoke(service, argumentArray);
return completableFuture.get();
} else {
CompletableFuture> completableFuture =
(CompletableFuture>) asyncMethod.invoke(service, paramRaw);
return completableFuture.get();
}
} catch (Exception e) {
throw new InvocationException(e.getCause());
}
}
/**
* Friendly method to generate logs during operation execution.
*
* @param level Log level. eg. Level.INFO, Level.ERROR etc.
* @param service String service name.
* @param opName Name of the operation under execution.
* @param logMessage Additional log message.
*/
private void printOpLog(Level level, String service, String opName, String logMessage) {
if (LOG_SERVICE_NAME.toLowerCase().equals(service.toLowerCase())) {
return;
}
String finalMessage =
"Service operation log : Service_Name -> " + service + ", Operation_Name -> " + opName;
if (logMessage != null && !logMessage.isEmpty()) {
finalMessage += ", Detailed message :- " + logMessage;
}
rootLogger.withLevel(level).withMessage(finalMessage);
}
private List getParameters(OpParameterMap postData, String opPathOrSignature,
ServiceOpMeta finalOperation) {
List params;
if (postData != null && !postData.isEmpty()) {
params = fromPostData(postData, finalOperation);
} else {
params =
fromRequestOrPathParameters(opPathOrSignature, finalOperation, getRequestParameters());
}
return params;
}
private List fromPostData(OpParameterMap postData, ServiceOpMeta finalOperation) {
List paramsToReturn = new ArrayList<>();
for (Integer order : postData.keySet()) {
Parameter parameter = postData.get(order);
Object value = parameter.getValue();
parameter = finalOperation.getParameters().get(order);
Class> typeClazz;
try {
if (parameter.getTypeName().equals("java.lang.Byte[]")) {
parameter.setTypeName("[Ljava.lang.Byte;");
}
typeClazz = Class.forName(parameter.getTypeName());
} catch (Exception e) {
throw new InvocationException("Cannot find type for parameter -> " + parameter.getName()
+ " type-name -> " + parameter.getTypeName());
}
if (!typeClazz.isAssignableFrom(value.getClass())) {
String stringVal;
if (value instanceof String) {
stringVal = (String) value;
if (typeClazz.isEnum()) {
value = Enum.valueOf((Class) typeClazz, stringVal);
stringVal = null;
}
} else {
stringVal = getMarshaller().marshall(value, DescriptorType.JSON);
}
if (stringVal != null) {
value = getMarshaller().unmarshall(stringVal, typeClazz, DescriptorType.JSON);
}
}
paramsToReturn.add(value);
}
return paramsToReturn;
}
/**
* Converts method parameters to actual types defined in op meta.
*
* @param path Path of service operation.
* @param finalOperation Operation meta information.
* @param requestParameters Parameters fetched from current request.
*
* @return List of typed method parameters.
*/
private List fromRequestOrPathParameters(String path, ServiceOpMeta finalOperation,
Map requestParameters) {
List requestParamList = new LinkedList<>(requestParameters.values());
Map pathParams = extractPathParameters(path, finalOperation.getPath());
List paramList = new ArrayList<>();
for (Parameter parameter : finalOperation.getParameters().values()) {
String parameterName = parameter.getName();
String strVal = null;
if (pathParams.containsKey(parameterName)) {
strVal = pathParams.get(parameterName);
} else {
String[] valArr = null;
if (requestParameters.containsKey(parameterName)) {
valArr = requestParameters.get(parameterName);
} else {
int order = parameter.getOrder();
valArr = requestParamList.get(order);
}
if (valArr != null && valArr.length > 0) {
strVal = valArr[0];
}
}
Class> typeClazz;
try {
typeClazz = Class.forName(parameter.getTypeName());
} catch (Exception e) {
throw new InvocationException("Cannot find type for parameter -> " + parameter.getName()
+ " type-name -> " + parameter.getTypeName());
}
if (strVal != null && !strVal.equals("null")) {
if (String.class.isAssignableFrom(typeClazz)) {
paramList.add(strVal);
} else {
Object value = getMarshaller().unmarshall(strVal, typeClazz, DescriptorType.JSON);
paramList.add(value);
}
} else {
paramList.add(null);
}
}
return paramList;
}
/**
* Extracts parameters from meta and actual paths passed.
*
* @param path Actual operation path to be used for invocation. e.g. users/addUser/1/vedang
* @param metaPath Path definition. e.g. users/addUser/{id}/{name}
*/
private Map extractPathParameters(String path, String metaPath) {
UrlEncodeUtil decoder = new UrlEncodeUtil();
Map pathParams = new HashMap<>();
if (metaPath != null && metaPath.contains("/") && path.contains("/")) {
String[] metaElements = metaPath.split("/");
String[] pathElements = path.split("/");
for (int i = 0; i < metaElements.length; i++) {
String metaElement = metaElements[i];
if (metaElement.startsWith("{") && metaElement.endsWith("}")) {
String parameterName =
metaElement.substring(metaElement.indexOf("{") + 1, metaElement.indexOf("}")).trim();
String value = pathElements[i];
pathParams.put(parameterName, decoder.decodePathParam(value));
}
}
}
return pathParams;
}
/**
* it gets the mapped operation.
*
* @param op indicates the operation.
* @return operation.
*/
private ServiceOpMeta getMappedOperation(ServiceOpMeta op) {
if (op instanceof MappedOpMeta) {
ServiceOpMeta mappedDestination =
getServiceStore().getOpMeta(((MappedOpMeta) op).getMappedTo());
if (mappedDestination.isDynamic()) {
getCallStack().addOperation(mappedDestination);
}
if (mappedDestination instanceof MappedOpMeta) {
return getMappedOperation(mappedDestination);
}
return mappedDestination;
} else {
return op;
}
}
/**
*
* getCallStack.
*
*
* @return a {@link org.appops.service.lifecycle.CallStack} object.
*/
public CallStack getCallStack() {
return callStackProvider.get();
}
/**
*
* Setter for the field callStackProvider
.
*
*
* @param callStackProvider a {@link com.google.inject.Provider} object.
*/
@Inject
public void setCallStackProvider(Provider callStackProvider) {
this.callStackProvider = callStackProvider;
}
private Map getRequestParameters() {
return requestParametersProvider.get();
}
/**
*
* Getter for the field methodLocator
.
*
*
* @return a {@link org.appops.service.invocation.MethodLocator} object.
*/
public MethodLocator getMethodLocator() {
return methodLocator.get();
}
/**
*
* Setter for the field methodLocator
.
*
*
* @param methodLocator a {@link com.google.inject.Provider} object.
*/
@Inject
public void setMethodLocator(Provider methodLocator) {
this.methodLocator = methodLocator;
}
/**
*
* Setter for the field requestParametersProvider
.
*
*
* @param requestParametersProvider a {@link com.google.inject.Provider} object.
*/
public void setRequestParametersProvider(
Provider> requestParametersProvider) {
this.requestParametersProvider = requestParametersProvider;
}
/**
*
* getServiceStore.
*
*
* @return a {@link org.appops.slim.base.api.ServiceMetaManager} object.
*/
public ServiceMetaManager getServiceStore() {
return serviceMetaManager.get();
}
/**
*
* Getter for the field marshaller
.
*
*
* @return a {@link org.appops.marshaller.Marshaller} object.
*/
public Marshaller getMarshaller() {
return marshaller;
}
/**
*
* Setter for the field marshaller
.
*
*
* @param marshaller a {@link org.appops.marshaller.Marshaller} object.
*/
@Inject
public void setMarshaller(Marshaller marshaller) {
this.marshaller = marshaller;
}
/**
*
* Getter for the field paramStoreProvider
.
*
*
* @return a {@link com.google.inject.Provider} object.
*/
public Provider getParamStoreProvider() {
return paramStoreProvider;
}
/**
*
* Setter for the field paramStoreProvider
.
*
*
* @param paramStoreProvider a {@link com.google.inject.Provider} object.
*/
@Inject
public void setParamStoreProvider(Provider paramStoreProvider) {
this.paramStoreProvider = paramStoreProvider;
}
/**
*
* Setter for the field injector
.
*
*
* @param injector a {@link com.google.inject.Injector} object.
*/
public void setInjector(Injector injector) {
this.injector = injector;
}
/**
*
* Getter for the field serviceMetaManager
.
*
*
* @return a {@link com.google.inject.Provider} object.
*/
public Provider getServiceMetaManager() {
return serviceMetaManager;
}
/**
*
* Setter for the field serviceMetaManager
.
*
*
* @param serviceMetaManager a {@link com.google.inject.Provider} object.
*/
public void setServiceMetaManager(Provider serviceMetaManager) {
this.serviceMetaManager = serviceMetaManager;
}
/**
*
* Getter for the field currentRunningServiceContext
.
*
*
* @return a {@link CurrentRunningServiceContext} object
*/
public CurrentRunningServiceContext getCurrentRunningServiceContext() {
return currentRunningServiceContext;
}
/**
*
* Setter for the field currentRunningServiceContext
.
*
*
* @param currentRunningServiceContext a {@link CurrentRunningServiceContext} object
*/
@Inject
public void setCurrentRunningServiceContext(
CurrentRunningServiceContext currentRunningServiceContext) {
this.currentRunningServiceContext = currentRunningServiceContext;
}
}