Index: logback-core/src/test/input/joran/define/noclass.xml =================================================================== --- logback-core/src/test/input/joran/define/noclass.xml Thu Feb 04 20:11:13 GMT+04:00 2010 +++ logback-core/src/test/input/joran/define/noclass.xml Thu Feb 04 20:11:13 GMT+04:00 2010 @@ -0,0 +1,3 @@ + + Monster + \ No newline at end of file Index: logback-core/src/test/input/joran/define/good.xml =================================================================== --- logback-core/src/test/input/joran/define/good.xml Thu Feb 04 20:28:31 GMT+04:00 2010 +++ logback-core/src/test/input/joran/define/good.xml Thu Feb 04 20:28:31 GMT+04:00 2010 @@ -0,0 +1,3 @@ + + Monster + \ No newline at end of file Index: logback-core/src/main/java/ch/qos/logback/core/spi/PropertyDefiner.java =================================================================== --- logback-core/src/main/java/ch/qos/logback/core/spi/PropertyDefiner.java Fri Mar 26 12:12:13 GMT+04:00 2010 +++ logback-core/src/main/java/ch/qos/logback/core/spi/PropertyDefiner.java Fri Mar 26 12:12:13 GMT+04:00 2010 @@ -0,0 +1,10 @@ +package ch.qos.logback.core.spi; + +public interface PropertyDefiner extends ContextAware { + + /** + * Get the property value, defined by this property definer + * @return defined property value + */ + String getPropertyValue(); +} Index: logback-core/src/test/input/joran/define/noname.xml =================================================================== --- logback-core/src/test/input/joran/define/noname.xml Thu Feb 04 20:28:31 GMT+04:00 2010 +++ logback-core/src/test/input/joran/define/noname.xml Thu Feb 04 20:28:31 GMT+04:00 2010 @@ -0,0 +1,3 @@ + + Monster + \ No newline at end of file Index: logback-core/src/main/java/ch/qos/logback/core/joran/action/DefinePropertyAction.java =================================================================== --- logback-core/src/main/java/ch/qos/logback/core/joran/action/DefinePropertyAction.java Fri Mar 26 12:11:15 GMT+04:00 2010 +++ logback-core/src/main/java/ch/qos/logback/core/joran/action/DefinePropertyAction.java Fri Mar 26 12:11:15 GMT+04:00 2010 @@ -0,0 +1,86 @@ +package ch.qos.logback.core.joran.action; + +import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.ActionException; +import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.spi.PropertyDefiner; +import org.xml.sax.Attributes; + + +/** + * Instantiate class for define property value. + * Get future property name and property definer class from attributes. + * Some property definer properties could be used. + * After defining put new property to context. + * + * @author Aleksey Didik + */ +public class DefinePropertyAction extends Action { + + String propertyName; + PropertyDefiner definer; + boolean inError; + + public void begin(InterpretationContext ec, + String localName, + Attributes attributes) throws ActionException { + //reset variables + propertyName = null; + definer = null; + inError = false; + + //read future property name + propertyName = attributes.getValue(NAME_ATTRIBUTE); + if (OptionHelper.isEmpty(propertyName)) { + addError("Missing property name for property definer. Near [" + localName + + "] line " + getLineNumber(ec)); + inError = true; + return; + } + + //read property definer class name + String className = attributes.getValue(CLASS_ATTRIBUTE); + if (OptionHelper.isEmpty(className)) { + addError("Missing class name for property definer. Near [" + localName + + "] line " + getLineNumber(ec)); + inError = true; + return; + } + + //try to instantiate property definer + try { + addInfo("About to instantiate property definer of type [" + className + "]"); + definer = (PropertyDefiner) OptionHelper.instantiateByClassName(className, + PropertyDefiner.class, context); + definer.setContext(context); + ec.pushObject(definer); + } catch (Exception oops) { + inError = true; + addError("Could not create an PropertyDefiner of type [" + className + "].", + oops); + throw new ActionException(oops); + } + } + + /** + * Now property definer is initialized by all properties and we can put property value to context + */ + public void end(InterpretationContext ec, String name) { + if (inError) { + return; + } + + Object o = ec.peekObject(); + + if (o != definer) { + addWarn("The object at the of the stack is not the property definer for property named [" + + propertyName + "] pushed earlier."); + } else { + addInfo("Popping property definer for property named [" + propertyName + + "] from the object stack"); + ec.popObject(); + //let's put defined property and value to context + context.putProperty(propertyName, definer.getPropertyValue()); + } + } +} Index: logback-core/src/main/java/ch/qos/logback/core/joran/JoranConfiguratorBase.java =================================================================== --- logback-core/src/main/java/ch/qos/logback/core/joran/JoranConfiguratorBase.java (revision 382f13d26b9718c7a5c3a467fcda487016dd3a17) +++ logback-core/src/main/java/ch/qos/logback/core/joran/JoranConfiguratorBase.java Fri Mar 26 12:12:13 GMT+04:00 2010 @@ -13,27 +13,16 @@ */ package ch.qos.logback.core.joran; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import ch.qos.logback.core.joran.action.ActionConst; -import ch.qos.logback.core.joran.action.AppenderAction; -import ch.qos.logback.core.joran.action.AppenderRefAction; -import ch.qos.logback.core.joran.action.ContextPropertyAction; -import ch.qos.logback.core.joran.action.ConversionRuleAction; -import ch.qos.logback.core.joran.action.NestedBasicPropertyIA; -import ch.qos.logback.core.joran.action.NestedComplexPropertyIA; -import ch.qos.logback.core.joran.action.NewRuleAction; -import ch.qos.logback.core.joran.action.ParamAction; -import ch.qos.logback.core.joran.action.PropertyAction; -import ch.qos.logback.core.joran.action.StatusListenerAction; -import ch.qos.logback.core.joran.action.TimestampAction; +import ch.qos.logback.core.joran.action.*; import ch.qos.logback.core.joran.spi.InterpretationContext; import ch.qos.logback.core.joran.spi.Interpreter; import ch.qos.logback.core.joran.spi.Pattern; import ch.qos.logback.core.joran.spi.RuleStore; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + // Based on 310985 revision 310985 as attested by http://tinyurl.com/8njps // see also http://tinyurl.com/c2rp5 @@ -65,7 +54,10 @@ rs.addRule(new Pattern("configuration/timestamp"), new TimestampAction()); - + + rs.addRule(new Pattern("configuration/define"), + new DefinePropertyAction()); + // the contextProperty pattern is deprecated. It is undocumented // and will be dropped in future versions of logback rs.addRule(new Pattern("configuration/contextProperty"), Index: logback-core/src/test/java/ch/qos/logback/core/joran/action/DefinePropertyActionTest.java =================================================================== --- logback-core/src/test/java/ch/qos/logback/core/joran/action/DefinePropertyActionTest.java Fri Mar 26 12:15:00 GMT+04:00 2010 +++ logback-core/src/test/java/ch/qos/logback/core/joran/action/DefinePropertyActionTest.java Fri Mar 26 12:15:00 GMT+04:00 2010 @@ -0,0 +1,113 @@ +package ch.qos.logback.core.joran.action; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.joran.SimpleConfigurator; +import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.joran.spi.Pattern; +import ch.qos.logback.core.status.ErrorStatus; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; +import org.junit.After; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Iterator; + +/** + * Test {@link DefinePropertyAction}. + * + * @author Aleksey Didik + */ +public class DefinePropertyActionTest { + + private static final String DEFINE_XML_DIR = "src/test/input/joran/define/"; + private static final String GOOD_XML = "good.xml"; + private static final String NONAME_XML = "noname.xml"; + private static final String NOCLASS_XML = "noclass.xml"; + private static final String BADCLASS_XML = "badclass.xml"; + + SimpleConfigurator sc; + Context context; + DefinePropertyAction definerAction; + InterpretationContext ic; + + @Before + public void setUp() throws Exception { + context = new ContextBase(); + HashMap rulesMap = new HashMap(); + rulesMap.put(new Pattern("define"), new DefinePropertyAction()); + sc = new SimpleConfigurator(rulesMap); + sc.setContext(context); + + } + + @Test + public void testAllRight() throws JoranException { + sc.doConfigure(DEFINE_XML_DIR + GOOD_XML); + //get from context + String inContextFoo = context.getProperty("foo"); + //get from real class + FooPropertyDefiner fooDefiner = new FooPropertyDefiner(); + fooDefiner.setFooName("Monster"); + String fromRealClassFoo = fooDefiner.getPropertyValue(); + assertEquals(inContextFoo, fromRealClassFoo); + } + + + @Test + public void testNoName() throws JoranException { + sc.doConfigure(DEFINE_XML_DIR + NONAME_XML); + //get from context + String inContextFoo = context.getProperty("foo"); + assertNull(inContextFoo); + //check context errors + assertTrue(checkError("Missing property name for property definer. Near [define] line 1")); + } + + @Test + public void testNoClass() throws JoranException { + sc.doConfigure(DEFINE_XML_DIR + NOCLASS_XML); + //get from context + String inContextFoo = context.getProperty("foo"); + assertNull(inContextFoo); + //check context errors + assertTrue(checkError("Missing class name for property definer. Near [define] line 1")); + } + + @Test + public void testBadClass() throws JoranException { + sc.doConfigure(DEFINE_XML_DIR + BADCLASS_XML); + //get from context + String inContextFoo = context.getProperty("foo"); + assertNull(inContextFoo); + //check context errors + assertTrue(checkBadClassError()); + } + + @After + public void tearDown() throws Exception { + context = null; + sc = null; + } + + + private boolean checkError(String waitedMsg) { + Iterator it = context.getStatusManager().getCopyOfStatusList().iterator(); + ErrorStatus es1 = (ErrorStatus) it.next(); + return waitedMsg.equals(es1.getMessage()); + } + + private boolean checkBadClassError() { + Iterator it = context.getStatusManager().getCopyOfStatusList().iterator(); + //skip info about class instantiating + it.next(); + //check error status + ErrorStatus es1 = (ErrorStatus) it.next(); + return "Could not create an PropertyDefiner of type [a.b.c.Foo].".equals(es1.getMessage()); + } + +} Index: logback-core/src/test/java/ch/qos/logback/core/joran/action/FooPropertyDefiner.java =================================================================== --- logback-core/src/test/java/ch/qos/logback/core/joran/action/FooPropertyDefiner.java Fri Mar 26 12:11:15 GMT+04:00 2010 +++ logback-core/src/test/java/ch/qos/logback/core/joran/action/FooPropertyDefiner.java Fri Mar 26 12:11:15 GMT+04:00 2010 @@ -0,0 +1,24 @@ +package ch.qos.logback.core.joran.action; + +import ch.qos.logback.core.PropertyDefinerBase;/* +* Project: Maxifier +* Author: Aleksey Didik +* Created: 04.02.2010 +* +* Copyright (c) 1999-2010 Magenta Corporation Ltd. All Rights Reserved. +* Magenta Technology proprietary and confidential. +* Use is subject to license terms. +*/ + +public class FooPropertyDefiner extends PropertyDefinerBase { + + private String fooName; + + public String getPropertyValue() { + return "Foo[" + fooName + "]"; + } + + public void setFooName(String fooName) { + this.fooName = fooName; + } +} Index: logback-core/src/main/java/ch/qos/logback/core/PropertyDefinerBase.java =================================================================== --- logback-core/src/main/java/ch/qos/logback/core/PropertyDefinerBase.java Fri Mar 26 12:11:15 GMT+04:00 2010 +++ logback-core/src/main/java/ch/qos/logback/core/PropertyDefinerBase.java Fri Mar 26 12:11:15 GMT+04:00 2010 @@ -0,0 +1,13 @@ +package ch.qos.logback.core; + +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.PropertyDefiner; + +/** + * Set a skeleton implementation for property definers + * just for have ContextAwareBase. + * + * @author Aleksey Didik + */ +public abstract class PropertyDefinerBase extends ContextAwareBase implements PropertyDefiner { +} Index: logback-core/src/test/input/joran/define/badclass.xml =================================================================== --- logback-core/src/test/input/joran/define/badclass.xml Thu Feb 04 20:27:17 GMT+04:00 2010 +++ logback-core/src/test/input/joran/define/badclass.xml Thu Feb 04 20:27:17 GMT+04:00 2010 @@ -0,0 +1,1 @@ +