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}