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.importer.support;
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.osgi.framework.BundleContext;
22  import org.osgi.framework.Filter;
23  import org.springframework.beans.factory.BeanClassLoaderAware;
24  import org.springframework.beans.factory.BeanNameAware;
25  import org.springframework.beans.factory.DisposableBean;
26  import org.springframework.beans.factory.FactoryBean;
27  import org.springframework.beans.factory.InitializingBean;
28  import org.springframework.osgi.context.BundleContextAware;
29  import org.springframework.osgi.service.exporter.OsgiServicePropertiesResolver;
30  import org.springframework.osgi.service.importer.OsgiServiceLifecycleListener;
31  import org.springframework.osgi.util.OsgiFilterUtils;
32  import org.springframework.osgi.util.internal.ClassUtils;
33  import org.springframework.util.Assert;
34  import org.springframework.util.ObjectUtils;
35  
36  /**
37   * Base class for importing OSGi services. Provides the common properties and
38   * contracts between importers.
39   * 
40   * @author Costin Leau
41   * @author Adrian Colyer
42   * @author Hal Hildebrand
43   */
44  public abstract class AbstractOsgiServiceImportFactoryBean implements FactoryBean, InitializingBean, DisposableBean,
45  		BundleContextAware, BeanClassLoaderAware, BeanNameAware {
46  
47  	private static final Log log = LogFactory.getLog(AbstractOsgiServiceImportFactoryBean.class);
48  
49  	/** context classloader */
50  	private ClassLoader classLoader;
51  
52  	private BundleContext bundleContext;
53  
54  	private ImportContextClassLoader contextClassLoader = ImportContextClassLoader.CLIENT;
55  
56  	// not required to be an interface, but usually should be...
57  	private Class[] interfaces;
58  
59  	// filter used to narrow service matches, may be null
60  	private String filter;
61  
62  	// Cumulated filter string between the specified classes/interfaces and the
63  	// given filter
64  	private Filter unifiedFilter;
65  
66  	// service lifecycle listener
67  	private OsgiServiceLifecycleListener[] listeners;
68  
69  	/** Service Bean property of the OSGi service * */
70  	private String serviceBeanName;
71  
72  	private Cardinality cardinality;
73  
74  	/** bean name */
75  	private String beanName = "";
76  
77  
78  	public void afterPropertiesSet() {
79  		Assert.notNull(this.bundleContext, "Required 'bundleContext' property was not set.");
80  		Assert.notNull(classLoader, "Required 'classLoader' property was not set.");
81  		Assert.notNull(interfaces, "Required 'interfaces' property was not set.");
82  		// validate specified classes
83  		Assert.isTrue(!ClassUtils.containsUnrelatedClasses(interfaces),
84  			"more then one concrete class specified; cannot create proxy.");
85  
86  		this.listeners = (listeners == null ? new OsgiServiceLifecycleListener[0] : listeners);
87  
88  		getUnifiedFilter(); // eager initialization of the cache to catch filter
89  		// errors
90  		Assert.notNull(interfaces, "Required serviceTypes property not specified.");
91  	}
92  
93  	/**
94  	 * Assembles the configuration properties into one unified OSGi filter. Note
95  	 * that this implementation creates the filter on the first call and caches
96  	 * it afterwards.
97  	 * 
98  	 * @return unified filter based on this factory bean configuration
99  	 */
100 	public Filter getUnifiedFilter() {
101 		if (unifiedFilter != null) {
102 			return unifiedFilter;
103 		}
104 
105 		String filterWithClasses = OsgiFilterUtils.unifyFilter(interfaces, filter);
106 
107 		boolean trace = log.isTraceEnabled();
108 		if (trace)
109 			log.trace("Unified classes=" + ObjectUtils.nullSafeToString(interfaces) + " and filter=[" + filter
110 					+ "]  in=[" + filterWithClasses + "]");
111 
112 		// add the serviceBeanName constraint
113 		String filterWithServiceBeanName = OsgiFilterUtils.unifyFilter(
114 			OsgiServicePropertiesResolver.BEAN_NAME_PROPERTY_KEY, new String[] { serviceBeanName }, filterWithClasses);
115 
116 		if (trace)
117 			log.trace("Unified serviceBeanName [" + ObjectUtils.nullSafeToString(serviceBeanName) + "] and filter=["
118 					+ filterWithClasses + "]  in=[" + filterWithServiceBeanName + "]");
119 
120 		// create (which implies validation) the actual filter
121 		unifiedFilter = OsgiFilterUtils.createFilter(filterWithServiceBeanName);
122 
123 		return unifiedFilter;
124 	}
125 
126 	/**
127 	 * Sets the classes that the imported service advertises.
128 	 * 
129 	 * @param interfaces array of advertised classes.
130 	 */
131 	public void setInterfaces(Class[] interfaces) {
132 		this.interfaces = interfaces;
133 	}
134 
135 	/**
136 	 * Sets the thread context class loader management strategy to use for
137 	 * services imported by this service. By default
138 	 * {@link ImportContextClassLoader#CLIENT} is used.
139 	 * 
140 	 * @param contextClassLoader import context class loader management strategy
141 	 * @see ImportContextClassLoader
142 	 */
143 	public void setContextClassLoader(ImportContextClassLoader contextClassLoader) {
144 		Assert.notNull(contextClassLoader);
145 		this.contextClassLoader = contextClassLoader;
146 	}
147 
148 	public void setBundleContext(BundleContext context) {
149 		this.bundleContext = context;
150 	}
151 
152 	/**
153 	 * Sets the OSGi service filter. The filter will be concatenated with the
154 	 * rest of the configuration properties specified (such as interfaces) so
155 	 * there is no need to include them in the filter.
156 	 * 
157 	 * @param filter OSGi filter describing the importing OSGi service
158 	 */
159 	public void setFilter(String filter) {
160 		this.filter = filter;
161 	}
162 
163 	/**
164 	 * Sets the lifecycle listeners interested in receiving events for this
165 	 * importer.
166 	 * 
167 	 * @param listeners importer listeners
168 	 */
169 	public void setListeners(OsgiServiceLifecycleListener[] listeners) {
170 		this.listeners = listeners;
171 	}
172 
173 	/**
174 	 * Sets the OSGi service bean name. This setting should be normally used
175 	 * when the imported service has been exported by Spring DM exporter. You
176 	 * may specify additional filtering criteria if needed (using the filter
177 	 * property) but this is not required.
178 	 * 
179 	 * @param serviceBeanName importer service bean name
180 	 */
181 	public void setServiceBeanName(String serviceBeanName) {
182 		this.serviceBeanName = serviceBeanName;
183 	}
184 
185 	/**
186 	 * {@inheritDoc}
187 	 * 
188 	 * This method is called automatically by the container.
189 	 */
190 	public void setBeanClassLoader(ClassLoader classLoader) {
191 		this.classLoader = classLoader;
192 	}
193 
194 	/**
195 	 * Returns the class loader used by this FactoryBean.
196 	 * 
197 	 * @return factory bean class loader
198 	 */
199 	public ClassLoader getBeanClassLoader() {
200 		return classLoader;
201 	}
202 
203 	/**
204 	 * Returns the bundleContext used by this FactoryBean.
205 	 * 
206 	 * @return factory bean class loader
207 	 */
208 	public BundleContext getBundleContext() {
209 		return bundleContext;
210 	}
211 
212 	/**
213 	 * Returns the interfaces used for discovering the imported service(s).
214 	 * 
215 	 * @return interfaces advertised by services in the OSGi space
216 	 */
217 	public Class[] getInterfaces() {
218 		return interfaces;
219 	}
220 
221 	/**
222 	 * Returns the filter describing the imported service(s).
223 	 * 
224 	 * @return filter describing the imported service(s)
225 	 */
226 	public String getFilter() {
227 		return filter;
228 	}
229 
230 	/**
231 	 * Returns the listeners interested in receiving events for this importer.
232 	 * 
233 	 * @return lifecycle listeners used by this importer
234 	 */
235 	public OsgiServiceLifecycleListener[] getListeners() {
236 		return listeners;
237 	}
238 
239 	/**
240 	 * Returns the context class loader management strategy.
241 	 * 
242 	 * @return the context class loader management strategy
243 	 */
244 	public ImportContextClassLoader getContextClassLoader() {
245 		return contextClassLoader;
246 	}
247 
248 	/**
249 	 * Returns the cardinality used by this importer.
250 	 * 
251 	 * @return importer cardinality
252 	 */
253 	public Cardinality getCardinality() {
254 		return cardinality;
255 	}
256 
257 	/**
258 	 * Sets the importer cardinality (0..1, 1..1, 0..N, or 1..N). Default is
259 	 * 1..X.
260 	 * 
261 	 * @param cardinality importer cardinality.
262 	 */
263 	public void setCardinality(Cardinality cardinality) {
264 		Assert.notNull(cardinality);
265 		this.cardinality = cardinality;
266 	}
267 
268 	/**
269 	 * Returns the bean name associated with the instance of this class (when
270 	 * running inside the Spring container).
271 	 * 
272 	 * @return component bean name
273 	 */
274 	public String getBeanName() {
275 		return beanName;
276 	}
277 
278 	public void setBeanName(String name) {
279 		beanName = name;
280 	}
281 }