DirectControlServlet.java 14.4 KB
package com.brainfood.ofbiz;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.util.Collections;
import java.util.Collection;
import java.util.Map;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Set;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Enumeration;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Iterator;
import java.sql.Timestamp;

import javax.script.ScriptContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.GroovyUtil;
import org.ofbiz.base.util.ScriptHelper;
import org.ofbiz.base.util.ScriptUtil;
import org.ofbiz.base.util.StringUtil;
import org.ofbiz.base.util.UtilHttp;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.base.util.UtilProperties;
import org.ofbiz.base.util.UtilIO;
import org.ofbiz.base.util.UtilValidate;
import org.ofbiz.entity.Delegator;
import org.ofbiz.entity.DelegatorFactory;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.entity.util.EntityUtil;
import org.ofbiz.entity.condition.EntityOperator;
import org.ofbiz.entity.condition.EntityCondition;
import org.ofbiz.service.DispatchContext;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ModelService;
import org.ofbiz.service.ServiceContainer;

import groovy.lang.GroovyClassLoader;
import groovy.lang.Script;

import org.codehaus.groovy.runtime.InvokerHelper;
import org.ofbiz.base.json.JSON;
import org.ofbiz.base.json.ParseException;

import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import net.sf.json.processors.JsonValueProcessor;

public class DirectControlServlet extends HttpServlet {
    public static final String module = DirectControlServlet.class.getName();
    public static final Map<String, String> serviceURLMappings = new HashMap<String, String>();
    private String sessionTokenName = "_AUTHTOKEN";
    private String checkSessionService;

