View Javadoc

1   /*
2    * Copyright 2006-2008 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.osgi.service.exporter.support.internal.support;
18  
19  import java.lang.ref.WeakReference;
20  import java.util.Map;
21  import java.util.WeakHashMap;
22  
23  import org.aopalliance.aop.Advice;
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.osgi.framework.Bundle;
27  import org.osgi.framework.BundleContext;
28  import org.osgi.framework.ServiceFactory;
29  import org.osgi.framework.ServiceRegistration;
30  import org.springframework.beans.factory.BeanFactory;
31  import org.springframework.osgi.service.util.internal.aop.ProxyUtils;
32  import org.springframework.osgi.service.util.internal.aop.ServiceTCCLInterceptor;
33  import org.springframework.osgi.util.DebugUtils;
34  
35  /**
36   * ServiceFactory used for publishing the service beans. Acts as a a wrapper
37   * around special beans (such as ServiceFactory). Additionally, used to create
38   * TCCL managing proxies.
39   * 
40   * @author Costin Leau
41   */
42  public class PublishingServiceFactory implements ServiceFactory {
43  
44  	/** logger */
45  	private static final Log log = LogFactory.getLog(PublishingServiceFactory.class);
46  
47  	// synchronization monitor
48  	private final Object monitor = new Object();
49  
50  	/** proxy cache in case the given bean has a non-singleton scope */
51  	private final Map proxyCache;
52  
53  	private final Class[] classes;
54  	private final Object target;
55  	private final BeanFactory beanFactory;
56  	private final String targetBeanName;
57  	private final boolean createTCCLProxy;
58  	private final ClassLoader classLoader;
59  	private final ClassLoader aopClassLoader;
60  	private final BundleContext bundleContext;
61  
62  
63  	/**
64  	 * Constructs a new <code>PublishingServiceFactory</code> instance. Since
65  	 * its an internal class, this constructor accepts a number of paramters to
66  	 * sacrifice readability for thread-safety.
67  	 * 
68  	 * @param classes
69  	 * @param target
70  	 * @param beanFactory
71  	 * @param targetBeanName
72  	 * @param createTCCLProxy
73  	 * @param classLoader
74  	 * @param aopClassLoader
75  	 * @param bundleContext
76  	 */
77  	public PublishingServiceFactory(Class[] classes, Object target, BeanFactory beanFactory, String targetBeanName,
78  			boolean createTCCLProxy, ClassLoader classLoader, ClassLoader aopClassLoader, BundleContext bundleContext) {
79  		super();
80  		this.classes = classes;
81  
82  		this.target = target;
83  		this.beanFactory = beanFactory;
84  		this.targetBeanName = targetBeanName;
85  		this.createTCCLProxy = createTCCLProxy;
86  		this.classLoader = classLoader;
87  		this.aopClassLoader = aopClassLoader;
88  		this.bundleContext = bundleContext;
89  
90  		proxyCache = (createTCCLProxy ? new WeakHashMap(4) : null);
91  	}
92  
93  	private Object getBean() {
94  		synchronized (monitor) {
95  			// no instance given
96  			// use container lookup
97  			return (target == null ? beanFactory.getBean(targetBeanName) : target);
98  		}
99  	}
100 
101 	public Object getService(Bundle bundle, ServiceRegistration serviceRegistration) {
102 		Object bn = getBean();
103 		// handle SF beans
104 		if (bn instanceof ServiceFactory) {
105 			bn = ((ServiceFactory) bn).getService(bundle, serviceRegistration);
106 		}
107 
108 		if (createTCCLProxy) {
109 			// check proxy cache
110 			synchronized (proxyCache) {
111                 WeakReference value = (WeakReference) proxyCache.get(bn);
112 				Object proxy = null;
113 				if (value != null) {
114 					proxy = value.get();
115 				}				
116 				if (proxy == null) {
117 					proxy = createCLLProxy(bn);
118 					proxyCache.put(bn, new WeakReference(proxy));
119 				}
120 				bn = proxy;
121 			}
122 		}
123 
124 		return bn;
125 	}
126 
127 	/**
128 	 * Proxy the target object with an interceptor that manages the context
129 	 * classloader. This should be applied only if such management is needed.
130 	 * Additionally, this method uses a cache to prevent multiple proxies to be
131 	 * created for the same object.
132 	 * 
133 	 * @param target
134 	 * @return
135 	 */
136 	private Object createCLLProxy(final Object target) {
137 		try {
138 			return ProxyUtils.createProxy(classes, target, aopClassLoader, bundleContext,
139 				new Advice[] { new ServiceTCCLInterceptor(classLoader) });
140 		}
141 		catch (Throwable th) {
142 			log.error("Cannot create TCCL managed proxy; falling back to the naked object", th);
143 			if (th instanceof NoClassDefFoundError) {
144 				NoClassDefFoundError ncdfe = (NoClassDefFoundError) th;
145 				if (log.isWarnEnabled()) {
146 					DebugUtils.debugClassLoadingThrowable(ncdfe, bundleContext.getBundle(), classes);
147 				}
148 				throw ncdfe;
149 			}
150 		}
151 
152 		return target;
153 	}
154 
155 	public void ungetService(Bundle bundle, ServiceRegistration serviceRegistration, Object service) {
156 		Object bn = getBean();
157 		// handle SF beans
158 		if (bn instanceof ServiceFactory) {
159 			((ServiceFactory) bn).ungetService(bundle, serviceRegistration, service);
160 		}
161 
162 		if (createTCCLProxy) {
163 			synchronized (proxyCache) {
164 				proxyCache.values().remove(new WeakReference(service));
165 			}
166 		}
167 	}
168 }