/* * Created on Jun 13, 2005 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * Copyright @2005 the original author or authors. */ package org.springmodules.remoting.xmlrpc; import java.io.InputStream; import java.io.OutputStream; import java.util.Iterator; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import org.springmodules.remoting.xmlrpc.dom.DomXmlRpcRequestParser; import org.springmodules.remoting.xmlrpc.dom.DomXmlRpcResponseWriter; import org.springmodules.remoting.xmlrpc.support.XmlRpcElement; import org.springmodules.remoting.xmlrpc.support.XmlRpcElementFactory; import org.springmodules.remoting.xmlrpc.support.XmlRpcElementFactoryImpl; import org.springmodules.remoting.xmlrpc.support.XmlRpcFault; import org.springmodules.remoting.xmlrpc.support.XmlRpcRequest; import org.springmodules.remoting.xmlrpc.support.XmlRpcResponse; import org.springmodules.util.Strings; /** * <p> * Spring MVC Controller that receives a XML-RPC request, sends it the service * specified in such request and writes the result of the execution to the HTTP * response. * </p> * * @author Alex Ruiz * * @version $Revision$ $Date$ */ public class XmlRpcServiceRouter implements InitializingBean, Controller { /** * Registry of services to export. Methods of this objects will be callable * over XML-RPC as "servicename.methodname". */ private Map exportedServices; protected final Log logger = LogFactory.getLog(getClass()); /** * <p> * Parses the XML-RPC request. * </p> * <p> * If not set, a default <code>{@link DomXmlRpcRequestParser}</code> will be * created. * </p> */ private XmlRpcRequestParser requestParser; /** * <p> * Writes the XML-RPC response. * </p> * <p> * If not set, a default <code>{@link DomXmlRpcResponseWriter}</code> will * be created. * </p> */ private XmlRpcResponseWriter responseWriter; /** * <p> * Creates implementations of <code>{@link XmlRpcElement}</code> from Java * objects. * </p> * <p> * If not set, a default <code>{@link XmlRpcElementFactoryImpl}</code> will * be created. * </p> */ private XmlRpcElementFactory xmlRpcElementFactory; /** * Constructor. */ public XmlRpcServiceRouter() { super(); } /** * @see InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() { // validate the services to export. if (exportedServices == null || exportedServices.isEmpty()) { throw new IllegalArgumentException( "This router should have at least one service to export"); } for (Iterator i = exportedServices.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); Object entryValue = entry.getValue(); if (!(entryValue instanceof XmlRpcServiceExporter)) { throw new IllegalArgumentException( "Services should be instances of 'XmlRpcServiceExporter'"); } } if (requestParser == null) { requestParser = new DomXmlRpcRequestParser(); } if (responseWriter == null) { responseWriter = new DomXmlRpcResponseWriter(); } if (xmlRpcElementFactory == null) { xmlRpcElementFactory = new XmlRpcElementFactoryImpl(); } } protected final XmlRpcRequestParser getRequestParser() { return requestParser; } protected final XmlRpcResponseWriter getResponseWriter() { return responseWriter; } protected final XmlRpcElementFactory getXmlRpcElementFactory() { return xmlRpcElementFactory; } /** * @see Controller#handleRequest(HttpServletRequest, HttpServletResponse) */ public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { Object result = null; XmlRpcException xmlRpcException = null; try { InputStream requestInputStream = request.getInputStream(); XmlRpcRequest xmlRpcRequest = this.requestParser .parseRequest(requestInputStream); String serviceName = xmlRpcRequest.getServiceName(); if (logger.isDebugEnabled()) { logger.debug("Remote service: " + serviceName); } if (!exportedServices.containsKey(serviceName)) { throw new XmlRpcServiceNotFoundException("The service " + Strings.quote(serviceName) + " was not found"); } XmlRpcServiceExporter serviceExporter = (XmlRpcServiceExporter) exportedServices .get(serviceName); result = serviceExporter.invoke(xmlRpcRequest); if (logger.isDebugEnabled()) { logger.debug("XML-RPC remote invocation result: " + result); } } catch (XmlRpcException exception) { xmlRpcException = exception; if (logger.isErrorEnabled()) { logger.error("XML-RPC Exception", exception); } } catch (Exception exception) { if (logger.isErrorEnabled()) { logger.error("Server Exception", exception); } xmlRpcException = new XmlRpcInternalException( "Server error. Internal xml-rpc error"); } XmlRpcResponse xmlRpcResponse = null; if (xmlRpcException == null) { XmlRpcElement parameter = xmlRpcElementFactory .createXmlRpcElement(result); xmlRpcResponse = new XmlRpcResponse(parameter); } else { XmlRpcFault xmlRpcFault = new XmlRpcFault(xmlRpcException.getCode(), xmlRpcException.getMessage()); xmlRpcResponse = new XmlRpcResponse(xmlRpcFault); } byte[] serializedResponse = responseWriter.writeResponse(xmlRpcResponse); response.setContentType("text/xml"); response.setContentLength(serializedResponse.length); try { OutputStream output = response.getOutputStream(); output.write(serializedResponse); output.flush(); } catch (Exception exception) { if (logger.isDebugEnabled()) { logger.debug( "Unable to write result to request. Server I/O Exception.", exception); } } return null; } public final void setExportedServices(Map newExportedServices) { exportedServices = newExportedServices; } public final void setRequestParser(XmlRpcRequestParser newRequestParser) { requestParser = newRequestParser; } public final void setResponseWriter(XmlRpcResponseWriter newResponseWriter) { responseWriter = newResponseWriter; } public final void setXmlRpcElementFactory( XmlRpcElementFactory newXmlRpcElementFactory) { xmlRpcElementFactory = newXmlRpcElementFactory; } }