blob: 8b9e6c4cfc44b17e125d849768cbc53f863b8b12 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
* is available at http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* VMware Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.config.internal.adapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.gemini.blueprint.context.support.internal.security.SecurityUtils;
import org.eclipse.gemini.blueprint.service.importer.ImportedOsgiServiceProxy;
import org.eclipse.gemini.blueprint.service.importer.OsgiServiceLifecycleListener;
import org.eclipse.gemini.blueprint.service.importer.ServiceReferenceProxy;
import org.eclipse.gemini.blueprint.util.internal.ReflectionUtils;
import org.osgi.framework.ServiceReference;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List;
import java.util.Map;
/**
* OsgiServiceLifecycleListener wrapper for custom beans, useful when custom methods are being used.
*
* <p/> <strong>Note:</strong> To support cyclic injection, this adapter does dependency lookup for the actual listener.
*
* @author Costin Leau
*/
public class OsgiServiceLifecycleListenerAdapter implements OsgiServiceLifecycleListener, InitializingBean,
BeanFactoryAware {
private static final Log log = LogFactory.getLog(OsgiServiceLifecycleListenerAdapter.class);
/**
* Map of methods keyed by the first parameter which indicates the service type expected.
*/
private Map<Class<?>, List<Method>> bindMethods, unbindMethods;
private boolean isBlueprintCompliant = false;
/**
* anyName(ServiceReference reference) method signature.
*/
private Method bindReference, unbindReference;
private String bindMethod, unbindMethod;
/** does the target implement the listener interface */
private boolean isLifecycleListener;
/** target bean factory */
private BeanFactory beanFactory;
/** used when dealing with a cycle */
private String targetBeanName;
/** target object (can be null at first when dealing with a cycle */
private Object target;
/** init flag */
private boolean initialized;
public void afterPropertiesSet() {
Assert.notNull(beanFactory);
Assert.isTrue(target != null || StringUtils.hasText(targetBeanName),
"one of 'target' or 'targetBeanName' properties has to be set");
if (target != null)
initialized = true;
// do validation (on the target type)
initialize();
// postpone target initialization until one of bind/unbind method is called
}
private void retrieveTarget() {
target = beanFactory.getBean(targetBeanName);
initialized = true;
}
/**
* Initialise adapter. Determine custom methods and do validation.
*/
private void initialize() {
Class<?> clazz = (target == null ? beanFactory.getType(targetBeanName) : target.getClass());
Assert.notNull(clazz, "listener " + targetBeanName + " class type cannot be determined");
isLifecycleListener = OsgiServiceLifecycleListener.class.isAssignableFrom(clazz);
if (isLifecycleListener)
if (log.isDebugEnabled())
log.debug(clazz.getName() + " is a lifecycle listener");
bindMethods = CustomListenerAdapterUtils.determineCustomMethods(clazz, bindMethod, isBlueprintCompliant);
boolean isSecurityEnabled = System.getSecurityManager() != null;
final Class<?> clz = clazz;
// determine methods using ServiceReference signature
if (StringUtils.hasText(bindMethod)) {
if (isSecurityEnabled) {
bindReference = AccessController.doPrivileged(new PrivilegedAction<Method>() {
public Method run() {
return findServiceReferenceMethod(clz, bindMethod);
}
});
} else {
bindReference = findServiceReferenceMethod(clz, bindMethod);
}
if (bindMethods.isEmpty()) {
String beanName = (target == null ? "" : " bean [" + targetBeanName + "] ;");
throw new IllegalArgumentException("Custom bind method [" + bindMethod + "] not found on " + beanName
+ "class " + clazz);
}
}
unbindMethods = CustomListenerAdapterUtils.determineCustomMethods(clazz, unbindMethod, isBlueprintCompliant);
if (StringUtils.hasText(unbindMethod)) {
if (isSecurityEnabled) {
unbindReference = AccessController.doPrivileged(new PrivilegedAction<Method>() {
public Method run() {
return findServiceReferenceMethod(clz, unbindMethod);
}
});
} else {
unbindReference = findServiceReferenceMethod(clz, unbindMethod);
}
if (unbindMethods.isEmpty()) {
String beanName = (target == null ? "" : " bean [" + targetBeanName + "] ;");
throw new IllegalArgumentException("Custom unbind method [" + unbindMethod + "] not found on "
+ beanName + "class " + clazz);
}
}
if (!isLifecycleListener
&& (bindMethods.isEmpty() && unbindMethods.isEmpty() && bindReference == null && unbindReference == null))
throw new IllegalArgumentException("target object needs to implement "
+ OsgiServiceLifecycleListener.class.getName()
+ " or custom bind/unbind methods have to be specified");
if (log.isTraceEnabled()) {
StringBuilder builder = new StringBuilder();
builder.append("Discovered bind methods=");
builder.append(bindMethods.values());
builder.append(", bind ServiceReference=");
builder.append(bindReference);
builder.append("\nunbind methods=");
builder.append(unbindMethods.values());
builder.append(", unbind ServiceReference=");
builder.append(unbindReference);
log.trace(builder.toString());
}
}
private Method findServiceReferenceMethod(Class<?> clazz, String methodName) {
Method method =
org.springframework.util.ReflectionUtils.findMethod(clazz, methodName,
new Class[] { ServiceReference.class });
if (method != null) {
org.springframework.util.ReflectionUtils.makeAccessible(method);
}
return method;
}
/**
* Invoke method with signature <code>bla(ServiceReference ref)</code>.
*
* @param target
* @param method
* @param service
*/
private void invokeCustomServiceReferenceMethod(Object target, Method method, Object service) {
if (method != null) {
boolean trace = log.isTraceEnabled();
// get the service reference
// find the compatible types (accept null service)
if (trace)
log.trace("invoking listener custom method " + method);
ServiceReference ref =
(service != null ? ((ImportedOsgiServiceProxy) service).getServiceReference() : null);
// Never expose the internal proxies; they cannot be used to safely obtain service instances.
if (ref != null) {
ref = ((ServiceReferenceProxy) ref).getTargetServiceReference();
}
try {
ReflectionUtils.invokeMethod(method, target, new Object[] { ref });
}
// make sure to log exceptions and continue with the
// rest of the listeners
catch (Exception ex) {
Exception cause = ReflectionUtils.getInvocationException(ex);
log.warn("custom method [" + method + "] threw exception when passing service ["
+ ObjectUtils.identityToString(service) + "]", cause);
}
}
}
public void bind(final Object service, final Map properties) throws Exception {
boolean trace = log.isTraceEnabled();
if (trace)
log.trace("Invoking bind method for service " + ObjectUtils.identityToString(service) + " with props="
+ properties);
if (!initialized)
retrieveTarget();
boolean isSecurityEnabled = (System.getSecurityManager() != null);
AccessControlContext acc = null;
if (isSecurityEnabled) {
acc = SecurityUtils.getAccFrom(beanFactory);
}
// first call interface method (if it exists)
if (isLifecycleListener) {
if (trace)
log.trace("Invoking listener interface methods");
try {
if (isSecurityEnabled) {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
((OsgiServiceLifecycleListener) target).bind(service, properties);
return null;
}
}, acc);
} else {
((OsgiServiceLifecycleListener) target).bind(service, properties);
}
} catch (Exception ex) {
if (ex instanceof PrivilegedActionException) {
ex = ((PrivilegedActionException) ex).getException();
}
log.warn("standard bind method on [" + target.getClass().getName() + "] threw exception", ex);
}
}
if (isSecurityEnabled) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
CustomListenerAdapterUtils.invokeCustomMethods(target, bindMethods, service, properties);
invokeCustomServiceReferenceMethod(target, bindReference, service);
return null;
}
}, acc);
} else {
CustomListenerAdapterUtils.invokeCustomMethods(target, bindMethods, service, properties);
invokeCustomServiceReferenceMethod(target, bindReference, service);
}
}
public void unbind(final Object service, final Map properties) throws Exception {
boolean trace = log.isTraceEnabled();
if (!initialized)
retrieveTarget();
if (trace)
log.trace("Invoking unbind method for service " + ObjectUtils.identityToString(service) + " with props="
+ properties);
boolean isSecurityEnabled = (System.getSecurityManager() != null);
AccessControlContext acc = null;
if (isSecurityEnabled) {
acc = SecurityUtils.getAccFrom(beanFactory);
}
// first call interface method (if it exists)
if (isLifecycleListener) {
if (trace)
log.trace("Invoking listener interface methods");
try {
if (isSecurityEnabled) {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
((OsgiServiceLifecycleListener) target).unbind(service, properties);
return null;
}
}, acc);
} else {
((OsgiServiceLifecycleListener) target).unbind(service, properties);
}
} catch (Exception ex) {
log.warn("Standard unbind method on [" + target.getClass().getName() + "] threw exception", ex);
}
}
if (isSecurityEnabled) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
CustomListenerAdapterUtils.invokeCustomMethods(target, unbindMethods, service, properties);
invokeCustomServiceReferenceMethod(target, unbindReference, service);
return null;
}
}, acc);
} else {
CustomListenerAdapterUtils.invokeCustomMethods(target, unbindMethods, service, properties);
invokeCustomServiceReferenceMethod(target, unbindReference, service);
}
}
/**
* @param bindMethod The bindMethod to set.
*/
public void setBindMethod(String bindMethod) {
this.bindMethod = bindMethod;
}
/**
* @param unbindMethod The unbindMethod to set.
*/
public void setUnbindMethod(String unbindMethod) {
this.unbindMethod = unbindMethod;
}
public void setTarget(Object target) {
this.target = target;
}
public void setTargetBeanName(String targetName) {
this.targetBeanName = targetName;
}
public void setBlueprintCompliant(boolean compliant) {
this.isBlueprintCompliant = compliant;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}