Skip to content

Commit

Permalink
Completed support for using dynamic proxies as generic aspects. Added…
Browse files Browse the repository at this point in the history
… a test to validate this behaviour.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@946137 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
m4rr5 committed May 19, 2010
1 parent 9b61208 commit a6ffb99
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ public Service createAspectService(Class serviceInterface, String serviceFilter,
public Service createAspectService(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, Dictionary properties) {
return m_manager.createAspectService(serviceInterface, serviceFilter, ranking, factory, factoryCreateMethod, properties);
}
public Service createAspectService(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, String attributeName, Dictionary properties) {
return m_manager.createAspectService(serviceInterface, serviceFilter, ranking, factory, factoryCreateMethod, attributeName, properties);
}

public Service createAdapterService(Class serviceInterface, String serviceFilter, String adapterInterface, Object adapterImplementation, Dictionary adapterProperties) {
return m_manager.createAdapterService(serviceInterface, serviceFilter, adapterInterface, adapterImplementation, adapterProperties);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
import org.apache.felix.dm.impl.AdapterImpl;
import org.apache.felix.dm.impl.AspectImpl;
import org.apache.felix.dm.impl.BundleAdapterImpl;
import org.apache.felix.dm.impl.FactoryConfigurationAdapterImpl;
import org.apache.felix.dm.impl.FactoryConfigurationAdapterMetaTypeImpl;
import org.apache.felix.dm.impl.Logger;
import org.apache.felix.dm.impl.FactoryConfigurationAdapterImpl;
import org.apache.felix.dm.impl.ResourceAdapterImpl;
import org.apache.felix.dm.impl.ServiceImpl;
import org.apache.felix.dm.impl.dependencies.BundleDependencyImpl;
Expand Down Expand Up @@ -201,6 +201,15 @@ public Service createAspectService(Class serviceInterface, String serviceFilter,
.setCallbacks("added", "removed")
);
}
public Service createAspectService(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, String attributeName, Dictionary aspectProperties) {
return createService()
.setImplementation(new AspectImpl(serviceInterface, serviceFilter, ranking, factory, factoryCreateMethod, attributeName, aspectProperties))
.add(createServiceDependency()
.setService(serviceInterface, createAspectFilter(serviceFilter))
.setAutoConfig(false)
.setCallbacks("added", "removed")
);
}
private String createAspectFilter(String filter) {
// we only want to match services which are not themselves aspects
if (filter == null || filter.length() == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class AspectImpl extends AbstractDecorator {
private final Object m_factory;
private final String m_factoryCreateMethod;
private final int m_ranking;
private final String m_attributeName;

public AspectImpl(Class serviceInterface, String serviceFilter, int ranking, Object aspectImplementation, Dictionary properties) {
m_serviceInterface = serviceInterface;
Expand All @@ -46,6 +47,7 @@ public AspectImpl(Class serviceInterface, String serviceFilter, int ranking, Obj
m_factory = null;
m_factoryCreateMethod = null;
m_ranking = ranking;
m_attributeName = null;
}

public AspectImpl(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, Dictionary properties) {
Expand All @@ -56,6 +58,18 @@ public AspectImpl(Class serviceInterface, String serviceFilter, int ranking, Obj
m_aspectProperties = properties;
m_aspectImplementation = null;
m_ranking = ranking;
m_attributeName = null;
}

public AspectImpl(Class serviceInterface, String serviceFilter, int ranking, Object factory, String factoryCreateMethod, String attributeName, Dictionary properties) {
m_serviceInterface = serviceInterface;
m_serviceFilter = serviceFilter;
m_factory = factory;
m_factoryCreateMethod = factoryCreateMethod;
m_attributeName = attributeName;
m_aspectProperties = properties;
m_aspectImplementation = null;
m_ranking = ranking;
}

public Service createService(Object[] properties) {
Expand All @@ -80,13 +94,25 @@ public Service createService(Object[] properties) {
List dependencies = m_service.getDependencies();
dependencies.remove(0);
if (m_aspectImplementation == null) {
return m_manager.createService()
.setInterface(m_serviceInterface.getName(), props)
.setFactory(m_factory, m_factoryCreateMethod)
.add(dependencies)
.add(m_manager.createServiceDependency()
.setService(m_serviceInterface, createAspectFilter(m_serviceFilter))
.setRequired(true));
if (m_attributeName == null) {
return m_manager.createService()
.setInterface(m_serviceInterface.getName(), props)
.setFactory(m_factory, m_factoryCreateMethod)
.add(dependencies)
.add(m_manager.createServiceDependency()
.setService(m_serviceInterface, createAspectFilter(m_serviceFilter))
.setRequired(true));
}
else {
return m_manager.createService()
.setInterface(m_serviceInterface.getName(), props)
.setFactory(m_factory, m_factoryCreateMethod)
.add(dependencies)
.add(m_manager.createServiceDependency()
.setService(m_serviceInterface, createAspectFilter(m_serviceFilter))
.setAutoConfig(m_attributeName)
.setRequired(true));
}
}
else {
return m_manager.createService()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static void invokeCallbackMethod(Object instance, String methodName, Clas
throw new NoSuchMethodException(methodName);
}

public static void invokeMethod(Object object, Class clazz, String name, Class[][] signatures, Object[][] parameters, boolean isSuper) throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException, IllegalAccessException {
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");
}
Expand All @@ -34,8 +34,7 @@ public static void invokeMethod(Object object, Class clazz, String name, Class[]
m = clazz.getDeclaredMethod(name, signature);
if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
m.setAccessible(true);
m.invoke(object, parameters[i]);
return;
return m.invoke(object, parameters[i]);
}
}
catch (NoSuchMethodException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -684,8 +684,10 @@ public void initService() {
}
else {
try {
Method m = factory.getClass().getDeclaredMethod(m_instanceFactoryCreateMethod, null);
m_serviceInstance = m.invoke(factory, null);
// Method m = factory.getClass().getDeclaredMethod(m_instanceFactoryCreateMethod, null);
// m_serviceInstance = m.invoke(factory, null);
//
m_serviceInstance = InvocationUtil.invokeMethod(factory, factory.getClass(), m_instanceFactoryCreateMethod, new Class[][] {{}}, new Object[][] {{}}, false);
}
catch (Exception e) {
m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* 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.felix.dm.test;

import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
import static org.ops4j.pax.exam.CoreOptions.provision;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import junit.framework.Assert;

import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.service.Service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.BundleContext;

@RunWith(JUnit4TestRunner.class)
public class DynamicProxyAspectTest extends Base {
@Configuration
public static Option[] configuration() {
return options(
provision(
mavenBundle().groupId("org.osgi").artifactId("org.osgi.compendium").version("4.1.0"),
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject()
)
);
}

@Test
public void testImplementGenericAspectWithDynamicProxy(BundleContext context) {
DependencyManager m = new DependencyManager(context);
// helper class that ensures certain steps get executed in sequence
Ensure e = new Ensure();

// create two service providers, each providing a different service interface
Service sp1 = m.createService().setImplementation(new ServiceProvider(e)).setInterface(ServiceInterface.class.getName(), null);
Service sp2 = m.createService().setImplementation(new ServiceProvider2(e)).setInterface(ServiceInterface2.class.getName(), null);

// create a dynamic proxy based aspect and hook it up to both services
Service a1 = m.createAspectService(ServiceInterface.class, null, 10, new Factory(e, ServiceInterface.class, "ServiceInterfaceProxy"), "create", "m_service", null);
Service a2 = m.createAspectService(ServiceInterface2.class, null, 10, new Factory(e, ServiceInterface2.class, "ServiceInterfaceProxy2"), "create", "m_service", null);

// create a client that invokes a method on boths services, validate that it goes
// through the proxy twice
Service sc = m.createService()
.setImplementation(new ServiceConsumer(e))
.add(m.createServiceDependency().setService(ServiceInterface.class).setRequired(true))
.add(m.createServiceDependency().setService(ServiceInterface2.class).setRequired(true))
;

// register both producers, validate that both services are started
m.add(sp1);
e.waitForStep(1, 2000);
m.add(sp2);
e.waitForStep(2, 2000);

// add both aspects, and validate that both instances have been created
m.add(a1);
m.add(a2);
e.waitForStep(4, 4000);

// add the client, which will automatically invoke both services
m.add(sc);

// wait until both services have been invoked
e.waitForStep(6, 4000);

// make sure the proxy has been called twice
Assert.assertEquals("Proxy should have been invoked this many times.", 2, DynamicProxyHandler.getCounter());
}

static interface ServiceInterface {
public void invoke(Runnable run);
}

static interface ServiceInterface2 {
public void invoke(Runnable run);
}

static class ServiceProvider implements ServiceInterface {
private final Ensure m_ensure;
public ServiceProvider(Ensure e) {
m_ensure = e;
}
public void start() {
m_ensure.step(1);
}
public void invoke(Runnable run) {
run.run();
}
}

static class ServiceProvider2 implements ServiceInterface2 {
private final Ensure m_ensure;
public ServiceProvider2(Ensure ensure) {
m_ensure = ensure;
}
public void start() {
m_ensure.step(2);
}
public void invoke(Runnable run) {
run.run();
}
}

static class ServiceConsumer implements Runnable {
private volatile ServiceInterface m_service;
private volatile ServiceInterface2 m_service2;
private final Ensure m_ensure;

public ServiceConsumer(Ensure e) {
m_ensure = e;
}

public void init() {
Thread t = new Thread(this);
t.start();
}

public void run() {
m_service.invoke(Ensure.createRunnableStep(m_ensure, 5));
m_service2.invoke(Ensure.createRunnableStep(m_ensure, 6));
}
}

static class DynamicProxyHandler implements InvocationHandler {
public volatile Object m_service; // ISSUE, we cannot inject into "Object" at the moment
private final String m_label;
private static volatile int m_counter = 0;

public DynamicProxyHandler(String label) {
m_label = label;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (m_service == null) {
Assert.fail("No service was injected into dynamic proxy handler " + m_label);
}
Method m = m_service.getClass().getMethod(method.getName(), method.getParameterTypes());
if (m == null) {
Assert.fail("No method " + method.getName() + " was found in instance " + m_service + " in dynamic proxy handler " + m_label);
}
m_counter++;
return m.invoke(m_service, args);
}

public static int getCounter() {
return m_counter;
}
}

static class Factory {
private final String m_label;
private Class m_class;
private final Ensure m_ensure;

public Factory(Ensure ensure, Class clazz, String label) {
m_ensure = ensure;
m_class = clazz;
m_label = label;
}

public Object create() {
m_ensure.step();
return Proxy.newProxyInstance(m_class.getClassLoader(), new Class[] { m_class }, new DynamicProxyHandler(m_label));
}
}
}

0 comments on commit a6ffb99

Please sign in to comment.