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

org.simple4j.eventdistributor.Main Maven / Gradle / Ivy

Go to download

RDBMS based messaging as an alternative to other standard messaging like JMS / Kafka

The newest version!
package org.simple4j.eventdistributor;

import java.lang.invoke.MethodHandles;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.simple4j.apiaopvalidator.beans.AppResponse;
import org.simple4j.apiaopvalidator.beans.ErrorDetails;
import org.simple4j.eventdistributor.beans.ErrorType;
import org.simple4j.eventdistributor.beans.Event;
import org.simple4j.eventdistributor.beans.EventStatus;
import org.simple4j.eventdistributor.beans.HealthCheck;
import org.simple4j.eventdistributor.japi.EventDistributorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

import io.javalin.Javalin;
import io.javalin.http.Context;


/**
 * This class holds the main method and the entry point for startup of the application.
 */
public class Main
{
    private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
    private static ApplicationContext context;
    private static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .serializationInclusion(Include.NON_NULL)
            .build();
    private static final String REQUEST_ID_KEY = "requestId";
    private static final String JAPI_RETURN_OBJECT = "returnObject";
    private static final String START_TIME_MILLISEC = "startTimeMillisec";

    private static Main main = null;
    
    public static boolean pauseEventFetcher = false;

    private int listenerPortNumber = 2410;
    private int listenerIdleTimeoutMillis = 600000;
    private String urlBase = "/eventdistributorws/eppublic/V1";

    private EventDistributorService eventDistributorService = null;
    private String userIdHeader = "userId";
    private String hostName = null;
    
    private Map> errorType2HTTPStatusMapping = null;
    

