/******************************************************************************
* 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.compendium.internal.cm;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.eclipse.gemini.blueprint.util.OsgiBundleUtils;
import org.eclipse.gemini.blueprint.util.OsgiServiceUtils;
import org.eclipse.gemini.blueprint.util.OsgiStringUtils;
import org.eclipse.gemini.blueprint.util.internal.MapBasedDictionary;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ConfigurationEvent;
import org.osgi.service.cm.ConfigurationListener;
import org.osgi.service.cm.ManagedService;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.util.StringUtils;
/**
* Utility class for the Configuration Admin package.
*
* @author Costin Leau
*/
public abstract class CMUtils {
/**
* Injects the properties from the given Map to the given object. Additionally, a bean factory can be passed in for
* copying property editors inside the injector.
*
* @param instance bean instance to configure
* @param properties
* @param beanFactory
*/
public static void applyMapOntoInstance(Object instance, Map<String, ?> properties, AbstractBeanFactory beanFactory) {
if (properties != null && !properties.isEmpty()) {
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(instance);
// configure bean wrapper (using method from Spring 2.5.6)
if (beanFactory != null) {
beanFactory.copyRegisteredEditorsTo(beanWrapper);
}
for (Iterator<?> iterator = properties.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<String, ?> entry = (Map.Entry<String, ?>) iterator.next();
String propertyName = entry.getKey();
if (beanWrapper.isWritableProperty(propertyName)) {
beanWrapper.setPropertyValue(propertyName, entry.getValue());
}
}
}
}
public static void bulkUpdate(UpdateCallback callback, Collection<?> instances, Map<?, ?> properties) {
for (Iterator<?> iterator = instances.iterator(); iterator.hasNext();) {
Object instance = iterator.next();
callback.update(instance, properties);
}
}
public static UpdateCallback createCallback(boolean autowireOnUpdate, String methodName, BeanFactory beanFactory) {
UpdateCallback beanManaged = null, containerManaged = null;
if (autowireOnUpdate) {
containerManaged = new ContainerManagedUpdate(beanFactory);
}
if (StringUtils.hasText(methodName)) {
beanManaged = new BeanManagedUpdate(methodName);
}
// if both strategies are present, return a chain
if (containerManaged != null && beanManaged != null)
return new ChainedManagedUpdate(new UpdateCallback[] { containerManaged, beanManaged });
// otherwise return the non-null one
return (containerManaged != null ? containerManaged : beanManaged);
}
/**
* Returns a map containing the Configuration Admin entry with given pid. Waits until a non-null (initialized)
* object is returned if initTimeout is bigger then 0.
*
* @param bundleContext
* @param pid
* @param initTimeout
* @return
* @throws IOException
*/
public static Map getConfiguration(BundleContext bundleContext, final String pid, long initTimeout)
throws IOException {
ServiceReference ref = bundleContext.getServiceReference(ConfigurationAdmin.class.getName());
if (ref != null) {
ConfigurationAdmin cm = (ConfigurationAdmin) bundleContext.getService(ref);
if (cm != null) {
Dictionary dict = cm.getConfiguration(pid).getProperties();
// if there are properties or no timeout, return as is
if (dict != null || initTimeout == 0) {
return new MapBasedDictionary(dict);
}
// no valid props, register a listener and start waiting
final Object monitor = new Object();
Properties props = new Properties();
props.put(Constants.SERVICE_PID, pid);
ServiceRegistration reg =
bundleContext.registerService(ConfigurationListener.class.getName(),
new ConfigurationListener() {
public void configurationEvent(ConfigurationEvent event) {
if (ConfigurationEvent.CM_UPDATED == event.getType()
&& pid.equals(event.getPid())) {
synchronized (monitor) {
monitor.notify();
}
}
}
}, props);
try {
// try to get the configuration one more time (in case the update was fired before the service was
// registered)
dict = cm.getConfiguration(pid).getProperties();
if (dict != null) {
return new MapBasedDictionary(dict);
}
// start waiting
synchronized (monitor) {
try {
monitor.wait(initTimeout);
} catch (InterruptedException ie) {
// consider the timeout has passed
}
}
// return whatever is available (either we timed out or an update occured)
return new MapBasedDictionary(cm.getConfiguration(pid).getProperties());
} finally {
OsgiServiceUtils.unregisterService(reg);
}
}
}
return Collections.EMPTY_MAP;
}
public static ServiceRegistration registerManagedService(BundleContext bundleContext, ManagedService listener,
String pid) {
Properties props = new Properties();
props.put(Constants.SERVICE_PID, pid);
Bundle bundle = bundleContext.getBundle();
props.put(Constants.BUNDLE_SYMBOLICNAME, OsgiStringUtils.nullSafeSymbolicName(bundle));
props.put(Constants.BUNDLE_VERSION, OsgiBundleUtils.getBundleVersion(bundle));
return bundleContext.registerService(ManagedService.class.getName(), listener, props);
}
}