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

com.brettonw.servlet.Base Maven / Gradle / Ivy

Go to download

ServletBase provides a base implementation for a JSON request/response server.

There is a newer version: 3.4.2
Show newest version
package com.brettonw.servlet;

import com.brettonw.bag.*;
import com.brettonw.bag.formats.MimeType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static com.brettonw.servlet.Keys.*;

public class Base extends HttpServlet {
    private static final Logger log = LogManager.getLogger (Base.class);

    private static ServletContext context;

    public static ServletContext getContext () {
        return context;
    }

    public static Object getAttribute (String key) {
        return (context != null) ? context.getAttribute (key) : null;
    }

    public static Object setAttribute (String key, Object value) {
        if (context != null) {
            Object oldValue = context.getAttribute (key);
            context.setAttribute (key, value);
            return oldValue;
        }
        return null;
    }

    private final Map> handlers;
    protected Handler defaultHandler;
    protected BagObject apiSchema;

    protected Base () {
        handlers = new HashMap<> ();
        defaultHandler = event -> {
            event.error ("Unhandled event: '" + event.getEventName () + "'");
        };

        // add a version handler
        onEvent (VERSION, event-> {
            String version = Base.class.getPackage ().getImplementationVersion ();
            event.ok (BagObject
                    .open (VERSION, version)
                    .put (NAME, context.getServletContextName ())
            );
        });

        // try to load the schema, give a default HELP handler
        apiSchema = BagObjectFrom.resource (Base.class, "/api.json");
        if (apiSchema != null) {
            onEvent (HELP, event -> event.ok (apiSchema));
        }
    }

    @Override
    public void init (ServletConfig config) throws ServletException {
        super.init (config);
        context = config.getServletContext ();
        log.debug ("STARTING " + context.getServletContextName ());
        super.init (config);
        setAttribute (SERVLET, this);
    }

    @Override
    public void destroy () {
        super.destroy ();
        log.debug (context.getServletContextName () + " DESTROYING...");
    }

    @Override
    public void doGet (HttpServletRequest request, HttpServletResponse response) throws IOException {
        BagObject query = BagObjectFrom.string (request.getQueryString (), MimeType.URL, () -> new BagObject ());
        handleRequest (query, request, response);
    }

    @Override
    public void doPost (HttpServletRequest request, HttpServletResponse response) throws IOException {
        // get the request data type, then tease out the response type (use a default if it's not present) and the
        // charset (if given, otherwise default to UTF-8, because that's what it will be in Java)
        String mimeType = MimeType.DEFAULT;
        String contentTypeHeader = request.getHeader (CONTENT_TYPE);
        if (contentTypeHeader != null) {
            String[] contentType = contentTypeHeader.replace (" ", "").split (";");
            mimeType = contentType[0];
            log.debug ("'Content-Type' is (" + mimeType + ")");
        } else {
            log.warn ("'Content-Type' is not set by the requestor, using default (" + mimeType + ")");
        }

        // extract the bag data that's been posted, we do it this roundabout way because
        // we don't know a priori if it's an object or array
        SourceAdapter sourceAdapter = new SourceAdapterReader(request.getInputStream (), mimeType);
        String requestString = sourceAdapter.getStringData ();
        Bag postData = BagObjectFrom.string (requestString, mimeType);
        if (postData == null) postData = BagArrayFrom.string (requestString);

        // handle the query part normally, but add the post data to it (if any)
        BagObject query = BagObjectFrom.string (request.getQueryString (), MimeType.URL, () -> new BagObject ())
                .put (POST_DATA, postData);
        handleRequest (query, request, response);
    }

    private void handleRequest (BagObject query, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // create the event object around the request parameters, and validate that it is
        // a known event
        Event event = new Event (query, request, response);
        if (event.hasRequiredParameters (EVENT)) {
            // get the name, and try to validate the event against the schema if we have one
            String eventName = event.getEventName ();
            if ((apiSchema == null) || event.hasRequiredParameters (apiSchema.getBagArray (Key.cat (eventName, REQUIRED)))) {
                // get the handler, and try to take care of business...
                Handler handler = handlers.get (eventName);
                if (handler != null) {
                    log.info (eventName);
                    handler.handle (event);
                } else {
                    defaultHandler.handle (event);
                }
            }
        }
    }

    public Base onEvent (String name, Handler handler) {
        handlers.put (name, handler);
        return this;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy