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

org.simple4j.wsfeeler.pojoashttp.HTTPExposer Maven / Gradle / Ivy

package org.simple4j.wsfeeler.pojoashttp;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.commons.beanutils.ConvertUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;

/**
 * This class can be used to expose any method in any bean in a Spring ApplicationContext as a HTTP service
 */
public abstract class HTTPExposer
{
    private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    private final ObjectMapper OBJECT_MAPPER = JsonMapper.builder()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .serializationInclusion(Include.NON_NULL)
            .build();

    /**
     * org.springframework.context.ApplicationContext instance from which the bean methods are exposed
     */
    private ApplicationContext context;
    
    private int listenerPortNumber = -1;
    
    private int listenerThreadMax = 100;
    
    private int listenerThreadMin = 1;

    private int listenerIdleTimeoutMillis = 60000;
    
    private String urlBase = "/pojoashttp";
    
    /**
     * Constructor with org.springframework.context.ApplicationContext instance as parameter
     * @param context - org.springframework.context.ApplicationContext instance
     */
    public HTTPExposer(ApplicationContext context)
    {
    	super();
    	this.context = context;
    }
    
    /**
     * This method will turn on the listener to expose bean methods as web service
     */
    public abstract void expose();
    
	protected String processRequest(String body)
			throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException, JsonProcessingException,
			JsonMappingException, NoSuchMethodException, InvocationTargetException
	{
		RequestJSON requestJSON = null;
		try
		{
		    requestJSON = OBJECT_MAPPER.readValue(body, RequestJSON.class);
		}
		catch(Exception e)
		{
		    throw new RuntimeException("Error parsing request body <" + body +">",  e);
		}
		
		LOGGER.info("parsed JSON");
		Object bean = context.getBean(requestJSON.getBeanId());
		LOGGER.info("got bean from Spring {}",bean);

		Class[] parameterTypes = null;
		Object[] parameterValues = null;
		MethodParameterJSON[] methodParameters = requestJSON.getMethodParameters();
		LOGGER.info("got method parameter");
		if(methodParameters != null && methodParameters.length > 0)
		{
		    LOGGER.info("method parameter not null");
		    parameterTypes = new Class[methodParameters.length];
		    parameterValues = new Object[methodParameters.length];

		    for (int i = 0; i < methodParameters.length; i++) {
		        LOGGER.info("creating instance for param:{}", methodParameters[i].getClassName());
		        LOGGER.info(":{}",methodParameters[i].getValue());
		        LOGGER.info(":{}",methodParameters[i].getValues());
		        //To call methods with primitive types, the client need to use TYPE field.
		        //Here is an example
		        //{"beanId":"someBean","methodName":"someMethod", "methodParameters" : [{"className":"java.lang.String","value":"someStringParam"},{"className":"java.lang.Integer.TYPE","value":"100"}]}
		        if(methodParameters[i].getClassName().endsWith(".TYPE"))
		        {
		            Class primitiveType = Class.forName(methodParameters[i].getClassName().replaceFirst("(\\.TYPE)$", ""));
		            parameterTypes[i] = (Class) primitiveType.getField("TYPE").get(null);
		        }
		        else
		        {
		            parameterTypes[i] = Class.forName(methodParameters[i].getClassName());
		        }
		        LOGGER.info("loaded param type {}", parameterTypes[i]);
		        if(methodParameters[i].getValue() != null)
		        {
		            if(parameterTypes[i].equals(methodParameters[i].getValue().getClass()))
		            {
		                LOGGER.info("parametertypes same as value type");
		                parameterValues[i] = methodParameters[i].getValue();
		            }
		            else
		            {
		                LOGGER.info("converting parametertype");
		                parameterValues[i] = ConvertUtils.convert(methodParameters[i].getValue(), parameterTypes[i]);
		                LOGGER.info("converting parametertype");
		            }
		        }
		        else if(methodParameters[i].getValues() != null)
		        {
		            parameterValues[i] = ConvertUtils.convert(methodParameters[i].getValues(), parameterTypes[i]);
		            parameterTypes[i] = parameterValues[i].getClass();
		        }
		        else
		        {
		            String parameterStringJSON = OBJECT_MAPPER.writeValueAsString(methodParameters[i].getValueJSON());
		            parameterValues[i] = OBJECT_MAPPER.readValue(parameterStringJSON, parameterTypes[i]);
		        }
		    }
		}

		LOGGER.info("getting method instance");
		Method method = bean.getClass().getMethod(requestJSON.getMethodName(), parameterTypes);
		LOGGER.info("got it:{}", method);
		Object responseObj = method.invoke(bean, parameterValues);

		LOGGER.info("invoked it:{}",responseObj);

		String ret = OBJECT_MAPPER.writeValueAsString(responseObj);
		return "{\"returnValue\":"+ret+"}";
	}

    /**
     * Port number to start the listener at
     */
	public int getListenerPortNumber()
	{
		return listenerPortNumber;
	}

	public void setListenerPortNumber(int listenerPortNumber)
	{
		this.listenerPortNumber = listenerPortNumber;
	}

    /**
     * Max thread count for the listener to serve requests
     */
	public int getListenerThreadMax()
	{
		return listenerThreadMax;
	}

	public void setListenerThreadMax(int listenerThreadMax)
	{
		this.listenerThreadMax = listenerThreadMax;
	}

    /**
     * Min thread count for the listener to serve requests
     */
	public int getListenerThreadMin()
	{
		return listenerThreadMin;
	}

	public void setListenerThreadMin(int listenerThreadMin)
	{
		this.listenerThreadMin = listenerThreadMin;
	}

    /**
     * Idle timeout in milli seconds for the listener thread to be destroyed to reach min thread count
     */
	public int getListenerIdleTimeoutMillis()
	{
		return listenerIdleTimeoutMillis;
	}

	public void setListenerIdleTimeoutMillis(int listenerIdleTimeoutMillis)
	{
		this.listenerIdleTimeoutMillis = listenerIdleTimeoutMillis;
	}

    /**
     * URL base path for the listener to expose the service
     */
	public String getUrlBase()
	{
		return urlBase;
	}

	public void setUrlBase(String urlBase)
	{
		this.urlBase = urlBase;
	}
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy