Java tutorial
/** * Copyright (C) 2013 Seajas, the Netherlands. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.seajas.search.contender.service.modifier; import com.seajas.search.bridge.profiler.model.modifier.ModifierScript; import com.seajas.search.contender.DefaultWebResolver; import com.seajas.search.contender.WebResolverSettings; import com.seajas.search.contender.scripting.FeedScript; import com.seajas.search.contender.scripting.FeedScriptCacheResolver; import com.seajas.search.contender.scripting.FeedScriptEvaluation; import com.seajas.search.contender.scripting.ResolvingTransformerCache; import com.seajas.search.contender.scripting.ScriptCache; import com.seajas.search.contender.scripting.ScriptCache.ScriptCacheEntry; import com.seajas.search.contender.scripting.ScriptCacheResolver; import com.seajas.search.contender.service.ContenderService; import com.seajas.search.contender.service.cache.CacheService; import com.seajas.search.utilities.web.WebFeeds; import com.seajas.search.utilities.web.WebPages; import com.sun.syndication.feed.synd.SyndFeed; import com.sun.syndication.io.FeedException; import com.sun.syndication.io.SyndFeedOutput; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.net.URI; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.w3c.dom.Node; import org.xml.sax.InputSource; import javax.script.Bindings; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; /** * Process the given modifier. * * @author Jasper van Veghel <jasper@seajas.com> * @author Pascal S. de Kloe <pascal@quies.net> */ @Component public class ModifierScriptProcessor { /** * The logger. */ private static final Logger logger = LoggerFactory.getLogger(ModifierScriptProcessor.class); /** * The document builder. */ private final DocumentBuilder documentBuilder; /** * The transformer cache. */ @Autowired private ResolvingTransformerCache transformerCache; /** * Contender service. */ @Autowired private ContenderService contenderService; /** * Cache service. */ @Autowired private CacheService cacheService; /** * Script manager. */ @Autowired private ScriptEngineManager engineManager; /** * Script cache. */ @Autowired private ScriptCache scriptCache; /** * Default constructor. * * @throws ParserConfigurationException */ public ModifierScriptProcessor() throws ParserConfigurationException { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setCoalescing(true); documentBuilderFactory.setNamespaceAware(true); documentBuilder = documentBuilderFactory.newDocumentBuilder(); } /** * Process the given reader using this script. * * @param script * @param input * @param uri * @param settings * @param enableCaching * @return Reader * @throws IOException * @throws ScriptException */ public Reader process(final ModifierScript script, final String input, final URI uri, final WebResolverSettings settings, final Boolean enableCaching) throws IOException, ScriptException { if (logger.isDebugEnabled()) { String msg = "Input for URI '%s' contains %d characters (maximumContentLength = %d, enableCaching = %b)"; logger.debug( String.format(msg, uri, input.length(), settings.getMaximumContentLength(), enableCaching)); if (logger.isTraceEnabled()) logger.trace("Input: " + input); } String result = null; if (script.getScriptLanguage().equalsIgnoreCase("xslt")) result = transformXML(script, input, uri); else result = evaluateScript(settings, script, input, uri); logger.trace(result); return new StringReader(result.toString()); } /** * Transforms the given Reader content to an XSL-transformed document. * * @param script * @param input * @param uri * @return String * @throws ScriptException */ private String transformXML(final ModifierScript script, final String input, final URI uri) throws ScriptException { try { Transformer transformer = transformerCache.getTransformer(script.getId(), "modifier", script.getModificationDate()); if (transformer == null) transformer = transformerCache.putContent(script.getId(), "modifier", script.getModificationDate(), script.getScriptContent()); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); Node document = documentBuilder.parse(new InputSource(new StringReader(input))); StringWriter buffer = new StringWriter(); transformer.transform(new DOMSource(document), new StreamResult(buffer)); return buffer.toString(); } catch (TransformerConfigurationException e) { logger.error("Unable to generate a (cached) transformer from the given content", e); throw new ScriptException("Unable to generate a (cached) transformer from the given content"); } catch (Exception e) { logger.error("Unable to perform content transformation modifier for " + uri, e); throw new ScriptException("Unable to perform content transformation modifier for " + uri); } } /** * Evaluate the given script. * * @param settings * @param script * @param input * @param uri * @return String * @throws ScriptException */ private String evaluateScript(final WebResolverSettings settings, final ModifierScript script, String input, URI uri) throws ScriptException { final ScriptCacheEntry entry = scriptCache.acquireScript(script); try { final ScriptEngine engine = entry != null ? entry.getScript().getEngine() : engineManager.getEngineByName(script.getScriptLanguage().toLowerCase()); FeedScriptEvaluation evaluation = new FeedScriptEvaluation(settings); evaluation.setScriptResolver(new ScriptCacheResolver<FeedScript>() { @Override public FeedScript resolve(final Bindings bindings) throws ScriptException { for (Map.Entry<String, Object> binding : bindings.entrySet()) engine.put(binding.getKey(), binding.getValue()); if (entry != null) { if (logger.isTraceEnabled()) logger.trace("Executing compiled script with ID " + script.getId()); entry.getScript().eval(); } else { if (logger.isTraceEnabled()) logger.trace(String.format("Executing non-compiled script with ID %d and content '%s'", script.getId(), script.getScriptContent())); engine.eval(script.getScriptContent()); } return ((Invocable) engine).getInterface(FeedScript.class); } }); evaluation.setFeedURI(uri); evaluation.setFeedContent(input); evaluation.setEngine(engine); evaluation.setCacheResolver(new FeedScriptCacheResolver() { @Override public boolean isCached(final String resource) { logger.info("Cache requested URI: " + resource); String cacheKey = cacheService.createCompositeKey(resource, settings.getResultParameters()); return cacheService.isCached(cacheKey); } }); evaluation.setWebPages(new WebPages()); DefaultWebResolver resolver = new DefaultWebResolver(); resolver.setSettings(settings); resolver.setContenderService(contenderService); evaluation.setWebResolver(resolver); evaluation.init(); evaluation.run(); SyndFeed feed = evaluation.getFeed(); if (feed == null) throw new ScriptException("No result available"); try { WebFeeds.validate(feed, uri); SyndFeedOutput serializer = new SyndFeedOutput(); return serializer.outputString(feed, true); } catch (FeedException e) { throw new ScriptException("Can't serialize feed result: " + e); } } finally { scriptCache.releaseScript(script, entry); } } }