View Javadoc

1   /*
2    * Copyright 2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.oxm.support;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.util.Iterator;
21  import java.util.Map;
22  import javax.servlet.ServletException;
23  import javax.servlet.ServletOutputStream;
24  import javax.servlet.http.HttpServletRequest;
25  import javax.servlet.http.HttpServletResponse;
26  import javax.xml.transform.stream.StreamResult;
27  
28  import org.springframework.beans.BeansException;
29  import org.springframework.oxm.Marshaller;
30  import org.springframework.util.Assert;
31  import org.springframework.web.servlet.View;
32  import org.springframework.web.servlet.view.AbstractUrlBasedView;
33  
34  /**
35   * Spring-MVC {@link View} that allows for response context to be rendered as the result of marshalling by a {@link
36   * Marshaller}.
37   * <p/>
38   * The Object to be marshalled is supplied as a parameter in the model and then {@linkplain #locateToBeMarshalled(Map)
39   * detected} during response rendering. Users can either specify a specific entry in the model via the {@link
40   * #setModelKey(String) sourceKey} property or have Spring locate the Source object.
41   *
42   * @author Arjen Poutsma
43   * @since 1.5.1
44   */
45  public class MarshallingView extends AbstractUrlBasedView {
46  
47      /** Default content type. Overridable as bean property. */
48      public static final String DEFAULT_CONTENT_TYPE = "application/xml";
49  
50      private Marshaller marshaller;
51  
52      private String modelKey;
53  
54      /**
55       * Constructs a new <code>MarshallingView</code> with no {@link Marshaller} set. The marshaller must be set after
56       * construction by invoking {@link #setMarshaller(Marshaller)}.
57       */
58      public MarshallingView() {
59          setContentType(DEFAULT_CONTENT_TYPE);
60      }
61  
62      /** Constructs a new <code>MarshallingView</code> with the given {@link Marshaller} set. */
63      public MarshallingView(Marshaller marshaller) {
64          Assert.notNull(marshaller, "'marshaller' must not be null");
65          setContentType(DEFAULT_CONTENT_TYPE);
66          this.marshaller = marshaller;
67      }
68  
69      /** Sets the {@link Marshaller} to be used by this view. */
70      public void setMarshaller(Marshaller marshaller) {
71          Assert.notNull(marshaller, "'marshaller' must not be null");
72          this.marshaller = marshaller;
73      }
74  
75      /**
76       * Set the name of the model key that represents the object to be marshalled. If not specified, the model map will
77       * be searched for a supported value type.
78       *
79       * @see Marshaller#supports(Class)
80       */
81      public void setModelKey(String modelKey) {
82          this.modelKey = modelKey;
83      }
84  
85      protected void initApplicationContext() throws BeansException {
86          Assert.notNull(marshaller, "Property 'marshaller' is required");
87      }
88  
89      protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response)
90              throws Exception {
91          Object toBeMarshalled = locateToBeMarshalled(model);
92          if (toBeMarshalled == null) {
93              throw new ServletException("Unable to locate object to be marshalled in model: " + model);
94          }
95          ByteArrayOutputStream bos = new ByteArrayOutputStream(2048);
96          marshaller.marshal(toBeMarshalled, new StreamResult(bos));
97  
98          response.setContentType(getContentType());
99          response.setContentLength(bos.size());
100 
101         ServletOutputStream out = response.getOutputStream();
102         bos.writeTo(out);
103         out.flush();
104     }
105 
106     /**
107      * Locates the object to be marshalled. The default implementation first attempts to look under the configured
108      * {@linkplain #setModelKey(String) model key}, if any, before attempting to locate an object of {@linkplain
109      * Marshaller#supports(Class) supported type}.
110      *
111      * @param model the model Map
112      * @return the Object to be marshalled (or <code>null</code> if none found)
113      * @throws ServletException if the model object specified by the {@linkplain #setModelKey(String) model key} is not
114      *                          supported by the marshaller
115      * @see #setModelKey(String)
116      */
117     protected Object locateToBeMarshalled(Map model) throws ServletException {
118         if (this.modelKey != null) {
119             Object o = model.get(this.modelKey);
120             if (!this.marshaller.supports(o.getClass())) {
121                 throw new ServletException("Model object [" + o + "] retrieved via key [" + modelKey +
122                         "] is not supported by the Marshaller");
123             }
124             return o;
125         }
126         for (Iterator iterator = model.values().iterator(); iterator.hasNext();) {
127             Object o = iterator.next();
128             if (this.marshaller.supports(o.getClass())) {
129                 return o;
130             }
131         }
132         return null;
133     }
134 }