Author: davidb
Date: Tue Jan 5 11:02:04 2010
New Revision: 895985
URL: http://svn.apache.org/viewvc?rev=895985&view=rev
Log:
Added remove functionality for local discovery.
Additional tests for local discovery.
Modified:
cxf/dosgi/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/LocalDiscovery.java
cxf/dosgi/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/LocalDiscoveryTest.java
Modified:
cxf/dosgi/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/LocalDiscovery.java
URL:
http://svn.apache.org/viewvc/cxf/dosgi/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/LocalDiscovery.java?rev=895985&r1=895984&r2=895985&view=diff
==============================================================================
---
cxf/dosgi/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/LocalDiscovery.java
(original)
+++
cxf/dosgi/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/LocalDiscovery.java
Tue Jan 5 11:02:04 2010
@@ -19,13 +19,14 @@
package org.apache.cxf.dosgi.discovery.local;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
@@ -61,39 +62,26 @@
@Override
public Object addingService(ServiceReference reference) {
- System.out.println("@@@@ " + reference);
- for (String key : reference.getPropertyKeys()) {
- Object val = reference.getProperty(key);
- if (val.getClass().isArray()) {
- val = Arrays.asList((Object []) val);
- }
- System.out.println("-----" + key + ":" + val);
- }
Object svc = super.addingService(reference);
- cacheTracker(reference, svc);
+ registerTracker(reference, svc);
return svc;
}
@Override
- public void modifiedService(ServiceReference reference,
- Object service) {
- System.out.println("#### " + reference);
- for (String key : reference.getPropertyKeys()) {
- Object val = reference.getProperty(key);
- if (val.getClass().isArray()) {
- val = Arrays.asList((Object []) val);
- }
- System.out.println("-----" + key + ":" + val);
- }
-
- cacheTracker(reference, service);
+ public void modifiedService(ServiceReference reference, Object
service) {
+ super.modifiedService(reference, service);
+ clearTracker(service);
+
+ // This may cause duplicate registrations of remote services,
+ // but that's fine and should be filtered out on another level.
+ // See Remove Service Admin spec section 122.6.3
+ registerTracker(reference, service);
}
@Override
- public void removedService(ServiceReference reference,
- Object service) {
- // TODO Auto-generated method stub
+ public void removedService(ServiceReference reference, Object
service) {
super.removedService(reference, service);
+ clearTracker(service);
}
};
@@ -116,7 +104,7 @@
}
}
- protected void cacheTracker(ServiceReference reference, Object svc) {
+ void registerTracker(ServiceReference reference, Object svc) {
if (svc instanceof EndpointListener) {
EndpointListener listener = (EndpointListener) svc;
Collection<String> filters = addListener(reference, listener);
@@ -124,6 +112,16 @@
}
}
+ void clearTracker(Object svc) {
+ if (svc instanceof EndpointListener) {
+ EndpointListener listener = (EndpointListener) svc;
+ removeListener(listener);
+ // If the tracker was removed or the scope was changed this
doesn't require
+ // additional callbacks on the tracker. Its the responsibility of
the tracker
+ // itself to clean up any orphans. See Remote Service Admin spec
122.6.3
+ }
+ }
+
private Collection<String> addListener(ServiceReference reference,
EndpointListener listener) {
List<String> filters =
@@ -145,6 +143,21 @@
return filters;
}
+
+ private void removeListener(EndpointListener listener) {
+ Collection<String> filters = listenerToFilters.remove(listener);
+ if (filters == null) {
+ return;
+ }
+
+ for (String filter : filters) {
+ Collection<EndpointListener> listeners =
filterToListeners.get(filter);
+ if (listeners == null) {
+ continue;
+ }
+ listeners.remove(listener);
+ }
+ }
public void shutDown() {
bundleContext.removeBundleListener(this);
@@ -158,7 +171,7 @@
findDeclaredRemoteServices(be.getBundle());
break;
case BundleEvent.STOPPING:
- // TODO
+ removeServicesDeclaredInBundle(be.getBundle());
break;
}
}
@@ -171,10 +184,24 @@
}
}
+ private void removeServicesDeclaredInBundle(Bundle bundle) {
+ for (Iterator<Entry<EndpointDescription, Bundle>> i =
endpointDescriptions.entrySet().iterator(); i.hasNext(); ) {
+ Entry<EndpointDescription, Bundle> entry = i.next();
+ if (bundle.equals(entry.getValue())) {
+ removedEndpointDescription(entry.getKey());
+ i.remove();
+ }
+ }
+ }
+
private void addedEndpointDescription(EndpointDescription ed) {
triggerCallbacks(ed, true);
}
+ private void removedEndpointDescription(EndpointDescription ed) {
+ triggerCallbacks(ed, false);
+ }
+
private void triggerCallbacks(EndpointDescription ed, boolean added) {
for (Map.Entry<EndpointListener, Collection<String>> entry :
listenerToFilters.entrySet()) {
for (String match : entry.getValue()) {
Modified:
cxf/dosgi/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/LocalDiscoveryTest.java
URL:
http://svn.apache.org/viewvc/cxf/dosgi/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/LocalDiscoveryTest.java?rev=895985&r1=895984&r2=895985&view=diff
==============================================================================
---
cxf/dosgi/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/LocalDiscoveryTest.java
(original)
+++
cxf/dosgi/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/LocalDiscoveryTest.java
Tue Jan 5 11:02:04 2010
@@ -21,10 +21,12 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -145,17 +147,50 @@
ld.bundleChanged(be0);
assertEquals(0, ld.endpointDescriptions.size());
+ // Create an EndpointListener
+ final Hashtable<String, Object> props = new Hashtable<String,
Object>();
+ props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE, "(objectClass=*)");
+ ServiceReference sr = EasyMock.createMock(ServiceReference.class);
+
EasyMock.expect(sr.getPropertyKeys()).andReturn(props.keySet().toArray(new
String [] {})).anyTimes();
+ EasyMock.expect(sr.getProperty((String)
EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return props.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sr);
+
+ EndpointListener el = EasyMock.createMock(EndpointListener.class);
+ el.endpointAdded((EndpointDescription) EasyMock.anyObject(),
EasyMock.eq("(objectClass=*)"));
+ EasyMock.expectLastCall();
+ EasyMock.replay(el);
+ ld.registerTracker(sr, el);
+
+ // Start the bundle
BundleEvent be = new BundleEvent(BundleEvent.STARTED, bundle);
ld.bundleChanged(be);
assertEquals(1, ld.endpointDescriptions.size());
EndpointDescription ed =
ld.endpointDescriptions.keySet().iterator().next();
assertEquals("http://somewhere:12345", ed.getRemoteURI());
assertSame(bundle, ld.endpointDescriptions.get(ed));
+
+ EasyMock.verify(el);
+
+ // Stop the bundle
+ EasyMock.reset(el);
+ el.endpointRemoved((EndpointDescription) EasyMock.anyObject(),
EasyMock.eq("(objectClass=*)"));
+ EasyMock.expectLastCall();
+ EasyMock.replay(el);
+
+ BundleEvent be1 = new BundleEvent(BundleEvent.STOPPING, bundle);
+ ld.bundleChanged(be1);
+ assertEquals(0, ld.endpointDescriptions.size());
+
+ EasyMock.verify(el);
}
- public void testAddingService() throws Exception {
+ public void testEndpointListenerService() throws Exception {
LocalDiscovery ld = getLocalDiscovery();
- ///
+
Bundle bundle = EasyMock.createMock(Bundle.class);
EasyMock.expect(bundle.getState()).andReturn(Bundle.ACTIVE);
Dictionary<String, Object> headers = new Hashtable<String, Object>();
@@ -169,7 +204,7 @@
BundleEvent be = new BundleEvent(BundleEvent.STARTED, bundle);
ld.bundleChanged(be);
assertEquals(2, ld.endpointDescriptions.size());
- ///
+
final Hashtable<String, Object> props = new Hashtable<String,
Object>();
props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE,
"(objectClass=org.example.ClassA)");
ServiceReference sr = EasyMock.createMock(ServiceReference.class);
@@ -185,6 +220,7 @@
EasyMock.reset(ld.bundleContext);
EndpointListener el = EasyMock.createMock(EndpointListener.class);
EasyMock.expect(ld.bundleContext.getService(sr)).andReturn(el);
+ EasyMock.expect(ld.bundleContext.ungetService(sr)).andReturn(true);
EasyMock.expect(ld.bundleContext.createFilter((String)
EasyMock.anyObject())).andAnswer(new IAnswer<Filter>() {
public Filter answer() throws Throwable {
return FrameworkUtil.createFilter((String)
EasyMock.getCurrentArguments()[0]);
@@ -197,6 +233,7 @@
EasyMock.expectLastCall();
EasyMock.replay(el);
+ // Add the EndpointListener Service
assertEquals("Precondition failed", 0, ld.listenerToFilters.size());
assertEquals("Precondition failed", 0, ld.filterToListeners.size());
assertSame(el, ld.listenerTracker.addingService(sr));
@@ -207,14 +244,150 @@
assertEquals(Collections.singletonList(el),
ld.filterToListeners.get("(objectClass=org.example.ClassA)"));
EasyMock.verify(el);
+
+ // Modify the EndpointListener Service
+ // no need to reset the mock for this...
+ props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE,
"(|(objectClass=org.example.ClassA)(objectClass=org.example.ClassB))");
+
+ EasyMock.reset(el);
+ final Set<String> actualEndpoints = new HashSet<String>();
+ el.endpointAdded((EndpointDescription) EasyMock.anyObject(),
+
EasyMock.eq("(|(objectClass=org.example.ClassA)(objectClass=org.example.ClassB))"));
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ EndpointDescription ed = (EndpointDescription)
EasyMock.getCurrentArguments()[0];
+ actualEndpoints.addAll(ed.getInterfaces());
+ return null;
+ }
+ }).times(2);
+ EasyMock.replay(el);
+
+ ld.listenerTracker.modifiedService(sr, el);
+ assertEquals(1, ld.listenerToFilters.size());
+
assertEquals(Arrays.asList("(|(objectClass=org.example.ClassA)(objectClass=org.example.ClassB))"),
+ ld.listenerToFilters.get(el));
+ assertEquals(2, ld.filterToListeners.size());
+ assertEquals(Collections.singletonList(el),
+
ld.filterToListeners.get("(|(objectClass=org.example.ClassA)(objectClass=org.example.ClassB))"));
+ assertEquals(0,
ld.filterToListeners.get("(objectClass=org.example.ClassA)").size());
+
+ EasyMock.verify(el);
+ Set<String> expectedEndpoints = new
HashSet<String>(Arrays.asList("org.example.ClassA", "org.example.ClassB"));
+ assertEquals(expectedEndpoints, actualEndpoints);
+
+ // Remove the EndpointListener Service
+ ld.listenerTracker.removedService(sr, el);
+ assertEquals(0, ld.listenerToFilters.size());
+ assertEquals(2, ld.filterToListeners.size());
+ Iterator<Collection<EndpointListener>> valIter =
ld.filterToListeners.values().iterator();
+ assertEquals(0, valIter.next().size());
+ assertEquals(0, valIter.next().size());
}
+
+ public void testRegisterTracker() throws Exception {
+ LocalDiscovery ld = getLocalDiscovery();
- private LocalDiscovery getLocalDiscovery() throws InvalidSyntaxException {
- Filter filter = EasyMock.createMock(Filter.class);
- EasyMock.replay(filter);
+ final Hashtable<String, Object> props = new Hashtable<String,
Object>();
+ props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE,
"(objectClass=Aaaa)");
+ ServiceReference sr = EasyMock.createMock(ServiceReference.class);
+
EasyMock.expect(sr.getPropertyKeys()).andReturn(props.keySet().toArray(new
String [] {})).anyTimes();
+ EasyMock.expect(sr.getProperty((String)
EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return props.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sr);
+
+ EndpointListener el = EasyMock.createMock(EndpointListener.class);
+ EasyMock.replay(el);
+ assertEquals("Precondition failed", 0, ld.listenerToFilters.size());
+ assertEquals("Precondition failed", 0, ld.filterToListeners.size());
+ ld.registerTracker(sr, el);
+
+ assertEquals(1, ld.listenerToFilters.size());
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"),
ld.listenerToFilters.get(el));
+ assertEquals(1, ld.filterToListeners.size());
+ assertEquals(Collections.singletonList(el),
ld.filterToListeners.get("(objectClass=Aaaa)"));
+
+ // Add another one with the same scope filter
+ ServiceReference sr2 = EasyMock.createMock(ServiceReference.class);
+
EasyMock.expect(sr2.getPropertyKeys()).andReturn(props.keySet().toArray(new
String [] {})).anyTimes();
+ EasyMock.expect(sr2.getProperty((String)
EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return props.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sr2);
+
+ EndpointListener el2 = EasyMock.createMock(EndpointListener.class);
+ EasyMock.replay(el2);
+ ld.registerTracker(sr2, el2);
+
+ assertEquals(2, ld.listenerToFilters.size());
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"),
ld.listenerToFilters.get(el));
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"),
ld.listenerToFilters.get(el2));
+
+ assertEquals(1, ld.filterToListeners.size());
+ assertEquals(Arrays.asList(el, el2),
ld.filterToListeners.get("(objectClass=Aaaa)"));
+
+ // Add another listener with a multi-value scope
+ final Hashtable<String, Object> props2 = new Hashtable<String,
Object>();
+ props2.put(EndpointListener.ENDPOINT_LISTENER_SCOPE,
Arrays.asList("(objectClass=X)","(objectClass=Y)"));
+ ServiceReference sr3 = EasyMock.createMock(ServiceReference.class);
+
EasyMock.expect(sr3.getPropertyKeys()).andReturn(props2.keySet().toArray(new
String [] {})).anyTimes();
+ EasyMock.expect(sr3.getProperty((String)
EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return props2.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sr3);
+
+ EndpointListener el3 = EasyMock.createMock(EndpointListener.class);
+ EasyMock.replay(el3);
+ ld.registerTracker(sr3, el3);
+
+ assertEquals(3, ld.listenerToFilters.size());
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"),
ld.listenerToFilters.get(el));
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"),
ld.listenerToFilters.get(el2));
+ assertEquals(Arrays.asList("(objectClass=X)","(objectClass=Y)"),
ld.listenerToFilters.get(el3));
+
+ assertEquals(3, ld.filterToListeners.size());
+ assertEquals(Arrays.asList(el, el2),
ld.filterToListeners.get("(objectClass=Aaaa)"));
+ assertEquals(Collections.singletonList(el3),
ld.filterToListeners.get("(objectClass=X)"));
+ assertEquals(Collections.singletonList(el3),
ld.filterToListeners.get("(objectClass=Y)"));
+ }
+
+ public void testClearTracker() throws Exception {
+ LocalDiscovery ld = getLocalDiscovery();
+
+ EndpointListener el = EasyMock.createMock(EndpointListener.class);
+ ld.listenerToFilters.put(el, new
ArrayList<String>(Arrays.asList("(a=b)", "(objectClass=foo.bar.Bheuaark)")));
+ ld.filterToListeners.put("(a=b)", new
ArrayList<EndpointListener>(Arrays.asList(el)));
+ ld.filterToListeners.put("(objectClass=foo.bar.Bheuaark)", new
ArrayList<EndpointListener>(Arrays.asList(el)));
+
+ ld.clearTracker("foobar"); // should not barf
+
+ assertEquals(1, ld.listenerToFilters.size());
+ assertEquals(2, ld.filterToListeners.size());
+ assertEquals(1,
ld.filterToListeners.values().iterator().next().size());
+ ld.clearTracker(EasyMock.createMock(EndpointListener.class));
+ assertEquals(1, ld.listenerToFilters.size());
+ assertEquals(2, ld.filterToListeners.size());
+ assertEquals(1,
ld.filterToListeners.values().iterator().next().size());
+ ld.clearTracker(el);
+ assertEquals(0, ld.listenerToFilters.size());
+ assertEquals(2, ld.filterToListeners.size());
+ assertEquals(0,
ld.filterToListeners.values().iterator().next().size());
+ }
+
+ private LocalDiscovery getLocalDiscovery() throws InvalidSyntaxException {
BundleContext bc = EasyMock.createMock(BundleContext.class);
-
EasyMock.expect(bc.createFilter("(objectClass=org.osgi.service.remoteserviceadmin.EndpointListener)")).andReturn(filter);
+ EasyMock.expect(bc.createFilter((String)
EasyMock.anyObject())).andAnswer(new IAnswer<Filter>() {
+ public Filter answer() throws Throwable {
+ return FrameworkUtil.createFilter((String)
EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
bc.addServiceListener((ServiceListener) EasyMock.anyObject(),
EasyMock.eq("(objectClass=org.osgi.service.remoteserviceadmin.EndpointListener)"));
EasyMock.expectLastCall();
@@ -226,5 +399,5 @@
LocalDiscovery ld = new LocalDiscovery(bc);
return ld;
- }
+ }
}