Package org.directwebremoting.jsonp

Source Code of org.directwebremoting.jsonp.JsonpCallHandler

/*
* Copyright 2005 Joe Walker
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.directwebremoting.jsonp;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.directwebremoting.ConversionException;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.extend.AccessControl;
import org.directwebremoting.extend.Call;
import org.directwebremoting.extend.Calls;
import org.directwebremoting.extend.ConverterManager;
import org.directwebremoting.extend.Handler;
import org.directwebremoting.extend.InboundContext;
import org.directwebremoting.extend.InboundVariable;
import org.directwebremoting.extend.MethodDeclaration;
import org.directwebremoting.extend.ModuleManager;
import org.directwebremoting.extend.ParameterProperty;
import org.directwebremoting.extend.Property;
import org.directwebremoting.extend.ProtocolConstants;
import org.directwebremoting.extend.Remoter;
import org.directwebremoting.extend.Replies;
import org.directwebremoting.extend.Reply;
import org.directwebremoting.extend.ScriptBufferUtil;
import org.directwebremoting.util.MimeConstants;

/**
* A Handler JSON/REST DWR calls.
* @author Joe Walker [joe at getahead dot ltd dot uk]
*/
public class JsonpCallHandler implements Handler
{
    /* (non-Javadoc)
     * @see org.directwebremoting.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException
    {
        if (!jsonpEnabled)
        {
            log.warn("JSONP request denied. To enable JSONP mode add an init-param of jsonpEnabled=true to web.xml");
            throw new SecurityException("JSONP interface disabled");
        }

        // Get the output stream and setup the mime type
        response.setContentType(MimeConstants.MIME_JS);
        String callback = getCallback(request);
        PrintWriter out = response.getWriter();

        try
        {
            Calls calls = convertToCalls(request);
            Replies replies = remoter.execute(calls);

            // There will only be one of these while JSON mode does not do batching
            for (int i = 0; i < replies.getReplyCount(); i++)
            {
                Reply reply = replies.getReply(i);

                try
                {
                    // The existence of a throwable indicates that something went wrong
                    if (reply.getThrowable() != null)
                    {
                        Throwable ex = reply.getThrowable();
                        writeData(out, ex, callback);

                        log.warn("--Erroring: message[" + ex.toString() + ']');
                    }
                    else
                    {
                        Object data = reply.getReply();
                        writeData(out, data, callback);
                    }
                }
                catch (Exception ex)
                {
                    // This is a bit of a "this can't happen" case so I am a bit
                    // nervous about sending the exception to the client, but we
                    // want to avoid silently dying so we need to do something.
                    writeData(out, ex, callback);
                    log.error("--ConversionException: message=" + ex.toString());
                }
            }
        }
        catch (Exception ex)
        {
            writeData(out, ex, callback);
        }
    }

    public String getCallback(HttpServletRequest request)
    {
        return request.getParameter("callback");
    }

    /**
     * Take an HttpServletRequest and create from it a Calls object.
     * @param request The input data
     * @return A Calls object that represents the data in the request
     */
    @SuppressWarnings("unchecked")
    public Calls convertToCalls(HttpServletRequest request)
    {
        InboundContext inboundContext = new InboundContext();
        String pathInfo = request.getPathInfo();
        String[] pathParts = pathInfo.split("/");

        if (pathParts.length < 4)
        {
            log.warn("pathInfo '" + pathInfo + "' contains " + pathParts.length + " parts. At least 4 are required.");
            throw new JsonpCallException("Bad JSON request. See logs for more details.");
        }

        if (pathParts.length > 4)
        {
            for (int i = 4; i < pathParts.length; i++)
            {
                String key = ProtocolConstants.INBOUND_CALLNUM_PREFIX + 0 +
                             ProtocolConstants.INBOUND_CALLNUM_SUFFIX +
                             ProtocolConstants.INBOUND_KEY_PARAM + (i - 4);
                inboundContext.createInboundVariable(0, key, "string", pathParts[i]);
            }
        }
        else
        {
            Map<String, String[]> requestParams = request.getParameterMap();
            int i = 0;
            while (true)
            {
                String[] values = requestParams.get("param" + i);
                if (values == null)
                {
                    break;
                }
                else
                {
                    String key = ProtocolConstants.INBOUND_CALLNUM_PREFIX + 0 +
                                 ProtocolConstants.INBOUND_CALLNUM_SUFFIX +
                                 ProtocolConstants.INBOUND_KEY_PARAM + i;
                    inboundContext.createInboundVariable(0, key, "string", values[0], true);
                    i++;
                }
            }
        }

        Call call = new Call(null, pathParts[2], pathParts[3]);

        // JSON does not support batching
        Calls calls = new Calls();
        calls.addCall(call);

        // Which method are we using?
        call.findMethod(moduleManager, converterManager, inboundContext, 0);
        MethodDeclaration method = call.getMethodDeclaration();

        // Check this method is accessible
        accessControl.assertGeneralExecutionIsPossible(call.getScriptName(), method);

        // We are now sure we have the set of input lined up. They may
        // cross-reference so we do the de-referencing all in one go.
        try
        {
            inboundContext.dereference();
        }
        catch (ConversionException ex)
        {
            log.warn("Dereferencing exception", ex);
            throw new JsonpCallException("Error dereferencing call. See logs for more details.");
        }

        // Convert all the parameters to the correct types
        Object[] params = new Object[method.getParameterTypes().length];
        for (int j = 0; j < method.getParameterTypes().length; j++)
        {
            Class<?> paramType = method.getParameterTypes()[j];
            InboundVariable param = inboundContext.getParameter(0, j);
            Property property = new ParameterProperty(method, j);

            try
            {
                params[j] = converterManager.convertInbound(paramType, param, property);
            }
            catch (ConversionException ex)
            {
                log.warn("Marshalling exception. Param " + j + ", ", ex);
                throw new JsonpCallException("Error marshalling parameters. See logs for more details.");
            }
        }

        call.setParameters(params);

        return calls;
    }

