001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.component.xslt;
018
019 import java.io.File;
020 import java.io.FileNotFoundException;
021 import java.util.Map;
022 import javax.xml.transform.TransformerConfigurationException;
023 import javax.xml.transform.TransformerFactory;
024 import javax.xml.transform.URIResolver;
025
026 import org.apache.camel.Endpoint;
027 import org.apache.camel.Exchange;
028 import org.apache.camel.builder.xml.ResultHandlerFactory;
029 import org.apache.camel.builder.xml.XsltBuilder;
030 import org.apache.camel.builder.xml.XsltUriResolver;
031 import org.apache.camel.component.ResourceBasedComponent;
032 import org.apache.camel.converter.jaxp.XmlConverter;
033 import org.apache.camel.impl.ProcessorEndpoint;
034 import org.apache.camel.util.ObjectHelper;
035 import org.springframework.core.io.Resource;
036 import org.springframework.core.io.UrlResource;
037
038 /**
039 * An <a href="http://camel.apache.org/xslt.html">XSLT Component</a>
040 * for performing XSLT transforms of messages
041 *
042 * @version
043 */
044 public class XsltComponent extends ResourceBasedComponent {
045 private XmlConverter xmlConverter;
046 private URIResolver uriResolver;
047 private boolean contentCache = true;
048
049 public XmlConverter getXmlConverter() {
050 return xmlConverter;
051 }
052
053 public void setXmlConverter(XmlConverter xmlConverter) {
054 this.xmlConverter = xmlConverter;
055 }
056
057 public URIResolver getUriResolver() {
058 return uriResolver;
059 }
060
061 public void setUriResolver(URIResolver uriResolver) {
062 this.uriResolver = uriResolver;
063 }
064
065 public boolean isContentCache() {
066 return contentCache;
067 }
068
069 public void setContentCache(boolean contentCache) {
070 this.contentCache = contentCache;
071 }
072
073 protected Endpoint createEndpoint(String uri, final String remaining, Map<String, Object> parameters) throws Exception {
074 final Resource resource = resolveMandatoryResource(remaining);
075 log.debug("{} using schema resource: {}", this, resource);
076 final XsltBuilder xslt = getCamelContext().getInjector().newInstance(XsltBuilder.class);
077
078 // lets allow the converter to be configured
079 XmlConverter converter = resolveAndRemoveReferenceParameter(parameters, "converter", XmlConverter.class);
080 if (converter == null) {
081 converter = getXmlConverter();
082 }
083 if (converter != null) {
084 xslt.setConverter(converter);
085 }
086
087 String transformerFactoryClassName = getAndRemoveParameter(parameters, "transformerFactoryClass", String.class);
088 TransformerFactory factory = null;
089 if (transformerFactoryClassName != null) {
090 // provide the class loader of this component to work in OSGi environments
091 Class<?> factoryClass = getCamelContext().getClassResolver().resolveClass(transformerFactoryClassName, XsltComponent.class.getClassLoader());
092 if (factoryClass != null) {
093 factory = (TransformerFactory) getCamelContext().getInjector().newInstance(factoryClass);
094 } else {
095 log.warn("Cannot find the TransformerFactoryClass with the class name: " + transformerFactoryClassName);
096 }
097 }
098
099 if (parameters.get("transformerFactory") != null) {
100 factory = resolveAndRemoveReferenceParameter(parameters, "transformerFactory", TransformerFactory.class);
101 }
102
103 if (factory != null) {
104 xslt.getConverter().setTransformerFactory(factory);
105 }
106
107 // lookup custom resolver to use
108 URIResolver resolver = resolveAndRemoveReferenceParameter(parameters, "uriResolver", URIResolver.class);
109 if (resolver == null) {
110 // not in endpoint then use component specific resolver
111 resolver = getUriResolver();
112 }
113 if (resolver == null) {
114 // fallback to use a Camel specific resolver
115 resolver = new XsltUriResolver(getCamelContext().getClassResolver(), remaining);
116 }
117 // set resolver before input stream as resolver is used when loading the input stream
118 xslt.setUriResolver(resolver);
119
120 ResultHandlerFactory resultHandlerFactory = resolveAndRemoveReferenceParameter(parameters, "resultHandlerFactory", ResultHandlerFactory.class);
121 if (resultHandlerFactory != null) {
122 xslt.setResultHandlerFactory(resultHandlerFactory);
123 }
124
125 Boolean failOnNullBody = getAndRemoveParameter(parameters, "failOnNullBody", Boolean.class);
126 if (failOnNullBody != null) {
127 xslt.setFailOnNullBody(failOnNullBody);
128 }
129 String output = getAndRemoveParameter(parameters, "output", String.class);
130 configureOutput(xslt, output);
131
132 configureXslt(xslt, uri, remaining, parameters);
133 loadResource(xslt, resource);
134
135 // default to use the cache option from the component if the endpoint did not have the contentCache parameter
136 boolean cache = getAndRemoveParameter(parameters, "contentCache", Boolean.class, contentCache);
137 if (!cache) {
138 return new ProcessorEndpoint(uri, this, xslt) {
139 @Override
140 protected void onExchange(Exchange exchange) throws Exception {
141 // force to load the resource on each exchange as we are not cached
142 loadResource(xslt, resource);
143 super.onExchange(exchange);
144 }
145 };
146 } else {
147 // we have already loaded xslt so we are cached
148 return new ProcessorEndpoint(uri, this, xslt);
149 }
150 }
151
152 private void loadResource(XsltBuilder xslt, Resource resource) throws TransformerConfigurationException {
153 log.trace("{} loading schema resource: {}", this, resource);
154 try {
155 if (resource instanceof UrlResource) {
156 // prefer to use file when a file based url
157 File file = resource.getFile();
158 if (file != null) {
159 // check if the file exists and report a better error as the XSLT
160 // will just say it cannot compile the stylesheet file
161 if (!file.exists()) {
162 throw new FileNotFoundException("File: " + file + " not found.");
163 }
164 xslt.setTransformerFile(file);
165 } else {
166 xslt.setTransformerURL(resource.getURL());
167 }
168 } else {
169 // fallback and use input stream
170 xslt.setTransformerInputStream(resource.getInputStream());
171 }
172 } catch (Exception e) {
173 // include information about the resource in the caused exception, so its easier for
174 // end users to know which resource failed
175 throw new TransformerConfigurationException(e.getMessage() + " " + resource.toString(), e);
176 }
177 }
178
179 protected void configureXslt(XsltBuilder xslt, String uri, String remaining, Map<String, Object> parameters) throws Exception {
180 setProperties(xslt, parameters);
181 }
182
183 protected void configureOutput(XsltBuilder xslt, String output) throws Exception {
184 if (ObjectHelper.isEmpty(output)) {
185 return;
186 }
187
188 if ("string".equalsIgnoreCase(output)) {
189 xslt.outputString();
190 } else if ("bytes".equalsIgnoreCase(output)) {
191 xslt.outputBytes();
192 } else if ("DOM".equalsIgnoreCase(output)) {
193 xslt.outputDOM();
194 } else if ("file".equalsIgnoreCase(output)) {
195 xslt.outputFile();
196 } else {
197 throw new IllegalArgumentException("Unknown output type: " + output);
198 }
199 }
200
201 }