001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.engine;
020
021import java.io.ByteArrayOutputStream;
022import java.io.DataInputStream;
023import java.io.DataOutputStream;
024import java.io.IOException;
025import java.util.Iterator;
026
027import javax.servlet.ServletException;
028import javax.servlet.ServletOutputStream;
029import javax.servlet.http.HttpServlet;
030import javax.servlet.http.HttpServletRequest;
031import javax.servlet.http.HttpServletResponse;
032
033import lucee.commons.io.IOUtil;
034import lucee.runtime.exp.PageException;
035import lucee.runtime.net.amf.CFMLProxy;
036import lucee.runtime.net.amf.OpenAMFCaster;
037
038import org.openamf.AMFBody;
039import org.openamf.AMFError;
040import org.openamf.AMFMessage;
041import org.openamf.ServiceRequest;
042import org.openamf.io.AMFDeserializer;
043import org.openamf.io.AMFSerializer;
044
045
046/**
047 * AMF Engine
048 */
049public final class AMFEngine {
050    
051
052
053    /**
054     * Main entry point for the servlet
055     * @param servlet 
056     * @param req 
057     * @param rsp 
058     *
059     * @throws ServletException
060     * @throws IOException
061     */
062    public void service(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp) throws IOException {
063        
064        AMFMessage requestMessage = null;
065        AMFMessage responseMessage = null;
066        requestMessage = deserializeAMFMessage(req);
067        responseMessage = processMessage(servlet, req, rsp, requestMessage);
068        serializeAMFMessage(rsp, responseMessage);
069    }
070
071    private AMFMessage deserializeAMFMessage(HttpServletRequest req) throws IOException {
072        DataInputStream dis = null;
073        try {
074                dis = new DataInputStream(req.getInputStream());
075                AMFDeserializer deserializer = new AMFDeserializer(dis);
076                AMFMessage message = deserializer.getAMFMessage();
077                return message;
078        }
079        finally {
080                IOUtil.closeEL(dis);
081        }
082    }
083
084    private void serializeAMFMessage(HttpServletResponse resp, AMFMessage message) throws IOException {
085        ByteArrayOutputStream baos = new ByteArrayOutputStream();
086        DataOutputStream dos = new DataOutputStream(baos);
087        AMFSerializer serializer = new AMFSerializer(dos);
088        serializer.serializeMessage(message);
089        resp.setContentType("application/x-amf");
090        resp.setContentLength(baos.size());
091        ServletOutputStream sos = resp.getOutputStream(); 
092        baos.writeTo(sos);
093        sos.flush();
094    }
095
096    /**
097     * Iterates through the request message's bodies, invokes each body and
098     * then, builds a message to send as the results
099     * @param req 
100     * @param rsp 
101     * @param message 
102     * @return AMFMessage
103     * @throws IOException 
104     * @throws ServletException 
105     */
106    private AMFMessage processMessage(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp, AMFMessage message)  {
107        AMFMessage responseMessage = new AMFMessage();
108        for (Iterator bodies = message.getBodies(); bodies.hasNext();) {
109            AMFBody requestBody = (AMFBody) bodies.next();
110            // invoke
111            Object serviceResult = invokeBody(servlet,req, rsp, requestBody);
112            String target = getTarget(requestBody, serviceResult);
113            AMFBody responseBody = new AMFBody(target, "null", serviceResult);
114            responseMessage.addBody(responseBody);
115        }
116        return responseMessage;
117    }
118
119    
120    private Object invokeBody(HttpServlet servlet, HttpServletRequest req, HttpServletResponse rsp, AMFBody requestBody) { 
121        try {
122                ServiceRequest request = new ServiceRequest(requestBody);
123                rsp.getOutputStream();// MUST muss das sein?
124               
125                return new CFMLProxy().invokeBody(OpenAMFCaster.getInstance(),null,servlet.getServletContext(),servlet.getServletConfig(), req, rsp, request.getServiceName(), request.getServiceMethodName(), request.getParameters());
126                } 
127        catch (Exception e) {
128                e.printStackTrace();
129            rsp.setStatus(200);
130            AMFError error=new AMFError();
131            e.setStackTrace(e.getStackTrace());
132            error.setDescription(e.getMessage());
133                        
134                        if(e instanceof PageException){
135                                PageException pe = (PageException)e;
136                    error.setCode(pe.getErrorCode());
137                    error.setCode(pe.getErrorCode());
138                    error.setDetails(pe.getDetail());
139                        }
140                        
141                        return error;
142                } 
143    }
144
145    private String getTarget(AMFBody requestBody, Object serviceResult) {
146        String target = "/onResult";
147        if (serviceResult instanceof AMFError) {
148            target = "/onStatus";
149        }
150        return requestBody.getResponse() + target;
151    }
152}