/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.osgi.util.service;
import java.io.Closeable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
/**
* @author Carlos Sierra Andrés
*/
public class ReflectionServiceTracker implements Closeable {
public ReflectionServiceTracker(Object target) {
Class<?> targetClass = target.getClass();
Bundle bundle = FrameworkUtil.getBundle(targetClass);
BundleContext bundleContext = bundle.getBundleContext();
List<InjectionPoint> injectionPoints = getInjectionPoints(target);
for (InjectionPoint injectionPoint : injectionPoints) {
ServiceTracker<?, ?> serviceTracker = track(
bundleContext, target, injectionPoint);
_serviceTrackers.add(serviceTracker);
}
}
@Override
public void close() {
for (ServiceTracker<?, ?> serviceTracker : _serviceTrackers) {
try {
serviceTracker.close();
}
catch (Exception e) {
}
}
_serviceTrackers.clear();
}
protected InjectionPoint createInjectionPoint(
Object target, Method method) {
Class<?> clazz = method.getParameterTypes()[0];
if (clazz.isInterface()) {
return new UnavailableProxyInjectionPoint(
target, method, _unavailableServiceProxy);
}
return new InjectionPoint(target, method);
}
protected List<Method> getInjectionPointMethods(Object target) {
List<Method> injectionPointMethods = new ArrayList<Method>();
Class<?> targetClass = target.getClass();
Method[] methods = targetClass.getDeclaredMethods();
for (Method method : methods) {
boolean annotationPresent = method.isAnnotationPresent(
Reference.class);
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> returnType = method.getReturnType();
if (annotationPresent && (parameterTypes.length == 1) &&
returnType.equals(void.class)) {
injectionPointMethods.add(method);
}
}
return injectionPointMethods;
}
protected List<InjectionPoint> getInjectionPoints(Object target) {
Class<?> targetClass = target.getClass();
ClassLoader classLoader = targetClass.getClassLoader();
List<Class<?>> interfaceClasses = new ArrayList<Class<?>>();
List<Method> injectionPointMethods = getInjectionPointMethods(target);
for (Method injectionPointMethod : injectionPointMethods) {
Class<?> parameterType =
injectionPointMethod.getParameterTypes()[0];
if (parameterType.isInterface()) {
interfaceClasses.add(parameterType);
}
}
_unavailableServiceProxy = Proxy.newProxyInstance(
classLoader, interfaceClasses.toArray(new Class<?>[0]),
_invocationHandler);
List<InjectionPoint> injectionPoints = new ArrayList<InjectionPoint>();
for (Method injectionPointMethod : injectionPointMethods) {
InjectionPoint injectionPoint = createInjectionPoint(
target, injectionPointMethod);
if (injectionPoint != null) {
injectionPoints.add(injectionPoint);
}
}
return injectionPoints;
}
protected ServiceTracker<?, ?> track(
BundleContext bundleContext, final Object target,
final InjectionPoint injectionPoint) {
try {
injectionPoint.reset();
}
catch (Exception e) {
throw new RuntimeException(
"Unable to unset " + injectionPoint.getName() + " on " + target,
e);
}
ServiceTracker<?, ?> serviceTracker =
new ServiceTracker<Object, Object>(
bundleContext, (Class<Object>)injectionPoint.getParameterType(),
null) {
@Override
public Object addingService(
ServiceReference<Object> serviceReference) {
Object service = super.addingService(serviceReference);
ServiceReference<Object> currentServiceReference =
getServiceReference();
if ((currentServiceReference == null) ||
(serviceReference.compareTo(currentServiceReference) >
0)) {
try {
injectionPoint.inject(service);
}
catch (Exception e) {
throw new RuntimeException(
"Unable to set service reference using " +
injectionPoint.getName() + " on " + target,
e);
}
}
return service;
}
@Override
public void modifiedService(
ServiceReference<Object> reference, Object service) {
super.modifiedService(reference, service);
ServiceReference<Object> currentServiceReference =
getServiceReference();
Object currentService = getService(currentServiceReference);
try {
injectionPoint.inject(currentService);
}
catch (Exception e) {
throw new RuntimeException(
"Unable to set injection point " +
injectionPoint.getName() + " on " +
target,
e);
}
}
@Override
public void removedService(
ServiceReference<Object> serviceReference, Object service) {
try {
super.removedService(serviceReference, service);
ServiceReference<Object> currentServiceReference =
getServiceReference();
if (currentServiceReference == null) {
injectionPoint.reset();
}
else {
Object currentService = getService(
currentServiceReference);
injectionPoint.inject(currentService);
}
}
catch (IllegalStateException ise) {
}
catch (Exception e) {
throw new RuntimeException(
"Unable to set injection point " +
injectionPoint.getName() + " on " +
target,
e);
}
}
};
serviceTracker.open();
return serviceTracker;
}
private static final InvocationHandler _invocationHandler =
new InvocationHandler() {
@Override
public Object invoke(
Object object, Method method, Object[] parameters)
throws Throwable {
throw new UnavailableServiceException(
method.getDeclaringClass());
}
};
private final List<ServiceTracker<?, ?>> _serviceTrackers =
new ArrayList<ServiceTracker<?, ?>>();
private Object _unavailableServiceProxy;
private static class InjectionPoint {
public String getName() {
return _method.getName();
}
public Class<?> getParameterType() {
return _method.getParameterTypes()[0];
}
public void inject(Object value)
throws IllegalAccessException, InvocationTargetException {
_method.invoke(_target, value);
}
public void reset()
throws IllegalAccessException, InvocationTargetException {
_method.invoke(_target, new Object[] {null});
}
private InjectionPoint(Object target, Method method) {
_target = target;
_method = method;
}
private final Method _method;
private final Object _target;
}
private static class UnavailableProxyInjectionPoint extends InjectionPoint {
public UnavailableProxyInjectionPoint(
Object target, Method method, Object proxy) {
super(target, method);
_proxy = proxy;
}
@Override
public void reset()
throws IllegalAccessException, InvocationTargetException {
super.inject(_proxy);
}
private final Object _proxy;
}
}