    public void init(ServletConfig config) throws ServletException {
        // get the mapping file for this webapp
        ServletContext context = config.getServletContext();
        String mappingFile = context.getInitParameter("serviceURLMappings");
        Debug.logInfo("Mapping file: " + mappingFile, module);

        if (context.getInitParameter("sessionTokenName") != null) {
            sessionTokenName = context.getInitParameter("sessionTokenName");
        }

        if (mappingFile == null) {
            Debug.logInfo("No mapping file configured", module);
        } else {
            try {
                    BufferedReader in = new BufferedReader(new InputStreamReader(context.getResourceAsStream(mappingFile)));
                    String line;
                    while ((line = in.readLine()) != null) {
                        String[] confItem = line.split("=");
                        serviceURLMappings.put(confItem[0], confItem[1]);
                    }
            } catch (IOException ex) {
                Debug.logInfo("Could not read mapping file " + mappingFile, module);
                throw new ServletException("Could not read mapping file " + mappingFile);
            }
        }

        checkSessionService = context.getInitParameter("checkSessionService");
        Debug.logInfo("Checking session with service: " + checkSessionService, module);
    }

    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String pathInfo = request.getPathInfo();
        String contentType = request.getContentType();
        try {
            Debug.logInfo("getPathInfo: " + pathInfo +
                    " request.getContentType: " + contentType, module);

            if (pathInfo == null || pathInfo.length() == 0) {
                return;
            }
            pathInfo = pathInfo.substring(1).replaceAll(":", ".");

            // Determine type of request and load the context from JSON content or
            // parameter values accordingly
            if (contentType != null) {
                int semi = contentType.indexOf(";");
                if (semi != -1) {
                    contentType = contentType.substring(0, semi);
                }
            }

            // Determine the request method for service lookup and parameter filters
            String method = "";
            method = request.getParameter("_method");
            String httpMethod = request.getMethod();
            if (method == null && httpMethod != null) method = httpMethod;
            if (method == null) method = "GET";

            // Load context
            Map<String, Object> context = new HashMap<String, Object>();
            if ("application/json".equals(contentType)) {
                // Read request body as JSON and insert into the context
                JSON json = new JSON(request.getReader());
                Map<String,Object> items = json.JSONObject();
                for (String key : items.keySet()) {
                    context.put(key, items.get(key));
                }
            } else {
                // Check if the request is a backbone style "emulateJSON" request
                if (contentType != null &&
			contentType.indexOf("x-www-form-urlencoded") != -1 &&
			request.getParameter("model") != null) {
                    Debug.logInfo("MODEL: " + request.getParameter("model"), module);
                    JSON json = new JSON(new StringReader(request.getParameter("model")));
                    Map<String,Object> items = json.JSONObject();
                    for (String key : items.keySet()) {
                        if (!"sessionId".equals(key)) {
                            context.put(key, items.get(key));
                        }
                    }
                } else {
                    // Directly copy request parameters into context.
                    for (Enumeration<String> params = request.getParameterNames(); params.hasMoreElements();) {
                        String param = params.nextElement();
                        Object[] values = request.getParameterValues(param);
                        if (!"sessionId".equals(param) && !"_method".equals(param)) {
                            context.put(param, values.length == 1 ? values[0] : values);
                        }
                    }
                }
            }

            Delegator delegator = getDelegator(request.getServletContext());
            LocalDispatcher dispatcher = getDispatcher(request.getServletContext());

            // If there is a mapping for this pathInfo, run the corresponding service
            // otherwise, return an error
            String serviceName = serviceURLMappings.get(pathInfo + "#" + method);
            Debug.logInfo("Service name " + serviceName, module);
            if (serviceName == null) {
                serviceName = serviceURLMappings.get(pathInfo);
                if (serviceName == null) {
                    response.setStatus(404);

                    Debug.logInfo("No mapping found for " + pathInfo + "#" + method, module);

                    PrintWriter writer = response.getWriter();
                    writer.println("No mapping found for URL \"" + pathInfo + "\"");
                    writer.flush();
                    writer.close();
                    return;
                }
            }

            Debug.logInfo("Service name" +serviceName + " mapped for " + pathInfo + "#" + method, module);

            // If the sessionId parameter is set, attempt to look up the corresponding
            // UserLogin and apply it to the service context
            String authToken = request.getParameter("sessionId");
            if (authToken != null) {
                GenericValue authTokenEntity = EntityUtil.getFirst(
                    EntityUtil.filterByDate(
                        delegator.findList("Visit", 
                            EntityCondition.makeCondition("cookie", 
                                EntityOperator.EQUALS, authToken),
                            null, null, null, false)
                    )
                );

                if (authTokenEntity != null) {
                    String userLoginId = authTokenEntity.getString("userLoginId");
                    if (UtilValidate.isNotEmpty(userLoginId)) {
                        GenericValue userLogin = EntityUtil.getFirst(
                                delegator.findList("UserLogin",
                                        EntityCondition.makeCondition("userLoginId", 
                                                EntityOperator.EQUALS, 
                                                userLoginId.toLowerCase()), 
                                                null, null, null, true));
                        if (userLogin != null) {
                            context.put("userLogin", userLogin);
                        }

                        // prolong the session
                        if (UtilValidate.isNotEmpty(checkSessionService)) {
                            dispatcher.runSync(checkSessionService, UtilMisc.<String, Object>toMap("authSessionId", authToken, "userLogin", userLogin));
                        }
                    }
                }
            }

            Debug.logInfo("USERLOGIN " + context.get("userLogin") + " AUTHTOKEN " + authToken, module);

            DispatchContext dctx = dispatcher.getDispatchContext();
            ModelService model = dctx.getModelService(serviceName);

            // some needed info for when running the service
            Locale locale = UtilHttp.getLocale(request);
            TimeZone timeZone = UtilHttp.getTimeZone(request);

            List<Object> errorMessages = new ArrayList<Object>();

            context = model.makeValid(context, ModelService.IN_PARAM, true, errorMessages, timeZone, locale);

            Map<String, Object> result = dispatcher.runSync(serviceName, context);

            result.remove("responseMessage");

            if (result.get("errorMessage") != null) {
                response.setStatus(400);
            }

            response.setContentType("application/x-json");

            PrintWriter writer = response.getWriter();

            JsonConfig jsonConfig = new JsonConfig();
            jsonConfig.registerJsonValueProcessor(Date.class, new ISODateValueProcessor());
            jsonConfig.registerJsonValueProcessor(Timestamp.class, new ISODateValueProcessor());
            JSONObject json = JSONObject.fromObject(result, jsonConfig);
            String jsonStr = json.toString();
            response.setContentLength(jsonStr.getBytes("UTF8").length);
            writer.write(jsonStr);

            writer.flush();
            writer.close();
        } catch (Throwable t) {
            response.setStatus(500);
            PrintWriter writer = response.getWriter();
            Debug.logInfo("Exception processing request", module);
            Debug.logInfo(t, module);
            while (t != null) {
                t.printStackTrace(writer);
                t = t.getCause();
            }
            writer.flush();
            writer.close();
        }
    }

    protected static LocalDispatcher getDispatcher(ServletContext servletContext) {
        LocalDispatcher dispatcher = (LocalDispatcher) servletContext.getAttribute("dispatcher");
        if (dispatcher == null) {
            Delegator delegator = getDelegator(servletContext);
            dispatcher = makeWebappDispatcher(servletContext, delegator);
            servletContext.setAttribute("dispatcher", dispatcher);
        }
        return dispatcher;
    }

    /** This method only sets up a dispatcher for the current webapp and passed in delegator, it does not save it to the ServletContext or anywhere else, just returns it */
    public static LocalDispatcher makeWebappDispatcher(ServletContext servletContext, Delegator delegator) {
        if (delegator == null) {
            Debug.logInfo("[ContextFilter.init] ERROR: delegator not defined.", module);
            return null;
        }
        // get the unique name of this dispatcher
        String dispatcherName = servletContext.getInitParameter("localDispatcherName");

        if (dispatcherName == null) {
            Debug.logInfo("No localDispatcherName specified in the web.xml file", module);
            dispatcherName = delegator.getDelegatorName();
        }

        LocalDispatcher dispatcher = ServiceContainer.getLocalDispatcher(dispatcherName, delegator);
        if (dispatcher == null) {
            Debug.logInfo("[ContextFilter.init] ERROR: dispatcher could not be initialized.", module);
        }

        return dispatcher;
    }

    protected static Delegator getDelegator(ServletContext servletContext) {
        Delegator delegator = (Delegator) servletContext.getAttribute("delegator");
        if (delegator == null) {
            String delegatorName = servletContext.getInitParameter("entityDelegatorName");

            if (delegatorName == null || delegatorName.length() <= 0) {
                delegatorName = "default";
            }
            if (Debug.verboseOn()) Debug.logVerbose("Setup Entity Engine Delegator with name " + delegatorName, module);
            delegator = DelegatorFactory.getDelegator(delegatorName);
            servletContext.setAttribute("delegator", delegator);
            if (delegator == null) {
                Debug.logInfo("[ContextFilter.init] ERROR: delegator factory returned null for delegatorName \"" + delegatorName + "\"", module);
            }
        }
        return delegator;
    }
}

class ISODateValueProcessor implements JsonValueProcessor {
    public static final String module = ISODateValueProcessor.class.getName();

    public ISODateValueProcessor() {
    }

    public Object processArrayValue( Object value, JsonConfig jsonConfig ) {
        return value;
    }

    public Object processObjectValue( String key, Object value, JsonConfig jsonConfig ) {
        return process(value, jsonConfig);
    }

    private Object process( Object value, JsonConfig jsonConfig ) {
        String newValue = value.toString();
        return newValue;
    }
}