/* * Created on Jul 7, 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.ByteArrayInputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletInputStream; import junit.framework.TestCase; import org.easymock.MockControl; import org.springframework.mock.web.DelegatingServletInputStream; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springmodules.remoting.xmlrpc.dom.DomXmlRpcRequestParser; import org.springmodules.remoting.xmlrpc.dom.DomXmlRpcResponseWriter; import org.springmodules.remoting.xmlrpc.support.XmlRpcElementFactory; import org.springmodules.remoting.xmlrpc.support.XmlRpcFault; import org.springmodules.remoting.xmlrpc.support.XmlRpcRequest; import org.springmodules.remoting.xmlrpc.support.XmlRpcResponse; import org.springmodules.remoting.xmlrpc.support.XmlRpcString; /** * <p> * Unit Tests for <code>{@link XmlRpcServiceRouter}</code>. * </p> * * @author Alex Ruiz * * @version $Revision$ $Date$ */ public class XmlRpcServiceRouterTests extends TestCase { /** * Mock implementation of the HttpServletRequest interface. */ private class CustomMockHttpServletRequest extends MockHttpServletRequest { /** * Version number of this class. * * @see java.io.Serializable */ private static final long serialVersionUID = 3924538574072022396L; ServletInputStream inputStream; public ServletInputStream getInputStream() { return this.inputStream; } } /** * Mock object that simulates a * <code>{@link javax.servlet.http.HttpServletRequest}</code> */ private CustomMockHttpServletRequest request; /** * Mock object that simulates a * <code>{@link javax.servlet.http.HttpServletResponse}</code>. */ private MockHttpServletResponse response; /** * Name of the service to export. */ private String serviceName; /** * Mock object that simulates a <code>{@link XmlRpcElementFactory}</code>. */ private XmlRpcElementFactory xmlRpcElementFactory; /** * Controls the behavior of <code>{@link #xmlRpcElementFactory}</code>. */ private MockControl xmlRpcElementFactoryControl; /** * Mock object that simulates a <code>{@link XmlRpcRequestParser}</code>. */ private XmlRpcRequestParser xmlRpcRequestParser; /** * Controls the behavior of <code>{@link #xmlRpcRequestParser}</code>. */ private MockControl xmlRpcRequestParserControl; /** * Mock object that simulates a <code>{@link XmlRpcResponseWriter}</code>. */ private XmlRpcResponseWriter xmlRpcResponseWriter; /** * Controls the behavior of <code>{@link #xmlRpcResponseWriter}</code>. */ private MockControl xmlRpcResponseWriterControl; /** * Mock object that simulates a <code>{@link XmlRpcServiceExporter}</code>. */ private XmlRpcServiceExporter xmlRpcServiceExporter; /** * Controls the behavior of <code>{@link #xmlRpcServiceExporter}</code>. */ private MockControl xmlRpcServiceExporterControl; /** * Primary object that is under test. */ private XmlRpcServiceRouter XmlRpcServiceRouter; /** * Constructor. * * @param name * the name of the test case to construct. */ public XmlRpcServiceRouterTests(String name) { super(name); } /** * Asserts that the XML-RPC response is sent back to the client (as the * content of the HTTP response). * * @param serializedXmlRpcResponse * the XML-RPC response serialized as an array of <code>byte</code>s. */ private void assertXmlRpcResponseIsReturnedToClient( byte[] serializedXmlRpcResponse) { assertEquals("<HTTP response content type>", "text/xml", this.response .getContentType()); assertEquals("<HTTP response content length>", serializedXmlRpcResponse.length, this.response.getContentLength()); // verify the HTTP response contains the XML-RPC response. byte[] responseContent = this.response.getContentAsByteArray(); assertTrue("<HTTP response content>. Expected: " + Arrays.toString(serializedXmlRpcResponse) + " but was: " + Arrays.toString(responseContent), Arrays.equals( serializedXmlRpcResponse, responseContent)); } /** * Sets the state of the mock controls to "replay". */ private void setMockControlStateToReplay() { this.xmlRpcElementFactoryControl.replay(); this.xmlRpcRequestParserControl.replay(); this.xmlRpcResponseWriterControl.replay(); this.xmlRpcServiceExporterControl.replay(); } /** * Sets up the test fixture. */ protected void setUp() throws Exception { super.setUp(); this.XmlRpcServiceRouter = new XmlRpcServiceRouter(); } /** * Sets up all the mock objects. */ private void setUpMockObjects() { this.request = new CustomMockHttpServletRequest(); byte[] content = { 4, 6, 7 }; ServletInputStream inputStream = new DelegatingServletInputStream( new ByteArrayInputStream(content)); this.request.inputStream = inputStream; this.response = new MockHttpServletResponse(); this.setUpXmlRpcElementFactory(); this.setUpXmlRpcRequestParser(); this.setUpXmlRpcResponseWriter(); this.setUpXmlRpcServiceExporterMap(); } /** * Sets up: * <ul> * <li>{@link #xmlRpcElementFactory}</li> * <li>{@link #xmlRpcElementFactoryControl}</li> * </ul> */ private void setUpXmlRpcElementFactory() { this.xmlRpcElementFactoryControl = MockControl .createControl(XmlRpcElementFactory.class); this.xmlRpcElementFactory = (XmlRpcElementFactory) this.xmlRpcElementFactoryControl .getMock(); this.XmlRpcServiceRouter.setXmlRpcElementFactory(this.xmlRpcElementFactory); } /** * Sets up: * <ul> * <li>{@link #xmlRpcRequestParser}</li> * <li>{@link #xmlRpcRequestParserControl}</li> * </ul> */ private void setUpXmlRpcRequestParser() { this.xmlRpcRequestParserControl = MockControl .createControl(XmlRpcRequestParser.class); this.xmlRpcRequestParser = (XmlRpcRequestParser) this.xmlRpcRequestParserControl .getMock(); this.XmlRpcServiceRouter.setRequestParser(this.xmlRpcRequestParser); } /** * Sets up: * <ul> * <li>{@link #xmlRpcResponseWriter}</li> * <li>{@link #xmlRpcResponseWriterControl}</li> * </ul> */ private void setUpXmlRpcResponseWriter() { this.xmlRpcResponseWriterControl = MockControl .createControl(XmlRpcResponseWriter.class); this.xmlRpcResponseWriter = (XmlRpcResponseWriter) this.xmlRpcResponseWriterControl .getMock(); this.XmlRpcServiceRouter.setResponseWriter(this.xmlRpcResponseWriter); } /** * Sets up: * <ul> * <li>{@link #xmlRpcServiceExporter}</li> * <li>{@link #xmlRpcServiceExporterControl}</li> * </ul> */ private void setUpXmlRpcServiceExporterMap() { this.xmlRpcServiceExporterControl = MockControl .createControl(XmlRpcServiceExporter.class); this.xmlRpcServiceExporter = (XmlRpcServiceExporter) this.xmlRpcServiceExporterControl .getMock(); // set up the exported service. this.serviceName = "service"; Map exportedServices = new HashMap(); exportedServices.put(this.serviceName, this.xmlRpcServiceExporter); this.XmlRpcServiceRouter.setExportedServices(exportedServices); } /** * Verifies that the method * <code>{@link XmlRpcServiceRouter#afterPropertiesSet()}</code> throws a * <code>IllegalArgumentException</code> if the map of exported services is * not empty but contains objects that are not instances of * <code>{@link XmlRpcServiceExporter}</code>. */ public void testAfterPropertiesSetWhenServiceMapIsNotEmptyAndDoesNotContainServiceExported() { Map serviceMap = new HashMap(); serviceMap.put("Luke", "Jedi Knight"); this.XmlRpcServiceRouter.setExportedServices(serviceMap); try { this.XmlRpcServiceRouter.afterPropertiesSet(); fail("An 'IllegalArgumentException' should have been thrown"); } catch (IllegalArgumentException exception) { // we are expecting this exception. } } /** * Asserts that the method * <code>{@link XmlRpcServiceRouter#afterPropertiesSet()}</code> creates a * new instance of <code>{@link DomXmlRpcRequestParser}</code> if the * XML-RPC request parser is <code>null</code>. */ public void testAfterPropertiesSetWithXmlRpcRequestParserEqualToNull() { this.setUpXmlRpcElementFactory(); this.setUpXmlRpcResponseWriter(); this.setUpXmlRpcServiceExporterMap(); assertNull("The XML-RPC request parser should be null", this.XmlRpcServiceRouter.getRequestParser()); this.XmlRpcServiceRouter.afterPropertiesSet(); XmlRpcRequestParser requestParser = this.XmlRpcServiceRouter .getRequestParser(); assertNotNull("The XML-RPC request parser should not be null", requestParser); assertEquals("<XML-RPC request parser>", DomXmlRpcRequestParser.class, requestParser.getClass()); } /** * Asserts that the method * <code>{@link XmlRpcServiceRouter#afterPropertiesSet()}</code> creates a * new instance of <code>{@link DomXmlRpcResponseWriter}</code> if the * XML-RPC response Writer is <code>null</code>. */ public void testAfterPropertiesSetWithXmlRpcResponseWriterEqualToNull() { this.setUpXmlRpcElementFactory(); this.setUpXmlRpcRequestParser(); this.setUpXmlRpcServiceExporterMap(); assertNull("The XML-RPC response writer should be null", this.XmlRpcServiceRouter.getResponseWriter()); this.XmlRpcServiceRouter.afterPropertiesSet(); XmlRpcResponseWriter responseWriter = this.XmlRpcServiceRouter .getResponseWriter(); assertNotNull("The XML-RPC response writer should not be null", responseWriter); Arrays.toString(new Object[0]); assertEquals("<XML-RPC response writer>", DomXmlRpcResponseWriter.class, responseWriter.getClass()); } /** * Verifies that the method * <code>{@link XmlRpcServiceRouter#afterPropertiesSet()}</code> throws a * <code>IllegalArgumentException</code> if the map of exported services is * empty. */ public void testAfterPropertiesSetWithEmptyServiceMap() { this.XmlRpcServiceRouter.setExportedServices(new HashMap()); try { this.XmlRpcServiceRouter.afterPropertiesSet(); fail("An 'IllegalArgumentException' should have been thrown"); } catch (IllegalArgumentException exception) { // we are expecting this exception. } } /** * Verifies that the method * <code>{@link XmlRpcServiceRouter#afterPropertiesSet()}</code> throws a * <code>IllegalArgumentException</code> if the map of exported services is * equal to <code>null</code>. */ public void testAfterPropertiesSetWithServiceMapEqualToNull() { this.XmlRpcServiceRouter.setExportedServices(null); try { this.XmlRpcServiceRouter.afterPropertiesSet(); fail("An 'IllegalArgumentException' should have been thrown"); } catch (IllegalArgumentException exception) { // we are expecting this exception. } } /** * Verifies that the method * <code>{@link XmlRpcServiceRouter#handleRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}</code> * returns a XML-RPC response containing the result of the execution of the * service specified in the XML-RPC request. */ public void testHandleRequest() throws Exception { this.setUpMockObjects(); XmlRpcRequest xmlRpcRequest = new XmlRpcRequest(); xmlRpcRequest.setServiceName(this.serviceName); // expectation: parse the XML-RPC request. this.xmlRpcRequestParser.parseRequest(this.request.getInputStream()); this.xmlRpcRequestParserControl.setReturnValue(xmlRpcRequest); // expectation: service exporter executes remote method. String result = "Luke"; this.xmlRpcServiceExporter.invoke(xmlRpcRequest); this.xmlRpcServiceExporterControl.setReturnValue(result); // expectation: create a XML-RPC element from the result. XmlRpcString parameter = new XmlRpcString(result); this.xmlRpcElementFactory.createXmlRpcElement(result); this.xmlRpcElementFactoryControl.setReturnValue(parameter); // expectation: serialize the XML-RPC response. byte[] serializedXmlRpcResponse = { 5, 7, 3 }; this.xmlRpcResponseWriter.writeResponse(new XmlRpcResponse(parameter)); this.xmlRpcResponseWriterControl.setReturnValue(serializedXmlRpcResponse); this.setMockControlStateToReplay(); // execute the method to test. ModelAndView modelAndView = this.XmlRpcServiceRouter.handleRequest( this.request, this.response); assertNull("<ModelAndView should be null>", modelAndView); this.assertXmlRpcResponseIsReturnedToClient(serializedXmlRpcResponse); this.verifyMockControlExpectations(); } /** * Verifies that the method * <code>{@link XmlRpcServiceRouter#handleRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}</code> * returns a XML-RPC response with a fault if the service invocation throws an * exception. */ public void testHandleRequestWhenServiceInvocationThrowsException() throws Exception { this.setUpMockObjects(); XmlRpcRequest xmlRpcRequest = new XmlRpcRequest(); xmlRpcRequest.setServiceName(this.serviceName); // expectation: parse the XML-RPC request. this.xmlRpcRequestParser.parseRequest(this.request.getInputStream()); this.xmlRpcRequestParserControl.setReturnValue(xmlRpcRequest); // expectation: service exporter executes remote method and throws an // exception. Exception expectedException = new Exception(); this.xmlRpcServiceExporter.invoke(xmlRpcRequest); this.xmlRpcServiceExporterControl.setThrowable(expectedException); // expectation: serialize the XML-RPC response. byte[] serializedXmlRpcResponse = { 5, 7, 3 }; XmlRpcException xmlRpcException = new XmlRpcInternalException( "Server error. Internal xml-rpc error"); XmlRpcFault xmlRpcFault = new XmlRpcFault(xmlRpcException.getCode(), xmlRpcException.getMessage()); XmlRpcResponse xmlRpcResponse = new XmlRpcResponse(xmlRpcFault); this.xmlRpcResponseWriter.writeResponse(xmlRpcResponse); this.xmlRpcResponseWriterControl.setReturnValue(serializedXmlRpcResponse); this.setMockControlStateToReplay(); // execute the method to test. ModelAndView modelAndView = this.XmlRpcServiceRouter.handleRequest( this.request, this.response); assertNull("<ModelAndView should be null>", modelAndView); this.assertXmlRpcResponseIsReturnedToClient(serializedXmlRpcResponse); this.verifyMockControlExpectations(); } /** * Verifies that the method * <code>{@link XmlRpcServiceRouter#handleRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}</code> * returns a XML-RPC response with a fault if the service specified in the * XML-RPC request does not exist. */ public void testHandleRequestWhenServiceIsNotFound() { this.setUpMockObjects(); String notExistingServiceName = "anotherService"; XmlRpcRequest xmlRpcRequest = new XmlRpcRequest(); xmlRpcRequest.setServiceName(notExistingServiceName); // expectation: parse the XML-RPC request. this.xmlRpcRequestParser.parseRequest(this.request.getInputStream()); this.xmlRpcRequestParserControl.setReturnValue(xmlRpcRequest); // expectation: serialize the XML-RPC response. byte[] serializedXmlRpcResponse = { 5, 7, 3 }; XmlRpcException xmlRpcException = new XmlRpcServiceNotFoundException( "The service '" + notExistingServiceName + "' was not found"); XmlRpcFault xmlRpcFault = new XmlRpcFault(xmlRpcException.getCode(), xmlRpcException.getMessage()); XmlRpcResponse xmlRpcResponse = new XmlRpcResponse(xmlRpcFault); this.xmlRpcResponseWriter.writeResponse(xmlRpcResponse); this.xmlRpcResponseWriterControl.setReturnValue(serializedXmlRpcResponse); this.setMockControlStateToReplay(); // execute the method to test. ModelAndView modelAndView = this.XmlRpcServiceRouter.handleRequest( this.request, this.response); assertNull("<ModelAndView should be null>", modelAndView); this.assertXmlRpcResponseIsReturnedToClient(serializedXmlRpcResponse); this.verifyMockControlExpectations(); } /** * Verifies that the expectations of the mock controls were met. */ private void verifyMockControlExpectations() { this.xmlRpcElementFactoryControl.verify(); this.xmlRpcRequestParserControl.verify(); this.xmlRpcResponseWriterControl.verify(); this.xmlRpcServiceExporterControl.verify(); } }