/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* licenses this file to you 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 the
* following location:
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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.apereo.portal.rendering.xslt;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stax.StAXSource;
import org.apache.commons.logging.LogFactory;
import org.apereo.portal.rendering.PipelineEventReader;
import org.apereo.portal.rendering.PipelineEventReaderImpl;
import org.apereo.portal.rendering.StAXPipelineComponentWrapper;
import org.apereo.portal.utils.cache.CacheKey;
import org.apereo.portal.xml.ResourceLoaderURIResolver;
import org.apereo.portal.xml.StaxUtils;
import org.apereo.portal.xml.stream.XMLEventBufferReader;
import org.apereo.portal.xml.stream.XMLEventBufferWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.xml.FixedXMLEventStreamReader;
import org.springframework.util.xml.SimpleTransformErrorListener;
import org.xml.sax.ContentHandler;
import org.xml.sax.helpers.LocatorImpl;
/**
*/
public class XSLTComponent extends StAXPipelineComponentWrapper
implements BeanNameAware, ResourceLoaderAware {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
private final ErrorListener errorListener;
private ResourceLoaderURIResolver uriResolver;
private TransformerSource transformerSource;
private TransformerConfigurationSource xsltParameterSource;
private String beanName;
public XSLTComponent() {
this.errorListener = new SimpleTransformErrorListener(LogFactory.getLog(this.getClass()));
}
public void setXsltParameterSource(TransformerConfigurationSource xsltParameterSource) {
this.xsltParameterSource = xsltParameterSource;
}
public void setTransformerSource(TransformerSource transformerSource) {
this.transformerSource = transformerSource;
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.uriResolver = new ResourceLoaderURIResolver(resourceLoader);
}
/* (non-Javadoc)
* @see org.apereo.portal.rendering.StAXPipelineComponent#getXmlStreamReader(java.lang.Object, java.lang.Object)
*/
@Override
public PipelineEventReader<XMLEventReader, XMLEvent> getEventReader(
HttpServletRequest request, HttpServletResponse response) {
final PipelineEventReader<XMLEventReader, XMLEvent> pipelineEventReader =
this.wrappedComponent.getEventReader(request, response);
final Transformer transformer = this.transformerSource.getTransformer(request, response);
//Setup a URIResolver based on the current resource loader
transformer.setURIResolver(this.uriResolver);
//Configure the Transformer via injected class
if (this.xsltParameterSource != null) {
final Map<String, Object> transformerParameters =
this.xsltParameterSource.getParameters(request, response);
if (transformerParameters != null) {
this.logger.debug(
"{} - Setting Transformer Parameters: ",
this.beanName,
transformerParameters);
for (final Map.Entry<String, Object> transformerParametersEntry :
transformerParameters.entrySet()) {
final String name = transformerParametersEntry.getKey();
final Object value = transformerParametersEntry.getValue();
if (value != null) {
transformer.setParameter(name, value);
}
}
}
final Properties outputProperties =
this.xsltParameterSource.getOutputProperties(request, response);
if (outputProperties != null) {
this.logger.debug(
"{} - Setting Transformer Output Properties: ",
this.beanName,
outputProperties);
transformer.setOutputProperties(outputProperties);
}
}
//The event reader from the previous component in the pipeline
final XMLEventReader eventReader = pipelineEventReader.getEventReader();
//Wrap the event reader in a stream reader to avoid a JDK bug
final XMLStreamReader streamReader;
try {
streamReader = new FixedXMLEventStreamReader(eventReader);
} catch (XMLStreamException e) {
throw new RuntimeException("Failed to create XMLStreamReader from XMLEventReader", e);
}
final Source xmlReaderSource = new StAXSource(streamReader);
//Setup logging for the transform
transformer.setErrorListener(this.errorListener);
//Transform to a SAX ContentHandler to avoid JDK bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6775588
final XMLEventBufferWriter eventWriterBuffer = new XMLEventBufferWriter();
final ContentHandler contentHandler =
StaxUtils.createLexicalContentHandler(eventWriterBuffer);
contentHandler.setDocumentLocator(new LocatorImpl());
final SAXResult outputTarget = new SAXResult(contentHandler);
try {
this.logger.debug("{} - Begining XML Transformation", this.beanName);
transformer.transform(xmlReaderSource, outputTarget);
this.logger.debug("{} - XML Transformation complete", this.beanName);
} catch (TransformerException e) {
throw new RuntimeException("Failed to transform document", e);
}
final String mediaType = transformer.getOutputProperty(OutputKeys.MEDIA_TYPE);
final List<XMLEvent> eventBuffer = eventWriterBuffer.getEventBuffer();
final XMLEventReader outputEventReader =
new XMLEventBufferReader(eventBuffer.listIterator());
final Map<String, String> outputProperties = pipelineEventReader.getOutputProperties();
final PipelineEventReaderImpl<XMLEventReader, XMLEvent> pipelineEventReaderImpl =
new PipelineEventReaderImpl<XMLEventReader, XMLEvent>(
outputEventReader, outputProperties);
pipelineEventReaderImpl.setOutputProperty(OutputKeys.MEDIA_TYPE, mediaType);
return pipelineEventReaderImpl;
}
@Override
public CacheKey getCacheKey(HttpServletRequest request, HttpServletResponse response) {
final CacheKey parentCacheKey = this.wrappedComponent.getCacheKey(request, response);
final CacheKey transformerKey;
if (transformerSource != null) {
transformerKey = this.transformerSource.getCacheKey(request, response);
} else {
transformerKey = null;
}
final CacheKey transformerConfigurationKey;
if (this.xsltParameterSource != null) {
transformerConfigurationKey = this.xsltParameterSource.getCacheKey(request, response);
} else {
transformerConfigurationKey = null;
}
return CacheKey.build(
this.beanName, parentCacheKey, transformerKey, transformerConfigurationKey);
}
}