1 16 17 package org.springframework.web.servlet.view.xslt; 18 19 import java.io.IOException ; 20 import java.io.InputStream ; 21 import java.io.Reader ; 22 import java.util.Enumeration ; 23 import java.util.Iterator ; 24 import java.util.Map ; 25 import java.util.Properties ; 26 27 import javax.servlet.http.HttpServletRequest ; 28 import javax.servlet.http.HttpServletResponse ; 29 import javax.xml.transform.ErrorListener ; 30 import javax.xml.transform.OutputKeys ; 31 import javax.xml.transform.Result ; 32 import javax.xml.transform.Source ; 33 import javax.xml.transform.Templates ; 34 import javax.xml.transform.Transformer ; 35 import javax.xml.transform.TransformerConfigurationException ; 36 import javax.xml.transform.TransformerFactory ; 37 import javax.xml.transform.URIResolver ; 38 import javax.xml.transform.dom.DOMSource ; 39 import javax.xml.transform.stream.StreamResult ; 40 import javax.xml.transform.stream.StreamSource ; 41 42 import org.w3c.dom.Document ; 43 import org.w3c.dom.Node ; 44 45 import org.springframework.beans.BeansException; 46 import org.springframework.context.ApplicationContextException; 47 import org.springframework.core.io.Resource; 48 import org.springframework.util.Assert; 49 import org.springframework.util.CollectionUtils; 50 import org.springframework.util.ObjectUtils; 51 import org.springframework.util.xml.SimpleTransformErrorListener; 52 import org.springframework.web.servlet.view.AbstractUrlBasedView; 53 54 72 public class XsltView extends AbstractUrlBasedView { 73 74 private TransformerFactory transformerFactory = TransformerFactory.newInstance(); 75 76 private ErrorListener errorListener = new SimpleTransformErrorListener(logger); 77 78 private URIResolver uriResolver; 79 80 private boolean cacheTemplates = true; 81 82 private Templates cachedTemplates; 83 84 private String sourceKey; 85 86 private Properties outputProperties; 87 88 private boolean indent = true; 89 90 91 96 public void setCacheTemplates(boolean cacheTemplates) { 97 this.cacheTemplates = cacheTemplates; 98 } 99 100 105 public void setUriResolver(URIResolver uriResolver) { 106 this.uriResolver = uriResolver; 107 } 108 109 public void setErrorListener(ErrorListener errorListener) { 110 Assert.notNull(errorListener, "'errorListener' cannot be null."); 111 this.errorListener = errorListener; 112 } 113 114 public void setSourceKey(String sourceKey) { 115 this.sourceKey = sourceKey; 116 } 117 118 public void setOutputProperties(Properties outputProperties) { 119 this.outputProperties = outputProperties; 120 } 121 122 public void setIndent(boolean indent) { 123 this.indent = indent; 124 } 125 126 protected final TransformerFactory getTransformerFactory() { 127 return this.transformerFactory; 128 } 129 130 131 protected void initApplicationContext() throws BeansException { 132 this.getTransformerFactory().setErrorListener(this.errorListener); 133 134 if (this.uriResolver != null) { 135 if (logger.isInfoEnabled()) { 136 logger.info("Using custom URIResolver '" + this.uriResolver 137 + "' in XSLT view with URL '" + getUrl() + "'"); 138 } 139 this.getTransformerFactory().setURIResolver(this.uriResolver); 140 } 141 142 if (logger.isDebugEnabled()) { 143 logger.debug("URL in view is '" + getUrl() + "'"); 144 } 145 146 if (this.cacheTemplates) { 147 this.cachedTemplates = loadTemplates(); 148 } 149 } 150 151 protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { 152 Templates templates = this.cachedTemplates; 153 if (templates == null) { 154 logger.warn("DEBUG SETTING: WILL IMPAIR PERFORMANCE: template will be refreshed"); 155 templates = loadTemplates(); 156 } 157 158 Transformer transformer = createTransformer(templates); 159 configureTransformer(model, response, transformer); 160 configureResponse(model, response, transformer); 161 Source source = null; 162 try { 163 source = locateSource(model); 164 if(source == null) { 165 throw new IllegalArgumentException ("Unable to locate Source object in model."); 166 } 167 transformer.transform(source, createResult(response)); 168 } 169 finally { 170 if (source != null) { 171 closeSourceIfNecessary(source); 172 } 173 } 174 } 175 176 181 protected Result createResult(HttpServletResponse response) throws Exception { 182 return new StreamResult (response.getOutputStream()); 183 } 184 185 190 protected Source locateSource(Map model) throws Exception { 191 if (this.sourceKey != null) { 192 return convertSource(model.get(this.sourceKey)); 193 } 194 195 Object source = CollectionUtils.findValueOfType(model.values(), getSourceTypes()); 196 return (source != null ? convertSource(source) : null); 197 } 198 199 204 protected Class [] getSourceTypes() { 205 return new Class []{Source .class, Document .class, Node .class, InputStream .class, Reader .class, Resource.class}; 206 } 207 208 214 protected Source convertSource(Object source) throws Exception { 215 if (source instanceof Source ) { 216 return (Source ) source; 217 } 218 else if (source instanceof Document ) { 219 return new DOMSource (((Document ) source).getDocumentElement()); 220 } 221 else if (source instanceof Node ) { 222 return new DOMSource ((Node ) source); 223 } 224 else if (source instanceof InputStream ) { 225 return new StreamSource ((InputStream ) source); 226 } 227 else if (source instanceof Reader ) { 228 return new StreamSource ((Reader ) source); 229 } 230 else if (source instanceof Resource) { 231 return new StreamSource (((Resource) source).getInputStream()); 232 } 233 else { 234 throw new IllegalArgumentException ("Value '" + source + "' cannot be converted to Source."); 235 } 236 } 237 238 249 protected void configureTransformer(Map model, HttpServletResponse response, Transformer transformer) { 250 copyModelParameters(model, transformer); 251 copyOutputProperties(transformer); 252 configureIndentation(transformer); 253 } 254 255 262 protected final void configureIndentation(Transformer transformer) { 263 if (this.indent) { 264 TransformerUtils.enableIndenting(transformer); 265 } 266 else { 267 TransformerUtils.disableIndenting(transformer); 268 } 269 } 270 271 276 protected final void copyOutputProperties(Transformer transformer) { 277 if (this.outputProperties != null) { 278 Enumeration en = this.outputProperties.propertyNames(); 279 while (en.hasMoreElements()) { 280 String name = (String ) en.nextElement(); 281 transformer.setOutputProperty(name, this.outputProperties.getProperty(name)); 282 } 283 } 284 } 285 286 291 protected final void copyModelParameters(Map model, Transformer transformer) { 292 copyMapEntriesToTransformerParameters(model, transformer); 293 } 294 295 301 protected void configureResponse(Map model, HttpServletResponse response, Transformer transformer) { 302 response.setContentType(transformer.getOutputProperty(OutputKeys.MEDIA_TYPE)); 303 response.setCharacterEncoding(transformer.getOutputProperty(OutputKeys.ENCODING)); 304 } 305 306 309 private Templates loadTemplates() throws ApplicationContextException { 310 Source stylesheetSource = getStylesheetSource(); 311 try { 312 Templates templates = getTransformerFactory().newTemplates(stylesheetSource); 313 if (logger.isDebugEnabled()) { 314 logger.debug("Loading templates '" + templates + "'"); 315 } 316 return templates; 317 } 318 catch (TransformerConfigurationException ex) { 319 throw new ApplicationContextException("Can't load stylesheet from '" + getUrl() + "'", ex); 320 } 321 finally { 322 closeSourceIfNecessary(stylesheetSource); 323 } 324 } 325 326 331 protected Transformer createTransformer(Templates templates) throws TransformerConfigurationException { 332 Transformer transformer = templates.newTransformer(); 333 if (this.uriResolver != null) { 334 transformer.setURIResolver(this.uriResolver); 335 } 336 return transformer; 337 } 338 339 342 protected Source getStylesheetSource() { 343 String url = getUrl(); 344 if (logger.isDebugEnabled()) { 345 logger.debug("Loading XSLT stylesheet from '" + url + "'"); 346 } 347 try { 348 Resource stylesheetResource = getApplicationContext().getResource(url); 349 String systemId = url.substring(0, url.lastIndexOf('/') + 1); 350 return new StreamSource (stylesheetResource.getInputStream(), systemId); 351 } 352 catch (IOException ex) { 353 throw new ApplicationContextException("Can't load XSLT stylesheet from '" + url + "'", ex); 354 } 355 } 356 357 362 private void copyMapEntriesToTransformerParameters(Map map, Transformer transformer) { 363 for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { 364 Map.Entry entry = (Map.Entry ) iterator.next(); 365 transformer.setParameter(ObjectUtils.nullSafeToString(entry.getKey()), entry.getValue()); 366 } 367 } 368 369 373 private void closeSourceIfNecessary(Source source) { 374 if (source instanceof StreamSource ) { 375 StreamSource streamSource = (StreamSource ) source; 376 377 if (streamSource.getInputStream() != null) { 378 try { 379 streamSource.getInputStream().close(); 380 } 381 catch (IOException e) { 382 logger.warn("Unable to close InputStream '" + streamSource.getInputStream() + "'"); 383 } 384 } 385 else if (streamSource.getReader() != null) { 386 try { 387 streamSource.getReader().close(); 388 } 389 catch (IOException e) { 390 logger.warn("Unable to close Reader '" + streamSource.getReader() + "'"); 391 } 392 } 393 } 394 } 395 396 } 397 | Popular Tags |