package org.meaningfulweb.cext; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.codehaus.jackson.JsonNode; import org.meaningfulweb.util.JsonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HtmlContentProcessorFactory { private final static Logger LOG = LoggerFactory .getLogger(HtmlContentProcessorFactory.class); Map<String, Class> classes = new HashMap<String, Class>(); Map<String, HtmlContentProcessor> components = new HashMap<String, HtmlContentProcessor>(); Map<String, List<String>> configs = new HashMap<String, List<String>>(); Map<String, List<String>> pipelines = new HashMap<String, List<String>>(); private void parseConfig(JsonNode root) throws Exception { JsonNode compNodes = root.get("components"); if (compNodes != null) { for (JsonNode compNode : compNodes) { String name = JsonUtils.getStringValue(compNode, "name"); String className = JsonUtils.getStringValue(compNode, "class"); Class compClass = null; try { compClass = Class.forName(className); classes.put(name, compClass); } catch (Exception e) { e.printStackTrace(); continue; } JsonNode compConfig = compNode.get("config"); if (compConfig != null) { List<String> configPropNames = JsonUtils.getFieldNames(compConfig); configs.put(name, configPropNames); components.put( name, (HtmlContentProcessor)JsonUtils.deserializeFromJson( compConfig.toString(), compClass)); } else { components.put(name, (HtmlContentProcessor)compClass.newInstance()); } } } JsonNode pipelineNodes = root.get("pipelines"); if (pipelineNodes != null) { for (JsonNode plNode : pipelineNodes) { String plName = JsonUtils.getStringValue(plNode, "name"); List<String> pipeline = new ArrayList<String>(); JsonNode plCompNodes = plNode.get("components"); if (compNodes != null) { for (JsonNode plCompNode : plCompNodes) { String name = JsonUtils.getStringValue(plCompNode, "name"); pipeline.add(name); JsonNode compConfig = plCompNode.get("config"); if (compConfig != null) { String json = compConfig.toString(); Class compClass = classes.get(name); String fullname = plName + "." + name; List<String> configPropNames = JsonUtils .getFieldNames(compConfig); configs.put(fullname, configPropNames); HtmlContentProcessor plProc = (HtmlContentProcessor)JsonUtils .deserializeFromJson(json, compClass); components.put(fullname, plProc); } } } pipelines.put(plName, pipeline); } } } private void handleOverlay(String name, HtmlContentProcessor processor, Object overlay) { if (overlay != null) { try { if (overlay instanceof HtmlContentProcessor) { HtmlContentProcessor overlayProc = (HtmlContentProcessor)overlay; List<String> props = configs.get(name); if (props != null && props.size() > 0) { for (String prop : props) { Object value = PropertyUtils.getProperty(overlayProc, prop); try { PropertyUtils.setProperty(processor, prop, value); } catch (Exception e) { // log and continue setting properties, ignore invalid LOG.error("ignoring invalid property: " + name + ":" + prop); } } } } else { Map<String, Object> overlayMap = (Map<String, Object>)overlay; if (overlayMap != null && overlayMap.size() > 0) { for (String prop : overlayMap.keySet()) { Object value = overlayMap.get(prop); try { PropertyUtils.setProperty(processor, prop, value); } catch (Exception e) { // log and continue setting properties, ignore invalid LOG.error("ignoring invalid property: " + name + ":" + prop); } } } } } catch (Exception e) { // just log the error, overlays are best completion instead of fail fast LOG.error("Error processing overlay", e); } } } public HtmlContentProcessorFactory(File configFile) throws Exception { String configSource = FileUtils.readFileToString(configFile); if (StringUtils.isBlank(configSource)) { throw new IllegalArgumentException("Configuration file cannot be empty."); } parseConfig(JsonUtils.parseJson(configSource)); } public HtmlContentProcessorFactory(String jsonString) throws Exception { parseConfig(JsonUtils.parseJson(jsonString)); } public HtmlContentProcessor getComponent(String fullname, Map<String, Object> runtime) { String[] names = StringUtils.split(fullname, "."); boolean hasPipeline = (names.length == 2); String component = hasPipeline ? names[1] : names[0]; // new instance of the component class Class compClass = classes.get(component); HtmlContentProcessor processor; try { processor = (HtmlContentProcessor)compClass.newInstance(); } catch (Exception e) { LOG.error("Error creating processor: " + compClass, e); return null; } // base component overlay HtmlContentProcessor baseProc = components.get(component); if (baseProc != null) { handleOverlay(component, processor, baseProc); } // pipeline component overlay if (hasPipeline) { HtmlContentProcessor plProc = components.get(fullname); handleOverlay(fullname, processor, plProc); } // runtime overlay if (runtime != null) { // allow for runtime component overlay Object rtCompObj = runtime.get(component); handleOverlay(component, processor, rtCompObj); // if there is a pipeline allow for runtime pipeline component overlay if (hasPipeline) { Object rtPlObj = runtime.get(fullname); handleOverlay(fullname, processor, rtPlObj); } } // name is set as to not be overwritten processor.setName(component); return processor; } public HtmlContentPipeline getPipeline(String name, Map<String, Object> runtime) { List<String> plCompNames = pipelines.get(name); if (plCompNames != null && plCompNames.size() > 0) { List<HtmlContentProcessor> plComps = new ArrayList<HtmlContentProcessor>(); for (String plCompName : plCompNames) { String fullname = name + "." + plCompName; HtmlContentProcessor curComp = getComponent(fullname, runtime); plComps.add(curComp); } HtmlContentPipeline pipeline = new HtmlContentPipeline(); pipeline.setName(name); pipeline.setProcessors(plComps); return pipeline; } return null; } public List<HtmlContentProcessor> getComponents(List<String> names, Map<String, Object> runtime) { List<HtmlContentProcessor> processors = new ArrayList<HtmlContentProcessor>(); for (String name : names) { HtmlContentProcessor processor = getComponent(name, runtime); if (processor != null) { processors.add(processor); } } return processors; } public Set<String> getComponentNames() { return components.keySet(); } public Set<String> getPipelineNames() { return pipelines.keySet(); } }