From 88fdfd42cb7bc11d26238136d276c32595d657e3 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 15 Apr 2008 12:10:59 +0000 Subject: [PATCH] SWS-331 --- .../oxm/support/MarshallingView.java | 134 +++++++++++++++++ .../oxm/support/MarshallingViewTest.java | 135 ++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 oxm/src/main/java/org/springframework/oxm/support/MarshallingView.java create mode 100644 oxm/src/test/java/org/springframework/oxm/support/MarshallingViewTest.java diff --git a/oxm/src/main/java/org/springframework/oxm/support/MarshallingView.java b/oxm/src/main/java/org/springframework/oxm/support/MarshallingView.java new file mode 100644 index 00000000..17f6b777 --- /dev/null +++ b/oxm/src/main/java/org/springframework/oxm/support/MarshallingView.java @@ -0,0 +1,134 @@ +/* + * Copyright 2007 the original author or authors. + * + * 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. + */ + +package org.springframework.oxm.support; + +import java.io.ByteArrayOutputStream; +import java.util.Iterator; +import java.util.Map; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.transform.stream.StreamResult; + +import org.springframework.beans.BeansException; +import org.springframework.oxm.Marshaller; +import org.springframework.util.Assert; +import org.springframework.web.servlet.View; +import org.springframework.web.servlet.view.AbstractUrlBasedView; + +/** + * Spring-MVC {@link View} that allows for response context to be rendered as the result of marshalling by a {@link + * Marshaller}. + *

+ * The Object to be marshalled is supplied as a parameter in the model and then {@linkplain #locateToBeMarshalled(Map) + * detected} during response rendering. Users can either specify a specific entry in the model via the {@link + * #setModelKey(String) sourceKey} property or have Spring locate the Source object. + * + * @author Arjen Poutsma + * @since 1.5.1 + */ +public class MarshallingView extends AbstractUrlBasedView { + + /** Default content type. Overridable as bean property. */ + public static final String DEFAULT_CONTENT_TYPE = "application/xml"; + + private Marshaller marshaller; + + private String modelKey; + + /** + * Constructs a new MarshallingView with no {@link Marshaller} set. The marshaller must be set after + * construction by invoking {@link #setMarshaller(Marshaller)}. + */ + public MarshallingView() { + setContentType(DEFAULT_CONTENT_TYPE); + } + + /** Constructs a new MarshallingView with the given {@link Marshaller} set. */ + public MarshallingView(Marshaller marshaller) { + Assert.notNull(marshaller, "'marshaller' must not be null"); + setContentType(DEFAULT_CONTENT_TYPE); + this.marshaller = marshaller; + } + + /** Sets the {@link Marshaller} to be used by this view. */ + public void setMarshaller(Marshaller marshaller) { + Assert.notNull(marshaller, "'marshaller' must not be null"); + this.marshaller = marshaller; + } + + /** + * Set the name of the model key that represents the object to be marshalled. If not specified, the model map will + * be searched for a supported value type. + * + * @see Marshaller#supports(Class) + */ + public void setModelKey(String modelKey) { + this.modelKey = modelKey; + } + + protected void initApplicationContext() throws BeansException { + Assert.notNull(marshaller, "Property 'marshaller' is required"); + } + + protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) + throws Exception { + Object toBeMarshalled = locateToBeMarshalled(model); + if (toBeMarshalled == null) { + throw new ServletException("Unable to locate object to be marshalled in model: " + model); + } + ByteArrayOutputStream bos = new ByteArrayOutputStream(2048); + marshaller.marshal(toBeMarshalled, new StreamResult(bos)); + + response.setContentType(getContentType()); + response.setContentLength(bos.size()); + + ServletOutputStream out = response.getOutputStream(); + bos.writeTo(out); + out.flush(); + } + + /** + * Locates the object to be marshalled. The default implementation first attempts to look under the configured + * {@linkplain #setModelKey(String) model key}, if any, before attempting to locate an object of {@linkplain + * Marshaller#supports(Class) supported type}. + * + * @param model the model Map + * @return the Object to be marshalled (or null if none found) + * @throws ServletException if the model object specified by the {@linkplain #setModelKey(String) model key} is not + * supported by the marshaller + * @see #setModelKey(String) + */ + protected Object locateToBeMarshalled(Map model) throws ServletException { + if (this.modelKey != null) { + Object o = model.get(this.modelKey); + if (!this.marshaller.supports(o.getClass())) { + throw new ServletException("Model object [" + o + "] retrieved via key [" + modelKey + + "] is not supported by the Marshaller"); + } + return o; + } + for (Iterator iterator = model.values().iterator(); iterator.hasNext();) { + Object o = iterator.next(); + if (this.marshaller.supports(o.getClass())) { + return o; + } + } + return null; + } +} diff --git a/oxm/src/test/java/org/springframework/oxm/support/MarshallingViewTest.java b/oxm/src/test/java/org/springframework/oxm/support/MarshallingViewTest.java new file mode 100644 index 00000000..54d982e3 --- /dev/null +++ b/oxm/src/test/java/org/springframework/oxm/support/MarshallingViewTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2007 the original author or authors. + * + * 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. + */ + +package org.springframework.oxm.support; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletException; +import javax.xml.transform.stream.StreamResult; + +import junit.framework.Assert; +import junit.framework.TestCase; +import org.easymock.MockControl; + +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.oxm.Marshaller; + +public class MarshallingViewTest extends TestCase { + + private MarshallingView view; + + private MockControl control; + + private Marshaller marshallerMock; + + protected void setUp() throws Exception { + control = MockControl.createControl(Marshaller.class); + marshallerMock = (Marshaller) control.getMock(); + view = new MarshallingView(marshallerMock); + } + + public void testGetContentType() { + Assert.assertEquals("Invalid content type", "application/xml", view.getContentType()); + } + + public void testRenderModelKey() throws Exception { + Object toBeMarshalled = new Object(); + String modelKey = "key"; + view.setModelKey(modelKey); + Map model = new HashMap(); + model.put(modelKey, toBeMarshalled); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + control.expectAndReturn(marshallerMock.supports(Object.class), true); + marshallerMock.marshal(toBeMarshalled, new StreamResult(response.getOutputStream())); + control.setMatcher(MockControl.ALWAYS_MATCHER); + + control.replay(); + view.render(model, request, response); + Assert.assertEquals("Invalid content type", "application/xml", response.getContentType()); + Assert.assertEquals("Invalid content length", 0, response.getContentLength()); + control.verify(); + } + + public void testRenderModelKeyUnsupported() throws Exception { + Object toBeMarshalled = new Object(); + String modelKey = "key"; + view.setModelKey(modelKey); + Map model = new HashMap(); + model.put(modelKey, toBeMarshalled); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + control.expectAndReturn(marshallerMock.supports(Object.class), false); + + control.replay(); + try { + view.render(model, request, response); + fail("ServletException expected"); + } + catch (ServletException ex) { + // expected + } + control.verify(); + } + + public void testRenderNoModelKey() throws Exception { + Object toBeMarshalled = new Object(); + String modelKey = "key"; + Map model = new HashMap(); + model.put(modelKey, toBeMarshalled); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + control.expectAndReturn(marshallerMock.supports(Object.class), true); + marshallerMock.marshal(toBeMarshalled, new StreamResult(response.getOutputStream())); + control.setMatcher(MockControl.ALWAYS_MATCHER); + + control.replay(); + view.render(model, request, response); + Assert.assertEquals("Invalid content type", "application/xml", response.getContentType()); + Assert.assertEquals("Invalid content length", 0, response.getContentLength()); + control.verify(); + } + + public void testRenderUnsupportedModel() throws Exception { + Object toBeMarshalled = new Object(); + String modelKey = "key"; + Map model = new HashMap(); + model.put(modelKey, toBeMarshalled); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + control.expectAndReturn(marshallerMock.supports(Object.class), false); + + control.replay(); + try { + view.render(model, request, response); + fail("ServletException expected"); + } + catch (ServletException ex) { + // expected + } + control.verify(); + } +} \ No newline at end of file