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