You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2013/10/22 05:13:23 UTC
svn commit: r1534467 [3/5] - in /karaf/trunk: ./
assemblies/features/framework/src/main/feature/
assemblies/features/framework/src/main/resources/resources/etc/
itests/src/test/java/org/apache/karaf/itests/
jaas/command/src/main/java/org/apache/karaf/j...
Added: karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/GuardProxyCatalogTest.java
URL: http://svn.apache.org/viewvc/karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/GuardProxyCatalogTest.java?rev=1534467&view=auto
==============================================================================
--- karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/GuardProxyCatalogTest.java (added)
+++ karaf/trunk/service/guard/src/test/java/org/apache/karaf/service/guard/impl/GuardProxyCatalogTest.java Tue Oct 22 03:13:20 2013
@@ -0,0 +1,1651 @@
+/*
+ * 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 org.apache.karaf.service.guard.impl;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+
+import org.apache.aries.proxy.ProxyManager;
+import org.apache.aries.proxy.impl.AsmProxyManager;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.apache.karaf.service.guard.impl.GuardProxyCatalog.CreateProxyRunnable;
+import org.apache.karaf.service.guard.impl.GuardProxyCatalog.ServiceRegistrationHolder;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class GuardProxyCatalogTest {
+ // Some assertions fail when run under a code coverage tool, they are skipped when this is set to true
+ private static final boolean runningUnderCoverage = false; // set to false before committing any changes
+
+ @Test
+ public void testGuardProxyCatalog() throws Exception {
+ Bundle b = EasyMock.createMock(Bundle.class);
+ EasyMock.expect(b.getBundleId()).andReturn(9823L).anyTimes();
+ EasyMock.replay(b);
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ bc.addServiceListener(EasyMock.isA(ServiceListener.class));
+ EasyMock.expectLastCall().once();
+ String caFilter = "(&(objectClass=org.osgi.service.cm.ConfigurationAdmin)"
+ + "(!(" + GuardProxyCatalog.PROXY_SERVICE_KEY + "=*)))";
+ EasyMock.expect(bc.createFilter(caFilter)).andReturn(FrameworkUtil.createFilter(caFilter)).anyTimes();
+ String pmFilter = "(&(objectClass=org.apache.aries.proxy.ProxyManager)"
+ + "(!(" + GuardProxyCatalog.PROXY_SERVICE_KEY + "=*)))";
+ EasyMock.expect(bc.createFilter(pmFilter)).andReturn(FrameworkUtil.createFilter(pmFilter)).anyTimes();
+ EasyMock.replay(bc);
+
+ GuardProxyCatalog gpc = new GuardProxyCatalog(bc);
+ assertTrue("Service Tracker for ConfigAdmin should be opened", gpc.configAdminTracker.getTrackingCount() != -1);
+ assertTrue("Service Tracker for ProxyManager should be opened", gpc.proxyManagerTracker.getTrackingCount() != -1);
+
+ EasyMock.verify(bc);
+
+ // Add some more behaviour checks to the bundle context
+ EasyMock.reset(bc);
+ bc.removeServiceListener(EasyMock.isA(ServiceListener.class));
+ EasyMock.expectLastCall().once();
+ EasyMock.replay(bc);
+
+ gpc.close();
+ assertEquals("Service Tracker for ConfigAdmin should be closed", -1, gpc.configAdminTracker.getTrackingCount());
+ assertEquals("Service Tracker for ProxyManager should be closed", -1, gpc.proxyManagerTracker.getTrackingCount());
+
+ EasyMock.verify(bc);
+ }
+
+ @Test
+ public void testIsProxy() throws Exception {
+ BundleContext bc = mockBundleContext();
+
+ GuardProxyCatalog gpc = new GuardProxyCatalog(bc);
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(GuardProxyCatalog.PROXY_SERVICE_KEY, Boolean.TRUE);
+ assertTrue(gpc.isProxy(mockServiceReference(props)));
+ assertFalse(gpc.isProxy(mockServiceReference(new Hashtable<String, Object>())));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testHandleProxificationForHook() throws Exception {
+ Dictionary<String, Object> config = new Hashtable<String, Object>();
+ config.put(Constants.SERVICE_PID, GuardProxyCatalog.SERVICE_ACL_PREFIX + "foo");
+ config.put(GuardProxyCatalog.SERVICE_GUARD_KEY, "(a>=5)");
+ BundleContext bc = mockConfigAdminBundleContext(config);
+ GuardProxyCatalog gpc = new GuardProxyCatalog(bc);
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(Constants.SERVICE_ID, 13L);
+ props.put("a", "6");
+ props.put(GuardProxyCatalog.PROXY_SERVICE_KEY, Boolean.TRUE);
+ ServiceReference<?> sref2 = mockServiceReference(props);
+ assertFalse("Should not hide an existing proxy for this client",
+ gpc.handleProxificationForHook(sref2));
+ assertEquals("No proxy should have been created", 0, gpc.proxyMap.size());
+
+ Dictionary<String, Object> props4 = new Hashtable<String, Object>();
+ props4.put(Constants.SERVICE_ID, 15L);
+ props4.put("a", "7");
+ ServiceReference<?> sref4 = mockServiceReference(props4);
+ assertTrue("Should hide a service that needs to be proxied",
+ gpc.handleProxificationForHook(sref4));
+ assertEquals("Should trigger proxy creation", 1, gpc.proxyMap.size());
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Test
+ public void testHandleServiceUnregistering() throws Exception {
+ BundleContext clientBC = openStrictMockBundleContext(mockBundle(12345L));
+ BundleContext client2BC = openStrictMockBundleContext(mockBundle(6L));
+ EasyMock.replay(clientBC);
+ EasyMock.replay(client2BC);
+
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ long originalServiceID = 12345678901234L;
+ props.put(Constants.SERVICE_ID, new Long(originalServiceID));
+ props.put("foo", "bar");
+ ServiceReference<?> originalRef = mockServiceReference(props);
+
+ Hashtable<String, Object> props2 = new Hashtable<String, Object>();
+ long anotherServiceID = 5123456789012345L;
+ props2.put(Constants.SERVICE_ID, anotherServiceID);
+ ServiceReference<?> anotherRef = mockServiceReference(props2);
+
+ GuardProxyCatalog gpc = new GuardProxyCatalog(mockBundleContext());
+
+ ServiceRegistration<?> proxyReg = EasyMock.createMock(ServiceRegistration.class);
+ EasyMock.expect(proxyReg.getReference()).andReturn((ServiceReference)
+ mockServiceReference(props)).anyTimes();
+ proxyReg.unregister();
+ EasyMock.expectLastCall().once();
+ EasyMock.replay(proxyReg);
+ ServiceRegistrationHolder srh = new GuardProxyCatalog.ServiceRegistrationHolder();
+ srh.registration = proxyReg;
+
+ ServiceRegistration<?> proxy2Reg = EasyMock.createMock(ServiceRegistration.class);
+ EasyMock.replay(proxy2Reg);
+ ServiceRegistrationHolder srh2 = new GuardProxyCatalog.ServiceRegistrationHolder();
+ srh2.registration = proxy2Reg;
+
+ gpc.proxyMap.put(originalServiceID, srh);
+ gpc.proxyMap.put(anotherServiceID, srh2);
+ assertEquals("Precondition", 2, gpc.proxyMap.size());
+
+ gpc.createProxyQueue.put(new MockCreateProxyRunnable(originalServiceID));
+ gpc.createProxyQueue.put(new MockCreateProxyRunnable(777));
+ assertEquals("Precondition", 2, gpc.createProxyQueue.size());
+
+ gpc.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, originalRef));
+ assertEquals("Registered events should be ignored", 2, gpc.proxyMap.size());
+ assertEquals("Registered events should be ignored", 2, gpc.createProxyQueue.size());
+
+ Hashtable<String, Object> proxyProps = new Hashtable<String, Object>(props);
+ proxyProps.put(GuardProxyCatalog.PROXY_SERVICE_KEY, Boolean.TRUE);
+ ServiceReference<?> proxyRef = mockServiceReference(proxyProps);
+ gpc.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, proxyRef));
+ assertEquals("Unregistering the proxy should be ignored by the listener", 2, gpc.proxyMap.size());
+ assertEquals("Unregistering the proxy should be ignored by the listener", 2, gpc.createProxyQueue.size());
+
+ gpc.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, originalRef));
+ assertEquals("The proxy for this service should have been removed", 1, gpc.proxyMap.size());
+ assertEquals(anotherRef.getProperty(Constants.SERVICE_ID), gpc.proxyMap.keySet().iterator().next());
+ assertEquals("The create proxy job for this service should have been removed", 1, gpc.createProxyQueue.size());
+ assertEquals(777, gpc.createProxyQueue.iterator().next().getOriginalServiceID());
+
+ EasyMock.verify(proxyReg);
+ EasyMock.verify(proxy2Reg);
+ }
+
+ @Test
+ public void testCreateProxy() throws Exception {
+ // This method tests proxy creation for various service implementation types.
+
+ testCreateProxy(TestServiceAPI.class, new TestService());
+ testCreateProxy(TestServiceAPI.class, new DescendantTestService());
+ testCreateProxy(TestServiceAPI.class, new PrivateTestService());
+ testCreateProxy(TestServiceAPI.class, new PrivateTestServiceNoDirectInterfaces());
+ testCreateProxy(TestServiceAPI.class, new FinalTestService());
+ testCreateProxy(TestObjectWithoutInterface.class, new TestObjectWithoutInterface());
+ testCreateProxy(TestServiceAPI.class, new CombinedTestService());
+ testCreateProxy(PrivateTestService.class, new PrivateTestService());
+ testCreateProxy(PrivateTestServiceNoDirectInterfaces.class, new PrivateTestServiceNoDirectInterfaces());
+ testCreateProxy(Object.class, new TestService());
+ testCreateProxy(Object.class, new DescendantTestService());
+ testCreateProxy(Object.class, new PrivateTestService());
+ testCreateProxy(Object.class, new TestObjectWithoutInterface());
+ testCreateProxy(Object.class, new CombinedTestService());
+ testCreateProxy(Object.class, new FinalTestService());
+ testCreateProxy(TestServiceAPI.class, new TestServiceAPI() {
+ @Override
+ public String doit() {
+ return "Doing it";
+ }
+ });
+ testCreateProxy(Object.class, new ClassWithFinalMethod());
+ testCreateProxy(Object.class, new ClassWithPrivateMethod());
+ }
+
+ @Test
+ public void testCreateProxyMultipleObjectClasses() throws Exception {
+ testCreateProxy(new Class [] {TestServiceAPI.class, TestService.class}, new TestService());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testAssignRoles() throws Exception {
+ Dictionary<String, Object> config = new Hashtable<String, Object>();
+ config.put(Constants.SERVICE_PID, "foobar");
+ config.put("service.guard", "(objectClass=" + TestServiceAPI.class.getName() + ")");
+ config.put("somemethod", "a,b");
+ config.put("someOtherMethod(int)", "c");
+ config.put("someOtherMethod(int)[/12/]", "d");
+ config.put("someOtherMethod(int)[\"42\"]", "e");
+ config.put("someFoo*", "f");
+
+ BundleContext bc = mockConfigAdminBundleContext(config);
+
+ Dictionary<String, Object> proxyProps = testCreateProxy(bc, TestServiceAPI.class, new TestService());
+ assertEquals(new HashSet<String>(Arrays.asList("a", "b", "c", "d", "e", "f")),
+ new HashSet<String>((Collection<String>) proxyProps.get(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY)));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testAssignRoles2() throws Exception {
+ Dictionary<String, Object> config = new Hashtable<String, Object>();
+ config.put(Constants.SERVICE_PID, "foobar");
+ config.put("service.guard", "(objectClass=" + TestServiceAPI2.class.getName() + ")");
+ config.put("doit", "X");
+
+ BundleContext bc = mockConfigAdminBundleContext(config);
+
+ Dictionary<String, Object> proxyProps = testCreateProxy(bc, TestServiceAPI.class, new TestService());
+ assertNull("No security defined for this API, so no roles should be specified at all",
+ proxyProps.get(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY));
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Test
+ public void testAssignRoles3() throws Exception {
+ abstract class MyAbstractClass implements TestServiceAPI, TestServiceAPI2 {};
+
+ Dictionary<String, Object> config = new Hashtable<String, Object>();
+ config.put(Constants.SERVICE_PID, "foobar");
+ config.put("service.guard", "(objectClass=" + TestServiceAPI2.class.getName() + ")");
+ config.put("doit", "X");
+
+ BundleContext bc = mockConfigAdminBundleContext(config);
+
+ Map<ServiceReference, Object> serviceMap = new HashMap<ServiceReference, Object>();
+ testCreateProxy(bc, new Class [] {TestServiceAPI.class, TestServiceAPI2.class}, new MyAbstractClass() {
+ @Override
+ public String doit() {
+ return null;
+ }
+
+ @Override
+ public String doit(String s) {
+ return null;
+ }
+ }, serviceMap);
+ assertEquals(1, serviceMap.size());
+ assertEquals(Collections.singleton("X"), serviceMap.keySet().iterator().next().
+ getProperty(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testAssignRoles4() throws Exception {
+ Dictionary<String, Object> config = new Hashtable<String, Object>();
+ config.put(Constants.SERVICE_PID, "foobar");
+ config.put("service.guard", "(objectClass=" + TestServiceAPI.class.getName() + ")");
+ config.put("somemethod", "b");
+ config.put("someOtherMethod", "b");
+ config.put("somethingelse", "*");
+
+ BundleContext bc = mockConfigAdminBundleContext(config);
+
+ Dictionary<String, Object> proxyProps = testCreateProxy(bc, TestServiceAPI.class, new TestService());
+ Collection<String> result = (Collection<String>) proxyProps.get(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY);
+ assertEquals(1, result.size());
+ assertEquals("b", result.iterator().next());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvocationBlocking1() throws Exception {
+ Dictionary<String, Object> c1 = new Hashtable<String, Object>();
+ c1.put(Constants.SERVICE_PID, "foobar");
+ c1.put("service.guard", "(objectClass=" + TestServiceAPI.class.getName() + ")");
+ c1.put("doit", "a,b");
+ Dictionary<String, Object> c2 = new Hashtable<String, Object>();
+ c2.put(Constants.SERVICE_PID, "barfoobar");
+ c2.put("service.guard", "(objectClass=" + TestObjectWithoutInterface.class.getName() + ")");
+ c2.put("compute", "c");
+
+ BundleContext bc = mockConfigAdminBundleContext(c1, c2);
+
+ final Object proxy = testCreateProxy(bc, new Class [] {TestServiceAPI.class, TestObjectWithoutInterface.class}, new CombinedTestService());
+
+ // Run with the right credentials so we can test the expected roles
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new RolePrincipal("b"));
+ Subject.doAs(subject, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ assertEquals("Doing it", ((TestServiceAPI) proxy).doit());
+ if (!runningUnderCoverage) {
+ try {
+ ((TestObjectWithoutInterface) proxy).compute(44L);
+ fail("Should have been blocked");
+ } catch (SecurityException se) {
+ // good
+ }
+ }
+
+ return null;
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvocationBlocking2() throws Exception {
+ Dictionary<String, Object> config = new Hashtable<String, Object>();
+ config.put(Constants.SERVICE_PID, "barfoobar");
+ config.put("service.guard", "(objectClass=" + TestObjectWithoutInterface.class.getName() + ")");
+ config.put("compute(long)[\"42\"]", "b");
+ config.put("compute(long)", "c");
+
+ BundleContext bc = mockConfigAdminBundleContext(config);
+
+ final Object proxy = testCreateProxy(bc, new Class [] {TestServiceAPI.class, TestObjectWithoutInterface.class}, new CombinedTestService());
+
+ // Run with the right credentials so we can test the expected roles
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new RolePrincipal("b"));
+ Subject.doAs(subject, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ if (!runningUnderCoverage) {
+ assertEquals(-42L, ((TestObjectWithoutInterface) proxy).compute(42L));
+ try {
+ ((TestObjectWithoutInterface) proxy).compute(44L);
+ fail("Should have been blocked");
+ } catch (SecurityException se) {
+ // good
+ }
+ }
+
+ return null;
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvocationBlocking3() throws Exception {
+ class MyService implements TestServiceAPI, TestServiceAPI2 {
+ public String doit(String s) {
+ return new StringBuilder(s).reverse().toString();
+ }
+
+ public String doit() {
+ return "Doing it";
+ }
+ };
+
+ Dictionary<String, Object> c1 = new Hashtable<String, Object>();
+ c1.put(Constants.SERVICE_PID, "foobar");
+ c1.put("service.guard", "(objectClass=" + TestServiceAPI.class.getName() + ")");
+ c1.put("do*", "c");
+ Dictionary<String, Object> c2 = new Hashtable<String, Object>();
+ c2.put(Constants.SERVICE_PID, "foobar2");
+ c2.put("service.guard", "(objectClass=" + TestServiceAPI2.class.getName() + ")");
+ c2.put("doit(java.lang.String)[/[tT][a]+/]", "b,d # a regex rule");
+ c2.put("doit(java.lang.String)", "a");
+
+ BundleContext bc = mockConfigAdminBundleContext(c1, c2);
+
+ final Object proxy = testCreateProxy(bc, new Class [] {TestServiceAPI.class, TestServiceAPI2.class}, new MyService());
+
+ // Run with the right credentials so we can test the expected roles
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new RolePrincipal("c"));
+ Subject.doAs(subject, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ assertEquals("Doing it", ((TestServiceAPI) proxy).doit());
+ return null;
+ }
+ });
+
+ Subject subject2 = new Subject();
+ subject2.getPrincipals().add(new RolePrincipal("b"));
+ subject2.getPrincipals().add(new RolePrincipal("f"));
+ Subject.doAs(subject2, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ try {
+ assertEquals("Doing it", ((TestServiceAPI) proxy).doit());
+ fail("Should have been blocked");
+ } catch (SecurityException se) {
+ // good
+ }
+ assertEquals("aaT", ((TestServiceAPI2) proxy).doit("Taa"));
+ try {
+ ((TestServiceAPI2) proxy).doit("t");
+ fail("Should have been blocked");
+ } catch (SecurityException se) {
+ // good
+ }
+ return null;
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvocationBlocking4() throws Exception {
+ BundleContext bc = mockConfigAdminBundleContext();
+
+ final Object proxy = testCreateProxy(bc, new Class [] {TestServiceAPI.class, TestObjectWithoutInterface.class}, new CombinedTestService());
+
+ // Run with the right credentials so we can test the expected roles
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new RolePrincipal("b"));
+ Subject.doAs(subject, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ assertEquals("Doing it", ((TestServiceAPI) proxy).doit());
+ if (!runningUnderCoverage) {
+ assertEquals(42L, ((TestObjectWithoutInterface) proxy).compute(-42L));
+ assertEquals(-44L, ((TestObjectWithoutInterface) proxy).compute(44L));
+ }
+
+ return null;
+ }
+ });
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvocationBlocking5() throws Exception {
+ Dictionary<String, Object> c1 = new Hashtable<String, Object>();
+ c1.put(Constants.SERVICE_PID, "foobar");
+ c1.put("service.guard", "(objectClass=" + TestServiceAPI.class.getName() + ")");
+ c1.put("doit", "a,b");
+
+ BundleContext bc = mockConfigAdminBundleContext(c1);
+
+ final Object proxy = testCreateProxy(bc, new Class [] {TestServiceAPI2.class}, new TestServiceAPI2() {
+ @Override
+ public String doit(String s) {
+ return s.toUpperCase();
+ }
+ });
+
+ // Invoke the service with role 'c'.
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new RolePrincipal("c"));
+ Subject.doAs(subject, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ assertEquals("The invocation under role 'c' should be ok, as there are no rules specified "
+ + "for this service at all.", "HELLO", ((TestServiceAPI2) proxy).doit("hello"));
+ return null;
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvocationBlocking6() throws Exception {
+ Dictionary<String, Object> c1 = new Hashtable<String, Object>();
+ c1.put(Constants.SERVICE_PID, "foobar");
+ c1.put("service.guard", "(objectClass=" + TestServiceAPI.class.getName() + ")");
+ c1.put("doit", "a,b");
+ Dictionary<String, Object> c2 = new Hashtable<String, Object>();
+ c2.put(Constants.SERVICE_PID, "foobar2");
+ c2.put("service.guard", "(objectClass=" + TestServiceAPI2.class.getName() + ")");
+ c2.put("bar", "c");
+
+ BundleContext bc = mockConfigAdminBundleContext(c1, c2);
+
+ final Object proxy = testCreateProxy(bc, new Class [] {TestServiceAPI2.class}, new TestServiceAPI2() {
+ @Override
+ public String doit(String s) {
+ return s.toUpperCase();
+ }
+ });
+
+ // Invoke the service with role 'c'.
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new RolePrincipal("a"));
+ subject.getPrincipals().add(new RolePrincipal("b"));
+ subject.getPrincipals().add(new RolePrincipal("c"));
+ Subject.doAs(subject, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ try {
+ ((TestServiceAPI2) proxy).doit("hello");
+ fail("The invocation should not process as the 'doit' operation has no roles associated with it");
+ } catch (SecurityException se) {
+ // good
+ }
+ return null;
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvocationBlocking7() throws Exception {
+ Dictionary<String, Object> c1 = new Hashtable<String, Object>();
+ c1.put(Constants.SERVICE_PID, "foobar");
+ c1.put("service.guard", "(objectClass=" + TestServiceAPI3.class.getName() + ")");
+ c1.put("foo()", "a");
+ c1.put("bar", "b");
+ c1.put("*", "*");
+
+ BundleContext bc = mockConfigAdminBundleContext(c1);
+
+ final Object proxy = testCreateProxy(bc, new Class [] {TestServiceAPI3.class}, new TestService3());
+
+ Subject s1 = new Subject();
+ Subject.doAs(s1, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ TestServiceAPI3 obj = (TestServiceAPI3) proxy;
+ assertEquals("Should have allowed this invocation for any (or no) role", -7, obj.foo(7));
+ try {
+ obj.foo();
+ fail("Should have been blocked");
+ } catch (SecurityException se) {
+ // good
+ }
+ try {
+ obj.bar();
+ fail("Should have been blocked");
+ } catch (SecurityException se) {
+ // good
+ }
+
+ return null;
+ }
+ });
+
+ Subject s2 = new Subject();
+ s2.getPrincipals().add(new RolePrincipal("a"));
+ s2.getPrincipals().add(new RolePrincipal("b"));
+ s2.getPrincipals().add(new RolePrincipal("d"));
+ Subject.doAs(s2, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ TestServiceAPI3 obj = (TestServiceAPI3) proxy;
+ assertEquals(42, obj.foo());
+ assertEquals(99, obj.bar());
+ assertEquals(-32767, obj.foo(32767));
+ return null;
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testCustomRole() throws Exception {
+ class MyRolePrincipal implements Principal {
+ @Override
+ public String getName() {
+ return "role1";
+ }
+ };
+
+ Dictionary<String, Object> c1 = new Hashtable<String, Object>();
+ c1.put(Constants.SERVICE_PID, "foobar");
+ c1.put("service.guard", "(objectClass=" + TestServiceAPI.class.getName() + ")");
+ c1.put("doit", MyRolePrincipal.class.getName() + ":role1");
+ BundleContext bc = mockConfigAdminBundleContext(c1);
+
+ final Object proxy = testCreateProxy(bc, new Class [] {TestServiceAPI.class}, new TestService());
+
+ Subject s1 = new Subject();
+ s1.getPrincipals().add(new RolePrincipal("role1"));
+ Subject.doAs(s1, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ try {
+ ((TestServiceAPI) proxy).doit();
+ fail("Should have prevented this invocation as the custom role is required");
+ } catch (SecurityException se) {
+ // good
+ }
+ return null;
+ }
+ });
+
+
+ Subject s2 = new Subject();
+ s2.getPrincipals().add(new MyRolePrincipal());
+ Subject.doAs(s2, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ ((TestServiceAPI) proxy).doit(); // Should work, the custom role is there
+ return null;
+ }
+ });
+
+ Subject s3 = new Subject();
+ s3.getPrincipals().add(new MyRolePrincipal());
+ s3.getPrincipals().add(new RolePrincipal("role1"));
+ Subject.doAs(s3, new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ ((TestServiceAPI) proxy).doit(); // Should work, the custom role is there
+ return null;
+ }
+ });
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Test
+ public void testProxyCreationThread() throws Exception {
+ ProxyManager proxyManager = getProxyManager();
+
+ ConfigurationAdmin ca = EasyMock.createMock(ConfigurationAdmin.class);
+ EasyMock.expect(ca.listConfigurations(EasyMock.anyObject(String.class))).andReturn(null).anyTimes();
+ EasyMock.replay(ca);
+
+ ServiceReference pmSref = EasyMock.createMock(ServiceReference.class);
+ EasyMock.replay(pmSref);
+ ServiceReference pmSref2 = EasyMock.createMock(ServiceReference.class);
+ EasyMock.replay(pmSref2);
+ ServiceReference cmSref = EasyMock.createMock(ServiceReference.class);
+ EasyMock.replay(cmSref);
+
+ Bundle b = EasyMock.createMock(Bundle.class);
+ EasyMock.expect(b.getBundleId()).andReturn(23992734L).anyTimes();
+ EasyMock.replay(b);
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ EasyMock.expect(bc.createFilter(EasyMock.isA(String.class))).andAnswer(new IAnswer<Filter>() {
+ @Override
+ public Filter answer() throws Throwable {
+ return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ final ServiceListener [] pmListenerHolder = new ServiceListener [1];
+ String pmFilter = "(&(objectClass=" + ProxyManager.class.getName() + ")" +
+ "(!(" + GuardProxyCatalog.PROXY_SERVICE_KEY + "=*)))";
+ bc.addServiceListener(EasyMock.isA(ServiceListener.class), EasyMock.eq(pmFilter));
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ pmListenerHolder[0] = (ServiceListener) EasyMock.getCurrentArguments()[0];
+ return null;
+ }
+ }).anyTimes();
+ EasyMock.expect(bc.getServiceReferences(EasyMock.anyObject(String.class),
+ EasyMock.contains(ConfigurationAdmin.class.getName()))).andReturn(new ServiceReference[] {cmSref}).anyTimes();
+ EasyMock.expect(bc.getService(pmSref)).andReturn(proxyManager).anyTimes();
+ EasyMock.expect(bc.getService(pmSref2)).andReturn(proxyManager).anyTimes();
+ EasyMock.expect(bc.getService(cmSref)).andReturn(ca).anyTimes();
+ EasyMock.replay(bc);
+
+ // This should put a ServiceListener in the pmListenerHolder, the ServiceTracker does that
+ GuardProxyCatalog gpc = new GuardProxyCatalog(bc);
+
+ // The service being proxied has these properties
+ final Hashtable<String, Object> serviceProps = new Hashtable<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String [] {TestServiceAPI.class.getName()});
+ serviceProps.put(Constants.SERVICE_ID, 162L);
+
+ final Map<ServiceReference<?>, Object> serviceMap = new HashMap<ServiceReference<?>, Object>();
+
+ // The mock bundle context for the bundle providing the service is set up here
+ BundleContext providerBC = EasyMock.createMock(BundleContext.class);
+ // These are the expected service properties of the proxy registration. Note the proxy marker...
+ final Hashtable<String, Object> expectedProxyProps = new Hashtable<String, Object>(serviceProps);
+ expectedProxyProps.put(GuardProxyCatalog.PROXY_SERVICE_KEY, Boolean.TRUE);
+ EasyMock.expect(providerBC.registerService(
+ EasyMock.isA(String[].class),
+ EasyMock.anyObject(),
+ EasyMock.isA(Dictionary.class))).andAnswer(new IAnswer() {
+ @Override
+ public ServiceRegistration answer() throws Throwable {
+ Dictionary<String,Object> props = (Dictionary<String, Object>) EasyMock.getCurrentArguments()[2];
+ ServiceRegistration reg = EasyMock.createMock(ServiceRegistration.class);
+ ServiceReference sr = mockServiceReference(props);
+ EasyMock.expect(reg.getReference()).andReturn(sr).anyTimes();
+ reg.unregister();
+ EasyMock.expectLastCall().once();
+ EasyMock.replay(reg);
+
+ serviceMap.put(sr, EasyMock.getCurrentArguments()[1]);
+
+ return reg;
+ }
+ }).once();
+ EasyMock.expect(providerBC.getService(EasyMock.isA(ServiceReference.class))).andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ return serviceMap.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(providerBC);
+
+ // In some cases the proxy-creating code is looking for a classloader (e.g. when run through
+ // a coverage tool such as EclEmma). This will satisfy that.
+ BundleWiring bw = EasyMock.createMock(BundleWiring.class);
+ EasyMock.expect(bw.getClassLoader()).andReturn(getClass().getClassLoader()).anyTimes();
+ EasyMock.replay(bw);
+
+ // The mock bundle that provides the original service (and also the proxy is registered with this)
+ Bundle providerBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(providerBundle.getBundleContext()).andReturn(providerBC).anyTimes();
+ EasyMock.expect(providerBundle.adapt(BundleWiring.class)).andReturn(bw).anyTimes();
+ EasyMock.replay(providerBundle);
+
+ ServiceReference sr = mockServiceReference(providerBundle, serviceProps);
+
+ assertEquals("Precondition", 0, gpc.proxyMap.size());
+ assertEquals("Precondition", 0, gpc.createProxyQueue.size());
+ // Create the proxy for the service
+ gpc.proxyIfNotAlreadyProxied(sr);
+ assertEquals(1, gpc.proxyMap.size());
+ assertEquals(1, gpc.createProxyQueue.size());
+
+ // The actual proxy creation is done asynchronously.
+ GuardProxyCatalog.ServiceRegistrationHolder holder = gpc.proxyMap.get(162L);
+ assertNull("The registration shouldn't have happened yet", holder.registration);
+ assertEquals(1, gpc.createProxyQueue.size());
+
+ Thread[] tarray = new Thread[Thread.activeCount()];
+ Thread.enumerate(tarray);
+ for (Thread t : tarray) {
+ if (t != null) {
+ assertTrue(!GuardProxyCatalog.PROXY_CREATOR_THREAD_NAME.equals(t.getName()));
+ }
+ }
+
+ // make the proxy manager appear
+ pmListenerHolder[0].serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, pmSref));
+ Thread.sleep(400); // give the system some time to send the events...
+
+ Thread ourThread = null;
+ Thread[] tarray2 = new Thread[Thread.activeCount()];
+ Thread.enumerate(tarray2);
+ for (Thread t : tarray2) {
+ if (t != null) {
+ if (t.getName().equals(GuardProxyCatalog.PROXY_CREATOR_THREAD_NAME)) {
+ ourThread = t;
+ }
+ }
+ }
+ assertNotNull(ourThread);
+ assertTrue(ourThread.isDaemon());
+ assertTrue(ourThread.isAlive());
+ assertNotNull(holder.registration);
+
+ assertEquals(0, gpc.createProxyQueue.size());
+
+ int numProxyThreads = 0;
+ pmListenerHolder[0].serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, pmSref2));
+ Thread.sleep(300); // give the system some time to send the events...
+
+ Thread[] tarray3 = new Thread[Thread.activeCount()];
+ Thread.enumerate(tarray3);
+ for (Thread t : tarray3) {
+ if (t != null) {
+ if (t.getName().equals(GuardProxyCatalog.PROXY_CREATOR_THREAD_NAME)) {
+ numProxyThreads++;
+ }
+ }
+ }
+ assertEquals("Maximum 1 proxy thread, even if there is more than 1 proxy service", 1, numProxyThreads);
+
+ // Clean up thread
+ pmListenerHolder[0].serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, pmSref));
+ Thread.sleep(300); // Give the system some time to stop the threads...
+ Thread[] tarray4 = new Thread[Thread.activeCount()];
+ Thread.enumerate(tarray4);
+ for (Thread t : tarray4) {
+ if (t != null) {
+ assertTrue(!GuardProxyCatalog.PROXY_CREATOR_THREAD_NAME.equals(t.getName()));
+ }
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Test
+ public void testHandleServiceModified() throws Exception {
+ Dictionary<String, Object> config = new Hashtable<String, Object>();
+ config.put(Constants.SERVICE_PID, "test.1.2.3");
+ config.put("service.guard", "(objectClass=" + TestServiceAPI.class.getName() + ")");
+ config.put("doit", "role.1");
+ Dictionary<String, Object> config2 = new Hashtable<String, Object>();
+ config2.put(Constants.SERVICE_PID, "test.1.2.4");
+ config2.put("service.guard", "(objectClass=" + TestServiceAPI2.class.getName() + ")");
+ config2.put("doit", "role.2");
+
+ BundleContext bc = mockConfigAdminBundleContext(config, config2);
+ GuardProxyCatalog gpc = new GuardProxyCatalog(bc);
+ // The service being proxied has these properties
+ long serviceID = 1L;
+ final Hashtable<String, Object> serviceProps = new Hashtable<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String [] {TestServiceAPI.class.getName(), TestServiceAPI2.class.getName()});
+ serviceProps.put(Constants.SERVICE_ID, serviceID);
+ serviceProps.put(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY, Arrays.asList("someone")); // will be overwritten
+ Object myObject = new Object();
+ serviceProps.put("foo.bar", myObject);
+
+ BundleContext providerBC = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(providerBC.registerService(
+ EasyMock.aryEq(new String [] {TestServiceAPI.class.getName(), TestServiceAPI2.class.getName()}),
+ EasyMock.anyObject(),
+ EasyMock.anyObject(Dictionary.class))).andAnswer(new IAnswer() {
+ @Override
+ public Object answer() throws Throwable {
+ final Dictionary props = (Dictionary) EasyMock.getCurrentArguments()[2];
+ assertEquals(Boolean.TRUE, props.get(GuardProxyCatalog.PROXY_SERVICE_KEY));
+
+ ServiceRegistration reg = EasyMock.createMock(ServiceRegistration.class);
+ ServiceReference sr = mockServiceReference(props);
+ EasyMock.expect(reg.getReference()).andReturn(sr).anyTimes();
+ reg.setProperties(EasyMock.isA(Dictionary.class));
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ // Push the update into the service reference
+ ArrayList<String> oldKeys = Collections.list(props.keys());
+ for (String key : oldKeys) {
+ props.remove(key);
+ }
+ Dictionary<String, Object> newProps = (Dictionary<String, Object>) EasyMock.getCurrentArguments()[0];
+ for (String key : Collections.list(newProps.keys())) {
+ props.put(key, newProps.get(key));
+ }
+ return null;
+ }
+ }).once();
+ EasyMock.replay(reg);
+
+ return reg;
+ }
+ }).anyTimes();
+
+ EasyMock.replay(providerBC);
+
+ // In some cases the proxy-creating code is looking for a classloader (e.g. when run through
+ // a coverage tool such as EclEmma). This will satisfy that.
+ BundleWiring bw = EasyMock.createMock(BundleWiring.class);
+ EasyMock.expect(bw.getClassLoader()).andReturn(getClass().getClassLoader()).anyTimes();
+ EasyMock.replay(bw);
+
+ // The mock bundle that provides the original service (and also the proxy is registered with this)
+ Bundle providerBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(providerBundle.getBundleContext()).andReturn(providerBC).anyTimes();
+ EasyMock.expect(providerBundle.adapt(BundleWiring.class)).andReturn(bw).anyTimes();
+ EasyMock.replay(providerBundle);
+
+ ServiceReference sr = mockServiceReference(providerBundle, serviceProps);
+
+ gpc.proxyIfNotAlreadyProxied(sr);
+ GuardProxyCatalog.CreateProxyRunnable runnable = gpc.createProxyQueue.take();
+ runnable.run(getProxyManager());
+
+ ServiceRegistrationHolder holder = gpc.proxyMap.get(serviceID);
+ ServiceRegistration<?> reg = holder.registration;
+
+ for (String key : serviceProps.keySet()) {
+ if (GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY.equals(key)) {
+ assertEquals(new HashSet(Arrays.asList("role.1", "role.2")), reg.getReference().getProperty(key));
+ } else {
+ assertEquals(serviceProps.get(key), reg.getReference().getProperty(key));
+ }
+ }
+ assertEquals(Boolean.TRUE, reg.getReference().getProperty(GuardProxyCatalog.PROXY_SERVICE_KEY));
+
+ // now change the original service and let the proxy react
+ serviceProps.put("test", "property");
+ assertEquals("Precondition, the mocked reference should have picked up this change",
+ "property", sr.getProperty("test"));
+
+ gpc.serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, sr));
+ assertEquals("Changing the service should not change the number of proxies", 1, gpc.proxyMap.size());
+
+ for (String key : serviceProps.keySet()) {
+ if (GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY.equals(key)) {
+ assertEquals(new HashSet(Arrays.asList("role.1", "role.2")), reg.getReference().getProperty(key));
+ } else {
+ assertEquals(serviceProps.get(key), reg.getReference().getProperty(key));
+ }
+ }
+ assertEquals("property", reg.getReference().getProperty("test"));
+ assertEquals(Boolean.TRUE, reg.getReference().getProperty(GuardProxyCatalog.PROXY_SERVICE_KEY));
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Test
+ public void testHandleServiceModified2() throws Exception {
+ BundleContext bc = mockConfigAdminBundleContext(); // no configuration used in this test...
+ GuardProxyCatalog gpc = new GuardProxyCatalog(bc);
+
+ // The service being proxied has these properties
+ long serviceID = 1L;
+ final Hashtable<String, Object> serviceProps = new Hashtable<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String [] {TestServiceAPI.class.getName()});
+ serviceProps.put(Constants.SERVICE_ID, serviceID);
+
+ BundleContext providerBC = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(providerBC.registerService(
+ EasyMock.aryEq(new String [] {TestServiceAPI.class.getName()}),
+ EasyMock.anyObject(),
+ EasyMock.anyObject(Dictionary.class))).andAnswer(new IAnswer() {
+ @Override
+ public Object answer() throws Throwable {
+ final Dictionary props = (Dictionary) EasyMock.getCurrentArguments()[2];
+ assertEquals(Boolean.TRUE, props.get(GuardProxyCatalog.PROXY_SERVICE_KEY));
+
+ ServiceRegistration reg = EasyMock.createMock(ServiceRegistration.class);
+ ServiceReference sr = mockServiceReference(props);
+ EasyMock.expect(reg.getReference()).andReturn(sr).anyTimes();
+ reg.setProperties(EasyMock.isA(Dictionary.class));
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ // Push the update into the service reference
+ ArrayList<String> oldKeys = Collections.list(props.keys());
+ for (String key : oldKeys) {
+ props.remove(key);
+ }
+ Dictionary<String, Object> newProps = (Dictionary<String, Object>) EasyMock.getCurrentArguments()[0];
+ for (String key : Collections.list(newProps.keys())) {
+ props.put(key, newProps.get(key));
+ }
+ return null;
+ }
+ }).once();
+ EasyMock.replay(reg);
+
+ return reg;
+ }
+ }).anyTimes();
+
+ EasyMock.replay(providerBC);
+
+ // In some cases the proxy-creating code is looking for a classloader (e.g. when run through
+ // a coverage tool such as EclEmma). This will satisfy that.
+ BundleWiring bw = EasyMock.createMock(BundleWiring.class);
+ EasyMock.expect(bw.getClassLoader()).andReturn(getClass().getClassLoader()).anyTimes();
+ EasyMock.replay(bw);
+
+ // The mock bundle that provides the original service (and also the proxy is registered with this)
+ Bundle providerBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(providerBundle.getBundleContext()).andReturn(providerBC).anyTimes();
+ EasyMock.expect(providerBundle.adapt(BundleWiring.class)).andReturn(bw).anyTimes();
+ EasyMock.replay(providerBundle);
+
+ ServiceReference sr = mockServiceReference(providerBundle, serviceProps);
+
+ gpc.proxyIfNotAlreadyProxied(sr);
+ GuardProxyCatalog.CreateProxyRunnable runnable = gpc.createProxyQueue.take();
+ runnable.run(getProxyManager());
+
+ ServiceRegistrationHolder holder = gpc.proxyMap.get(serviceID);
+ ServiceRegistration<?> reg = holder.registration;
+
+ assertFalse("No roles defined for this service using configuration, so roles property should not be set",
+ Arrays.asList(reg.getReference().getPropertyKeys()).contains(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY));
+ for (String key : serviceProps.keySet()) {
+ assertEquals(serviceProps.get(key), reg.getReference().getProperty(key));
+ }
+ assertEquals(Boolean.TRUE, reg.getReference().getProperty(GuardProxyCatalog.PROXY_SERVICE_KEY));
+
+ // now change the original service and let the proxy react
+ serviceProps.put(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY, "foobar");
+ assertEquals("Precondition, the mocked reference should have picked up this change",
+ "foobar", sr.getProperty(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY));
+
+ gpc.serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, sr));
+ assertEquals("Changing the service should not change the number of proxies", 1, gpc.proxyMap.size());
+
+ assertFalse("The roles property set on the modified service should have been removed",
+ Arrays.asList(reg.getReference().getPropertyKeys()).contains(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY));
+ assertEquals(Boolean.TRUE, reg.getReference().getProperty(GuardProxyCatalog.PROXY_SERVICE_KEY));
+ }
+
+ @Test
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void testServiceFactoryBehaviour() throws Exception {
+ final Map<ServiceReference, Object> serviceMap = new HashMap<ServiceReference, Object>();
+ TestServiceAPI testService = new TestService();
+
+ BundleContext bc = mockConfigAdminBundleContext();
+ GuardProxyCatalog gpc = new GuardProxyCatalog(bc);
+
+ // The service being proxied has these properties
+ long serviceID = 117L;
+ final Hashtable<String, Object> serviceProps = new Hashtable<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String [] {TestServiceAPI.class.getName()});
+ serviceProps.put(Constants.SERVICE_ID, serviceID);
+ serviceProps.put("bar", 42L);
+
+ BundleContext providerBC = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(providerBC.registerService(
+ EasyMock.isA(String[].class),
+ EasyMock.anyObject(),
+ EasyMock.isA(Dictionary.class))).andAnswer(new IAnswer() {
+ @Override
+ public ServiceRegistration answer() throws Throwable {
+ Dictionary<String,Object> props = (Dictionary<String, Object>) EasyMock.getCurrentArguments()[2];
+ ServiceRegistration reg = EasyMock.createMock(ServiceRegistration.class);
+ ServiceReference sr = mockServiceReference(props);
+ EasyMock.expect(reg.getReference()).andReturn(sr).anyTimes();
+ EasyMock.replay(reg);
+
+ serviceMap.put(sr, EasyMock.getCurrentArguments()[1]);
+
+ return reg;
+ }
+ }).once();
+ EasyMock.expect(providerBC.getService(EasyMock.isA(ServiceReference.class))).andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ return serviceMap.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(providerBC);
+
+ // In some cases the proxy-creating code is looking for a classloader (e.g. when run through
+ // a coverage tool such as EclEmma). This will satisfy that.
+ BundleWiring bw = EasyMock.createMock(BundleWiring.class);
+ EasyMock.expect(bw.getClassLoader()).andReturn(getClass().getClassLoader()).anyTimes();
+ EasyMock.replay(bw);
+
+ // The mock bundle that provides the original service (and also the proxy is registered with this)
+ Bundle providerBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(providerBundle.getBundleContext()).andReturn(providerBC).anyTimes();
+ EasyMock.expect(providerBundle.adapt(BundleWiring.class)).andReturn(bw).anyTimes();
+ EasyMock.replay(providerBundle);
+
+ ServiceReference sr = mockServiceReference(providerBundle, serviceProps);
+
+ // The mock bundle context for the client bundle
+ BundleContext clientBC = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(clientBC.getService(sr)).andReturn(testService).anyTimes();
+ EasyMock.replay(clientBC);
+
+ // The mock bundle that consumes the service
+ Bundle clientBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(clientBundle.getBundleContext()).andReturn(clientBC).anyTimes();
+ EasyMock.replay(clientBundle);
+
+ gpc.proxyIfNotAlreadyProxied(sr);
+
+ // The actual proxy creation is done asynchronously.
+ GuardProxyCatalog.ServiceRegistrationHolder holder = gpc.proxyMap.get(serviceID);
+ // Mimic the thread that works the queue to create the proxy
+ GuardProxyCatalog.CreateProxyRunnable runnable = gpc.createProxyQueue.take();
+ assertEquals(117L, runnable.getOriginalServiceID());
+
+ ProxyManager pm = getProxyManager();
+ runnable.run(pm);
+
+ // The runnable should have put the actual registration in the holder
+ ServiceReference<?> proxySR = holder.registration.getReference();
+
+ // Check that the proxy registration was done on the original provider bundle's context
+ EasyMock.verify(providerBC);
+
+ // Test that the actual proxy invokes the original service...
+ ServiceFactory proxyServiceSF = (ServiceFactory) serviceMap.get(proxySR);
+ TestServiceAPI proxyService = (TestServiceAPI) proxyServiceSF.getService(clientBundle, null);
+
+ assertNotSame("The proxy should not be the same object as the original service", testService, proxyService);
+ assertEquals("Doing it", proxyService.doit());
+
+ EasyMock.reset(clientBC);
+ EasyMock.expect(clientBC.ungetService(sr)).andReturn(true).once();
+ EasyMock.replay(clientBC);
+
+ proxyServiceSF.ungetService(clientBundle, null, proxyService);
+
+ EasyMock.verify(clientBC);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Dictionary<String, Object> testCreateProxy(Class<?> intf, Object testService) throws Exception {
+ return testCreateProxy(mockConfigAdminBundleContext(), intf, intf, testService);
+ }
+
+ public Dictionary<String, Object> testCreateProxy(BundleContext bc, Class<?> intf, Object testService) throws Exception {
+ return testCreateProxy(bc, intf, intf, testService);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Dictionary<String, Object> testCreateProxy(BundleContext bc, Class intf, final Class proxyRegClass, Object testService) throws Exception {
+ // Create the object that is actually being tested here
+ GuardProxyCatalog gpc = new GuardProxyCatalog(bc);
+
+ // The service being proxied has these properties
+ long serviceID = 456L;
+ final Hashtable<String, Object> serviceProps = new Hashtable<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String [] {intf.getName()});
+ serviceProps.put(Constants.SERVICE_ID, serviceID);
+ serviceProps.put(".foo", 123L);
+
+ final Map<ServiceReference<?>, Object> serviceMap = new HashMap<ServiceReference<?>, Object>();
+
+ // The mock bundle context for the bundle providing the service is set up here
+ BundleContext providerBC = EasyMock.createMock(BundleContext.class);
+ // These are the expected service properties of the proxy registration. Note the proxy marker...
+ final Hashtable<String, Object> expectedProxyProps = new Hashtable<String, Object>(serviceProps);
+ expectedProxyProps.put(GuardProxyCatalog.PROXY_SERVICE_KEY, Boolean.TRUE);
+ // This will check that the right proxy is being registered.
+ EasyMock.expect(providerBC.registerService(
+ EasyMock.isA(String[].class),
+ EasyMock.anyObject(),
+ EasyMock.isA(Dictionary.class))).andAnswer(new IAnswer() {
+ @Override
+ public ServiceRegistration answer() throws Throwable {
+ if (!runningUnderCoverage) {
+ // Some of these checks don't work when running under coverage
+ assertArrayEquals(new String [] {proxyRegClass.getName()},
+ (String []) EasyMock.getCurrentArguments()[0]);
+
+ Object svc = EasyMock.getCurrentArguments()[1];
+ assertTrue(svc instanceof ServiceFactory);
+ }
+
+ Dictionary<String,Object> props = (Dictionary<String, Object>) EasyMock.getCurrentArguments()[2];
+ for (String key : expectedProxyProps.keySet()) {
+ assertEquals(expectedProxyProps.get(key), props.get(key));
+ }
+
+ ServiceRegistration reg = EasyMock.createMock(ServiceRegistration.class);
+ ServiceReference sr = mockServiceReference(props);
+ EasyMock.expect(reg.getReference()).andReturn(sr).anyTimes();
+ reg.unregister();
+ EasyMock.expectLastCall().once();
+ EasyMock.replay(reg);
+
+ serviceMap.put(sr, EasyMock.getCurrentArguments()[1]);
+
+ return reg;
+ }
+ }).once();
+ EasyMock.expect(providerBC.getService(EasyMock.isA(ServiceReference.class))).andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ return serviceMap.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(providerBC);
+
+ // In some cases the proxy-creating code is looking for a classloader (e.g. when run through
+ // a coverage tool such as EclEmma). This will satisfy that.
+ BundleWiring bw = EasyMock.createMock(BundleWiring.class);
+ EasyMock.expect(bw.getClassLoader()).andReturn(getClass().getClassLoader()).anyTimes();
+ EasyMock.replay(bw);
+
+ // The mock bundle that provides the original service (and also the proxy is registered with this)
+ Bundle providerBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(providerBundle.getBundleContext()).andReturn(providerBC).anyTimes();
+ EasyMock.expect(providerBundle.adapt(BundleWiring.class)).andReturn(bw).anyTimes();
+ EasyMock.replay(providerBundle);
+
+ ServiceReference sr = mockServiceReference(providerBundle, serviceProps);
+
+ assertEquals("Precondition", 0, gpc.proxyMap.size());
+ assertEquals("Precondition", 0, gpc.createProxyQueue.size());
+ // Create the proxy for the service
+ gpc.proxyIfNotAlreadyProxied(sr);
+ assertEquals(1, gpc.proxyMap.size());
+
+ // The actual proxy creation is done asynchronously.
+ GuardProxyCatalog.ServiceRegistrationHolder holder = gpc.proxyMap.get(serviceID);
+ assertNull("The registration shouldn't have happened yet", holder.registration);
+ assertEquals(1, gpc.createProxyQueue.size());
+
+ // Mimic the thread that works the queue to create the proxy
+ GuardProxyCatalog.CreateProxyRunnable runnable = gpc.createProxyQueue.take();
+ ProxyManager pm = getProxyManager();
+ runnable.run(pm);
+
+ // The runnable should have put the actual registration in the holder
+ ServiceReference<?> proxySR = holder.registration.getReference();
+ for (String key : expectedProxyProps.keySet()) {
+ assertEquals(expectedProxyProps.get(key), proxySR.getProperty(key));
+ }
+
+ // Check that the proxy registration was done on the original provider bundle's context
+ EasyMock.verify(providerBC);
+
+ // Test that the actual proxy invokes the original service...
+ Object proxyService = serviceMap.get(proxySR);
+ assertNotSame("The proxy should not be the same object as the original service", testService, proxyService);
+
+ // Attempt to proxy the service again, make sure that no re-proxying happens
+ assertEquals("Precondition", 1, gpc.proxyMap.size());
+ assertEquals("Precondition", 0, gpc.createProxyQueue.size());
+ gpc.proxyIfNotAlreadyProxied(sr);
+ assertEquals("No additional proxy should have been created", 1, gpc.proxyMap.size());
+ assertEquals("No additional work on the queue is expected", 0, gpc.createProxyQueue.size());
+
+ Dictionary<String, Object> proxyProps = getServiceReferenceProperties(proxySR);
+
+ gpc.close();
+ EasyMock.verify(holder.registration); // checks that the unregister call was made
+
+ return proxyProps;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Object testCreateProxy(Class<?> [] objectClasses, Object testService) throws Exception {
+ return testCreateProxy(mockConfigAdminBundleContext(), objectClasses, objectClasses, testService, new HashMap<ServiceReference, Object>());
+ }
+
+ @SuppressWarnings("rawtypes")
+ public Object testCreateProxy(BundleContext bc, Class<?> [] objectClasses, Object testService) throws Exception {
+ return testCreateProxy(bc, objectClasses, objectClasses, testService, new HashMap<ServiceReference, Object>());
+ }
+
+ @SuppressWarnings("rawtypes")
+ public Object testCreateProxy(BundleContext bc, Class<?> [] objectClasses, Object testService, Map<ServiceReference, Object> serviceMap) throws Exception {
+ return testCreateProxy(bc, objectClasses, objectClasses, testService, serviceMap);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Object testCreateProxy(BundleContext bc, Class [] objectClasses, final Class [] proxyRegClasses, Object testService, final Map<ServiceReference, Object> serviceMap) throws Exception {
+ // A linked hash map to keep iteration order over the keys predictable
+ final LinkedHashMap<String, Class> objClsMap = new LinkedHashMap<String, Class>();
+ for (Class cls : objectClasses) {
+ objClsMap.put(cls.getName(), cls);
+ }
+
+ // A linked hash map to keep iteration order over the keys predictable
+ final LinkedHashMap<String, Class> proxyRegClsMap = new LinkedHashMap<String, Class>();
+ for (Class cls : proxyRegClasses) {
+ proxyRegClsMap.put(cls.getName(), cls);
+ }
+
+ // Create the object that is actually being tested here
+ GuardProxyCatalog gpc = new GuardProxyCatalog(bc);
+
+ // The service being proxied has these properties
+ long serviceID = Long.MAX_VALUE;
+ final Hashtable<String, Object> serviceProps = new Hashtable<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, objClsMap.keySet().toArray(new String [] {}));
+ serviceProps.put(Constants.SERVICE_ID, serviceID);
+ serviceProps.put(GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY, Arrays.asList("everyone")); // will be overwritten
+ serviceProps.put("bar", "foo");
+
+ // The mock bundle context for the bundle providing the service is set up here
+ BundleContext providerBC = EasyMock.createMock(BundleContext.class);
+ // These are the expected service properties of the proxy registration. Note the proxy marker...
+ final Hashtable<String, Object> expectedProxyProps = new Hashtable<String, Object>(serviceProps);
+ expectedProxyProps.put(GuardProxyCatalog.PROXY_SERVICE_KEY, Boolean.TRUE);
+ // This will check that the right proxy is being registered.
+ EasyMock.expect(providerBC.registerService(
+ EasyMock.isA(String[].class),
+ EasyMock.anyObject(),
+ EasyMock.isA(Dictionary.class))).andAnswer(new IAnswer() {
+ @Override
+ public ServiceRegistration answer() throws Throwable {
+ if (!runningUnderCoverage) {
+ // Some of these checks don't work when running under coverage
+ assertArrayEquals(proxyRegClsMap.keySet().toArray(new String [] {}),
+ (String []) EasyMock.getCurrentArguments()[0]);
+
+ Object svc = EasyMock.getCurrentArguments()[1];
+ assertTrue(svc instanceof ServiceFactory);
+ }
+
+ Dictionary<String,Object> props = (Dictionary<String, Object>) EasyMock.getCurrentArguments()[2];
+ for (String key : expectedProxyProps.keySet()) {
+ if (GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY.equals(key)) {
+ assertTrue("The roles property should have been overwritten",
+ !Arrays.asList("everyone").equals(props.get(key)));
+ } else {
+ assertEquals(expectedProxyProps.get(key), props.get(key));
+ }
+ }
+
+ ServiceRegistration reg = EasyMock.createMock(ServiceRegistration.class);
+ ServiceReference sr = mockServiceReference(props);
+ EasyMock.expect(reg.getReference()).andReturn(sr).anyTimes();
+ reg.unregister();
+ EasyMock.expectLastCall().once();
+ EasyMock.replay(reg);
+
+ serviceMap.put(sr, EasyMock.getCurrentArguments()[1]);
+
+ return reg;
+ }
+ }).once();
+ EasyMock.expect(providerBC.getService(EasyMock.isA(ServiceReference.class))).andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ return serviceMap.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(providerBC);
+
+ // In some cases the proxy-creating code is looking for a classloader (e.g. when run through
+ // a coverage tool such as EclEmma). This will satisfy that.
+ BundleWiring bw = EasyMock.createMock(BundleWiring.class);
+ EasyMock.expect(bw.getClassLoader()).andReturn(getClass().getClassLoader()).anyTimes();
+ EasyMock.replay(bw);
+
+ // The mock bundle that provides the original service (and also the proxy is registered with this)
+ Bundle providerBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(providerBundle.getBundleContext()).andReturn(providerBC).anyTimes();
+ EasyMock.expect(providerBundle.adapt(BundleWiring.class)).andReturn(bw).anyTimes();
+ EasyMock.replay(providerBundle);
+
+ ServiceReference sr = mockServiceReference(providerBundle, serviceProps);
+
+ // The mock bundle context for the client bundle
+ BundleContext clientBC = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(clientBC.getService(sr)).andReturn(testService).anyTimes();
+ EasyMock.replay(clientBC);
+
+ // The mock bundle that consumes the service
+ Bundle clientBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(clientBundle.getBundleId()).andReturn(2999L).anyTimes();
+ EasyMock.expect(clientBundle.getBundleContext()).andReturn(clientBC).anyTimes();
+ EasyMock.expect(clientBundle.loadClass(EasyMock.isA(String.class))).andAnswer(new IAnswer() {
+ @Override
+ public Class answer() throws Throwable {
+ return objClsMap.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(clientBundle);
+
+ assertEquals("Precondition", 0, gpc.proxyMap.size());
+ assertEquals("Precondition", 0, gpc.createProxyQueue.size());
+ // Create the proxy for the service
+ gpc.proxyIfNotAlreadyProxied(sr);
+ assertEquals(1, gpc.proxyMap.size());
+
+ // The actual proxy creation is done asynchronously.
+ GuardProxyCatalog.ServiceRegistrationHolder holder = gpc.proxyMap.get(serviceID);
+ assertNull("The registration shouldn't have happened yet", holder.registration);
+ assertEquals(1, gpc.createProxyQueue.size());
+
+ // Mimic the thread that works the queue to create the proxy
+ GuardProxyCatalog.CreateProxyRunnable runnable = gpc.createProxyQueue.take();
+ ProxyManager pm = getProxyManager();
+ runnable.run(pm);
+
+ // The runnable should have put the actual registration in the holder
+ ServiceReference<?> proxySR = holder.registration.getReference();
+ for (String key : expectedProxyProps.keySet()) {
+ if (GuardProxyCatalog.SERVICE_GUARD_ROLES_PROPERTY.equals(key)) {
+ assertTrue("The roles property should have been overwritten",
+ !Arrays.asList("everyone").equals(proxySR.getProperty(key)));
+ } else {
+ assertEquals(expectedProxyProps.get(key), proxySR.getProperty(key));
+ }
+ }
+
+ // Check that the proxy registration was done on the original provider bundle's context
+ EasyMock.verify(providerBC);
+
+ // Test that the actual proxy invokes the original service...
+ ServiceFactory proxyServiceSF = (ServiceFactory) serviceMap.get(proxySR);
+ Object proxyService = proxyServiceSF.getService(clientBundle, null);
+ assertNotSame("The proxy should not be the same object as the original service", testService, proxyService);
+
+ return proxyService;
+ }
+
+ private ProxyManager getProxyManager() {
+ return new AsmProxyManager();
+ }
+
+ private Dictionary<String, Object> getServiceReferenceProperties(ServiceReference<?> sr) {
+ Dictionary<String, Object> dict = new Hashtable<String, Object>();
+
+ for (String key : sr.getPropertyKeys()) {
+ dict.put(key, sr.getProperty(key));
+ }
+
+ return dict;
+ }
+
+ private Bundle mockBundle(long id) {
+ Bundle bundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(bundle.getBundleId()).andReturn(id).anyTimes();
+ EasyMock.replay(bundle);
+ return bundle;
+ }
+
+ private BundleContext mockBundleContext() throws InvalidSyntaxException {
+ return mockBundleContext(null);
+ }
+
+ private BundleContext mockBundleContext(Bundle b) throws InvalidSyntaxException {
+ if (b == null) {
+ b = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(b.getBundleId()).andReturn(89334L).anyTimes();
+ EasyMock.replay(b);
+ }
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.createFilter(EasyMock.isA(String.class))).andAnswer(new IAnswer<Filter>() {
+ @Override
+ public Filter answer() throws Throwable {
+ return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ EasyMock.replay(bc);
+ return bc;
+ }
+
+ private BundleContext openStrictMockBundleContext(Bundle b) throws InvalidSyntaxException {
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(bc.createFilter(EasyMock.isA(String.class))).andAnswer(new IAnswer<Filter>() {
+ @Override
+ public Filter answer() throws Throwable {
+ return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ if (b != null) {
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ }
+ return bc;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private BundleContext mockConfigAdminBundleContext(Dictionary<String, Object> ... configs) throws IOException,
+ InvalidSyntaxException {
+ Configuration [] configurations = new Configuration[configs.length];
+
+ for (int i = 0; i < configs.length; i++) {
+ Configuration conf = EasyMock.createMock(Configuration.class);
+ EasyMock.expect(conf.getProperties()).andReturn(configs[i]).anyTimes();
+ EasyMock.expect(conf.getPid()).andReturn((String) configs[i].get(Constants.SERVICE_PID)).anyTimes();
+ EasyMock.replay(conf);
+ configurations[i] = conf;
+ }
+ if (configurations.length == 0) {
+ configurations = null;
+ }
+
+ ConfigurationAdmin ca = EasyMock.createMock(ConfigurationAdmin.class);
+ EasyMock.expect(ca.listConfigurations("(&(service.pid=org.apache.karaf.service.acl.*)(service.guard=*))")).andReturn(configurations).anyTimes();
+ EasyMock.replay(ca);
+
+ final ServiceReference caSR = EasyMock.createMock(ServiceReference.class);
+ EasyMock.replay(caSR);
+
+ Bundle b = EasyMock.createMock(Bundle.class);
+ EasyMock.expect(b.getBundleId()).andReturn(877342449L).anyTimes();
+ EasyMock.replay(b);
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ EasyMock.expect(bc.createFilter(EasyMock.isA(String.class))).andAnswer(new IAnswer<Filter>() {
+ @Override
+ public Filter answer() throws Throwable {
+ return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ String cmFilter = "(&(objectClass=" + ConfigurationAdmin.class.getName() + ")"
+ + "(!(" + GuardProxyCatalog.PROXY_SERVICE_KEY + "=*)))";
+ bc.addServiceListener(EasyMock.isA(ServiceListener.class), EasyMock.eq(cmFilter));
+ EasyMock.expectLastCall().anyTimes();
+ EasyMock.expect(bc.getServiceReferences(EasyMock.anyObject(String.class), EasyMock.eq(cmFilter))).
+ andReturn(new ServiceReference<?> [] {caSR}).anyTimes();
+ EasyMock.expect(bc.getService(caSR)).andReturn(ca).anyTimes();
+ EasyMock.replay(bc);
+ return bc;
+ }
+
+ private ServiceReference<?> mockServiceReference(final Dictionary<String, Object> props) {
+ return mockServiceReference(props, Object.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> ServiceReference<T> mockServiceReference(Dictionary<String, Object> props, Class<T> cls) {
+ return (ServiceReference<T>) mockServiceReference(null, props);
+ }
+
+ private ServiceReference<?> mockServiceReference(Bundle providerBundle,
+ final Dictionary<String, Object> serviceProps) {
+ ServiceReference<?> sr = EasyMock.createMock(ServiceReference.class);
+
+ // Make sure the properties are 'live' in that if they change the reference changes too
+ EasyMock.expect(sr.getPropertyKeys()).andAnswer(new IAnswer<String[]>() {
+ @Override
+ public String[] answer() throws Throwable {
+ return Collections.list(serviceProps.keys()).toArray(new String [] {});
+ }
+ }).anyTimes();
+ EasyMock.expect(sr.getProperty(EasyMock.isA(String.class))).andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ return serviceProps.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ if (providerBundle != null) {
+ EasyMock.expect(sr.getBundle()).andReturn(providerBundle).anyTimes();
+ }
+ EasyMock.replay(sr);
+ return sr;
+ }
+
+ class MockCreateProxyRunnable implements CreateProxyRunnable {
+ private final long orgServiceID;
+
+ public MockCreateProxyRunnable(long serviceID) {
+ orgServiceID = serviceID;
+ }
+
+ @Override
+ public long getOriginalServiceID() {
+ return orgServiceID;
+ }
+
+ @Override
+ public void run(ProxyManager pm) throws Exception {}
+ }
+
+ public interface TestServiceAPI {
+ String doit();
+ }
+
+ public class TestService implements TestServiceAPI {
+ @Override
+ public String doit() {
+ return "Doing it";
+ }
+ }
+
+ public interface TestServiceAPI2 {
+ String doit(String s);
+ }
+
+ public interface TestServiceAPI3 {
+ int foo();
+ int foo(int f);
+ int bar();
+ }
+
+ class TestService3 implements TestServiceAPI3 {
+ public int foo() {
+ return 42;
+ }
+
+ public int foo(int f) {
+ return -f;
+ }
+
+ public int bar() {
+ return 99;
+ }
+ }
+
+ public class TestObjectWithoutInterface {
+ public long compute(long l) {
+ return -l;
+ }
+ }
+
+ public class CombinedTestService extends TestObjectWithoutInterface implements TestServiceAPI {
+ @Override
+ public String doit() {
+ return "Doing it";
+ }
+ }
+
+ private abstract class AbstractService implements TestServiceAPI {
+ @Override
+ public String doit() {
+ return "Doing it";
+ }
+ }
+
+ public class EmptyPublicTestService extends AbstractService {}
+
+ public class DescendantTestService extends EmptyPublicTestService {}
+
+ private class PrivateTestService implements TestServiceAPI {
+ @Override
+ public String doit() {
+ return "Doing it";
+ }
+ }
+
+ private class PrivateTestServiceNoDirectInterfaces extends PrivateTestService {}
+
+ public final class FinalTestService extends AbstractService implements TestServiceAPI {}
+
+ public class ClassWithFinalMethod {
+ public void foo() {}
+ public final String bar() { return "Bar"; }
+ }
+
+ public class ClassWithPrivateMethod {
+ public void foo() {}
+ @SuppressWarnings("unused")
+ private void bar() {}
+ }
+}