package com.intrbiz.bergamot.config.validator; import org.apache.log4j.Logger; import com.codahale.metrics.Timer; import com.intrbiz.bergamot.config.model.TemplatedObjectCfg; import com.intrbiz.bergamot.config.model.TimePeriodCfg; import com.intrbiz.gerald.witchcraft.Witchcraft; public class BergamotConfigResolver { private Logger logger = Logger.getLogger(BergamotConfigResolver.class); private final BergamotObjectLocator locator; private final Timer lookupTimer = Witchcraft.get().source("com.intrbiz.config.bergamot").getRegistry().timer(Witchcraft.name(BergamotConfigResolver.class, "lookup")); private final Timer computeInheritenanceTimer = Witchcraft.get().source("com.intrbiz.config.bergamot").getRegistry().timer(Witchcraft.name(BergamotConfigResolver.class, "computeInheritenance")); private final Timer resolveInheritTimer = Witchcraft.get().source("com.intrbiz.config.bergamot").getRegistry().timer(Witchcraft.name(BergamotConfigResolver.class, "resolveInherit")); private final Timer resolveChildInheritTimer = Witchcraft.get().source("com.intrbiz.config.bergamot").getRegistry().timer(Witchcraft.name(BergamotConfigResolver.class, "resolveChildInherit")); private final Timer lookupChildTemplateTimer = Witchcraft.get().source("com.intrbiz.config.bergamot").getRegistry().timer(Witchcraft.name(BergamotConfigResolver.class, "lookupChildTemplate")); private final Timer resolveExcludesTimer = Witchcraft.get().source("com.intrbiz.config.bergamot").getRegistry().timer(Witchcraft.name(BergamotConfigResolver.class, "resolveExcludes")); public BergamotConfigResolver(BergamotObjectLocator locator) { super(); this.locator = locator; } public BergamotObjectLocator getLocator() { return this.locator; } private String stipSmartMergePrefix(String name) { return (name != null && name.length() > 1 && (name.startsWith("-") || name.startsWith("+"))) ? name.substring(1) : name; } public <T extends TemplatedObjectCfg<T>> T lookup(Class<T> type, String name) { try (Timer.Context tctx = lookupTimer.time()) { // sanitize the name name = this.stipSmartMergePrefix(name); T inherited = this.locator.lookup(type, name); logger.debug("Looked up inherited object: " + type.getSimpleName() + "::" + name + " ==> " + inherited); return inherited; } } public <T extends TemplatedObjectCfg<T>> T computeInheritenance(T object) { this.computeInheritenance(object, null); return object; } @SuppressWarnings({ "unchecked", "rawtypes" }) public <T extends TemplatedObjectCfg<T>> T computeInheritenance(T object, BergamotValidationReport report) { try (Timer.Context tctx = computeInheritenanceTimer.time()) { logger.debug("Computing inheritenance for: " + object.getClass().getSimpleName() + " " + object.getName()); // resolve the object this.resolveInherit(object, report); // now resolve the children for (TemplatedObjectCfg<?> child : object.getTemplatedChildObjects()) { this.resolveChildInherit(object, (TemplatedObjectCfg) child, report); } return object; } } @SuppressWarnings({ "unchecked", "rawtypes" }) protected void resolveInherit(TemplatedObjectCfg<?> object, BergamotValidationReport report) { try (Timer.Context tctx = resolveInheritTimer.time()) { if (object.getInherits().isEmpty()) { logger.debug("Resolving inheritenance for: " + object.getClass().getSimpleName() + " " + object.getName()); for (String inheritsFrom : object.getInheritedTemplates()) { TemplatedObjectCfg<?> superObject = this.lookup(object.getClass(), inheritsFrom); if (superObject != null) { // we need to recursively ensure that the inherited object is resolved this.computeInheritenance((TemplatedObjectCfg) superObject, report); // add the inherited object ((TemplatedObjectCfg) object).addInheritedObject(superObject); } else { // error if (report != null) report.logError("Error: Cannot find the inherited " + object.getClass().getSimpleName() + " named '" + inheritsFrom + "' which is inherited by " + object); } } // special cases if (object instanceof TimePeriodCfg) { resolveExcludes((TimePeriodCfg) object, report); } } } } /** * Search recursively up the template chain * @param parent the parent object * @param name the template we are looking for * @return the template if found, or null */ protected <C extends TemplatedObjectCfg<C>> TemplatedObjectCfg<?> lookupChildTemplate(C container, String name) { try (Timer.Context tctx = lookupChildTemplateTimer.time()) { for (C containerTemplate : container.getInherits()) { // search for the child object in the inherited template for (TemplatedObjectCfg<?> child : containerTemplate.getTemplatedChildObjects()) { if (name.equals(child.getName())) return child; } // recurse up the template chain TemplatedObjectCfg<?> template = this.lookupChildTemplate(containerTemplate, name); if (template != null) return template; } return null; } } @SuppressWarnings({ "unchecked", "rawtypes" }) protected <T extends TemplatedObjectCfg<T>, C extends TemplatedObjectCfg<C>> void resolveChildInherit(C container, T object, BergamotValidationReport report) { try (Timer.Context tctx = resolveChildInheritTimer.time()) { if (object.getInherits().isEmpty()) { logger.debug("Resolving child inheritenance for: " + object.getClass().getSimpleName() + " " + object.getName() + " <<<< " + container.getClass().getSimpleName() + " " + container.getName()); // Firstly look for are we inheriting from a child object of the parent object's templates for (String inheritsFrom : object.getInheritedTemplates()) { // lookup in the containers inheritance tree TemplatedObjectCfg<?> superObject = this.lookupChildTemplate(container, inheritsFrom); // fallback to a global template if (superObject == null) superObject = this.lookup(object.getClass(), inheritsFrom); // yay or nay if (superObject != null) { // resolve the super object this.resolveInherit(superObject, report); // add the inherited object ((TemplatedObjectCfg) object).addInheritedObject(superObject); } else { if (report != null) report.logError("Error: Cannot find the inherited " + object.getClass().getSimpleName() + " named '" + inheritsFrom + "' which is inherited by " + object); } } } } } protected TimePeriodCfg resolveExcludes(TimePeriodCfg object, BergamotValidationReport report) { try (Timer.Context tctx = resolveExcludesTimer.time()) { for (String exclude : object.getExcludes()) { TimePeriodCfg excludedTimePeriod = this.lookup(TimePeriodCfg.class, exclude); if (excludedTimePeriod == null) { // error if (report != null) report.logError("Cannot find the excluded time period named '" + exclude + "' which is excluded by " + object); } } return object; } } }