1 16 17 package org.springframework.web.servlet.view.xslt; 18 19 import java.io.BufferedOutputStream ; 20 import java.io.IOException ; 21 import java.net.URL ; 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.ServletException ; 28 import javax.servlet.http.HttpServletRequest ; 29 import javax.servlet.http.HttpServletResponse ; 30 import javax.xml.transform.ErrorListener ; 31 import javax.xml.transform.OutputKeys ; 32 import javax.xml.transform.Result ; 33 import javax.xml.transform.Source ; 34 import javax.xml.transform.Templates ; 35 import javax.xml.transform.Transformer ; 36 import javax.xml.transform.TransformerConfigurationException ; 37 import javax.xml.transform.TransformerException ; 38 import javax.xml.transform.TransformerFactory ; 39 import javax.xml.transform.URIResolver ; 40 import javax.xml.transform.dom.DOMSource ; 41 import javax.xml.transform.stream.StreamResult ; 42 import javax.xml.transform.stream.StreamSource ; 43 44 import org.w3c.dom.Node ; 45 46 import org.springframework.context.ApplicationContextException; 47 import org.springframework.core.io.Resource; 48 import org.springframework.util.xml.SimpleTransformErrorListener; 49 import org.springframework.web.servlet.view.AbstractView; 50 import org.springframework.web.util.NestedServletException; 51 52 82 public abstract class AbstractXsltView extends AbstractView { 83 84 87 public static final String DEFAULT_ROOT = "DocRoot"; 88 89 90 private Resource stylesheetLocation; 91 92 private String root = DEFAULT_ROOT; 93 94 private boolean useSingleModelNameAsRoot = true; 95 96 private URIResolver uriResolver; 97 98 private ErrorListener errorListener = new SimpleTransformErrorListener(logger); 99 100 private boolean indent = true; 101 102 private Properties outputProperties; 103 104 private boolean cache = true; 105 106 private TransformerFactory transformerFactory; 107 108 private Templates templates; 109 110 111 120 public void setStylesheetLocation(Resource stylesheetLocation) { 121 this.stylesheetLocation = stylesheetLocation; 122 if (this.transformerFactory != null) { 124 cacheTemplates(); 125 } 126 } 127 128 134 public void setRoot(String root) { 135 this.root = root; 136 } 137 138 150 public void setUseSingleModelNameAsRoot(boolean useSingleModelNameAsRoot) { 151 this.useSingleModelNameAsRoot = useSingleModelNameAsRoot; 152 } 153 154 160 public void setUriResolver(URIResolver uriResolver) { 161 this.uriResolver = uriResolver; 162 } 163 164 173 public void setErrorListener(ErrorListener errorListener) { 174 this.errorListener = errorListener; 175 } 176 177 184 public void setIndent(boolean indent) { 185 this.indent = indent; 186 } 187 188 194 public void setOutputProperties(Properties outputProperties) { 195 this.outputProperties = outputProperties; 196 } 197 198 201 public void setCache(boolean cache) { 202 this.cache = cache; 203 } 204 205 206 210 protected final void initApplicationContext() throws ApplicationContextException { 211 this.transformerFactory = TransformerFactory.newInstance(); 212 this.transformerFactory.setErrorListener(this.errorListener); 213 if (this.uriResolver != null) { 214 if (logger.isInfoEnabled()) { 215 logger.info("Using custom URIResolver [" + this.uriResolver + "] in XSLT view with name '" + 216 getBeanName() + "'"); 217 } 218 this.transformerFactory.setURIResolver(this.uriResolver); 219 } 220 if (logger.isDebugEnabled()) { 221 logger.debug("URL in view is " + this.stylesheetLocation); 222 } 223 cacheTemplates(); 224 } 225 226 private synchronized void cacheTemplates() throws ApplicationContextException { 227 if (this.stylesheetLocation != null) { 228 try { 229 this.templates = this.transformerFactory.newTemplates(getStylesheetSource(this.stylesheetLocation)); 230 if (logger.isDebugEnabled()) { 231 logger.debug("Loaded templates [" + this.templates + "] in XSLT view '" + getBeanName() + "'"); 232 } 233 } 234 catch (TransformerConfigurationException ex) { 235 throw new ApplicationContextException("Can't load stylesheet from " + this.stylesheetLocation + 236 " in XSLT view '" + getBeanName() + "'", ex); 237 } 238 } 239 } 240 241 247 protected Source getStylesheetSource(Resource stylesheetLocation) throws ApplicationContextException { 248 if (logger.isDebugEnabled()) { 249 logger.debug("Loading XSLT stylesheet from " + stylesheetLocation); 250 } 251 try { 252 URL url = stylesheetLocation.getURL(); 253 String urlPath = url.toString(); 254 String systemId = urlPath.substring(0, urlPath.lastIndexOf('/') + 1); 255 return new StreamSource (url.openStream(), systemId); 256 } 257 catch (IOException ex) { 258 throw new ApplicationContextException("Can't load XSLT stylesheet from " + stylesheetLocation, ex); 259 } 260 } 261 262 263 protected final void renderMergedOutputModel( 264 Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { 265 266 if (!this.cache) { 267 logger.warn("DEBUG SETTING: NOT THREADSAFE AND WILL IMPAIR PERFORMANCE: template will be refreshed"); 268 cacheTemplates(); 269 } 270 271 if (this.templates == null) { 272 if (this.transformerFactory == null) { 273 throw new ServletException ("XLST view is incorrectly configured. Templates AND TransformerFactory are null"); 274 } 275 276 logger.warn("XSLT view is not configured: will copy XML input"); 277 response.setContentType("text/xml; charset=ISO-8859-1"); 278 } 279 else { 280 response.setContentType(getContentType()); 282 } 283 284 Source source = null; 285 String docRoot = null; 286 287 Object singleModel = null; 289 290 if (this.useSingleModelNameAsRoot && model.size() == 1) { 291 docRoot = (String ) model.keySet().iterator().next(); 292 if (logger.isDebugEnabled()) { 293 logger.debug("Single model object received, key [" + docRoot + "] will be used as root tag"); 294 } 295 singleModel = model.get(docRoot); 296 } 297 298 if (singleModel instanceof Node || singleModel instanceof Source ) { 300 logger.debug("No need to domify: was passed an XML Node or Source"); 304 source = (singleModel instanceof Node ? new DOMSource ((Node ) singleModel) : (Source ) singleModel); 305 } 306 else { 307 source = createXsltSource(model, (docRoot != null ? docRoot : this.root), request, response); 309 } 310 311 doTransform(model, source, request, response); 312 } 313 314 330 protected Source createXsltSource( 331 Map model, String root, HttpServletRequest request, HttpServletResponse response) 332 throws Exception { 333 334 return null; 335 } 336 337 353 protected void doTransform( 354 Map model, Source source, HttpServletRequest request, HttpServletResponse response) 355 throws Exception { 356 357 Map parameters = getParameters(request); 358 Result result = (useWriter() ? 359 new StreamResult (response.getWriter()) : 360 new StreamResult (new BufferedOutputStream (response.getOutputStream()))); 361 String encoding = response.getCharacterEncoding(); 362 doTransform(source, parameters, result, encoding); 363 } 364 365 373 protected void doTransform(Source source, Map parameters, Result result, String encoding) 374 throws Exception { 375 376 try { 377 Transformer trans = (this.templates != null) ? 378 this.templates.newTransformer() : this.transformerFactory.newTransformer(); 381 if (this.uriResolver != null) { 383 trans.setURIResolver(this.uriResolver); 384 } 385 386 if (parameters != null) { 388 for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) { 389 Map.Entry entry = (Map.Entry ) it.next(); 390 trans.setParameter(entry.getKey().toString(), entry.getValue()); 391 } 392 if (logger.isDebugEnabled()) { 393 logger.debug("Added parameters [" + parameters + "] to transformer object"); 394 } 395 } 396 397 trans.setOutputProperty(OutputKeys.ENCODING, encoding); 399 if (this.indent) { 400 TransformerUtils.enableIndenting(trans); 401 } 402 403 if (this.outputProperties != null) { 405 Enumeration propsEnum = this.outputProperties.propertyNames(); 406 while (propsEnum.hasMoreElements()) { 407 String propName = (String ) propsEnum.nextElement(); 408 trans.setOutputProperty(propName, this.outputProperties.getProperty(propName)); 409 } 410 } 411 412 trans.transform(source, result); 414 if (logger.isDebugEnabled()) { 415 logger.debug("XSLT transformed with stylesheet [" + this.stylesheetLocation + "]"); 416 } 417 } 418 catch (TransformerConfigurationException ex) { 419 throw new NestedServletException("Couldn't create XSLT transformer for stylesheet [" + 420 this.stylesheetLocation + "] in XSLT view with name [" + getBeanName() + "]", ex); 421 } 422 catch (TransformerException ex) { 423 throw new NestedServletException("Couldn't perform transform with stylesheet [" + 424 this.stylesheetLocation + "] in XSLT view with name [" + getBeanName() + "]", ex); 425 } 426 } 427 428 439 protected Map getParameters(HttpServletRequest request) { 440 return getParameters(); 441 } 442 443 452 protected Map getParameters() { 453 return null; 454 } 455 456 465 protected boolean useWriter() { 466 return false; 467 } 468 469 } 470 | Popular Tags |