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     */
019    package org.apache.wiki.xmlrpc;
020    
021    import java.io.IOException;
022    import java.io.OutputStream;
023    import java.io.OutputStreamWriter;
024    import java.io.PrintWriter;
025    import java.util.Vector;
026    
027    import javax.servlet.ServletConfig;
028    import javax.servlet.ServletException;
029    import javax.servlet.http.HttpServlet;
030    import javax.servlet.http.HttpServletRequest;
031    import javax.servlet.http.HttpServletResponse;
032    
033    import org.apache.log4j.Logger;
034    import org.apache.xmlrpc.*;
035    
036    import org.apache.wiki.WikiContext;
037    import org.apache.wiki.WikiEngine;
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     */
050    public 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    }