    /**
     * This method being the entry point, it initializes all the beans with dependency injection,
     *  initializes web service routes
     * @param args
     */
    public static void main(String[] args)
    {
        LOGGER.info("EventDistributor is starting, please wait...");

        if(args.length>0)
        {
        	try
        	{
	        	pauseEventFetcher = Boolean.parseBoolean(args[0]);
        	}
        	catch(Throwable t)
        	{
        		LOGGER.warn("Error while parsing arguments", t);
        	}
        }
        context = new ClassPathXmlApplicationContext("appContext.xml");

        main = context.getBean("main", Main.class);
        main.init();

		Javalin javalin = Javalin.create();

        javalin.before(ctx ->
        {
            long startTimeMillisec = System.currentTimeMillis();
            ctx.attribute(START_TIME_MILLISEC, startTimeMillisec);
            
			String requestId = startTimeMillisec + "@" + main.hostName;

            String headerRequestId = ctx.header(REQUEST_ID_KEY);
            if (StringUtils.isNotBlank(headerRequestId))
            {
                requestId = headerRequestId;
            }

            MDC.put(REQUEST_ID_KEY, requestId);
            String body = "";
            String query = ctx.queryString();
            ctx.header("content-type", "application/json");
            LOGGER.info("Start request url is {}, method is {}, {}{}", ctx.url(), ctx.method(),
                    StringUtils.isNotBlank(body) ? ("body is " + body + ", ") : "",
                    StringUtils.isNotBlank(query) ? ("query string is " + query) : "");
        });

        javalin.after(ctx ->
        {
            
            Object returnObject = ctx.attribute(JAPI_RETURN_OBJECT);
            if(returnObject instanceof AppResponse)
            {
                AppResponse appResponse = ctx.attribute(JAPI_RETURN_OBJECT);
                ctx.status(main.getStatusCode(appResponse));
            }
            else
            {
                ctx.status(200);
            }
            
            finishCall(ctx);
        });

        javalin.exception(Exception.class, (e, ctx) ->
        {
            LOGGER.error("Unhandled exception", e);
            setHeader(ctx);
            ctx.status(500);
            ErrorDetails errorDetails = new ErrorDetails();
            errorDetails.errorId = MDC.get(REQUEST_ID_KEY);
            errorDetails.errorType = ErrorType.RUNTIME_ERROR.toString();
            errorDetails.errorDescription = e.toString();

            finishCall(ctx);

            try
            {
                ctx.result(OBJECT_MAPPER.writeValueAsString(errorDetails));
            }
            catch (JsonProcessingException e1)
            {
                LOGGER.error("Marshaling error:", e1);
                throw new RuntimeException("Marshaling error:", e1);
            }
        });

        javalin.post(main.getUrlBase()+"/serverhealth.json", ctx -> 
        {
            String bodyJson = ctx.body();
            HealthCheck healthCheckReq = null;
            try
            {
                healthCheckReq = OBJECT_MAPPER.readValue(bodyJson, HealthCheck.class);
            }
            catch(Exception e)
            {
                throw new RuntimeException("Error parsing request body <" + bodyJson +">",  e);
            }
            main.getEventDistributorService().setHealthCheck(healthCheckReq.status);
            setHeader(ctx);
            ctx.result("{}");
        });

        javalin.post(main.getUrlBase()+"/eventFetcherTrigger.json", ctx -> 
        {
        	pauseEventFetcher = false;
            setHeader(ctx);
            ctx.result("{}");
        });

        javalin.get(main.getUrlBase()+"/serverhealth.json", ctx -> 
        {
            AppResponse ret = main.getEventDistributorService().getHealthCheck();
            HealthCheck healthCheckRes = ret.responseObject;
            setHeader(ctx);
            ctx.result(OBJECT_MAPPER.writeValueAsString(healthCheckRes));
        });

        javalin.post(main.getUrlBase()+"/event.json", ctx -> 
        {
            AppResponse ret = null;
            String callerId = ctx.header("callerId");
            String userId = ctx.header(main.getUserIdHeader());
            
            String body = ctx.body();
            Event event = null;
            try
            {
            	event = OBJECT_MAPPER.readValue(body, Event.class);
            	if(callerId != null && callerId.trim().length() > 0)
            	{
            		event.setCreateBy(callerId);
            	}
            	if(userId != null && userId.trim().length() > 0)
            	{
            		event.setCreateBy(userId);
            	}
            }
            catch(Exception e)
            {
                throw new RuntimeException("Error parsing request body <" + body +">",  e);
            }
            
            ret = main.getEventDistributorService().postEvent(event);

            //setting AppResponse object for usage in javalin.after handler
            ctx.attribute(JAPI_RETURN_OBJECT, ret);

            if(ret.errorDetails != null)
            {
            	ctx.result(OBJECT_MAPPER.writeValueAsString(ret.errorDetails));
                return;
            }
            
            ctx.result("{}");
        });

        javalin.get(main.getUrlBase()+"/event.json", ctx -> 
        {
            AppResponse ret = null;
            String callerId = ctx.header("callerId");
            String eventId = ctx.queryParam("eventId");
            
            ret = main.getEventDistributorService().getEvent(callerId, eventId);
            
            LOGGER.info("ret:{}", ret);

            //setting AppResponse object for usage in javalin.after handler
            ctx.attribute(JAPI_RETURN_OBJECT, ret);
            if(ret.errorDetails != null)
            {
            	ctx.result(OBJECT_MAPPER.writeValueAsString(ret.errorDetails));
                return;
            }
            ctx.result(OBJECT_MAPPER.writeValueAsString(ret.responseObject));
        });

        javalin.get(main.getUrlBase()+"/events.json", ctx -> 
        {
            AppResponse> ret = null;
            String callerId = ctx.header("callerId");

            Event event = new Event();
            String eventIdStr = getStringWithEmptyCheck(ctx.queryParam("eventId"), null);
    		if (eventIdStr != null)
    		{
    			long eventId = Long.parseLong(eventIdStr);
    			event.setEventId(eventId);
    		}

            event.setBusinessRecordId(getStringWithEmptyCheck(ctx.queryParam("businessRecordId"), null));
            event.setBusinessRecordType(getStringWithEmptyCheck(ctx.queryParam("businessRecordType"), null));
            event.setBusinessRecordSubType(getStringWithEmptyCheck(ctx.queryParam("businessRecordSubType"), null));
            event.setBusinessRecordVersion(getStringWithEmptyCheck(ctx.queryParam("businessRecordVersion"), null));
            event.setSource(getStringWithEmptyCheck(ctx.queryParam("source"), null));
            String statusStr = getStringWithEmptyCheck(ctx.queryParam("status"), null);
            if(statusStr != null)
				event.setStatus(EventStatus.valueOf(statusStr));
            event.setProcessingHost(getStringWithEmptyCheck(ctx.queryParam("processingHost"), null));
            event.setCreateBy(getStringWithEmptyCheck(ctx.queryParam("createBy"), null));
            String startPosition = getStringWithEmptyCheck(ctx.queryParam("startPosition"), null);
            String numberOfRecords = getStringWithEmptyCheck(ctx.queryParam("numberOfRecords"), null);
            
            ret = main.getEventDistributorService().getEvents(callerId, startPosition, numberOfRecords, event);
            
            LOGGER.info("ret:{}", ret);

            //setting AppResponse object for usage in javalin.after handler
            ctx.attribute(JAPI_RETURN_OBJECT, ret);
            if(ret.errorDetails != null)
                if(ret.errorDetails != null)
                {
                	ctx.result(OBJECT_MAPPER.writeValueAsString(ret.errorDetails));
                    return;
                }
            
            HashMap> retMap = new HashMap>();
            retMap.put("events", ret.responseObject);
            ctx.result(OBJECT_MAPPER.writeValueAsString(retMap));
        });

        javalin.post(main.getUrlBase()+"/repost/event.json", ctx -> 
        {
            AppResponse ret = null;
            String callerId = ctx.header("callerId");
            String userId = ctx.header(main.getUserIdHeader());
            String eventId = ctx.queryParam("eventId");
            
    		String createBy = null;
        	createBy = getStringWithEmptyCheck(callerId, null);
        	createBy = getStringWithEmptyCheck(userId, createBy);

        	ret = main.getEventDistributorService().repostEvent(eventId, createBy);

            //setting AppResponse object for usage in javalin.after handler
            ctx.attribute(JAPI_RETURN_OBJECT, ret);

            if(ret.errorDetails != null)
            {
            	ctx.result(OBJECT_MAPPER.writeValueAsString(ret.errorDetails));
                return;
            }
            
            ctx.result("{}");
        });

        javalin.post(main.getUrlBase()+"/repost/publish.json", ctx -> 
        {
            AppResponse ret = null;
            String callerId = ctx.header("callerId");
            String userId = ctx.header(main.getUserIdHeader());
            String publishId = ctx.queryParam("publishId");
            
    		String createBy = null;
        	createBy = getStringWithEmptyCheck(callerId, null);
        	createBy = getStringWithEmptyCheck(userId, createBy);
            
            ret = main.getEventDistributorService().republish(publishId, createBy);

            //setting AppResponse object for usage in javalin.after handler
            ctx.attribute(JAPI_RETURN_OBJECT, ret);

            if(ret.errorDetails != null)
            {
            	ctx.result(OBJECT_MAPPER.writeValueAsString(ret.errorDetails));
                return;
            }
            
            ctx.result("{}");
        });

        javalin.post(main.getUrlBase()+"/abort/event.json", ctx -> 
        {
            AppResponse ret = null;
            String callerId = ctx.header("callerId");
            String userId = ctx.header(main.getUserIdHeader());
            String eventId = ctx.queryParam("eventId");
            
    		String updateBy = null;
        	updateBy = getStringWithEmptyCheck(callerId, null);
        	updateBy = getStringWithEmptyCheck(userId, updateBy);

        	ret = main.getEventDistributorService().abortEvent(eventId, updateBy);

            //setting AppResponse object for usage in javalin.after handler
            ctx.attribute(JAPI_RETURN_OBJECT, ret);

            if(ret.errorDetails != null)
            {
            	ctx.result(OBJECT_MAPPER.writeValueAsString(ret.errorDetails));
                return;
            }
            
            ctx.result("{}");
        });

        javalin.start(main.getListenerPortNumber());
        
    	LOGGER.info("Start up completed");
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        	LOGGER.info("Shutdown hook called");
        	javalin.stop();
        }));

        javalin.events(event -> {
            event.serverStopping(() -> {
            	LOGGER.info("Stopping event");
            });
            event.serverStopped(() -> {
            	LOGGER.info("Stopped event");
            });
        });
    	LOGGER.info("End of main method");
    }

	private static String getStringWithEmptyCheck(String in, String defaultValue)
	{
		String ret = defaultValue;
		if(in != null && in.trim().length() > 0)
		{
			ret = in;
		}
		return ret;
	}

    private void init()
    {
    	OBJECT_MAPPER.registerModule(new JavaTimeModule());
    	try
		{
			this.hostName = InetAddress.getLocalHost().getHostName();
		}
    	catch (UnknownHostException e)
		{
			throw new RuntimeException(e);
		}
    	this.getEventDistributorService().init();
    }

    private int getStatusCode(AppResponse appResponse)
    {
        if(appResponse.errorDetails == null)
        {
            return 200;
        }
        Map apiCallName2HTTPStatusMapping = this.getErrorType2HTTPStatusMapping().get(appResponse.errorDetails.errorType.toString());
        if(apiCallName2HTTPStatusMapping != null)
        {
            if(apiCallName2HTTPStatusMapping.get(appResponse.apiCallName) != null)
            {
               return apiCallName2HTTPStatusMapping.get(appResponse.apiCallName);
            }
            else
                if(apiCallName2HTTPStatusMapping.get("") != null)
                {
                    return apiCallName2HTTPStatusMapping.get("");
                }
                else
                {
                    LOGGER.error("Missing mapping for apiCallName {} or for fallback", appResponse.apiCallName);
                }
        }
        else
        {
            LOGGER.error("Missing mapping for error type {}", appResponse.errorDetails.errorType);
        }
        throw new RuntimeException("main.getErrorType2HTTPStatusMapping() not configured for :"+appResponse);
    }

    private static void setHeader(Context ctx)
    {
        ctx.contentType("application/json;charset=utf-8");
        ctx.header("Content-Language", "en");
        ctx.header("requestid", MDC.get(REQUEST_ID_KEY));
    }

    private static void finishCall(Context ctx)
    {
        long startTimeMillisec = ctx.attribute(START_TIME_MILLISEC);
        LOGGER.info("End request url is {}, method is {}, time {}s, query string is {}", ctx.url(),
        		ctx.method(), (System.currentTimeMillis() - startTimeMillisec) / 1000.0,
        		ctx.queryString());
        MDC.clear();
    }
    
    public static ApplicationContext getContext()
	{
		return context;
	}

	public int getListenerPortNumber()
    {
        return listenerPortNumber;
    }

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

    public int getListenerIdleTimeoutMillis()
    {
        return listenerIdleTimeoutMillis;
    }

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

    public String getUrlBase()
    {
        return urlBase;
    }

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

    public Map> getErrorType2HTTPStatusMapping()
    {
        return errorType2HTTPStatusMapping;
    }

    /**
     * This setter will set the mapping for AppResponse to HTTP codes.
     * The mapping is set via dependency injection
     * {
     *      "SUCCESS" : 
     *      {
     *          "" : "200",
     *          "<API name>" : "201"
     *      },
     *      "PARAMETER_ERROR" : 
     *      {
     *          "" : "412",
     *      }
     * }
     */
    public void setErrorType2HTTPStatusMapping(
            Map> errorType2HTTPStatusMapping)
    {
        this.errorType2HTTPStatusMapping = errorType2HTTPStatusMapping;
    }

	public EventDistributorService getEventDistributorService()
	{
		if(this.eventDistributorService == null)
			throw new RuntimeException("eventDistributorService not configured");
		return eventDistributorService;
	}

	public void setEventDistributorService(EventDistributorService eventDistributorService)
	{
		this.eventDistributorService = eventDistributorService;
	}

	public String getUserIdHeader()
	{
		return userIdHeader;
	}

	/**
	 * This method can be used to set the header name for sending userid by the authentication systems.
	 * 
	 * @param userIdHeader
	 */
	public void setUserIdHeader(String userIdHeader)
	{
		this.userIdHeader = userIdHeader;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy