/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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.apache.camel.component.mustache; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.util.Map; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; import org.apache.camel.Component; import org.apache.camel.Exchange; import org.apache.camel.ExchangePattern; import org.apache.camel.Message; import org.apache.camel.component.ResourceEndpoint; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; import org.apache.camel.util.ExchangeHelper; import static org.apache.camel.component.mustache.MustacheConstants.MUSTACHE_ENDPOINT_URI_PREFIX; import static org.apache.camel.component.mustache.MustacheConstants.MUSTACHE_RESOURCE_URI; import static org.apache.camel.component.mustache.MustacheConstants.MUSTACHE_TEMPLATE; /** * Transforms the message using a Mustache template. */ @UriEndpoint(firstVersion = "2.12.0", scheme = "mustache", title = "Mustache", syntax = "mustache:resourceUri", producerOnly = true, label = "transformation") public class MustacheEndpoint extends ResourceEndpoint { private MustacheFactory mustacheFactory; private Mustache mustache; @UriParam private String encoding; @UriParam(defaultValue = "{{") private String startDelimiter; @UriParam(defaultValue = "}}") private String endDelimiter; public MustacheEndpoint() { } public MustacheEndpoint(String endpointUri, Component component, String resourceUri) { super(endpointUri, component, resourceUri); } @Override public boolean isSingleton() { return true; } @Override public ExchangePattern getExchangePattern() { return ExchangePattern.InOut; } @Override protected String createEndpointUri() { return MUSTACHE_ENDPOINT_URI_PREFIX + getResourceUri(); } @Override public void clearContentCache() { this.mustache = null; super.clearContentCache(); } @Override protected void onExchange(Exchange exchange) throws Exception { String newResourceUri = exchange.getIn().getHeader(MUSTACHE_RESOURCE_URI, String.class); if (newResourceUri == null) { // Get Mustache String newTemplate = exchange.getIn().getHeader(MUSTACHE_TEMPLATE, String.class); Mustache newMustache; if (newTemplate == null) { newMustache = getOrCreateMustache(); } else { newMustache = createMustache(new StringReader(newTemplate), "mustache:temp#" + newTemplate.hashCode()); exchange.getIn().removeHeader(MUSTACHE_TEMPLATE); } // Execute Mustache Map<String, Object> variableMap = ExchangeHelper.createVariableMap(exchange); StringWriter writer = new StringWriter(); newMustache.execute(writer, variableMap); writer.flush(); // Fill out message Message out = exchange.getOut(); out.setBody(writer.toString()); out.setHeaders(exchange.getIn().getHeaders()); out.setAttachments(exchange.getIn().getAttachments()); } else { exchange.getIn().removeHeader(MustacheConstants.MUSTACHE_RESOURCE_URI); MustacheEndpoint newEndpoint = getCamelContext().getEndpoint(MUSTACHE_ENDPOINT_URI_PREFIX + newResourceUri, MustacheEndpoint.class); newEndpoint.onExchange(exchange); } } /** * Read and compile a Mustache template * * @param resourceReader Reader used to get template * @param resourceUri Template Id * @return Template */ private Mustache createMustache(Reader resourceReader, String resourceUri) throws IOException { ClassLoader oldcl = Thread.currentThread().getContextClassLoader(); try { ClassLoader apcl = getCamelContext().getApplicationContextClassLoader(); if (apcl != null) { Thread.currentThread().setContextClassLoader(apcl); } Mustache newMustache; if (startDelimiter != null && endDelimiter != null && mustacheFactory instanceof DefaultMustacheFactory) { DefaultMustacheFactory defaultMustacheFactory = (DefaultMustacheFactory) mustacheFactory; newMustache = defaultMustacheFactory.compile(resourceReader, resourceUri, startDelimiter, endDelimiter); } else { newMustache = mustacheFactory.compile(resourceReader, resourceUri); } return newMustache; } finally { resourceReader.close(); if (oldcl != null) { Thread.currentThread().setContextClassLoader(oldcl); } } } private Mustache getOrCreateMustache() throws IOException { if (mustache == null) { mustache = createMustache(getResourceAsReader(), getResourceUri()); } return mustache; } @Override public String getResourceUri() { // do not have leading slash as mustache cannot find the resource, as that entails classpath root String uri = super.getResourceUri(); if (uri != null && (uri.startsWith("/") || uri.startsWith("\\"))) { return uri.substring(1); } else { return uri; } } public MustacheFactory getMustacheFactory() { return mustacheFactory; } /** * To use a custom {@link MustacheFactory} */ public void setMustacheFactory(MustacheFactory mustacheFactory) { this.mustacheFactory = mustacheFactory; } public String getEncoding() { return encoding; } /** * Character encoding of the resource content. */ public void setEncoding(String encoding) { this.encoding = encoding; } private Reader getResourceAsReader() throws IOException { return encoding == null ? new InputStreamReader(getResourceAsInputStream()) : new InputStreamReader(getResourceAsInputStream(), encoding); } public String getStartDelimiter() { return startDelimiter; } /** * Characters used to mark template code beginning. */ public void setStartDelimiter(String startDelimiter) { this.startDelimiter = startDelimiter; } public String getEndDelimiter() { return endDelimiter; } /** * Characters used to mark template code end. */ public void setEndDelimiter(String endDelimiter) { this.endDelimiter = endDelimiter; } }