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

org.appops.service.invocation.OperationInvoker Maven / Gradle / Ivy

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