felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ma...@apache.org
Subject svn commit: r1567206 - in /felix/sandbox/marrs/dependencymanager-prototype/dm: src/dm/ src/dm/impl/ test/test/
Date Tue, 11 Feb 2014 16:21:32 GMT
Author: marrs
Date: Tue Feb 11 16:21:31 2014
New Revision: 1567206

URL: http://svn.apache.org/r1567206
Log:
Added basic support for component life cycle and callbacks. Plus a few tests. Still to go
are the dependency callbacks.

Added:
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentState.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/InvocationUtil.java
Removed:
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/State.java
Modified:
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Component.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentStateListener.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Dependency.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/ComponentImpl.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/DependencyImpl.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/EventImpl.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ComponentTest.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ConcurrencyTest.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/Ensure.java
    felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ServiceRaceTest.java

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Component.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Component.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Component.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Component.java Tue Feb 11 16:21:31
2014
@@ -8,6 +8,8 @@ import dm.impl.SerialExecutor;
  * A component. Has dependencies. 
  */
 public interface Component {
+	public Component setImplementation(Object implementation);
+	
 	public SerialExecutor getExecutor(); // shared between a component and its dependencies
 
 	public void add(Dependency d);

Added: felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentState.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentState.java?rev=1567206&view=auto
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentState.java (added)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentState.java Tue Feb
11 16:21:31 2014
@@ -0,0 +1,8 @@
+package dm;
+
+public enum ComponentState {
+	INACTIVE, 
+	WAITING_FOR_REQUIRED, 
+	INSTANTIATED_AND_WAITING_FOR_REQUIRED, 
+	TRACKING_OPTIONAL
+}

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentStateListener.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentStateListener.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentStateListener.java
(original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/ComponentStateListener.java
Tue Feb 11 16:21:31 2014
@@ -1,5 +1,5 @@
 package dm;
 
 public interface ComponentStateListener {
-	public void changed(State state);
+	public void changed(ComponentState state);
 }

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Dependency.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Dependency.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Dependency.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/Dependency.java Tue Feb 11 16:21:31
2014
@@ -7,11 +7,18 @@ public interface Dependency {
 	public void add(Component component);
 	public void remove(Component component);
 
+	/** Whenever the dependency changes state, this method is invoked with the Event containing
the new state information. */
 	public void changed(final Event e);
 
+	/** Invoked by the component when the dependency should start working. */
 	public void start();
+	/** Invoked by the component when the dependency should stop working. */
 	public void stop();
 
 	public boolean isAvailable();
+	
+	public boolean isRequired();
 
+	public boolean isInstanceBound();
+	public void setInstanceBound(boolean instanceBound);
 }

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/ComponentImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/ComponentImpl.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/ComponentImpl.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/ComponentImpl.java Tue
Feb 11 16:21:31 2014
@@ -1,19 +1,26 @@
 package dm.impl;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import dm.Component;
+import dm.ComponentState;
 import dm.ComponentStateListener;
 import dm.Dependency;
-import dm.State;
 
 public class ComponentImpl implements Component {
+    private static final Class[] VOID = new Class[] {};
 	private final SerialExecutor m_executor = new SerialExecutor(new Logger(null));
+	private ComponentState m_state = ComponentState.INACTIVE;
 	private List<Dependency> m_dependencies = new ArrayList<>();
-	private State m_state = new State(false);
-	private boolean m_available;
-	private List<ComponentStateListener> m_listeners = new ArrayList<>();
+	private List<ComponentStateListener> m_listeners = new CopyOnWriteArrayList<>();
+	private boolean m_isStarted;
+	
+	private Object m_componentDefinition;
+	private Object m_componentInstance;
 
 	public SerialExecutor getExecutor() {
 		return m_executor;
@@ -26,6 +33,10 @@ public class ComponentImpl implements Co
 			public void run() {
 				m_dependencies.add(d);
 				d.add(ComponentImpl.this);
+				if (!(m_state == ComponentState.INACTIVE)) {
+					d.setInstanceBound(true);
+					d.start();
+				}
 				handleChange();
 			}
 		});
@@ -36,6 +47,9 @@ public class ComponentImpl implements Co
 		getExecutor().execute(new Runnable() {
 			@Override
 			public void run() {
+				if (!(m_state == ComponentState.INACTIVE)) {
+					d.stop();
+				}
 				m_dependencies.remove(d);
 				d.remove(ComponentImpl.this);
 				handleChange();
@@ -47,9 +61,7 @@ public class ComponentImpl implements Co
 		getExecutor().execute(new Runnable() {
 			@Override
 			public void run() {
-				for (Dependency d : m_dependencies) {
-					d.start();
-				}
+				m_isStarted = true;
 				handleChange();
 			}
 		});
@@ -60,63 +72,192 @@ public class ComponentImpl implements Co
 		getExecutor().execute(new Runnable() {
 			@Override
 			public void run() {
-				for (Dependency d : m_dependencies) {
-					d.stop();
-				}
-				m_available = false;
+				m_isStarted = false;
+				handleChange();
 			}
 		});
-		
 	}
 
 	@Override
 	public void handleChange() {
-		State oldState;
-		State newState;
+		ComponentState oldState;
+		ComponentState newState;
 		do {
 			oldState = m_state;
-			newState = calculateNewState();
+			newState = calculateNewState(oldState);
 			m_state = newState;
 		}
 		while (performTransition(oldState, newState));
 	}
 
-	private boolean performTransition(State oldState, State newState) {
-		if (!oldState.isAvailable() && newState.isAvailable()) {
-			m_available = true;
-			
+	/** Based on the current state, calculate the new state. */
+	private ComponentState calculateNewState(ComponentState currentState) {
+		if (currentState == ComponentState.INACTIVE) {
+			if (m_isStarted) {
+				return ComponentState.WAITING_FOR_REQUIRED;
+			}
+		}
+		if (currentState == ComponentState.WAITING_FOR_REQUIRED) {
+			if (!m_isStarted) {
+				return ComponentState.INACTIVE;
+			}
+			if (allRequiredAvailable()) {
+				return ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+			}
+		}
+		if (currentState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+			if (m_isStarted && allRequiredAvailable()) {
+				if (allInstanceBoundAvailable()) {
+					return ComponentState.TRACKING_OPTIONAL;
+				}
+				return currentState;
+			}
+			return ComponentState.WAITING_FOR_REQUIRED;
+		}
+		if (currentState == ComponentState.TRACKING_OPTIONAL) {
+			if (m_isStarted && allRequiredAvailable() && allInstanceBoundAvailable())
{
+				return currentState;
+			}
+			return ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+		}
+		return currentState;
+	}
+
+	private boolean allRequiredAvailable() {
+		boolean available = true;
+		for (Dependency d : m_dependencies) {
+			if (d.isRequired() && !d.isInstanceBound()) {
+				if (!d.isAvailable()) {
+					available = false;
+					break;
+				}
+			}
+		}
+		return available;
+	}
+
+	private boolean allInstanceBoundAvailable() {
+		boolean available = true;
+		for (Dependency d : m_dependencies) {
+			if (d.isRequired() && d.isInstanceBound()) {
+				if (!d.isAvailable()) {
+					available = false;
+					break;
+				}
+			}
+		}
+		return available;
+	}
+
+	/** Perform all the actions associated with state transitions. Returns true if a transition
was performed. */
+	private boolean performTransition(ComponentState oldState, ComponentState newState) {
+		if (oldState == ComponentState.INACTIVE && newState == ComponentState.WAITING_FOR_REQUIRED)
{
+			for (Dependency d : m_dependencies) {
+				d.start();
+			}
 			notifyListeners(newState);
 			return true;
 		}
-		if (oldState.isAvailable() && !newState.isAvailable()) {
-			m_available = false;
-
+		if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED)
{
+			instantiateComponent();
+	        invoke("init" /* TODO make callbacks configurable */ );
+			notifyListeners(newState);
+			return true;
+		}
+		if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState
== ComponentState.TRACKING_OPTIONAL) {
+			invoke("start");
+			notifyListeners(newState);
+			return true;
+		}
+		if (oldState == ComponentState.TRACKING_OPTIONAL && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED)
{
+			invoke("stop");
+			notifyListeners(newState);
+			return true;
+		}
+		if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState
== ComponentState.WAITING_FOR_REQUIRED) {
+			invoke("destroy");
+			notifyListeners(newState);
+			destroyComponent();
+			return true;
+		}
+		if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INACTIVE)
{
+			for (Dependency d : m_dependencies) {
+				d.stop();
+			}
 			notifyListeners(newState);
 			return true;
 		}
 		return false;
 	}
 	
-	private void notifyListeners(State state) {
-		List<ComponentStateListener> listeners = new ArrayList(m_listeners);
-		for (ComponentStateListener l : listeners) {
-			l.changed(state);
+	private void instantiateComponent() {
+		// TODO add more complex factory instantiations of one or more components in a composition
here
+		if (m_componentDefinition instanceof Class) {
+			try {
+				m_componentInstance = createInstance((Class) m_componentDefinition);
+			}
+			catch (Exception e) {
+				e.printStackTrace();
+			}
+		}
+		else {
+			m_componentInstance = m_componentDefinition;
 		}
 	}
+	
+	private void destroyComponent() {
+		m_componentInstance = null;
+	}
+	
+    private void invoke(String name) {
+        if (name != null) {
+            // if a callback instance was specified, look for the method there, if not,
+            // ask the service for its composition instances
+        	
+        	// TODO
+//            Object[] instances = m_callbackInstance != null ? new Object[] { m_callbackInstance
} : getCompositionInstances();
+            
+            Object[] instances = { m_componentInstance };
+            invokeCallbackMethod(instances, name, 
+                new Class[][] {{ Component.class }, {}}, 
+                new Object[][] {{ this }, {}});
+        }
+    }
+    
+    public void invokeCallbackMethod(Object[] instances, String methodName, Class[][] signatures,
Object[][] parameters) {
+        for (int i = 0; i < instances.length; i++) {
+            try {
+                InvocationUtil.invokeCallbackMethod(instances[i], methodName, signatures,
parameters);
+            }
+            catch (NoSuchMethodException e) {
+                // if the method does not exist, ignore it
+            }
+            catch (InvocationTargetException e) {
+                // the method itself threw an exception, log that
+//                m_logger.log(Logger.LOG_WARNING, "Invocation of '" + methodName + "' failed.",
e.getCause());
+            }
+            catch (Exception e) {
+//                m_logger.log(Logger.LOG_WARNING, "Could not invoke '" + methodName + "'.",
e);
+            }
+        }
+    }
+	
+	
 
-	private State calculateNewState() {
-		boolean available = true;
-		for (Dependency d : m_dependencies) {
-			if (!d.isAvailable()) {
-				available = false;
-				break;
-			}
+    private Object createInstance(Class clazz) throws SecurityException, NoSuchMethodException,
InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
+		Constructor constructor = clazz.getConstructor(VOID);
+		constructor.setAccessible(true);
+        return constructor.newInstance(null);
+    }
+
+	private void notifyListeners(ComponentState state) {
+		for (ComponentStateListener l : m_listeners) {
+			l.changed(state);
 		}
-		return new State(available);
 	}
 
 	public boolean isAvailable() {
-		return m_available;
+		return m_state == ComponentState.TRACKING_OPTIONAL;
 	}
 
 	@Override
@@ -133,4 +274,10 @@ public class ComponentImpl implements Co
 	public List<Dependency> getDependencies() {
 		return m_dependencies;
 	}
+
+	@Override
+	public Component setImplementation(Object implementation) {
+		m_componentDefinition = implementation;
+		return this;
+	}
 }

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/DependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/DependencyImpl.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/DependencyImpl.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/DependencyImpl.java Tue
Feb 11 16:21:31 2014
@@ -7,7 +7,12 @@ import dm.Event;
 public class DependencyImpl implements Dependency {
 	private Component m_component;
 	private boolean m_available;
-	public void changed(final Event e) {
+	private boolean m_instanceBound;
+	
+	public synchronized void changed(final Event e) {
+		// since this method can be invoked by anyone from any thread, we need to
+		// pass on the event to a runnable that we execute using the component's
+		// executor
 		m_component.getExecutor().execute(new Runnable() {
 			@Override
 			public void run() {
@@ -25,7 +30,7 @@ public class DependencyImpl implements D
 	}
 
 	@Override
-	public void add(Component component) {
+	public synchronized void add(Component component) {
 		m_component = component;
 	}
 	@Override
@@ -35,6 +40,8 @@ public class DependencyImpl implements D
 
 	@Override
 	public void start() {
+		// you would normally start tracking this dependency here, so for example
+		// for a service dependency you might start a service tracker here
 	}
 
 	@Override
@@ -45,4 +52,15 @@ public class DependencyImpl implements D
 	public boolean isAvailable() {
 		return m_available;
 	}
+	
+	@Override
+	public boolean isRequired() {
+		return true;
+	}
+	public boolean isInstanceBound() {
+		return m_instanceBound;
+	}
+	public void setInstanceBound(boolean instanceBound) {
+		m_instanceBound = instanceBound;
+	}
 }

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/EventImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/EventImpl.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/EventImpl.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/EventImpl.java Tue Feb
11 16:21:31 2014
@@ -2,6 +2,9 @@ package dm.impl;
 
 import dm.Event;
 
+/* in real life, this event might contain a service reference and service instance
+ * or something similar
+ */
 public class EventImpl implements Event {
 
 	private final boolean m_available;

Added: felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/InvocationUtil.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/InvocationUtil.java?rev=1567206&view=auto
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/InvocationUtil.java (added)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/InvocationUtil.java Tue
Feb 11 16:21:31 2014
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package dm.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Utility methods for invoking callbacks. Lookups of callbacks are accellerated by using
a LRU cache.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InvocationUtil {
+    private static final Map /* <Key, Method> */ m_methodCache;
+    static {
+        int size = 2048;
+        // TODO enable this again
+//        try {
+//            String value = System.getProperty(DependencyManager.METHOD_CACHE_SIZE);
+//            if (value != null) {
+//                size = Integer.parseInt(value);
+//            }
+//        }
+//        catch (Exception e) {}
+        m_methodCache = new LRUMap(Math.max(size, 64));
+    }
+    
+    /**
+     * Invokes a callback method on an instance. The code will search for a callback method
with
+     * the supplied name and any of the supplied signatures in order, invoking the first
one it finds.
+     * 
+     * @param instance the instance to invoke the method on
+     * @param methodName the name of the method
+     * @param signatures the ordered list of signatures to look for
+     * @param parameters the parameter values to use for each potential signature
+     * @return whatever the method returns
+     * @throws NoSuchMethodException when no method could be found
+     * @throws IllegalArgumentException when illegal values for this methods arguments are
supplied 
+     * @throws IllegalAccessException when the method cannot be accessed
+     * @throws InvocationTargetException when the method that was invoked throws an exception
+     */
+    public static Object invokeCallbackMethod(Object instance, String methodName, Class[][]
signatures, Object[][] parameters) throws NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
+        Class currentClazz = instance.getClass();
+        while (currentClazz != null && currentClazz != Object.class) {
+            try {
+                return invokeMethod(instance, currentClazz, methodName, signatures, parameters,
false);
+            }
+            catch (NoSuchMethodException nsme) {
+                // ignore
+            }
+            currentClazz = currentClazz.getSuperclass();
+        }
+        throw new NoSuchMethodException(methodName);
+    }
+
+    /**
+     * Invoke a method on an instance.
+     * 
+     * @param object the instance to invoke the method on
+     * @param clazz the class of the instance
+     * @param name the name of the method
+     * @param signatures the signatures to look for in order
+     * @param parameters the parameter values for the signatures
+     * @param isSuper <code>true</code> if this is a superclass and we should
therefore not look for private methods
+     * @return whatever the method returns
+     * @throws NoSuchMethodException when no method could be found
+     * @throws IllegalArgumentException when illegal values for this methods arguments are
supplied 
+     * @throws IllegalAccessException when the method cannot be accessed
+     * @throws InvocationTargetException when the method that was invoked throws an exception
+     */
+    public static Object invokeMethod(Object object, Class clazz, String name, Class[][]
signatures, Object[][] parameters, boolean isSuper) throws NoSuchMethodException, InvocationTargetException,
IllegalArgumentException, IllegalAccessException {
+        if (object == null) {
+            throw new IllegalArgumentException("Instance cannot be null");
+        }
+        if (clazz == null) {
+            throw new IllegalArgumentException("Class cannot be null");
+        }
+        
+        // if we're talking to a proxy here, dig one level deeper to expose the
+        // underlying invocation handler (we do the same for injecting instances)
+        if (Proxy.isProxyClass(clazz)) {
+            object = Proxy.getInvocationHandler(object);
+            clazz = object.getClass();
+        }
+        
+        Method m = null;
+        for (int i = 0; i < signatures.length; i++) {
+            Class[] signature = signatures[i];
+            m = getDeclaredMethod(clazz, name, signature, isSuper);
+            if (m != null) {
+                return m.invoke(object, parameters[i]);
+            }
+        }
+        throw new NoSuchMethodException(name);
+    }
+    
+    private static Method getDeclaredMethod(Class clazz, String name, Class[] signature,
boolean isSuper) {
+        // first check our cache
+        Key key = new Key(clazz, name, signature);
+        Method m = null;
+        synchronized (m_methodCache) {
+            m = (Method) m_methodCache.get(key);
+            if (m != null) {
+                return m;
+            }
+            else if (m_methodCache.containsKey(key)) {
+                // the key is in our cache, it just happens to have a null value
+                return null;
+            }
+        }
+        // then do a lookup
+        try {
+            m = clazz.getDeclaredMethod(name, signature);
+            if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
+                m.setAccessible(true);
+            }
+        }
+        catch (NoSuchMethodException e) {
+            // ignore
+        }
+        synchronized (m_methodCache) {
+            m_methodCache.put(key, m);
+        }
+        return m;
+    }
+    
+    public static class Key {
+        private final Class m_clazz;
+        private final String m_name;
+        private final Class[] m_signature;
+
+        public Key(Class clazz, String name, Class[] signature) {
+            m_clazz = clazz;
+            m_name = name;
+            m_signature = signature;
+        }
+
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((m_clazz == null) ? 0 : m_clazz.hashCode());
+            result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
+            result = prime * result + Arrays.hashCode(m_signature);
+            return result;
+        }
+
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            Key other = (Key) obj;
+            if (m_clazz == null) {
+                if (other.m_clazz != null)
+                    return false;
+            }
+            else if (!m_clazz.equals(other.m_clazz))
+                return false;
+            if (m_name == null) {
+                if (other.m_name != null)
+                    return false;
+            }
+            else if (!m_name.equals(other.m_name))
+                return false;
+            if (!Arrays.equals(m_signature, other.m_signature))
+                return false;
+            return true;
+        }
+    }
+    
+    public static class LRUMap extends LinkedHashMap {
+        private final int m_size;
+        
+        public LRUMap(int size) {
+            m_size = size;
+        }
+        
+        protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
+            return size() > m_size;
+        }
+    }
+}

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ComponentTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ComponentTest.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ComponentTest.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ComponentTest.java Tue Feb
11 16:21:31 2014
@@ -4,17 +4,23 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import dm.Component;
+import dm.ComponentState;
 import dm.ComponentStateListener;
 import dm.Dependency;
-import dm.State;
 import dm.impl.ComponentImpl;
 import dm.impl.DependencyImpl;
 import dm.impl.EventImpl;
 
 public class ComponentTest {
+	static class MyComponent {
+		public MyComponent() {
+		}
+	}
+	
 	@Test
 	public void createStartAndStopComponent() {
 		Component c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
 		Assert.assertEquals("should not be available until started", false, c.isAvailable());
 		c.start();
 		Assert.assertEquals("should be available", true, c.isAvailable());
@@ -23,8 +29,64 @@ public class ComponentTest {
 	}
 	
 	@Test
+	public void testInitCallbackOfComponent() {
+		final Ensure e = new Ensure();
+		Component c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			void init() {
+				e.step(2);
+			}
+			void start() {
+				e.step(3);
+			}
+			void stop() {
+				e.step(5);
+			}
+			void destroy() {
+				e.step(6);
+			}
+		});
+		e.step(1);
+		c.start();
+		e.step(4);
+		c.stop();
+		e.step(7);
+	}
+
+	@Test
+	public void testAddDependencyFromInitCallback() {
+		final Ensure e = new Ensure();
+		final Dependency d = new DependencyImpl();
+		Component c = new ComponentImpl();
+		c.setImplementation(new Object() {
+			void init(Component c) {
+				e.step(2);
+				c.add(d);
+			}
+			void start() {
+				e.step(4);
+			}
+			void stop() {
+				e.step(6);
+			}
+			void destroy() {
+				e.step(7);
+			}
+		});
+		e.step(1);
+		c.start();
+		e.step(3);
+		d.changed(new EventImpl(true));
+		e.step(5);
+		d.changed(new EventImpl(false));
+		c.stop();
+		e.step(8);
+	}
+
+	@Test
 	public void createComponentAddDependencyAndStartComponent() {
 		Component c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
 		Dependency d = new DependencyImpl();
 		c.add(d);
 		c.start();
@@ -36,6 +98,7 @@ public class ComponentTest {
 	@Test
 	public void createComponentStartItAndAddDependency() {
 		Component c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
 		Dependency d = new DependencyImpl();
 		c.start();
 		Assert.assertEquals("should be available when started", true, c.isAvailable());
@@ -49,6 +112,7 @@ public class ComponentTest {
 	@Test
 	public void createComponentStartItAddDependencyAndMakeDependencyAvailable() {
 		Component c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
 		Dependency d = new DependencyImpl();
 		c.start();
 		c.add(d);
@@ -66,10 +130,11 @@ public class ComponentTest {
 	@Test
 	public void createComponentStartItAddDependencyAndListenerMakeDependencyAvailableAndUnavailableImmediately()
{
 		Component c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
 		final Dependency d = new DependencyImpl();
 		ComponentStateListener l = new ComponentStateListener() {
 			@Override
-			public void changed(State state) {
+			public void changed(ComponentState state) {
 				// make the dependency unavailable
 				d.changed(new EventImpl(false));
 			}
@@ -95,6 +160,7 @@ public class ComponentTest {
 	@Test
 	public void createComponentAddTwoDependenciesMakeBothAvailableAndUnavailable() {
 		Component c = new ComponentImpl();
+		c.setImplementation(MyComponent.class);
 		Dependency d1 = new DependencyImpl();
 		Dependency d2 = new DependencyImpl();
 		c.start();

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ConcurrencyTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ConcurrencyTest.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ConcurrencyTest.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ConcurrencyTest.java Tue
Feb 11 16:21:31 2014
@@ -1,14 +1,17 @@
 package test;
 
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 
 import org.junit.Assert;
 import org.junit.Test;
 
 import dm.Component;
+import dm.ComponentState;
 import dm.ComponentStateListener;
 import dm.Dependency;
-import dm.State;
 import dm.impl.ComponentImpl;
 import dm.impl.DependencyImpl;
 import dm.impl.EventImpl;
@@ -33,7 +36,7 @@ public class ConcurrencyTest {
 		};
 		ComponentStateListener l = new ComponentStateListener() {
 			@Override
-			public void changed(State state) {
+			public void changed(ComponentState state) {
 				try {
 					c.remove(this);
 					// launch a second thread interacting with our component and block this thread until
the
@@ -70,4 +73,26 @@ public class ConcurrencyTest {
 		Assert.assertEquals("component should not be available", false, c.isAvailable());
 	}
 
+	@Test
+	public void createComponentAddAndRemoveDependenciesInParallelThreads() throws Exception
{
+		final Component c = new ComponentImpl();
+		ExecutorService e = Executors.newFixedThreadPool(16); 
+		c.start();
+		for (int i = 0; i < 1000; i++) {
+			e.execute(new Runnable() {
+				@Override
+				public void run() {
+					Dependency d = new DependencyImpl();
+					c.add(d);
+//					d.changed(new EventImpl(true));
+//					d.changed(new EventImpl(false));
+					c.remove(d);
+				}});
+		}
+		e.shutdown();
+		e.awaitTermination(10, TimeUnit.SECONDS);
+//		Assert.assertEquals("component should not be available", false, c.isAvailable());
+		c.stop();
+		Assert.assertEquals("component should not be available", false, c.isAvailable());
+	}
 }

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/Ensure.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/Ensure.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/Ensure.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/Ensure.java Tue Feb 11 16:21:31
2014
@@ -38,7 +38,7 @@ public class Ensure {
      * @param nr the step we are in
      */
     public synchronized void step(int nr) {
-        step ++;
+        step++;
         Assert.assertEquals(nr, step);
         if (DEBUG) {
             String info = getLineInfo(3);

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ServiceRaceTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ServiceRaceTest.java?rev=1567206&r1=1567205&r2=1567206&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ServiceRaceTest.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/test/test/ServiceRaceTest.java Tue
Feb 11 16:21:31 2014
@@ -8,9 +8,9 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import dm.Component;
+import dm.ComponentState;
 import dm.ComponentStateListener;
 import dm.Dependency;
-import dm.State;
 import dm.impl.ComponentImpl;
 import dm.impl.DependencyImpl;
 import dm.impl.EventImpl;
@@ -21,7 +21,7 @@ import dm.impl.EventImpl;
 public class ServiceRaceTest extends TestBase {
     final static int STEP_WAIT = 10000;
     final static int DEPENDENCIES = 10;
-    final static int LOOPS = 100000;
+    final static int LOOPS = 1000;
 
     // Executor used to bind/unbind service dependencies.
     ExecutorService m_execDependencies;
@@ -74,10 +74,11 @@ public class ServiceRaceTest extends Tes
         // Create a client listener tracking client activation 
         ComponentStateListener clientListener = new ComponentStateListener() {
             @Override
-            public void changed(State state) {
-                Assert.assertTrue(state.isAvailable());
-                clientStarted.step(1); // Our component must be started at most one time.
-                debug("Client started");
+            public void changed(ComponentState state) {
+            	if (state == ComponentState.TRACKING_OPTIONAL) {
+            		clientStarted.step(1); // Our component must be started at most one time.
+            		debug("Client started");
+            	}
             }
         };
         client.add(clientListener);
@@ -89,6 +90,8 @@ public class ServiceRaceTest extends Tes
             client.add(dependencies[i]);
         }
         
+        client.start();
+        
         // Activate the client service dependencies concurrently.
         for (int i = 0; i < DEPENDENCIES; i++) {
             final Dependency dep = dependencies[i];
@@ -101,16 +104,21 @@ public class ServiceRaceTest extends Tes
         
         // Ensure that client has been started at most one time.
         clientStarted.waitForStep(1, 10000);
+        
+//        System.out.println("DONE");
+//        System.exit(5);
+        
         client.remove(clientListener);
 
         // Stop the client service dependencies concurrently
         final Ensure clientStopped = new Ensure(false);
         clientListener = new ComponentStateListener() {
             @Override
-            public void changed(State state) {
-                Assert.assertFalse(state.isAvailable());
-                clientStopped.step(1); // Our component must be started at most one time.
-                debug("Client stopped");
+            public void changed(ComponentState state) {
+            	if (state == ComponentState.WAITING_FOR_REQUIRED) {
+            		clientStopped.step(1); // Our component must be started at most one time.
+            		debug("Client stopped");
+            	}
             }
         };
         client.add(clientListener);



Mime
View raw message