001/*
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.
018 */
019package org.apache.wiki.xmlrpc;
020
021import java.io.IOException;
022import java.io.OutputStream;
023import java.io.OutputStreamWriter;
024import java.io.PrintWriter;
025import java.util.Vector;
026
027import javax.servlet.ServletConfig;
028import javax.servlet.ServletException;
029import javax.servlet.http.HttpServlet;
030import javax.servlet.http.HttpServletRequest;
031import javax.servlet.http.HttpServletResponse;
032
033import org.apache.log4j.Logger;
034import org.apache.wiki.WikiContext;
035import org.apache.wiki.WikiEngine;
036import org.apache.xmlrpc.ContextXmlRpcHandler;
037import org.apache.xmlrpc.Invoker;
038import org.apache.xmlrpc.XmlRpcContext;
039import org.apache.xmlrpc.XmlRpcHandlerMapping;
040import org.apache.xmlrpc.XmlRpcServer;
041
042/**
043 *  Handles all incoming servlet requests for XML-RPC calls.
044 *  <P>
045 *  Uses two initialization parameters:
046 *  <UL>
047 *  <LI><B>handler</B> : the class which is used to handle the RPC calls.
048 *  <LI><B>prefix</B> : The command prefix for that particular handler.
049 *  </UL>
050 *
051 *  @since 1.6.6
052 */
053public class RPCServlet extends HttpServlet
054{
055    private static final long serialVersionUID = 3976735878410416180L;
056
057    /** This is what is appended to each command, if the handler has
058        not been specified.  */
059    // FIXME: Should this be $default?
060    public static final String XMLRPC_PREFIX = "wiki";
061
062    private WikiEngine       m_engine;
063    private XmlRpcServer     m_xmlrpcServer = new XmlRpcServer();
064
065    static Logger log = Logger.getLogger( RPCServlet.class );
066
067    public void initHandler( String prefix, String handlerName )
068        throws ClassNotFoundException,
069               InstantiationException,
070               IllegalAccessException
071    {
072        /*
073        Class handlerClass = Class.forName( handlerName );
074        WikiRPCHandler rpchandler = (WikiRPCHandler) handlerClass.newInstance();
075        rpchandler.initialize( m_engine );
076        m_xmlrpcServer.addHandler( prefix, rpchandler );
077        */
078        Class< ? > handlerClass = Class.forName( handlerName );
079        m_xmlrpcServer.addHandler( prefix, new LocalHandler(handlerClass) );
080    }
081
082    /**
083     *  Initializes the servlet.
084     */
085    public void init( ServletConfig config )
086        throws ServletException
087    {
088        m_engine = WikiEngine.getInstance( config );
089
090        String handlerName = config.getInitParameter( "handler" );
091        String prefix      = config.getInitParameter( "prefix" );
092
093        if( handlerName == null ) handlerName = "org.apache.wiki.xmlrpc.RPCHandler";
094        if( prefix == null )      prefix = XMLRPC_PREFIX;
095
096        try
097        {
098            initHandler( prefix, handlerName );
099
100            //
101            // FIXME: The metaweblog API should be possible to turn off.
102            //
103            initHandler( "metaWeblog",
104                         "org.apache.wiki.xmlrpc.MetaWeblogHandler" );
105        }
106        catch( Exception e )
107        {
108            log.fatal("Unable to start RPC interface: ", e);
109            throw new ServletException( "No RPC interface", e );
110        }
111    }
112
113    /**
114     *  Handle HTTP POST.  This is an XML-RPC call, and we'll just forward
115     *  the query to an XmlRpcServer.
116     */
117    public void doPost( HttpServletRequest request, HttpServletResponse response )
118        throws ServletException
119    {
120        log.debug("Received POST to RPCServlet");
121
122        try
123        {
124            WikiContext ctx = m_engine.createContext( request, WikiContext.NONE );
125
126            XmlRpcContext xmlrpcContext = new WikiXmlRpcContext( m_xmlrpcServer.getHandlerMapping(),
127                                                                 ctx );
128
129            byte[] result = m_xmlrpcServer.execute( request.getInputStream(), xmlrpcContext );
130
131            //
132            //  I think it's safe to write the output as UTF-8:
133            //  The XML-RPC standard never creates other than USASCII
134            //  (which is UTF-8 compatible), and our special UTF-8
135            //  hack just creates UTF-8.  So in all cases our butt
136            //  should be covered.
137            //
138            response.setContentType( "text/xml; charset=utf-8" );
139            response.setContentLength( result.length );
140
141            OutputStream out = response.getOutputStream();
142            out.write( result );
143            out.flush();
144
145            // log.debug("Result = "+new String(result) );
146        }
147        catch( IOException e )
148        {
149            throw new ServletException("Failed to build RPC result", e);
150        }
151    }
152
153    /**
154     *  Handles HTTP GET.  However, we do not respond to GET requests,
155     *  other than to show an explanatory text.
156     */
157    public void doGet( HttpServletRequest request, HttpServletResponse response )
158        throws ServletException
159    {
160        log.debug("Received HTTP GET to RPCServlet");
161
162        try
163        {
164            String msg = "We do not support HTTP GET here.  Sorry.";
165            response.setContentType( "text/plain" );
166            response.setContentLength( msg.length() );
167
168            PrintWriter writer = new PrintWriter( new OutputStreamWriter( response.getOutputStream() ) );
169
170            writer.println( msg );
171            writer.flush();
172        }
173        catch( IOException e )
174        {
175            throw new ServletException("Failed to build RPC result", e);
176        }
177    }
178
179    private static class LocalHandler
180        implements ContextXmlRpcHandler
181    {
182        private Class< ? > m_clazz;
183
184        public LocalHandler( Class< ? > clazz )
185        {
186            m_clazz = clazz;
187        }
188
189        public Object execute(String method, Vector params, XmlRpcContext context) throws Exception
190        {
191            WikiRPCHandler rpchandler = (WikiRPCHandler) m_clazz.newInstance();
192            rpchandler.initialize( ((WikiXmlRpcContext)context).getWikiContext() );
193
194            Invoker invoker = new Invoker( rpchandler );
195
196            return invoker.execute( method, params );
197        }
198    }
199
200    private static class WikiXmlRpcContext
201        implements XmlRpcContext
202    {
203        private XmlRpcHandlerMapping m_mapping;
204        private WikiContext m_context;
205
206        public WikiXmlRpcContext( XmlRpcHandlerMapping map, WikiContext ctx )
207        {
208            m_mapping = map;
209            m_context = ctx;
210        }
211
212        public XmlRpcHandlerMapping getHandlerMapping()
213        {
214            return m_mapping;
215        }
216
217        public String getPassword()
218        {
219            // TODO Auto-generated method stub
220            return null;
221        }
222
223        public String getUserName()
224        {
225            // TODO Auto-generated method stub
226            return null;
227        }
228
229        public WikiContext getWikiContext()
230        {
231            return m_context;
232        }
233    }
234}