/******************************************************************************
* 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.exporter.support.internal.support;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
import org.aopalliance.aop.Advice;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.gemini.blueprint.service.util.internal.aop.ProxyUtils;
import org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor;
import org.eclipse.gemini.blueprint.util.DebugUtils;
import org.eclipse.gemini.blueprint.util.OsgiStringUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
/**
* ServiceFactory used for publishing the service beans. Acts as a a wrapper around special beans (such as
* ServiceFactory). Additionally, used to create TCCL managing proxies.
*
* @author Costin Leau
*/
public class PublishingServiceFactory implements ServiceFactory {
/** logger */
private static final Log log = LogFactory.getLog(PublishingServiceFactory.class);
/** proxy cache in case the given bean has a non-singleton scope */
private final Map<Object, WeakReference<Object>> proxyCache;
private final LazyTargetResolver targetResolver;
private final Class<?>[] classes;
private final boolean createTCCLProxy;
private final ClassLoader classLoader;
private final ClassLoader aopClassLoader;
private final BundleContext bundleContext;
private final Object lock = new Object();
/**
* Constructs a new <code>PublishingServiceFactory</code> instance. Since its an internal class, this constructor
* accepts a number of parameters to sacrifice readability for thread-safety.
*
* @param classes
* @param target
* @param beanFactory
* @param targetBeanName
* @param createTCCLProxy
* @param classLoader
* @param aopClassLoader
* @param bundleContext
*/
public PublishingServiceFactory(LazyTargetResolver targetResolver, Class<?>[] classes, boolean createTCCLProxy,
ClassLoader classLoader, ClassLoader aopClassLoader, BundleContext bundleContext) {
super();
this.targetResolver = targetResolver;
this.classes = classes;
this.createTCCLProxy = createTCCLProxy;
this.classLoader = classLoader;
this.aopClassLoader = aopClassLoader;
this.bundleContext = bundleContext;
proxyCache = (createTCCLProxy ? new WeakHashMap<Object, WeakReference<Object>>(4) : null);
}
public Object getService(Bundle bundle, ServiceRegistration serviceRegistration) {
if (log.isTraceEnabled()) {
log.trace("Get service called by bundle " + OsgiStringUtils.nullSafeName(bundle) + " on registration "
+ OsgiStringUtils.nullSafeToString(serviceRegistration.getReference()));
}
targetResolver.activate();
Object bn = targetResolver.getBean();
// handle SF beans
if (bn instanceof ServiceFactory) {
bn = ((ServiceFactory) bn).getService(bundle, serviceRegistration);
}
if (createTCCLProxy) {
// check proxy cache
synchronized (proxyCache) {
WeakReference<Object> value = proxyCache.get(bn);
Object proxy = null;
if (value != null) {
proxy = value.get();
}
if (proxy == null) {
proxy = createCLLProxy(bn);
proxyCache.put(bn, new WeakReference<Object>(proxy));
}
bn = proxy;
}
}
return bn;
}
/**
* Proxy the target object with an interceptor that manages the context classloader. This should be applied only if
* such management is needed. Additionally, this method uses a cache to prevent multiple proxies to be created for
* the same object.
*
* @param target
* @return
*/
private Object createCLLProxy(final Object target) {
try {
return ProxyUtils.createProxy(classes, target, aopClassLoader, bundleContext,
new Advice[] { new ServiceTCCLInterceptor(classLoader) });
} catch (Throwable th) {
log.error("Cannot create TCCL managed proxy; falling back to the naked object", th);
if (th instanceof NoClassDefFoundError) {
NoClassDefFoundError ncdfe = (NoClassDefFoundError) th;
if (log.isWarnEnabled()) {
DebugUtils.debugClassLoadingThrowable(ncdfe, bundleContext.getBundle(), classes);
}
throw ncdfe;
}
}
return target;
}
public void ungetService(Bundle bundle, ServiceRegistration serviceRegistration, Object service) {
if (log.isTraceEnabled()) {
log.trace("Unget service called by bundle " + OsgiStringUtils.nullSafeName(bundle) + " on registration "
+ OsgiStringUtils.nullSafeToString(serviceRegistration.getReference()));
}
Class<?> type = targetResolver.getType();
// handle SF beans
if (ServiceFactory.class.isAssignableFrom(type)) {
ServiceFactory sf = (ServiceFactory) targetResolver.getBean();
sf.ungetService(bundle, serviceRegistration, service);
}
if (createTCCLProxy) {
synchronized (proxyCache) {
// trigger purging of unused entries
proxyCache.size();
}
}
}
}