    /**
     * Create output for some data and write it to the given stream.
     */
    public void writeData(PrintWriter out, Object data, String callback)
    {
        try
        {
            ScriptBuffer buffer = new ScriptBuffer();
            buffer.appendData(data);

            String output = ScriptBufferUtil.createOutput(buffer, converterManager, true);
            if (data instanceof Exception)
            {
                output = "{\"error\":" + output + "}";
            }
            if (callback != null && !"".equals(callback.trim()))
            {
                output = callback + "(" + output + ")";
            }
            out.println(output);
        }
        catch (ConversionException ex)
        {
            log.warn("--ConversionException: class=" + ex.getConversionType().getName(), ex);

            ScriptBuffer buffer = new ScriptBuffer();
            buffer.appendData(ex);

            try
            {
                String output = ScriptBufferUtil.createOutput(buffer, converterManager, true);
                output = "{\"error\":" + output + "}";
                if (callback != null && !"".equals(callback.trim()))
                {
                    output = callback + "(" + output + ")";
                }
                out.println(output);
            }
            catch (ConversionException ex1)
            {
                log.error("--Nested ConversionException: Is there an exception handler registered? class=" + ex.getConversionType().getName(), ex);
            }
        }
    }

    /* (non-Javadoc)
     * @see org.directwebremoting.Marshaller#isConvertable(java.lang.Class)
     */
    public boolean isConvertable(Class<?> paramType)
    {
        return converterManager.isConvertable(paramType);
    }

    /**
     * Accessor for the ConverterManager that we configure
     * @param converterManager
     */
    public void setConverterManager(ConverterManager converterManager)
    {
        this.converterManager = converterManager;
    }

    /**
     * How we convert parameters
     */
    protected ConverterManager converterManager = null;

    /**
     * Accessor for the ModuleManager that we configure
     * @param moduleManager
     */
    public void setModuleManager(ModuleManager moduleManager)
    {
        this.moduleManager = moduleManager;
    }

    /**
     * How we create new beans
     */
    protected ModuleManager moduleManager = null;

    /**
     * Accessor for the security manager
     * @param accessControl The accessControl to set.
     */
    public void setAccessControl(AccessControl accessControl)
    {
        this.accessControl = accessControl;
    }

    /**
     * The security manager
     */
    protected AccessControl accessControl = null;

    /**
     * Are we allowing remote hosts to contact us using JSON?
     */
    public void setJsonpEnabled(boolean jsonpEnabled)
    {
        this.jsonpEnabled = jsonpEnabled;
    }

    /**
     * Are we allowing remote hosts to contact us using JSON?
     */
    protected boolean jsonpEnabled = false;

    /**
     * Setter for the remoter
     * @param remoter The new remoter
     */
    public void setRemoter(Remoter remoter)
    {
        this.remoter = remoter;
    }

    /**
     * The bean to execute remote requests and generate interfaces
     */
    protected Remoter remoter = null;

    /**
     * The log stream
     */
    private static final Log log = LogFactory.getLog(JsonpCallHandler.class);
}
TOP

Related Classes of org.directwebremoting.jsonp.JsonpCallHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.