r1800 - in trunk/jaxx-runtime/src: main/java/jaxx/runtime/context test/java/jaxx/runtime/context
Author: tchemit Date: 2010-03-23 11:56:00 +0100 (Tue, 23 Mar 2010) New Revision: 1800 Log: Evolution #410: Can listen modifications of a DefaultApplicationContext via the PCS api Modified: trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/DefaultApplicationContext.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/DefaultJAXXContext.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/JAXXContextEntryDef.java trunk/jaxx-runtime/src/test/java/jaxx/runtime/context/DefaultApplicationContextTest.java Modified: trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/DefaultApplicationContext.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/DefaultApplicationContext.java 2010-03-22 14:05:21 UTC (rev 1799) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/DefaultApplicationContext.java 2010-03-23 10:56:00 UTC (rev 1800) @@ -20,10 +20,16 @@ */ package jaxx.runtime.context; -import jaxx.runtime.*; +import jaxx.runtime.JAXXContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -31,87 +37,85 @@ import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * The default context to be used for an application. + * <p/> + * This extends the {@link DefaultJAXXContext} and add a possibility to + * auto-instanciate some classes asked via {@link #getContextValue(Class)} and + * {@link #getContextValue(Class, String)} methods. + * <p/> + * To registred a such class, just annotate your class with {@link AutoLoad}. * - * This extends the {@link DefaultJAXXContext} and add a possibility to auto-instanciate - * some classes asked via {@link #getContextValue(java.lang.Class)} and - * {@link #getContextValue(java.lang.Class, java.lang.String)} methods. - * - * To registred a such class, just annotate your class with {@link AutoLoad}. - * * @author chemit + * @see DefaultJAXXContext * @since 1.2 - * @see DefaultJAXXContext */ public class DefaultApplicationContext extends DefaultJAXXContext { /** - * A class annotated @AutoLoad is used by context to auto - * instanciate the class when required. - * + * A class annotated @AutoLoad is used by context to auto instanciate + * the class when required. + * <p/> * Note : A such class always have a public default constructor. * - * @author chemit + * @author chemit * @version 1.0, 21/02/09 * @since 1.2 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface AutoLoad { + public @interface AutoLoad { //TODO use this to add a method to init String initMethod() default ""; } /** - * A class annotated @MethodAccess is used by context to obtain the value - * of an entry via a declared method. - * - * Note : A such class must have a method called {@link #methodName()} with + * A class annotated @MethodAccess is used by context to obtain the + * value of an entry via a declared method. + * <p/> + * Note : A such class must have a method called {@link #methodName()} with * a single String parameter. * - * @author chemit + * @author chemit * @version 1.0, 21/02/09 * @since 1.2 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface MethodAccess { + public @interface MethodAccess { /** - * Define a forward to a target class. When the target class - * will be asked with method {@link JAXXContext#getContextValue(java.lang.Class, java.lang.String)} + * Define a forward to a target class. When the target class will be + * asked with method {@link JAXXContext#getContextValue(Class, String)} * it will be delegating to this class. * * @return the forwarded class */ Class<?> target() default Object.class; - /** - * @return the name of the method to access data - */ + /** @return the name of the method to access data */ String methodName(); } + + /** Map of forwarded classes (key) to classes (values). */ + protected Map<Class<?>, Class<?>> forwards; + /** - * Map of forwarded classes (key) to classes (values). + * Map of entries to watch associated with the property to fires if a + * modification was found. */ - protected Map<Class<?>, Class<?>> forwards; + protected Map<JAXXContextEntryDef<?>, String> entryListened; public DefaultApplicationContext() { - super(); forwards = new HashMap<Class<?>, Class<?>>(); pcs = new PropertyChangeSupport(this); } -// public DefaultApplicationContext(JAXXObject ui) { -// super(); -// } /** to use log facility, just put in your code: log.info(\"...\"); */ - static private final Log log = LogFactory.getLog(DefaultApplicationContext.class); + static private final Log log = + LogFactory.getLog(DefaultApplicationContext.class); /** to manage properties modifications */ protected PropertyChangeSupport pcs; @@ -130,7 +134,8 @@ // always call the forwarder with no name value = getContextValue(realClass, null); if (log.isDebugEnabled()) { - log.debug("detect forward from " + clazz + " to " + realClass + " (" + value + ")"); + log.debug("detect forward from " + clazz + " to " + realClass + + " (" + value + ")"); } } else { @@ -149,10 +154,12 @@ } if (name != null) { - throw new IllegalArgumentException("an " + AutoLoad.class.getName() + " can not have a named context but was call with this one : " + name); + throw new IllegalArgumentException( + "an " + AutoLoad.class.getName() + " can not have " + + "a named context but was call with this one : " + name); } value = newInstance(clazz); - if (!anno.initMethod().trim().isEmpty()){ + if (!anno.initMethod().trim().isEmpty()) { // apply method on class newAccess(clazz, value, anno.initMethod().trim()); } @@ -167,14 +174,15 @@ if (access != null) { if (name == null) { - if (access.target() != Object.class) { + if (!Object.class.equals(access.target())) { // register forward Class<?> targetClass = access.target(); if (!forwards.containsKey(targetClass)) { // register forward forwards.put(targetClass, clazz); if (log.isDebugEnabled()) { - log.debug("register forward from " + targetClass + " to " + clazz); + log.debug("register forward from " + targetClass + + " to " + clazz); } } } @@ -187,31 +195,109 @@ } @Override - public <T> void removeContextValue(Class<T> klazz, String name) { + public <T> void removeContextValue(Class<T> klass, String name) { Entry<Class<?>, Class<?>> entry; - if (name == null && forwards.containsValue(klazz)) { + if (name == null && forwards.containsValue(klass)) { // remove forward - Iterator<Entry<Class<?>, Class<?>>> itr = forwards.entrySet().iterator(); + Iterator<Entry<Class<?>, Class<?>>> itr = + forwards.entrySet().iterator(); while (itr.hasNext()) { entry = itr.next(); - if (entry.getValue().equals(klazz)) { + if (entry.getValue().equals(klass)) { itr.remove(); if (log.isDebugEnabled()) { - log.debug("removed forward from " + entry.getKey() + " to " + klazz); + log.debug("removed forward from " + entry.getKey() + + " to " + klass); } break; } } } //FIXME should update forwards state - super.removeContextValue(klazz, name); + JAXXContextEntryDef<?> entryToDel = getEntry(klass, name); + Object t = remove0(klass, name); + if (log.isDebugEnabled()) { + log.debug("removed object = " + t); + } + if (t != null && entryToDel != null) { + // a value was removed + // notify listeners + fireEntryChanged(entryToDel, t, null); + } +// super.removeContextValue(klass, name); } + @Override + public <T> void setContextValue(T o, String name) { +// super.setContextValue(o, name); + if (name == null && PARENT_CONTEXT_ENTRY.accept2(o.getClass(), null)) { + setParentContext((JAXXContext) o); + return; + } + JAXXContextEntryDef<?> entry = getKey(name, o.getClass()); + // first remove entry + Object oldValue = remove0(o.getClass(), name); + if (oldValue != null) { + if (log.isDebugEnabled()) { + log.debug("remove value " + oldValue.getClass() + " for " + + entry); + } + } + // then can put safely + data.put(entry, o); + if (log.isDebugEnabled()) { + log.debug("[" + name + "] set value for entry " + entry); + } + // firezs + fireEntryChanged(entry.getKlass(), name, oldValue, o); + } + + /** + * To add a listen modification of the given entry in the context. + * + * @param entry the entry to listen + * @param name the property name to fire if necessary + * @param listener the listener to notify if entry has changed + * @since 2.0.1 + */ + public void addPropertyChangeListener(JAXXContextEntryDef<?> entry, + String name, + PropertyChangeListener listener) { + if (entryListened == null) { + entryListened = new HashMap<JAXXContextEntryDef<?>, String>(); + } + entryListened.put(entry, name); + if (log.isDebugEnabled()) { + log.debug("[" + name + "] for " + entry); + } + pcs.addPropertyChangeListener(name, listener); + } + + /** + * To remove a listen modification of the given entry in the context. + * + * @param entry the entry to listen + * @param name the property name to fire if necessary + * @param listener the listener to notify if entry has changed + * @since 2.0.1 + */ + public void removePropertyChangeListener(JAXXContextEntryDef<?> entry, + String name, + PropertyChangeListener listener) { + if (entryListened == null) { + entryListened = new HashMap<JAXXContextEntryDef<?>, String>(); + } + entryListened.remove(entry); + pcs.removePropertyChangeListener(name, listener); + } + public void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } - public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + + public void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener) { pcs.addPropertyChangeListener(propertyName, listener); } @@ -219,7 +305,8 @@ pcs.removePropertyChangeListener(listener); } - public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + public void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener) { pcs.removePropertyChangeListener(propertyName, listener); } @@ -227,7 +314,8 @@ return pcs.hasListeners(propertyName); } - public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { + public synchronized PropertyChangeListener[] getPropertyChangeListeners( + String propertyName) { return pcs.getPropertyChangeListeners(propertyName); } @@ -235,8 +323,78 @@ return pcs.getPropertyChangeListeners(); } - protected Object newInstance(Class<?> clazz) throws IllegalArgumentException { + protected void fireEntryChanged(Class<?> klass, + String name, + Object oldValue, + Object newValue) { + // a value was removed + if (entryListened != null) { + // look if the entry was listened + if (log.isTraceEnabled()) { + log.trace("data size : " + data.size()); + for (Entry<JAXXContextEntryDef<?>, Object> e : data.entrySet()) { + log.trace(e.getKey()); + } + } + JAXXContextEntryDef<?> entry2 = getEntry(klass, name); + if (log.isDebugEnabled()) { + log.debug("[" + name + "] : try to find a changer for " + entry2); + } + if (entry2 != null) { + // entry find directly on this context + String propertyName = entryListened.get(entry2); + if (log.isTraceEnabled()) { + log.trace("registred property name = " + propertyName); + } + if (propertyName != null) { + if (log.isDebugEnabled()) { + log.debug("will notify modification on " + entry2); + } + // fires the removed + firePropertyChange(propertyName, oldValue, newValue); + } + } + } + } + + protected void fireEntryChanged(JAXXContextEntryDef<?> entryDef, + Object oldValue, + Object newValue) { + // a value was removed + if (entryListened != null) { + // look if the entry was listened + + if (log.isTraceEnabled()) { + log.trace("data size : " + data.size()); + for (Entry<JAXXContextEntryDef<?>, Object> e : data.entrySet()) { + log.trace(e.getKey()); + } + } +// JAXXContextEntryDef<?> entry2 = getEntry(klass, name); +// if (log.isDebugEnabled()) { +// log.debug("[" + name + "] : try to find a changer for " + entry2); +// } + if (entryDef != null) { + // entry find directly on this context + String propertyName = entryListened.get(entryDef); + if (log.isTraceEnabled()) { + log.trace("registred property name = " + propertyName); + } + if (propertyName != null) { + if (log.isDebugEnabled()) { + log.debug("will notify modification on " + entryDef); + } + // fires the removed + firePropertyChange(propertyName, oldValue, newValue); + } + } + } + } + + protected Object newInstance(Class<?> clazz) throws + IllegalArgumentException { + Object value; Constructor<?> constructor; @@ -244,7 +402,8 @@ constructor = clazz.getConstructor(); // auto instanciate the class if (constructor == null) { - throw new IllegalArgumentException(clazz + " has no public constructor"); + throw new IllegalArgumentException( + clazz + " has no public constructor"); } } catch (NoSuchMethodException ex) { throw new IllegalArgumentException(ex); @@ -264,7 +423,11 @@ return value; } - protected Object newAccess(Class<?> clazz, Object parent, String methodName, String name) throws IllegalArgumentException { + protected Object newAccess( + Class<?> clazz, + Object parent, + String methodName, + String name) throws IllegalArgumentException { Object value; try { Method m = clazz.getMethod(methodName, String.class); @@ -281,7 +444,10 @@ } } - protected Object newAccess(Class<?> clazz, Object parent, String methodName) throws IllegalArgumentException { + protected Object newAccess( + Class<?> clazz, + Object parent, + String methodName) throws IllegalArgumentException { Object value; try { Method m = clazz.getMethod(methodName); @@ -298,7 +464,9 @@ } } - protected void firePropertyChange(String name, Object oldValue, Object newValue) { + protected void firePropertyChange(String name, + Object oldValue, + Object newValue) { pcs.firePropertyChange(name, oldValue, newValue); } } Modified: trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/DefaultJAXXContext.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/DefaultJAXXContext.java 2010-03-22 14:05:21 UTC (rev 1799) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/DefaultJAXXContext.java 2010-03-23 10:56:00 UTC (rev 1800) @@ -158,44 +158,62 @@ } protected JAXXContextEntryDef<?> getKey(String name, Class<?> klass) { + //FIXME-TC20100322 : must change this to have a store, we instanciate + // a new object each time we wants to deal with the context... return JAXXUtil.newContextEntryDef(name, klass); } @SuppressWarnings({"unchecked"}) - protected <T> T remove0(Class<T> klazz, String name) { - if (PARENT_CONTEXT_ENTRY.accept(klazz, name)) { + protected <T> T remove0(Class<T> klass, String name) { + if (PARENT_CONTEXT_ENTRY.accept(klass, name)) { JAXXContext old = getParentContext(); setParentContext(null); return (T) old; } - JAXXContextEntryDef<?> entry = null; - for (JAXXContextEntryDef<?> entryDef : data.keySet()) { - if (entryDef.accept(klazz, name)) { - entry = entryDef; - break; - } - } + JAXXContextEntryDef<?> entry = getEntry(klass, name); +// JAXXContextEntryDef<?> entry = null; +// for (JAXXContextEntryDef<?> entryDef : data.keySet()) { +// if (entryDef.accept(klass, name)) { +// entry = entryDef; +// break; +// } +// } if (entry != null) { return (T) data.remove(entry); } - if (JAXXContext.class.equals(klazz)) { + if (JAXXContext.class.equals(klass)) { return null; } - // try in parentContext + + // try on parent JAXXContext parent = getParentContext(); if (parent == null) { + // no parent, stop now, can not found entry return null; } if (parent instanceof DefaultJAXXContext) { - return ((DefaultJAXXContext) parent).remove0(klazz, name); + // try now on the parent + return ((DefaultJAXXContext) parent).remove0(klass, name); } - + + // can not find the entry anywhere, so says that nothing was removed return null; } + protected JAXXContextEntryDef<?> getEntry(Class<?> klass, String name) { + JAXXContextEntryDef<?> entry = null; + for (JAXXContextEntryDef<?> entryDef : data.keySet()) { + if (entryDef.accept(klass, name)) { + entry = entryDef; + break; + } + } + return entry; + } + protected JAXXContext getParentContext() { return parentContext; } Modified: trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/JAXXContextEntryDef.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/JAXXContextEntryDef.java 2010-03-22 14:05:21 UTC (rev 1799) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/context/JAXXContextEntryDef.java 2010-03-23 10:56:00 UTC (rev 1800) @@ -96,7 +96,6 @@ JAXXContextEntryDef<?> that = (JAXXContextEntryDef<?>) o; return klass.equals(that.klass) && !(name != null ? !name.equals(that.name) : that.name != null); - } @Override @@ -106,7 +105,7 @@ } public boolean accept(Class<?> klass, String name) { - if (klass == Object.class && this.klass != Object.class) { + if (Object.class.equals(klass) && !Object.class.equals(this.klass)) { // try on name only return this.name != null && name != null && this.name.equals(name); } Modified: trunk/jaxx-runtime/src/test/java/jaxx/runtime/context/DefaultApplicationContextTest.java =================================================================== --- trunk/jaxx-runtime/src/test/java/jaxx/runtime/context/DefaultApplicationContextTest.java 2010-03-22 14:05:21 UTC (rev 1799) +++ trunk/jaxx-runtime/src/test/java/jaxx/runtime/context/DefaultApplicationContextTest.java 2010-03-23 10:56:00 UTC (rev 1800) @@ -20,24 +20,37 @@ */ package jaxx.runtime.context; +import jaxx.runtime.JAXXUtil; import jaxx.runtime.context.DefaultApplicationContext.AutoLoad; import jaxx.runtime.context.DefaultApplicationContext.MethodAccess; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.*; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Arrays; +import java.util.List; + import static org.junit.Assert.*; -/** - * - * @author tchemit <chemit@codelutin.com> - */ +/** @author tchemit <chemit@codelutin.com> */ public class DefaultApplicationContextTest { + /** Logger */ + private static final Log log = + LogFactory.getLog(DefaultApplicationContextTest.class); + static int helloCount; + static int helloGetCount; + static final JAXXContextEntryDef<String> STRING_ENTRY = + JAXXUtil.newContextEntryDef("myStringEntryKey", String.class); + + static final JAXXContextEntryDef<List<String>> LIST_STRING_ENTRY = + JAXXUtil.newListContextEntryDef("myListEntryKey"); + @AutoLoad @MethodAccess(methodName = "hello", target = String.class) public static class Hello { @@ -51,6 +64,7 @@ return "hello " + name; } } + DefaultApplicationContext context; @BeforeClass @@ -130,4 +144,70 @@ context.getContextValue(Hello.class); assertEquals(2, helloCount); } + + static int yoCount; + + @Test + public void testEntryListener() { + + PropertyChangeListener listener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (log.isInfoEnabled()) { + log.info("changed detected on " + evt.getSource()); + } + yoCount++; + } + }; + context.addPropertyChangeListener(STRING_ENTRY, "myKey", listener); + + STRING_ENTRY.setContextValue(context, "myValue"); + + Assert.assertEquals(1, yoCount); + + STRING_ENTRY.removeContextValue(context); + + Assert.assertEquals(2, yoCount); + + context.removePropertyChangeListener(STRING_ENTRY, "myKey", listener); + + // test that nothing changed now + + STRING_ENTRY.setContextValue(context, "myValue"); + + Assert.assertEquals(2, yoCount); + + STRING_ENTRY.removeContextValue(context); + + Assert.assertEquals(2, yoCount); + + // test with a list entry + + context.addPropertyChangeListener(LIST_STRING_ENTRY, "myKey2", listener); + + LIST_STRING_ENTRY.setContextValue(context, Arrays.asList("myValue")); + + Assert.assertEquals(3, yoCount); + + LIST_STRING_ENTRY.removeContextValue(context); + + Assert.assertEquals(4, yoCount); + + + // test that nothing changed now + + context.removePropertyChangeListener(LIST_STRING_ENTRY, "myKey2", listener); + + LIST_STRING_ENTRY.setContextValue(context, Arrays.asList("myValue2")); + + Assert.assertEquals(4, yoCount); + + LIST_STRING_ENTRY.removeContextValue(context); + + Assert.assertEquals(4, yoCount); + + + } + + }
participants (1)
-
tchemit@users.nuiton.org