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