blob: a9adf7eb7d28951b4a63921bb6cd796698095363 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc., Oracle 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.
* Oracle Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.service.importer.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.gemini.blueprint.context.BundleContextAware;
import org.eclipse.gemini.blueprint.service.exporter.OsgiServicePropertiesResolver;
import org.eclipse.gemini.blueprint.service.importer.OsgiServiceLifecycleListener;
import org.eclipse.gemini.blueprint.util.OsgiFilterUtils;
import org.eclipse.gemini.blueprint.util.internal.ClassUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.springframework.beans.factory.*;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Base class for importing OSGi services. Provides the common properties and contracts between importers.
*
* @author Costin Leau
* @author Adrian Colyer
* @author Hal Hildebrand
*/
public abstract class AbstractOsgiServiceImportFactoryBean implements FactoryBean<Object>, InitializingBean,
DisposableBean, BundleContextAware, BeanClassLoaderAware, BeanNameAware {
private static final Log log = LogFactory.getLog(AbstractOsgiServiceImportFactoryBean.class);
/** context classloader */
private ClassLoader classLoader;
private BundleContext bundleContext;
private ImportContextClassLoaderEnum contextClassLoader = ImportContextClassLoaderEnum.CLIENT;
// not required to be an interface, but usually should be...
private Class<?>[] interfaces;
// filter used to narrow service matches, may be null
private String filter;
// Cumulated filter string between the specified classes/interfaces and the
// given filter
private Filter unifiedFilter;
// service lifecycle listener
private OsgiServiceLifecycleListener[] listeners;
/** Service Bean property of the OSGi service * */
private String serviceBeanName;
private Availability availability = Availability.MANDATORY;
/** bean name */
private String beanName = "";
public void afterPropertiesSet() {
Assert.notNull(this.bundleContext, "Required 'bundleContext' property was not set.");
Assert.notNull(classLoader, "Required 'classLoader' property was not set.");
Assert.isTrue(!ObjectUtils.isEmpty(interfaces) || StringUtils.hasText(filter)
|| StringUtils.hasText(serviceBeanName),
"At least the interface or filter or service name needs to be defined to import an OSGi service");
if (ObjectUtils.isEmpty(interfaces)) {
log.warn("OSGi importer [" + beanName + "] definition contains no interfaces: "
+ "all invocations will be executed on the proxy and not on the backing service");
}
// validate specified classes
Assert.isTrue(!ClassUtils.containsUnrelatedClasses(interfaces),
"More then one concrete class specified; cannot create proxy.");
this.listeners = (listeners == null ? new OsgiServiceLifecycleListener[0] : listeners);
this.interfaces = (interfaces == null ? new Class<?>[0] : interfaces);
this.filter = (StringUtils.hasText(filter) ? filter : "");
getUnifiedFilter(); // eager initialization of the cache to catch filter errors
}
/**
* Assembles the configuration properties into one unified OSGi filter. Note that this implementation creates the
* filter on the first call and caches it afterwards.
*
* @return unified filter based on this factory bean configuration
*/
public Filter getUnifiedFilter() {
if (unifiedFilter != null) {
return unifiedFilter;
}
String filterWithClasses =
(!ObjectUtils.isEmpty(interfaces) ? OsgiFilterUtils.unifyFilter(interfaces, filter) : filter);
boolean trace = log.isTraceEnabled();
if (trace)
log.trace("Unified classes=" + ObjectUtils.nullSafeToString(interfaces) + " and filter=[" + filter
+ "] in=[" + filterWithClasses + "]");
// add the serviceBeanName/Blueprint component name constraint
String nameFilter;
if (StringUtils.hasText(serviceBeanName)) {
StringBuilder nsFilter = new StringBuilder("(|(");
nsFilter.append(OsgiServicePropertiesResolver.BEAN_NAME_PROPERTY_KEY);
nsFilter.append("=");
nsFilter.append(serviceBeanName);
nsFilter.append(")(");
nsFilter.append(OsgiServicePropertiesResolver.SPRING_DM_BEAN_NAME_PROPERTY_KEY);
nsFilter.append("=");
nsFilter.append(serviceBeanName);
nsFilter.append(")(");
nsFilter.append(OsgiServicePropertiesResolver.BLUEPRINT_COMP_NAME);
nsFilter.append("=");
nsFilter.append(serviceBeanName);
nsFilter.append("))");
nameFilter = nsFilter.toString();
} else {
nameFilter = null;
}
String filterWithServiceBeanName = filterWithClasses;
if (nameFilter != null) {
StringBuilder finalFilter = new StringBuilder();
finalFilter.append("(&");
finalFilter.append(filterWithClasses);
finalFilter.append(nameFilter);
finalFilter.append(")");
filterWithServiceBeanName = finalFilter.toString();
}
if (trace)
log.trace("Unified serviceBeanName [" + ObjectUtils.nullSafeToString(serviceBeanName) + "] and filter=["
+ filterWithClasses + "] in=[" + filterWithServiceBeanName + "]");
// create (which implies validation) the actual filter
unifiedFilter = OsgiFilterUtils.createFilter(filterWithServiceBeanName);
return unifiedFilter;
}
/**
* Sets the classes that the imported service advertises.
*
* @param interfaces array of advertised classes.
*/
public void setInterfaces(Class<?>[] interfaces) {
this.interfaces = interfaces;
}
/**
* Sets the thread context class loader management strategy to use for services imported by this service. By default
* {@link ImportContextClassLoaderEnum#CLIENT} is used.
*
* @param contextClassLoader import context class loader management strategy
* @see ImportContextClassLoaderEnum
*/
public void setImportContextClassLoader(ImportContextClassLoaderEnum contextClassLoader) {
Assert.notNull(contextClassLoader);
this.contextClassLoader = contextClassLoader;
}
public void setBundleContext(BundleContext context) {
this.bundleContext = context;
}
/**
* Sets the OSGi service filter. The filter will be concatenated with the rest of the configuration properties
* specified (such as interfaces) so there is no need to include them in the filter.
*
* @param filter OSGi filter describing the importing OSGi service
*/
public void setFilter(String filter) {
this.filter = filter;
}
/**
* Sets the lifecycle listeners interested in receiving events for this importer.
*
* @param listeners importer listeners
*/
public void setListeners(OsgiServiceLifecycleListener[] listeners) {
this.listeners = listeners;
}
/**
* Sets the OSGi service bean name. This setting should be normally used when the imported service has been exported
* by Spring DM exporter. You may specify additional filtering criteria if needed (using the filter property) but
* this is not required.
*
* @param serviceBeanName importer service bean name
*/
public void setServiceBeanName(String serviceBeanName) {
this.serviceBeanName = serviceBeanName;
}
/**
* {@inheritDoc}
*
* This method is called automatically by the container.
*/
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* Returns the class loader used by this FactoryBean.
*
* @return factory bean class loader
*/
public ClassLoader getBeanClassLoader() {
return classLoader;
}
/**
* Returns the bundleContext used by this FactoryBean.
*
* @return factory bean class loader
*/
public BundleContext getBundleContext() {
return bundleContext;
}
/**
* Returns the interfaces used for discovering the imported service(s).
*
* @return interfaces advertised by services in the OSGi space
*/
public Class<?>[] getInterfaces() {
return interfaces;
}
/**
* Returns the filter describing the imported service(s).
*
* @return filter describing the imported service(s)
*/
public String getFilter() {
return filter;
}
/**
* Returns the listeners interested in receiving events for this importer.
*
* @return lifecycle listeners used by this importer
*/
public OsgiServiceLifecycleListener[] getListeners() {
return listeners;
}
/**
* Returns the context class loader management strategy.
*
* @return the context class loader management strategy
*/
public ImportContextClassLoaderEnum getImportContextClassLoader() {
return contextClassLoader;
}
public Availability getAvailability() {
return availability;
}
/**
* Sets the importer availability. Default is mandatory ({@link Availability#MANDATORY}
* @param availability
*/
public void setAvailability(Availability availability) {
Assert.notNull(availability);
this.availability = availability;
}
/**
* Returns the bean name associated with the instance of this class (when running inside the Spring container).
*
* @return component bean name
*/
public String getBeanName() {
return beanName;
}
public void setBeanName(String name) {
beanName = name;
}
}