blob: f1326828e793e1bd9c726fb64ea290f315705350 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.aries.blueprint.compendium.cm;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.aries.blueprint.BeanProcessor;
import org.apache.aries.blueprint.ExtendedBlueprintContainer;
import org.apache.aries.blueprint.utils.ReflectionUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.service.blueprint.container.ReifiedType;
import org.osgi.service.blueprint.reflect.BeanMetadata;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* TODO
*
* @version $Rev: 896324 $, $Date: 2010-01-06 06:05:04 +0000 (Wed, 06 Jan 2010) $
*/
public class CmManagedProperties implements ManagedObject, BeanProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(CmManagedProperties.class);
private ExtendedBlueprintContainer blueprintContainer;
private ConfigurationAdmin configAdmin;
private ManagedObjectManager managedObjectManager;
private String persistentId;
private String updateStrategy;
private String updateMethod;
private String beanName;
private final Object lock = new Object();
private final Set<Object> beans = new HashSet<Object>();
private Dictionary<String,Object> properties;
public ExtendedBlueprintContainer getBlueprintContainer() {
return blueprintContainer;
}
public void setBlueprintContainer(ExtendedBlueprintContainer blueprintContainer) {
this.blueprintContainer = blueprintContainer;
}
public ConfigurationAdmin getConfigAdmin() {
return configAdmin;
}
public void setConfigAdmin(ConfigurationAdmin configAdmin) {
this.configAdmin = configAdmin;
}
public void setManagedObjectManager(ManagedObjectManager managedObjectManager) {
this.managedObjectManager = managedObjectManager;
}
public ManagedObjectManager getManagedObjectManager() {
return managedObjectManager;
}
public Bundle getBundle() {
return blueprintContainer.getBundleContext().getBundle();
}
public String getPersistentId() {
return persistentId;
}
public void setPersistentId(String persistentId) {
this.persistentId = persistentId;
}
public String getUpdateStrategy() {
return updateStrategy;
}
public void setUpdateStrategy(String updateStrategy) {
this.updateStrategy = updateStrategy;
}
public String getUpdateMethod() {
return updateMethod;
}
public void setUpdateMethod(String updateMethod) {
this.updateMethod = updateMethod;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public void init() throws Exception {
LOGGER.debug("Initializing CmManagedProperties for bean={} / pid={}", beanName, persistentId);
Properties props = new Properties();
props.put(Constants.SERVICE_PID, persistentId);
Bundle bundle = blueprintContainer.getBundleContext().getBundle();
props.put(Constants.BUNDLE_SYMBOLICNAME, bundle.getSymbolicName());
props.put(Constants.BUNDLE_VERSION, bundle.getHeaders().get(Constants.BUNDLE_VERSION));
synchronized (lock) {
managedObjectManager.register(this, props);
Configuration config = CmUtils.getConfiguration(configAdmin, persistentId);
if (config != null) {
properties = config.getProperties();
}
}
}
public void destroy() {
managedObjectManager.unregister(this);
}
public void updated(final Dictionary props) {
LOGGER.debug("Configuration updated for bean={} / pid={}", beanName, persistentId);
// Run in a separate thread to avoid re-entrance
new Thread() {
public void run() {
synchronized (lock) {
properties = props;
for (Object bean : beans) {
inject(bean, false);
}
}
}
}.start();
}
public Object beforeInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanData) {
if (beanName != null && beanName.equals(this.beanName)) {
LOGGER.debug("Adding bean for bean={} / pid={}", beanName, persistentId);
synchronized (lock) {
beans.add(bean);
inject(bean, true);
}
}
return bean;
}
public Object afterInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanData) {
return bean;
}
public void beforeDestroy(Object bean, String beanName) {
if (beanName.equals(this.beanName)) {
LOGGER.debug("Removing bean for bean={} / pid={}", beanName, persistentId);
synchronized (lock) {
beans.remove(bean);
}
}
}
public void afterDestroy(Object bean, String beanName) {
}
private void inject(Object bean, boolean initial) {
LOGGER.debug("Injecting bean for bean={} / pid={}", beanName, persistentId);
LOGGER.debug("Configuration: {}", properties);
if (initial || "container-managed".equals(updateStrategy)) {
if (properties != null) {
for (Enumeration<String> e = properties.keys(); e.hasMoreElements();) {
String key = e.nextElement();
Object val = properties.get(key);
String setterName = "set" + Character.toUpperCase(key.charAt(0));
if (key.length() > 0) {
setterName += key.substring(1);
}
Set<Method> validSetters = new LinkedHashSet<Method>();
List<Method> methods = new ArrayList<Method>(Arrays.asList(bean.getClass().getMethods()));
methods.addAll(Arrays.asList(bean.getClass().getDeclaredMethods()));
for (Method method : methods) {
if (method.getName().equals(setterName)) {
if (method.getParameterTypes().length == 0) {
LOGGER.debug("Setter takes no parameters: {}", method);
continue;
}
if (method.getParameterTypes().length > 1) {
LOGGER.debug("Setter takes more than one parameter: {}", method);
continue;
}
if (method.getReturnType() != Void.TYPE) {
LOGGER.debug("Setter returns a value: {}", method);
continue;
}
if (Modifier.isAbstract(method.getModifiers())) {
LOGGER.debug("Setter is abstract: {}", method);
continue;
}
if (!Modifier.isPublic(method.getModifiers())) {
LOGGER.debug("Setter is not public: {}", method);
continue;
}
if (Modifier.isStatic(method.getModifiers())) {
LOGGER.debug("Setter is static: {}", method);
continue;
}
Class methodParameterType = method.getParameterTypes()[0];
Object propertyValue;
try {
propertyValue = blueprintContainer.getConverter().convert(val, new ReifiedType(methodParameterType));
} catch (Throwable t) {
LOGGER.debug("Unable to convert value for setter: " + method, t);
continue;
}
if (methodParameterType.isPrimitive() && propertyValue == null) {
LOGGER.debug("Null can not be assigned to {}: {}", methodParameterType.getName(), method);
continue;
}
if (validSetters.add(method)) {
try {
method.invoke(bean, propertyValue);
} catch (Exception t) {
LOGGER.debug("Setter can not be invoked: " + method, getRealCause(t));
}
}
}
}
if (validSetters.isEmpty()) {
LOGGER.debug("Unable to find a valid setter method for property {} and value {}", key, val);
}
}
}
} else if ("component-managed".equals(updateStrategy) && updateMethod != null) {
List<Method> methods = ReflectionUtils.findCompatibleMethods(bean.getClass(), updateMethod, new Class[] { Map.class });
Map map = null;
if (properties != null) {
map = new HashMap();
for (Enumeration<String> e = properties.keys(); e.hasMoreElements();) {
String key = e.nextElement();
Object val = properties.get(key);
map.put(key, val);
}
}
for (Method method : methods) {
try {
method.invoke(bean, map);
} catch (Throwable t) {
LOGGER.warn("Unable to call method " + method + " on bean " + beanName, getRealCause(t));
}
}
}
}
private static Throwable getRealCause(Throwable t) {
if (t instanceof InvocationTargetException && t.getCause() != null) {
return t.getCause();
}
return t;
}
}