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.net.rpc.server;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.PrintWriter;
024import java.lang.reflect.InvocationTargetException;
025import java.lang.reflect.Method;
026import java.util.Enumeration;
027import java.util.HashMap;
028import java.util.Iterator;
029import java.util.Map;
030import java.util.WeakHashMap;
031
032import javax.servlet.ServletContext;
033import javax.servlet.ServletException;
034import javax.servlet.http.HttpServletRequest;
035import javax.servlet.http.HttpServletResponse;
036import javax.servlet.http.HttpUtils;
037import javax.xml.namespace.QName;
038import javax.xml.soap.MimeHeader;
039import javax.xml.soap.MimeHeaders;
040import javax.xml.soap.SOAPException;
041import javax.xml.soap.SOAPMessage;
042
043import lucee.commons.io.IOUtil;
044import lucee.commons.lang.ClassException;
045import lucee.commons.lang.ClassUtil;
046import lucee.commons.lang.ExceptionUtil;
047import lucee.runtime.Component;
048import lucee.runtime.exp.PageException;
049import lucee.runtime.exp.PageServletException;
050import lucee.runtime.net.http.ReqRspUtil;
051import lucee.runtime.net.rpc.AxisCaster;
052import lucee.runtime.net.rpc.TypeMappingUtil;
053import lucee.runtime.op.Caster;
054
055import org.apache.axis.AxisEngine;
056import org.apache.axis.AxisFault;
057import org.apache.axis.ConfigurationException;
058import org.apache.axis.Constants;
059import org.apache.axis.Handler;
060import org.apache.axis.Message;
061import org.apache.axis.MessageContext;
062import org.apache.axis.SimpleChain;
063import org.apache.axis.SimpleTargetedChain;
064import org.apache.axis.components.logger.LogFactory;
065import org.apache.axis.management.ServiceAdmin;
066import org.apache.axis.security.servlet.ServletSecurityProvider;
067import org.apache.axis.server.AxisServer;
068import org.apache.axis.transport.http.AxisHttpSession;
069import org.apache.axis.transport.http.FilterPrintWriter;
070import org.apache.axis.transport.http.HTTPConstants;
071import org.apache.axis.transport.http.ServletEndpointContextImpl;
072import org.apache.axis.utils.Messages;
073import org.apache.commons.logging.Log;
074import org.w3c.dom.Element;
075
076/**
077 * xdoclet tags are not active yet; keep web.xml in sync.
078 * To change the location of the services, change url-pattern in web.xml and
079 * set parameter axis.servicesPath in server-config.wsdd. For more information see
080 * <a href="http://ws.apache.org/axis/java/reference.html">Axis Reference Guide</a>.
081 */
082public final class RPCServer{
083
084        protected static Log log =LogFactory.getLog(RPCServer.class.getName());
085        private static Log tlog =LogFactory.getLog(Constants.TIME_LOG_CATEGORY);
086        private static Log exceptionLog =LogFactory.getLog(Constants.EXCEPTION_LOG_CATEGORY);
087
088        public static final String INIT_PROPERTY_TRANSPORT_NAME ="transport.name";
089        public static final String INIT_PROPERTY_USE_SECURITY ="use-servlet-security";
090        public static final String INIT_PROPERTY_ENABLE_LIST ="axis.enableListQuery";
091        public static final String INIT_PROPERTY_JWS_CLASS_DIR ="axis.jws.servletClassDir";
092        public static final String INIT_PROPERTY_DISABLE_SERVICES_LIST ="axis.disableServiceList";
093        public static final String INIT_PROPERTY_SERVICES_PATH ="axis.servicesPath";
094
095        private Handler transport;
096        private ServletSecurityProvider securityProvider = null;
097        private ServletContext context;
098        private String webInfPath;
099        private String homeDir;
100        private AxisServer axisServer;
101        //private org.apache.axis.encoding.TypeMapping typeMapping;
102        
103        private static boolean isDevelopment=false;
104        private static boolean isDebug = false;
105        private static Map servers=new WeakHashMap();
106
107
108        /**
109         * Initialization method.
110         * @throws AxisFault 
111         */
112        private RPCServer(ServletContext context) throws AxisFault {
113                this.context=context;
114                
115                initQueryStringHandlers();
116                ServiceAdmin.setEngine(this.getEngine(), context.getServerInfo());
117                
118                webInfPath = context.getRealPath("/WEB-INF");
119                homeDir = ReqRspUtil.getRootPath(context);
120                
121        }
122        
123
124
125
126        /**
127         * Process GET requests. This includes handoff of pseudo-SOAP requests
128         *
129         * @param request request in
130         * @param response request out
131         * @throws ServletException
132         */
133        public void doGet(HttpServletRequest request, HttpServletResponse response, Component component) throws ServletException {
134                PrintWriter writer = new FilterPrintWriter(response);
135                
136                try {
137                        if (!doGet(request, response, writer,component)) {
138                                response.setContentType("text/html; charset=utf-8");
139                                writer.println("<html><h1>Lucee Webservice</h1>");
140                                writer.println(Messages.getMessage("reachedServlet00"));
141                                writer.println("<p>" + Messages.getMessage("transportName00","<b>http</b>"));
142                                writer.println("</html>");
143                        }
144                } 
145                catch (Throwable e) {
146                        ExceptionUtil.rethrowIfNecessary(e);
147                        if(e instanceof InvocationTargetException)
148                                e= ((InvocationTargetException)e).getTargetException();
149                        if(e instanceof PageException)
150                                throw new PageServletException((PageException)e);
151                        throw new ServletException(e);
152                }
153        }
154
155        /**
156         * routine called whenever an axis fault is caught; where they
157         * are logged and any other business. The method may modify the fault
158         * in the process
159         * @param fault what went wrong.
160         */
161        private void processAxisFault(AxisFault fault) {
162                //log the fault
163                Element runtimeException = fault.lookupFaultDetail(
164                                Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
165                if (runtimeException != null) {
166                        exceptionLog.info(Messages.getMessage("axisFault00"), fault);
167                        //strip runtime details
168                        fault.removeFaultDetail(Constants.
169                                                                        QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
170                } else if (exceptionLog.isDebugEnabled()) {
171                        exceptionLog.debug(Messages.getMessage("axisFault00"), fault);
172                }
173                //dev systems only give fault dumps
174                //if (!isDevelopment()) {
175                        //strip out the stack trace
176                        fault.removeFaultDetail(Constants.QNAME_FAULTDETAIL_STACKTRACE);
177                //}
178        }
179
180        /**
181         * log any exception to our output log, at our chosen level
182         * @param e what went wrong
183         */
184        private void logException(Throwable e) {
185                exceptionLog.info(Messages.getMessage("exception00"), e);
186        }
187
188        /* *
189         * print a snippet of service info.
190         * @param service service
191         * @param writer output channel
192         * @param serviceName where to put stuff
193         * /
194        private void reportServiceInfo(HttpServletResponse response,
195                                                                         PrintWriter writer, SOAPService service,
196                                                                         String serviceName) {
197                response.setContentType("text/html; charset=utf-8");
198
199                writer.println("<h1>"
200                                           + service.getName()
201                                           + "</h1>");
202                writer.println(
203                                "<p>" +
204                                Messages.getMessage("axisService00") +
205                                "</p>");
206                writer.println(
207                                "<i>" +
208                                Messages.getMessage("perhaps00") +
209                                "</i>");
210        }*/
211
212
213
214
215
216        /**
217         * Process a POST to the servlet by handing it off to the Axis Engine.
218         * Here is where SOAP messages are received
219         * @param req posted request
220         * @param res respose
221         * @throws ServletException trouble
222         * @throws IOException different trouble
223         */
224        public void doPost(HttpServletRequest req, HttpServletResponse res, Component component) throws
225                        ServletException, IOException {
226                long t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
227                String soapAction = null;
228                MessageContext msgContext = null;
229                
230                Message rspMsg = null;
231                String contentType = null;
232                InputStream is=null;
233                try {
234                        AxisEngine engine = getEngine();
235
236                        if (engine == null) {
237                                // !!! should return a SOAP fault...
238                                ServletException se =
239                                                new ServletException(Messages.getMessage("noEngine00"));
240                                log.debug("No Engine!", se);
241                                throw se;
242                        }
243
244                        res.setBufferSize(1024 * 8); // provide performance boost.
245
246                        /** get message context w/ various properties set
247                         */
248                        msgContext = createMessageContext(engine, req, res,component);
249                        ComponentController.set(msgContext);
250                        
251                        // ? OK to move this to 'getMessageContext',
252                        // ? where it would also be picked up for 'doGet()' ?
253                        if (securityProvider != null) {
254                                if (isDebug) {
255                                        log.debug("securityProvider:" + securityProvider);
256                                }
257                                msgContext.setProperty(MessageContext.SECURITY_PROVIDER,
258                                                                           securityProvider);
259                        }
260
261                        is=req.getInputStream();
262                        Message requestMsg =
263                                        new Message(is,
264                                                                false,
265                                                                req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE),
266                                                                req.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION));
267                        // Transfer HTTP headers to MIME headers for request message.
268                        MimeHeaders requestMimeHeaders = requestMsg.getMimeHeaders();
269                        for (Enumeration e = req.getHeaderNames(); e.hasMoreElements(); ) {
270                                String headerName = (String) e.nextElement();
271                                for (Enumeration f = req.getHeaders(headerName);
272                                                                         f.hasMoreElements(); ) {
273                                        String headerValue = (String) f.nextElement();
274                                        requestMimeHeaders.addHeader(headerName, headerValue);
275                                }
276                        }
277
278                        if (isDebug) {
279                                log.debug("Request Message:" + requestMsg);
280
281                                /* Set the request(incoming) message field in the context */
282                                /**********************************************************/
283                        }
284                        msgContext.setRequestMessage(requestMsg);
285                        String url = HttpUtils.getRequestURL(req).toString().toLowerCase();
286                        msgContext.setProperty(MessageContext.TRANS_URL, url);
287                        // put character encoding of request to message context
288                        // in order to reuse it during the whole process.
289                         
290                        try {
291                                String reqEnc = (String) requestMsg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING);
292                                if (reqEnc!=null) msgContext.setProperty(SOAPMessage.CHARACTER_SET_ENCODING,reqEnc);
293                        }
294                        catch (SOAPException e1) {}
295
296                        try {
297                                /**
298                                 * Save the SOAPAction header in the MessageContext bag.
299                                 * This will be used to tell the Axis Engine which service
300                                 * is being invoked.  This will save us the trouble of
301                                 * having to parse the Request message - although we will
302                                 * need to double-check later on that the SOAPAction header
303                                 * does in fact match the URI in the body.
304                                 */
305                                // (is this last stmt true??? (I don't think so - Glen))
306                                /********************************************************/
307                                soapAction = getSoapAction(req);
308                                if (soapAction != null) {
309                                        msgContext.setUseSOAPAction(true);
310                                        msgContext.setSOAPActionURI(soapAction);
311                                }
312
313                                // Create a Session wrapper for the HTTP session.
314                                // These can/should be pooled at some point.
315                                // (Sam is Watching! :-)
316                                msgContext.setSession(new AxisHttpSession(req));
317
318                                if (tlog.isDebugEnabled()) {
319                                        t1 = System.currentTimeMillis();
320                                }
321                                /* Invoke the Axis engine... */
322                                /*****************************/
323                                if (isDebug) {
324                                        log.debug("Invoking Axis Engine.");
325                                        //here we run the message by the engine
326                                }
327                                //msgContext.setProperty("disablePrettyXML", "false");
328                                engine.invoke(msgContext);
329                                if (isDebug) {
330                                        log.debug("Return from Axis Engine.");
331                                }
332                                if (tlog.isDebugEnabled()) {
333                                        t2 = System.currentTimeMillis();
334                                }
335
336                                rspMsg = msgContext.getResponseMessage();
337                                
338                                
339                                
340                                // We used to throw exceptions on null response messages.
341                                // They are actually OK in certain situations (asynchronous
342                                // services), so fall through here and return an ACCEPTED
343                                // status code below.  Might want to install a configurable
344                                // error check for this later.
345                        }
346                        catch (AxisFault fault) {
347                                
348                                //log and sanitize
349                                processAxisFault(fault);
350                                configureResponseFromAxisFault(res, fault);
351                                rspMsg = msgContext.getResponseMessage();
352                                if (rspMsg == null) {
353                                        rspMsg = new Message(fault);
354                                        ((org.apache.axis.SOAPPart) rspMsg.getSOAPPart()).
355                                                        getMessage().setMessageContext(msgContext);
356                                }
357                        } catch (Throwable t) {
358                                ExceptionUtil.rethrowIfNecessary(t);
359                                if(t instanceof InvocationTargetException)
360                                        t=((InvocationTargetException)t).getTargetException();
361                                // Exception
362                                if(t instanceof Exception) {
363                                        Exception e=(Exception) t;
364                                        //other exceptions are internal trouble
365                                        rspMsg = msgContext.getResponseMessage();
366                                        res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
367                                        rspMsg = convertExceptionToAxisFault(e, rspMsg);
368                                        ((org.apache.axis.SOAPPart) rspMsg.getSOAPPart()).
369                                                        getMessage().setMessageContext(msgContext);
370                                        
371                                }
372                                // throwable
373                                else {
374                                        logException(t);
375                                        //other exceptions are internal trouble
376                                        rspMsg = msgContext.getResponseMessage();
377                                        res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
378                                        rspMsg = new Message(new AxisFault(t.toString(),t));
379                                        ((org.apache.axis.SOAPPart) rspMsg.getSOAPPart()).
380                                                        getMessage().setMessageContext(msgContext);
381                                }
382                        } 
383                } catch (AxisFault fault) {
384                        
385                        processAxisFault(fault);
386                        configureResponseFromAxisFault(res, fault);
387                        rspMsg = msgContext.getResponseMessage();
388                        if (rspMsg == null) {
389                                rspMsg = new Message(fault);
390                                ((org.apache.axis.SOAPPart) rspMsg.getSOAPPart()).
391                                                getMessage().setMessageContext(msgContext);
392                        }
393                }
394                finally {
395                        IOUtil.closeEL(is);
396                }
397
398                if (tlog.isDebugEnabled()) {
399                        t3 = System.currentTimeMillis();
400                }
401
402                // Send response back along the wire... 
403                if (rspMsg != null) {
404                        
405                        // Transfer MIME headers to HTTP headers for response message.
406                        MimeHeaders responseMimeHeaders = rspMsg.getMimeHeaders();
407                        for (Iterator i = responseMimeHeaders.getAllHeaders(); i.hasNext(); ) {
408                                MimeHeader responseMimeHeader = (MimeHeader) i.next();
409                                res.addHeader(responseMimeHeader.getName(), responseMimeHeader.getValue());
410                        }
411                        // synchronize the character encoding of request and response
412                        String responseEncoding = (String) msgContext.getProperty(SOAPMessage.CHARACTER_SET_ENCODING);
413                        if (responseEncoding != null) {
414                                try {
415                                        rspMsg.setProperty(SOAPMessage.CHARACTER_SET_ENCODING,responseEncoding);
416                                }
417                                catch (SOAPException e) {}
418                        }
419                        
420                        //determine content type from message response
421                        contentType = rspMsg.getContentType(msgContext.getSOAPConstants());
422                        if (isDebug) log.debug("Returned Content-Type:" + contentType);
423                        
424                        // write result to response stream
425                        try {
426                                res.setContentType(contentType);
427                                rspMsg.writeTo(res.getOutputStream());
428                        } 
429                        catch (SOAPException e) {
430                                logException(e);
431                        }
432
433                        if (!res.isCommitted()) res.flushBuffer(); // Force it right now.
434                }
435                else {
436                        // No content, so just indicate accepted
437                        res.setStatus(202);
438                }
439                
440                if (isDebug) {
441                        log.debug("Response sent.");
442                        log.debug("Exit: doPost()");
443                }
444                if (tlog.isDebugEnabled()) {
445                        t4 = System.currentTimeMillis();
446                        tlog.debug("axisServlet.doPost: " + soapAction +
447                                           " pre=" + (t1 - t0) +
448                                           " invoke=" + (t2 - t1) +
449                                           " post=" + (t3 - t2) +
450                                           " send=" + (t4 - t3) +
451                                           " " + msgContext.getTargetService() + "." +
452                                           ((msgContext.getOperation() == null) ?
453                                                "" : msgContext.getOperation().getName()));
454                }
455
456        }
457
458        /**
459         * Configure the servlet response status code and maybe other headers
460         * from the fault info.
461         * @param response response to configure
462         * @param fault what went wrong
463         */
464        private void configureResponseFromAxisFault(HttpServletResponse response,
465                                                                                                AxisFault fault) {
466                // then get the status code
467                // It's been suggested that a lack of SOAPAction
468                // should produce some other error code (in the 400s)...
469                int status = getHttpServletResponseStatus(fault);
470                if (status == HttpServletResponse.SC_UNAUTHORIZED) {
471                        response.setHeader("WWW-Authenticate", "Basic realm=\"AXIS\"");
472                }
473                response.setStatus(status);
474        }
475
476        /**
477         * turn any Exception into an AxisFault, log it, set the response
478         * status code according to what the specifications say and
479         * return a response message for posting. This will be the response
480         * message passed in if non-null; one generated from the fault otherwise.
481         *
482         * @param exception what went wrong
483         * @param responseMsg what response we have (if any)
484         * @return a response message to send to the user
485         */
486        private Message convertExceptionToAxisFault(Exception exception,
487                                                                                                Message responseMsg) {
488                logException(exception);
489                if (responseMsg == null) {
490                        AxisFault fault = AxisFault.makeFault(exception);
491                        processAxisFault(fault);
492                        responseMsg = new Message(fault);
493                }
494                return responseMsg;
495        }
496
497        /**
498         * Extract information from AxisFault and map it to a HTTP Status code.
499         *
500         * @param af Axis Fault
501         * @return HTTP Status code.
502         */
503        private int getHttpServletResponseStatus(AxisFault af) {
504                // subclasses... --Glen
505                return af.getFaultCode().getLocalPart().startsWith("Server.Unauth")
506                                ? HttpServletResponse.SC_UNAUTHORIZED
507                                : HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
508                // This will raise a 401 for both
509                // "Unauthenticated" & "Unauthorized"...
510        }
511
512        /**
513         * write a message to the response, set appropriate headers for content
514         * type..etc.
515         * @param res   response
516         * @param responseMsg message to write
517         * @throws AxisFault
518         * @throws IOException if the response stream can not be written to
519         */
520        private void sendResponseX(String contentType,
521                                                          HttpServletResponse res,
522                                                          Message responseMsg) throws AxisFault,
523                        IOException {
524                if (responseMsg == null) {
525                        res.setStatus(HttpServletResponse.SC_NO_CONTENT);
526                        if (isDebug) {
527                                log.debug("NO AXIS MESSAGE TO RETURN!");
528                        }
529                } else {
530                        if (isDebug) {
531                                log.debug("Returned Content-Type:" + contentType);
532                        }
533
534                        try {
535                                
536                                res.setContentType(contentType);
537                                responseMsg.writeTo(res.getOutputStream());
538                        } catch (SOAPException e) {
539                                logException(e);
540                        }
541                }
542
543                if (!res.isCommitted()) {
544                        res.flushBuffer(); // Force it right now.
545                }
546        }
547
548        /**
549         * Place the Request message in the MessagContext object - notice
550         * that we just leave it as a 'ServletRequest' object and let the
551         * Message processing routine convert it - we don't do it since we
552         * don't know how it's going to be used - perhaps it might not
553         * even need to be parsed.
554         * @return a message context
555         */
556        private MessageContext createMessageContext(AxisEngine engine, HttpServletRequest req, HttpServletResponse res, Component component) {
557                MessageContext msgContext = new MessageContext(engine);
558
559                String requestPath = getRequestPath(req);
560
561                if (isDebug) {
562                        log.debug("MessageContext:" + msgContext);
563                        log.debug("HEADER_CONTENT_TYPE:" +
564                                          req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE));
565                        log.debug("HEADER_CONTENT_LOCATION:" +
566                                          req.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION));
567                        log.debug("Constants.MC_HOME_DIR:" + String.valueOf(homeDir));
568                        log.debug("Constants.MC_RELATIVE_PATH:" + requestPath);
569                        log.debug("HTTPConstants.MC_HTTP_SERVLETLOCATION:" +String.valueOf(webInfPath));
570                        log.debug("HTTPConstants.MC_HTTP_SERVLETPATHINFO:" +req.getPathInfo());
571                        log.debug("HTTPConstants.HEADER_AUTHORIZATION:" +req.getHeader(HTTPConstants.HEADER_AUTHORIZATION));
572                        log.debug("Constants.MC_REMOTE_ADDR:" + req.getRemoteAddr());
573                        log.debug("configPath:" + String.valueOf(webInfPath));
574                }
575
576                /* Set the Transport */
577                /*********************/
578                msgContext.setTransportName("http");
579
580                /* Save some HTTP specific info in the bag in case someone needs it */
581                /********************************************************************/
582                //msgContext.setProperty(Constants.MC_JWS_CLASSDIR, jwsClassDir);
583                msgContext.setProperty(Constants.MC_HOME_DIR, homeDir);
584                msgContext.setProperty(Constants.MC_RELATIVE_PATH, requestPath);
585                msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLET, this);
586                msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST, req);
587                msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE, res);
588                msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETLOCATION,webInfPath);
589                msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETPATHINFO,req.getPathInfo());
590                msgContext.setProperty(HTTPConstants.HEADER_AUTHORIZATION,req.getHeader(HTTPConstants.HEADER_AUTHORIZATION));
591                msgContext.setProperty(lucee.runtime.net.rpc.server.Constants.COMPONENT, component);
592                msgContext.setProperty(Constants.MC_REMOTE_ADDR, req.getRemoteAddr());
593                
594                // Set up a javax.xml.rpc.server.ServletEndpointContext
595                ServletEndpointContextImpl sec = new ServletEndpointContextImpl();
596
597                msgContext.setProperty(Constants.MC_SERVLET_ENDPOINT_CONTEXT, sec);
598                /* Save the real path */
599                /**********************/
600                String relpath = context.getRealPath(requestPath);
601
602                if (relpath != null) {
603                        msgContext.setProperty(Constants.MC_REALPATH, relpath);
604                }
605
606                msgContext.setProperty(Constants.MC_CONFIGPATH, webInfPath);
607
608                return msgContext;
609        }
610
611        /**
612         * Extract the SOAPAction header.
613         * if SOAPAction is null then we'll we be forced to scan the body for it.
614         * if SOAPAction is "" then use the URL
615         * @param req incoming request
616         * @return the action
617         * @throws AxisFault
618         */
619        private String getSoapAction(HttpServletRequest req) throws AxisFault {
620                String soapAction = req.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
621                if (soapAction == null) {
622                        String contentType = req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
623                        if(contentType != null) {
624                                int index = contentType.indexOf("action");
625                                if(index != -1){
626                                        soapAction = contentType.substring(index + 7);
627                                }
628                        }
629                }
630
631                if (isDebug) {
632                        log.debug("HEADER_SOAP_ACTION:" + soapAction);
633
634                        /**
635                         * Technically, if we don't find this header, we should probably fault.
636                         * It's required in the SOAP HTTP binding.
637                         */
638                }
639                if (soapAction == null) {
640                        AxisFault af = new AxisFault("Client.NoSOAPAction",
641                                                                                 Messages.getMessage("noHeader00",
642                                        "SOAPAction"),
643                                                                                 null, null);
644
645                        exceptionLog.error(Messages.getMessage("genFault00"), af);
646
647                        throw af;
648                }
649                // the SOAP 1.1 spec & WS-I 1.0 says:
650                // soapaction   = "SOAPAction" ":" [ <"> URI-reference <"> ]
651                // some implementations leave off the quotes
652                // we strip them if they are present
653                if (soapAction.startsWith("\"") && soapAction.endsWith("\"")
654                        && soapAction.length() >= 2) {
655                        int end = soapAction.length() - 1;
656                        soapAction = soapAction.substring(1, end);
657                }
658
659                if (soapAction.length() == 0) {
660                        soapAction = req.getContextPath(); // Is this right?
661
662                }
663                return soapAction;
664        }
665
666
667        /**
668         * Initialize a Handler for the transport defined in the Axis server config.
669         * This includes optionally filling in query string handlers.
670         */
671
672        public void initQueryStringHandlers() {
673                        this.transport = new SimpleTargetedChain();
674                        this.transport.setOption("qs.list","org.apache.axis.transport.http.QSListHandler");
675                        this.transport.setOption("qs.method","org.apache.axis.transport.http.QSMethodHandler");
676                        this.transport.setOption("qs.wsdl","org.apache.axis.transport.http.QSWSDLHandler");
677                        
678        }
679        
680        private boolean doGet(HttpServletRequest request,HttpServletResponse response,PrintWriter writer,Component component) throws AxisFault, ClassException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException  {
681
682                String path = request.getServletPath();
683                String queryString = request.getQueryString();
684                
685                AxisEngine engine = getEngine();
686                
687                Iterator i = this.transport.getOptions().keySet().iterator();
688
689                if (queryString == null) {
690                        return false;
691                }
692
693                String servletURI = request.getContextPath() + path;
694                String reqURI = request.getRequestURI();
695                
696                // service name
697                String serviceName;
698                if (servletURI.length() + 1 < reqURI.length()) {
699                        serviceName = reqURI.substring(servletURI.length() + 1);
700                } else {
701                        serviceName = "";
702                } 
703                
704                while (i.hasNext()) {
705                        String queryHandler = (String) i.next();
706                        if (queryHandler.startsWith("qs.")) {
707                                // Only attempt to match the query string with transport
708                                // parameters prefixed with "qs:".
709
710                                String handlerName = queryHandler.substring
711                                                                         (queryHandler.indexOf(".") + 1).
712                                                                         toLowerCase();
713                                // Determine the name of the plugin to invoke by using all text
714                                // in the query string up to the first occurence of &, =, or the
715                                // whole string if neither is present.
716
717                                int length = 0;
718                                boolean firstParamFound = false;
719
720                                while (firstParamFound == false && length < queryString.length()) {
721                                        char ch = queryString.charAt(length++);
722
723                                        if (ch == '&' || ch == '=') {
724                                                firstParamFound = true;
725
726                                                --length;
727                                        }
728                                }
729
730                                if (length < queryString.length()) {
731                                        queryString = queryString.substring(0, length);
732                                }
733
734                                if (queryString.toLowerCase().equals(handlerName) == true) {
735                                        // Query string matches a defined query string handler name.
736
737                                        // If the defined class name for this query string handler is blank,
738                                        // just return (the handler is "turned off" in effect).
739
740                                        if (this.transport.getOption(queryHandler).equals("")) {
741                                                return false;
742                                        }
743
744                                                // Attempt to dynamically load the query string handler
745                                                // and its "invoke" method.
746
747                                                MessageContext msgContext = createMessageContext(engine,request, response,component);
748                                                Class plugin=ClassUtil.loadClass((String)this.transport.getOption(queryHandler));
749                                                Method pluginMethod = plugin.getDeclaredMethod("invoke", new Class[] {msgContext.getClass()});
750
751                                                msgContext.setProperty(MessageContext.TRANS_URL, HttpUtils.getRequestURL(request).toString().toLowerCase());
752                                                //msgContext.setProperty(MessageContext.TRANS_URL, "http://DefaultNamespace");
753                                                msgContext.setProperty(HTTPConstants.PLUGIN_SERVICE_NAME, serviceName);
754                                                msgContext.setProperty(HTTPConstants.PLUGIN_NAME,handlerName);
755                                                msgContext.setProperty(HTTPConstants.PLUGIN_IS_DEVELOPMENT,Caster.toBoolean(isDevelopment));
756                                                msgContext.setProperty(HTTPConstants.PLUGIN_ENABLE_LIST,Boolean.FALSE);
757                                                msgContext.setProperty(HTTPConstants.PLUGIN_ENGINE,engine);
758                                                msgContext.setProperty(HTTPConstants.PLUGIN_WRITER,writer);
759                                                msgContext.setProperty(HTTPConstants.PLUGIN_LOG, log);
760                                                msgContext.setProperty(HTTPConstants.PLUGIN_EXCEPTION_LOG,exceptionLog);
761                                                
762                                                // Invoke the plugin.
763                                                pluginMethod.invoke(ClassUtil.loadInstance(plugin),new Object[] {msgContext});
764                                                writer.close();
765
766                                                return true;
767                                        
768                                }
769                        }
770                }
771
772                return false;
773        }
774        
775
776        /**
777         * getRequestPath a returns request path for web service padded with
778         * request.getPathInfo for web services served from /services directory.
779         * This is a required to support serving .jws web services from /services
780         * URL. See AXIS-843 for more information.
781         *
782         * @param request HttpServletRequest
783         * @return String
784         */
785        private static String getRequestPath(HttpServletRequest request) {
786                return request.getServletPath() + ((request.getPathInfo() != null) ?
787                                                                                   request.getPathInfo() : "");
788        }
789        
790
791        /**
792         * get the engine for this Server from cache or context
793         * @return
794         * @throws AxisFault 
795         */
796        public AxisServer getEngine() throws AxisFault {
797                if (axisServer == null) {
798                        synchronized (context) {
799                                Map environment = new HashMap();
800                                environment.put(AxisEngine.ENV_SERVLET_CONTEXT, context);
801                                axisServer = AxisServer.getServer(environment);
802                                axisServer.setName("LuceeServer");
803                        }
804                        
805                        // add Component Handler
806                        try {
807                                SimpleChain sc=(SimpleChain) axisServer.getGlobalRequest();
808                                sc.addHandler(new ComponentHandler());  
809                        }
810                        catch (ConfigurationException e) {
811                                throw AxisFault.makeFault(e);
812                        }
813                        TypeMappingUtil.registerDefaults(axisServer.getTypeMappingRegistry());
814                        
815                }
816                return axisServer;
817        }
818        
819        public static RPCServer getInstance(int id, ServletContext servletContext) throws AxisFault {
820                RPCServer server=(RPCServer) servers.get(Caster.toString(id));
821                if(server==null){
822                        servers.put(Caster.toString(id), server=new RPCServer(servletContext));
823                }
824                return server;
825        }
826
827
828
829
830        public void registerTypeMapping(Class clazz) {
831                String fullname = clazz.getName();//,name,packages;
832                //registerTypeMapping(clazz, new QName(AxisCaster.getRequestNameSpace(),fullname));
833                registerTypeMapping(clazz, new QName(AxisCaster.getRequestDefaultNameSpace(),fullname));
834        }
835        
836        private void registerTypeMapping(Class clazz,QName qname) {
837                
838                org.apache.axis.encoding.TypeMapping tm = TypeMappingUtil.getServerTypeMapping(axisServer);
839                TypeMappingUtil.registerBeanTypeMapping(tm,clazz, qname);
840        }
841}