/******************************************************************************
* 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.cm;
import java.io.IOException;
import java.util.Dictionary;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.gemini.blueprint.compendium.internal.cm.CMUtils;
import org.eclipse.gemini.blueprint.compendium.internal.cm.util.ChangeableProperties;
import org.eclipse.gemini.blueprint.compendium.internal.cm.util.PropertiesUtil;
import org.eclipse.gemini.blueprint.context.BundleContextAware;
import org.eclipse.gemini.blueprint.service.exporter.support.ServicePropertiesChangeListener;
import org.eclipse.gemini.blueprint.util.OsgiServiceUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* FactoryBean returning the properties stored under a given persistent id in the ConfigurationAdmin service. Once
* retrieved, the properties will remain the same, even when the configuration object that it maps, changes.
*
* <b>Note:</b> This implementation performs a lazy initialization of the properties to receive the most up to date
* configuration.
*
* @author Costin Leau
* @see Configuration
* @see ConfigurationAdmin
* @see org.springframework.core.io.support.PropertiesFactoryBean
*/
public class ConfigAdminPropertiesFactoryBean implements BundleContextAware, InitializingBean, DisposableBean,
FactoryBean<Properties> {
@SuppressWarnings("unchecked")
private class ConfigurationWatcher implements ManagedService {
public void updated(Dictionary props) throws ConfigurationException {
if (log.isTraceEnabled())
log.trace("Configuration [" + persistentId + "] has been updated with properties " + props);
// update properties
PropertiesUtil.initProperties(localProperties, localOverride, props, properties);
// inform listeners (the dynamic check is redundant but nevertheless safe)
if (dynamic) {
((ChangeableProperties) properties).notifyListeners();
}
}
}
/** logger */
private static final Log log = LogFactory.getLog(ConfigAdminPropertiesFactoryBean.class);
private volatile String persistentId;
private volatile Properties properties;
private BundleContext bundleContext;
private boolean localOverride = false;
private Properties localProperties;
private volatile boolean dynamic = false;
private volatile ServiceRegistration registration;
private boolean initLazy = true;
private long initTimeout = 0;
private final Object monitor = new Object();
public void afterPropertiesSet() throws Exception {
Assert.hasText(persistentId, "persistentId property is required");
Assert.notNull(bundleContext, "bundleContext property is required");
Assert.isTrue(initTimeout >= 0, "a positive initTimeout is required");
if (!initLazy) {
createProperties();
}
}
public void destroy() throws Exception {
OsgiServiceUtils.unregisterService(registration);
registration = null;
}
private void createProperties() {
if (properties == null) {
properties = (dynamic ? new ChangeableProperties() : new Properties());
// init properties by copying config admin properties
try {
PropertiesUtil.initProperties(localProperties, localOverride, CMUtils.getConfiguration(bundleContext,
persistentId, initTimeout), properties);
} catch (IOException ioe) {
throw new BeanInitializationException("Cannot retrieve configuration for pid=" + persistentId, ioe);
}
if (dynamic) {
// perform eager registration
registration = CMUtils.registerManagedService(bundleContext, new ConfigurationWatcher(), persistentId);
}
}
}
public Properties getObject() throws Exception {
// perform lazy initialization (if needed)
if (properties == null) {
synchronized (monitor) {
if (properties == null) {
createProperties();
}
}
}
return properties;
}
public Class<? extends Properties> getObjectType() {
return (dynamic ? ChangeableProperties.class : Properties.class);
}
public boolean isSingleton() {
return true;
}
/**
* Returns the persistentId.
*
* @return Returns the persistentId
*/
public String getPersistentId() {
return persistentId;
}
/**
* Sets the ConfigurationAdmin persistent Id that the bean should read.
*
* @param persistentId The persistentId to set.
*/
public void setPersistentId(String persistentId) {
this.persistentId = persistentId;
}
/**
* Sets the local properties, e.g. via the nested tag in XML bean definitions. These can be considered defaults, to
* be overridden by properties loaded from the Configuration Admin.
*/
public void setProperties(Properties properties) {
this.localProperties = properties;
}
/**
* Sets whether local properties override properties from files. <p> Default is "false": Properties from the
* Configuration Admin override local defaults. Can be switched to "true" to let local properties override the
* Configuration Admin properties.
*/
public void setLocalOverride(boolean localOverride) {
this.localOverride = localOverride;
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
/**
* Indicates whether the returned properties object is dynamic or not.
*
* @return boolean indicating if the configuration object is dynamic
*/
public boolean isDynamic() {
return dynamic;
}
/**
* Indicates if the returned configuration is dynamic or static. A static configuration (default) ignores any
* updates made to the configuration admin entry that it maps. A dynamic configuration on the other hand will
* reflect the changes in its content. Third parties can be notified through the
* {@link ServicePropertiesChangeListener} contract.
*
* @param dynamic whether the returned object reflects the changes in the configuration admin or not.
*/
public void setDynamic(boolean dynamic) {
this.dynamic = dynamic;
}
/**
* Specifies whether the properties reflecting the Configuration Admin service entry will be initialized lazy or
* not. Default is "true": meaning the properties will be initialized just before being requested (from the factory)
* for the first time. This is the common case as it allows the most recent entry to be used. If set to "false", the
* properties object will be initialized at startup, along with the bean factory.
*
* @param initLazy whether or not the bean is lazily initialized
*/
public void setInitLazy(boolean initLazy) {
this.initLazy = initLazy;
}
/**
* Specifies the amount of time (in milliseconds) the bean factory will wait for the Configuration Admin entry to be
* initialized (return a non-null value). If the entry is not null at startup, no waiting will be performed. Similar
* to the other timeout options, a value of '0' means no waiting. By default, no waiting (0) is performed.
*
* @param initTimeout the amount of time to wait for the entry to be initialized.
*/
public void setInitTimeout(long initTimeout) {
this.initTimeout = initTimeout;
}
}