/******************************************************************************
* 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.service.importer.support.internal.aop;
import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.eclipse.gemini.blueprint.context.support.internal.classloader.ClassLoaderFactory;
import org.eclipse.gemini.blueprint.service.importer.ImportedOsgiServiceProxy;
import org.eclipse.gemini.blueprint.service.importer.OsgiServiceLifecycleListener;
import org.eclipse.gemini.blueprint.util.internal.PrivilegedUtils;
import org.osgi.framework.Bundle;
import org.springframework.util.ObjectUtils;
/**
* Special Thread Context ClassLoading handling interceptor dealing with "service-provided" case, in which the backing
* service reference can be updated which requires update of the classloader used as TCCL.
*
* This interceptor requires registration of a dedicated {@link OsgiServiceLifecycleListener} which updates the
* classloader used.
*
* @author Costin Leau
*
*/
public class ServiceProviderTCCLInterceptor implements MethodInterceptor {
public class ServiceProviderTCCLListener implements OsgiServiceLifecycleListener {
public void bind(Object service, Map properties) throws Exception {
// check cast just to be sure (useful when doing testing for
// example)
if (service instanceof ImportedOsgiServiceProxy) {
// get the service reference from object
setServiceProvidedClassLoader(((ImportedOsgiServiceProxy) service).getServiceReference().getBundle());
}
}
public void unbind(Object service, Map properties) throws Exception {
// do nothing on unbind
}
}
private static final int hashCode = ServiceProviderTCCLInterceptor.class.hashCode() * 13;
/** internal lock used for synchronized access to the serviceBundle */
private final Object lock = new Object();
private Bundle serviceBundle;
private ClassLoader serviceClassLoader;
public Object invoke(MethodInvocation invocation) throws Throwable {
if (System.getSecurityManager() != null) {
return invokePrivileged(invocation);
} else {
return invokeUnprivileged(invocation);
}
}
private Object invokePrivileged(final MethodInvocation invocation) throws Throwable {
return PrivilegedUtils.executeWithCustomTCCL(getServiceProvidedClassLoader(),
new PrivilegedUtils.UnprivilegedThrowableExecution() {
public Object run() throws Throwable {
return invocation.proceed();
}
});
}
private Object invokeUnprivileged(MethodInvocation invocation) throws Throwable {
ClassLoader current = getServiceProvidedClassLoader();
ClassLoader previous = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(current);
return invocation.proceed();
} finally {
Thread.currentThread().setContextClassLoader(previous);
}
}
private ClassLoader getServiceProvidedClassLoader() {
synchronized (lock) {
return serviceClassLoader;
}
}
private void setServiceProvidedClassLoader(Bundle serviceBundle) {
synchronized (lock) {
this.serviceBundle = serviceBundle;
if (serviceBundle != null) {
serviceClassLoader = ClassLoaderFactory.getBundleClassLoaderFor(serviceBundle);
} else
serviceClassLoader = null;
}
}
public boolean equals(Object other) {
if (this == other)
return true;
if (other instanceof ServiceProviderTCCLInterceptor) {
ServiceProviderTCCLInterceptor oth = (ServiceProviderTCCLInterceptor) other;
return (ObjectUtils.nullSafeEquals(serviceBundle, oth.serviceBundle));
}
return false;
}
public int hashCode() {
return hashCode;
}
}