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.xmlrpc.*;
035import org.apache.wiki.WikiContext;
036import org.apache.wiki.WikiEngine;
037import org.apache.wiki.ajax.WikiAjaxDispatcherServlet;
038
039/**
040 *  Handles all incoming servlet requests for XML-RPC calls.
041 *  <P>
042 *  Uses two initialization parameters:
043 *  <UL>
044 *  <LI><B>handler</B> : the class which is used to handle the RPC calls.
045 *  <LI><B>prefix</B> : The command prefix for that particular handler.
046 *  </UL>
047 *
048 *  @since 1.6.6
049 */
050public class RPCServlet extends HttpServlet
051{
052    private static final long serialVersionUID = 3976735878410416180L;
053
054    /** This is what is appended to each command, if the handler has
055        not been specified.  */
056    // FIXME: Should this be $default?
057    public static final String XMLRPC_PREFIX = "wiki";
058
059    private WikiEngine       m_engine;
060    private XmlRpcServer     m_xmlrpcServer = new XmlRpcServer();
061
062    static Logger log = Logger.getLogger( RPCServlet.class );
063
064    public void initHandler( String prefix, String handlerName )
065        throws ClassNotFoundException,
066               InstantiationException,
067               IllegalAccessException
068    {
069        /*
070        Class handlerClass = Class.forName( handlerName );
071        WikiRPCHandler rpchandler = (WikiRPCHandler) handlerClass.newInstance();
072        rpchandler.initialize( m_engine );
073        m_xmlrpcServer.addHandler( prefix, rpchandler );
074        */
075        Class handlerClass = Class.forName( handlerName );
076        m_xmlrpcServer.addHandler( prefix, new LocalHandler(handlerClass) );
077    }
078
079    /**
080     *  Initializes the servlet.
081     */
082    public void init( ServletConfig config )
083        throws ServletException
084    {
085        m_engine = WikiEngine.getInstance( config );
086
087        String handlerName = config.getInitParameter( "handler" );
088        String prefix      = config.getInitParameter( "prefix" );
089
090        if( handlerName == null ) handlerName = "org.apache.wiki.xmlrpc.RPCHandler";
091        if( prefix == null )      prefix = XMLRPC_PREFIX;
092
093        try
094        {
095            initHandler( prefix, handlerName );
096
097            //
098            // FIXME: The metaweblog API should be possible to turn off.
099            //
100            initHandler( "metaWeblog",
101                         "org.apache.wiki.xmlrpc.MetaWeblogHandler" );
102        }
103        catch( Exception e )
104        {
105            log.fatal("Unable to start RPC interface: ", e);
106            throw new ServletException( "No RPC interface", e );
107        }
108    }
109
110    /**
111     *  Handle HTTP POST.  This is an XML-RPC call, and we'll just forward
112     *  the query to an XmlRpcServer.
113     */
114    public void doPost( HttpServletRequest request, HttpServletResponse response )
115        throws ServletException
116    {
117        log.debug("Received POST to RPCServlet");
118
119        try
120        {
121            WikiContext ctx = m_engine.createContext( request, WikiContext.NONE );
122
123            XmlRpcContext xmlrpcContext = new WikiXmlRpcContext( m_xmlrpcServer.getHandlerMapping(),
124                                                                 ctx );
125
126            byte[] result = m_xmlrpcServer.execute( request.getInputStream(), xmlrpcContext );
127
128            //
129            //  I think it's safe to write the output as UTF-8:
130            //  The XML-RPC standard never creates other than USASCII
131            //  (which is UTF-8 compatible), and our special UTF-8
132            //  hack just creates UTF-8.  So in all cases our butt
133            //  should be covered.
134            //
135            response.setContentType( "text/xml; charset=utf-8" );
136            response.setContentLength( result.length );
137
138            OutputStream out = response.getOutputStream();
139            out.write( result );
140            out.flush();
141
142            // log.debug("Result = "+new String(result) );
143        }
144        catch( IOException e )
145        {
146            throw new ServletException("Failed to build RPC result", e);
147        }
148    }
149
150    /**
151     *  Handles HTTP GET.  However, we do not respond to GET requests,
152     *  other than to show an explanatory text.
153     */
154    public void doGet( HttpServletRequest request, HttpServletResponse response )
155        throws ServletException
156    {
157        log.debug("Received HTTP GET to RPCServlet");
158
159        try
160        {
161            String msg = "We do not support HTTP GET here.  Sorry.";
162            response.setContentType( "text/plain" );
163            response.setContentLength( msg.length() );
164
165            PrintWriter writer = new PrintWriter( new OutputStreamWriter( response.getOutputStream() ) );
166
167            writer.println( msg );
168            writer.flush();
169        }
170        catch( IOException e )
171        {
172            throw new ServletException("Failed to build RPC result", e);
173        }
174    }
175
176    private static class LocalHandler
177        implements ContextXmlRpcHandler
178    {
179        private Class m_clazz;
180
181        public LocalHandler( Class clazz )
182        {
183            m_clazz = clazz;
184        }
185
186        public Object execute(String method, Vector params, XmlRpcContext context) throws Exception
187        {
188            WikiRPCHandler rpchandler = (WikiRPCHandler) m_clazz.newInstance();
189            rpchandler.initialize( ((WikiXmlRpcContext)context).getWikiContext() );
190
191            Invoker invoker = new Invoker( rpchandler );
192
193            return invoker.execute( method, params );
194        }
195    }
196
197    private static class WikiXmlRpcContext
198        implements XmlRpcContext
199    {
200        private XmlRpcHandlerMapping m_mapping;
201        private WikiContext m_context;
202
203        public WikiXmlRpcContext( XmlRpcHandlerMapping map, WikiContext ctx )
204        {
205            m_mapping = map;
206            m_context = ctx;
207        }
208
209        public XmlRpcHandlerMapping getHandlerMapping()
210        {
211            return m_mapping;
212        }
213
214        public String getPassword()
215        {
216            // TODO Auto-generated method stub
217            return null;
218        }
219
220        public String getUserName()
221        {
222            // TODO Auto-generated method stub
223            return null;
224        }
225
226        public WikiContext getWikiContext()
227        {
228            return m_context;
229        }
230    }
231}