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