svn commit: r2130 - in logback/trunk: logback-classic/src/main/java/ch/qos/logback/classic/joran logback-core/src/main/java/ch/qos/logback/core logback-core/src/main/java/ch/qos/logback/core/joran logback-core/src/main/java/ch/qos/logback/core/joran/action logback-core/src/main/java/ch/qos/logback/core/joran/spi logback-core/src/test/java/ch/qos/logback/core/joran/spi

Author: ceki Date: Wed Jan 21 23:06:57 2009 New Revision: 2130 Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/PropertySetter.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PropertySetterTest.java Log: Fixing LBCLASSIC-103 Joran is now able to assume a default type for nested components according to rules declared in a JoranConfigurator instance. The rules are located in an instance of the DefaultNestedComponentRegistry class. Thus, in many cases it will no longer be necessary to declare the class of a component in configuration files. Since the logback-classic and logback-access modes have their own JoranConfigurator classes, it is now possible to register rules specific to each logback module. For example, there is now a rule which maps the layout of an appender to a c.q.l.classic.PatternLayout instance for all appenders in logback-classic and to and instance of c.q.l.access.PatternLayout for all appenders in logback-access. This is still ongoing and incomplete work. Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java Wed Jan 21 23:06:57 2009 @@ -1,7 +1,7 @@ /** - * LOGBack: the generic, reliable, fast and flexible logging framework. + * Logback: the generic, reliable, fast and flexible logging framework. * - * Copyright (C) 1999-2006, QOS.ch + * Copyright (C) 2000-2009, QOS.ch * * This library is free software, you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -10,27 +10,31 @@ package ch.qos.logback.classic.joran; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.boolex.JaninoEventEvaluator; import ch.qos.logback.classic.joran.action.ConfigurationAction; import ch.qos.logback.classic.joran.action.ConsolePluginAction; import ch.qos.logback.classic.joran.action.ContextNameAction; import ch.qos.logback.classic.joran.action.EvaluatorAction; import ch.qos.logback.classic.joran.action.InsertFromJNDIAction; import ch.qos.logback.classic.joran.action.JMXConfiguratorAction; -import ch.qos.logback.classic.joran.action.LayoutAction; import ch.qos.logback.classic.joran.action.LevelAction; import ch.qos.logback.classic.joran.action.LoggerAction; import ch.qos.logback.classic.joran.action.RootLoggerAction; import ch.qos.logback.classic.sift.SiftAction; import ch.qos.logback.classic.spi.PlatformInfo; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.filter.EvaluatorFilter; import ch.qos.logback.core.joran.JoranConfiguratorBase; import ch.qos.logback.core.joran.action.AppenderRefAction; import ch.qos.logback.core.joran.action.IncludeAction; import ch.qos.logback.core.joran.action.NOPAction; +import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; import ch.qos.logback.core.joran.spi.Pattern; import ch.qos.logback.core.joran.spi.RuleStore; /** - * A JoranConfigurator add few rules specific to the Classic module. + * This JoranConfiguratorclass adds rules specific to logback-classic. * * @author Ceki Gülcü */ @@ -46,14 +50,15 @@ rs.addRule(new Pattern("configuration"), new ConfigurationAction()); - rs.addRule(new Pattern("configuration/contextName"), new ContextNameAction()); - rs.addRule(new Pattern("configuration/insertFromJNDI"), new InsertFromJNDIAction()); + rs.addRule(new Pattern("configuration/contextName"), + new ContextNameAction()); + rs.addRule(new Pattern("configuration/insertFromJNDI"), + new InsertFromJNDIAction()); rs.addRule(new Pattern("configuration/evaluator"), new EvaluatorAction()); rs.addRule(new Pattern("configuration/appender/sift"), new SiftAction()); rs.addRule(new Pattern("configuration/appender/sift/*"), new NOPAction()); - - + rs.addRule(new Pattern("configuration/logger"), new LoggerAction()); rs.addRule(new Pattern("configuration/logger/level"), new LevelAction()); @@ -63,19 +68,31 @@ new AppenderRefAction()); rs.addRule(new Pattern("configuration/root/appender-ref"), new AppenderRefAction()); - rs - .addRule(new Pattern("configuration/appender/layout"), - new LayoutAction()); - + + //rs + // .addRule(new Pattern("configuration/appender/layout"), + // new LayoutAction()); + // add jmxConfigurator only if we have JMX available. // If running under JDK 1.4 (retrotranslateed logback) then we // might not have JMX. - if(PlatformInfo.hasJMXObjectName()) { - rs.addRule(new Pattern("configuration/jmxConfigurator"), new JMXConfiguratorAction()); + if (PlatformInfo.hasJMXObjectName()) { + rs.addRule(new Pattern("configuration/jmxConfigurator"), + new JMXConfiguratorAction()); } rs.addRule(new Pattern("configuration/include"), new IncludeAction()); - - rs.addRule(new Pattern("configuration/consolePlugin"), new ConsolePluginAction()); + + rs.addRule(new Pattern("configuration/consolePlugin"), + new ConsolePluginAction()); + } + + @Override + protected void addDefaultNestedComponentRegistryRules( + DefaultNestedComponentRegistry registry) { + registry.add(AppenderBase.class, "layout", PatternLayout.class); + registry + .add(EvaluatorFilter.class, "evaluator", JaninoEventEvaluator.class); + } } Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java Wed Jan 21 23:06:57 2009 @@ -1,7 +1,7 @@ /** * Logback: the generic, reliable, fast and flexible logging framework. * - * Copyright (C) 1999-2007, QOS.ch + * Copyright (C) 2000-2009, QOS.ch * * This library is free software, you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java Wed Jan 21 23:06:57 2009 @@ -20,6 +20,7 @@ import ch.qos.logback.core.joran.event.SaxEvent; import ch.qos.logback.core.joran.event.SaxEventRecorder; +import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; import ch.qos.logback.core.joran.spi.EventPlayer; import ch.qos.logback.core.joran.spi.InterpretationContext; import ch.qos.logback.core.joran.spi.Interpreter; @@ -79,6 +80,10 @@ abstract protected void addImplicitRules(Interpreter interpreter); + protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { + + } + protected Pattern initialPattern() { return new Pattern(); } @@ -90,6 +95,7 @@ InterpretationContext ec = interpreter.getInterpretationContext(); ec.setContext(context); addImplicitRules(interpreter); + addDefaultNestedComponentRegistryRules(ec.getDefaultNestedComponentRegistry()); } final public void doConfigure(final InputSource inputSource) Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java Wed Jan 21 23:06:57 2009 @@ -91,23 +91,32 @@ // perform variable name substitution className = ec.subst(className); - // guess class name via implicit rules - if (OptionHelper.isEmpty(className)) { - PropertySetter parentBean = actionData.parentBean; - className = parentBean.getClassNameViaImplicitRules(actionData - .getComplexPropertyName(), actionData.getAggregationType()); - } + Class componentClass = null; + try { - if (OptionHelper.isEmpty(className)) { - actionData.inError = true; - String errMsg = "No class name attribute in [" + localName + "]"; - addError(errMsg); - return; - } + if (!OptionHelper.isEmpty(className)) { + componentClass = Loader.loadClass(className, context); + } else { + // guess class name via implicit rules + PropertySetter parentBean = actionData.parentBean; + componentClass = parentBean.getClassNameViaImplicitRules(actionData + .getComplexPropertyName(), actionData.getAggregationType(), ec + .getDefaultNestedComponentRegistry()); + } - try { - actionData.setNestedComplexProperty(Loader.loadClass(className, context) - .newInstance()); + if (componentClass == null) { + actionData.inError = true; + String errMsg = "Could not find an appropriate class for property [" + + localName + "]"; + addError(errMsg); + return; + } + + if(OptionHelper.isEmpty(className)) { + addInfo("Assuming default type ["+componentClass.getName()+"] for ["+localName+"] property"); + } + + actionData.setNestedComplexProperty(componentClass.newInstance()); // pass along the repository if (actionData.getNestedComplexProperty() instanceof ContextAware) { @@ -118,12 +127,14 @@ addInfo("Pushing component [" + localName + "] on top of the object stack."); ec.pushObject(actionData.getNestedComplexProperty()); + } catch (Exception oops) { actionData.inError = true; String msg = "Could not create component [" + localName + "] of type [" + className + "]"; addError(msg, oops); } + } public void end(InterpretationContext ec, String tagName) { Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java Wed Jan 21 23:06:57 2009 @@ -0,0 +1,49 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 2000-2009, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.core.joran.spi; + +import java.util.HashMap; +import java.util.Map; + +/** + * A registry which maps a property in a host class to a default class. + * + * @author Cek Gülcü + * + */ +public class DefaultNestedComponentRegistry { + + Map<HostClassAndPropertyDouble, Class> defaultComponentMap = new HashMap<HostClassAndPropertyDouble, Class>(); + + public void add(Class hostClass, String propertyName, Class componentClass) { + HostClassAndPropertyDouble hpDouble = new HostClassAndPropertyDouble( + hostClass, propertyName.toLowerCase()); + defaultComponentMap.put(hpDouble, componentClass); + } + + public Class findDefaultComponentType(Class hostClass, String propertyName) { + propertyName = propertyName.toLowerCase(); + while (hostClass != null) { + Class componentClass = oneShotFind(hostClass, propertyName); + if (componentClass != null) { + return componentClass; + } + hostClass = hostClass.getSuperclass(); + } + return null; + } + + private Class oneShotFind(Class hostClass, String propertyName) { + HostClassAndPropertyDouble hpDouble = new HostClassAndPropertyDouble( + hostClass, propertyName); + return defaultComponentMap.get(hpDouble); + } + +} Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java Wed Jan 21 23:06:57 2009 @@ -0,0 +1,72 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 2000-2009, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.core.joran.spi; + +/** + * A 2-tuple (a double) consisting of a Class and a String. The Class references + * the hosting class of a component and the String represents the property name + * under which a nested component is referenced the host. + * + * This class is used by {@link DefaultNestedComponentRegistry}. + * + * @author Ceki Gulcu + * + */ +public class HostClassAndPropertyDouble { + + final Class hostClass; + final String propertyName; + + public HostClassAndPropertyDouble(Class hostClass, String propertyName) { + this.hostClass = hostClass; + this.propertyName = propertyName; + } + + public Class getHostClass() { + return hostClass; + } + + public String getPropertyName() { + return propertyName; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((hostClass == null) ? 0 : hostClass.hashCode()); + result = prime * result + + ((propertyName == null) ? 0 : propertyName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final HostClassAndPropertyDouble other = (HostClassAndPropertyDouble) obj; + if (hostClass == null) { + if (other.hostClass != null) + return false; + } else if (!hostClass.equals(other.hostClass)) + return false; + if (propertyName == null) { + if (other.propertyName != null) + return false; + } else if (!propertyName.equals(other.propertyName)) + return false; + return true; + } + +} Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java Wed Jan 21 23:06:57 2009 @@ -43,7 +43,8 @@ Interpreter joranInterpreter; final List<InPlayListener> listenerList = new ArrayList<InPlayListener>(); - + DefaultNestedComponentRegistry defaultNestedComponentRegistry = new DefaultNestedComponentRegistry(); + public InterpretationContext(Context context, Interpreter joranInterpreter) { this.context = context; this.joranInterpreter = joranInterpreter; @@ -52,6 +53,11 @@ propertiesMap = new HashMap<String, String>(5); } + + public DefaultNestedComponentRegistry getDefaultNestedComponentRegistry() { + return defaultNestedComponentRegistry; + } + void setPropertiesMap(Map<String, String> propertiesMap) { this.propertiesMap = propertiesMap; } Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/PropertySetter.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/PropertySetter.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/spi/PropertySetter.java Wed Jan 21 23:06:57 2009 @@ -559,19 +559,17 @@ } } - String getDefaultClassNameByAnnonation(String name, Method relevantMethod) { + Class getDefaultClassNameByAnnonation(String name, Method relevantMethod) { DefaultClass defaultClassAnnon = getAnnotation(name, DefaultClass.class, relevantMethod); if (defaultClassAnnon != null) { Class defaultClass = defaultClassAnnon.value(); - if (defaultClass != null) { - return defaultClass.getName(); - } + return defaultClass; } return null; } - String getByConcreteType(String name, Method relevantMethod) { + Class getByConcreteType(String name, Method relevantMethod) { Class<?> paramType = getParameterClassForMethod(relevantMethod); if (paramType == null) { @@ -580,22 +578,26 @@ boolean isUnequivocallyInstantiable = isUnequivocallyInstantiable(paramType); if(isUnequivocallyInstantiable) { - return paramType.getName(); + return paramType; } else { return null; } } - public String getClassNameViaImplicitRules(String name, - AggregationType aggregationType) { + public Class getClassNameViaImplicitRules(String name, + AggregationType aggregationType, DefaultNestedComponentRegistry registry) { + Class registryResult = registry.findDefaultComponentType(obj.getClass(), name); + if(registryResult!= null) { + return registryResult; + } // find the relevant method for the given property name and aggregationType Method relevantMethod = getRelevantMethod(name, aggregationType); if (relevantMethod == null) { - + return null; } - String byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod); + Class byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod); if (byAnnotation != null) { return byAnnotation; } Added: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java Wed Jan 21 23:06:57 2009 @@ -0,0 +1,37 @@ +package ch.qos.logback.core.joran.spi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class DefaultNestedComponentRegistryTest { + + DefaultNestedComponentRegistry registry = new DefaultNestedComponentRegistry(); + + @Before + public void setUp() throws Exception { + + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void smoke() { + String propertyName = "window"; + registry.add(House.class, propertyName, Window.class); + Class result = registry.findDefaultComponentType(House.class, propertyName); + assertEquals(Window.class, result); + } + + @Test + public void absent() { + registry.add(House.class, "a", Window.class); + Class result = registry.findDefaultComponentType(House.class, "other"); + assertNull(result); + } +} Modified: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java ============================================================================== --- logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java (original) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java Wed Jan 21 23:06:57 2009 @@ -14,6 +14,7 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({PatternTest.class, SimpleStoreTest.class, PropertySetterTest.class}) +@SuiteClasses( { PatternTest.class, SimpleStoreTest.class, + PropertySetterTest.class, DefaultNestedComponentRegistryTest.class }) public class PackageTest { } Modified: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PropertySetterTest.java ============================================================================== --- logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PropertySetterTest.java (original) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PropertySetterTest.java Wed Jan 21 23:06:57 2009 @@ -20,6 +20,8 @@ public class PropertySetterTest { + DefaultNestedComponentRegistry defaultComponentRegistry = new DefaultNestedComponentRegistry(); + @Test public void testCanAggregateComponent() { House house = new House(); @@ -112,23 +114,21 @@ public void testgetClassNameViaImplicitRules() { House house = new House(); PropertySetter setter = new PropertySetter(house); - String className = setter.getClassNameViaImplicitRules("door", - AggregationType.AS_COMPLEX_PROPERTY); - assertEquals(Door.class.getName(), className); + Class compClass = setter.getClassNameViaImplicitRules("door", + AggregationType.AS_COMPLEX_PROPERTY, defaultComponentRegistry); + assertEquals(Door.class, compClass); } - - @Test public void testgetComplexPropertyColleClassNameViaImplicitRules() { House house = new House(); PropertySetter setter = new PropertySetter(house); - String className = setter.getClassNameViaImplicitRules("window", - AggregationType.AS_COMPLEX_PROPERTY_COLLECTION); - assertEquals(Window.class.getName(), className); + Class compClass = setter.getClassNameViaImplicitRules("window", + AggregationType.AS_COMPLEX_PROPERTY_COLLECTION, + defaultComponentRegistry); + assertEquals(Window.class, compClass); } - @Test public void testPropertyCollection() { House house = new House(); @@ -208,13 +208,14 @@ Method relevantMethod = setter.getRelevantMethod("SwimmingPool", AggregationType.AS_COMPLEX_PROPERTY); assertNotNull(relevantMethod); - String spClassName = setter.getDefaultClassNameByAnnonation("SwimmingPool", + Class spClass = setter.getDefaultClassNameByAnnonation("SwimmingPool", relevantMethod); - assertEquals(SwimmingPoolImpl.class.getName(), spClassName); + assertEquals(SwimmingPoolImpl.class, spClass); - String classNameViaImplicitRules = setter.getClassNameViaImplicitRules( - "SwimmingPool", AggregationType.AS_COMPLEX_PROPERTY); - assertEquals(SwimmingPoolImpl.class.getName(), classNameViaImplicitRules); + Class classViaImplicitRules = setter.getClassNameViaImplicitRules( + "SwimmingPool", AggregationType.AS_COMPLEX_PROPERTY, + defaultComponentRegistry); + assertEquals(SwimmingPoolImpl.class, classViaImplicitRules); } }
participants (1)
-
noreply.ceki@qos.ch