blob: 34d01a939c689d8703a800ae3455f50f0479079f [file] [log] [blame]
/**
*
* Copyright 2005 the original author or authors.
*
* Licensed 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.gbean.geronimo;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.ObjectName;
import org.apache.geronimo.gbean.GBeanLifecycleController;
import org.apache.geronimo.kernel.Kernel;
import org.gbean.metadata.ClassMetadata;
import org.gbean.metadata.ConstructorMetadata;
import org.gbean.metadata.MetadataManager;
import org.gbean.metadata.ParameterMetadata;
import org.gbean.proxy.ProxyManager;
import org.gbean.service.ConfigurableServiceFactory;
import org.gbean.service.ServiceContext;
import org.gbean.spring.LifecycleDetector;
import org.gbean.spring.NamedConstructorArgs;
import org.gbean.spring.ServiceContextThreadLocal;
import org.gbean.spring.SpringUtil;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
/**
* @version $Revision$ $Date$
*/
public class GeronimoServiceFactory implements ConfigurableServiceFactory {
private final MetadataManager metadataManager;
private final Set persistentProperties;
private final ConstructorMetadata constructor;
private final Map dependencies;
private final Map dynamicProperties;
private final ProxyManager proxyManager;
private RootBeanDefinition beanDefinition;
private GenericApplicationContext applicationContext;
private Object service;
private boolean enabled;
private final List constructorArgNames;
public GeronimoServiceFactory(RootBeanDefinition beanDefinition, Map dynamicProperties, MetadataManager metadataManager, ProxyManager proxyManager) throws Exception {
this.metadataManager = metadataManager;
this.proxyManager = proxyManager;
this.beanDefinition = beanDefinition;
this.dynamicProperties = dynamicProperties;
dependencies = SpringUtil.extractDependencies(beanDefinition, Collections.EMPTY_MAP);
// find the constructor... geronimo always uses the same constructor
ClassMetadata classMetadata = metadataManager.getClassMetadata(beanDefinition.getBeanClass());
ConstructorMetadata constructorMetadata = null;
for (Iterator iterator = classMetadata.getConstructors().iterator(); iterator.hasNext();) {
ConstructorMetadata c = (ConstructorMetadata) iterator.next();
if (c.getProperties().containsKey("always-use")) {
constructorMetadata = c;
break;
}
}
constructor = constructorMetadata;
// determine the constructor arg names... these are alwasys defined in the gbean info
List constructorArgNames = new LinkedList();
for (Iterator iterator = constructor.getParameters().iterator(); iterator.hasNext();) {
ParameterMetadata parameter = (ParameterMetadata) iterator.next();
Object parameterName = parameter.get("name");
if (parameterName == null) {
throw new IllegalArgumentException("Parameter name is not defined");
}
constructorArgNames.add(parameterName);
}
this.constructorArgNames = Collections.unmodifiableList(constructorArgNames);
// determine the persistent properties
Set persistentProperties = (Set) classMetadata.get("persistentProperties");
if (persistentProperties == null) {
persistentProperties = Collections.EMPTY_SET;
}
this.persistentProperties = Collections.unmodifiableSet(persistentProperties);
}
public Set getPersistentProperties() {
return persistentProperties;
}
public List getConstructorArgNames() {
return constructorArgNames;
}
public Map getDynamicProperties() {
return dynamicProperties;
}
public RootBeanDefinition getBeanDefinition() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(this.beanDefinition);
if (service != null) {
updatePersistentValues(service, beanDefinition);
}
return this.beanDefinition;
}
public void setBeanDefinition(RootBeanDefinition beanDefinition) {
this.beanDefinition = beanDefinition;
}
public Map getDependencies() {
return dependencies;
}
public void addDependency(String name, Set patterns) {
dependencies.put(name, patterns);
}
public Object createService(ServiceContext serviceContext) throws Exception {
try {
Object service = null;
ServiceContext oldServiceContext = ServiceContextThreadLocal.get();
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
try {
ServiceContextThreadLocal.set(serviceContext);
Thread.currentThread().setContextClassLoader(serviceContext.getClassLoader());
// dereference all factories
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
PropertyValue[] values = propertyValues.getPropertyValues();
for (int i = 0; i < values.length; i++) {
PropertyValue propertyValue = values[i];
if (propertyValue.getValue() instanceof FactoryBean) {
FactoryBean factoryBean = (FactoryBean) propertyValue.getValue();
Object object = factoryBean.getObject();
propertyValues.removePropertyValue(propertyValue.getName());
propertyValues.addPropertyValue(propertyValue.getName(), object);
}
}
applicationContext = new GenericApplicationContext();
// register the post processors
NamedConstructorArgs namedConstructorArgs = new NamedConstructorArgs(metadataManager);
namedConstructorArgs.addDefaultValue("objectName", String.class, serviceContext.getObjectName());
namedConstructorArgs.addDefaultValue("objectName", ObjectName.class, new ObjectName(serviceContext.getObjectName()));
namedConstructorArgs.addDefaultValue("classLoader", ClassLoader.class, serviceContext.getClassLoader());
namedConstructorArgs.addDefaultValue("gbeanLifecycleController", GBeanLifecycleController.class, new GeronimoLifecycleControllerReference().getObject());
namedConstructorArgs.addDefaultValue("kernel", Kernel.class, serviceContext.getKernel().getService(Kernel.KERNEL));
applicationContext.addBeanFactoryPostProcessor(namedConstructorArgs);
LifecycleDetector lifecycleDetector = new LifecycleDetector();
lifecycleDetector.addLifecycleInterface(org.apache.geronimo.gbean.GBeanLifecycle.class, "doStart", "doStop");
applicationContext.addBeanFactoryPostProcessor(lifecycleDetector);
applicationContext.getBeanFactory().addBeanPostProcessor(new DynamicGBeanProcessor(serviceContext.getObjectName(), dynamicProperties));
// copy the bean definition, so we don't modify the original value
RootBeanDefinition beanDefinition = new RootBeanDefinition(this.beanDefinition);
// build the bean
applicationContext.registerBeanDefinition(serviceContext.getObjectName(), beanDefinition);
applicationContext.refresh();
service = applicationContext.getBean(serviceContext.getObjectName());
} finally {
ServiceContextThreadLocal.set(oldServiceContext);
Thread.currentThread().setContextClassLoader(oldClassLoader);
}
this.service = service;
return service;
} catch (Throwable t) {
applicationContext = null;
if (t instanceof Exception) {
throw (Exception) t;
} else if (t instanceof Error) {
throw (Error) t;
} else {
throw new Error(t);
}
}
}
public void destroyService(ServiceContext serviceContext, Object service) {
// update the persistent attributes
try {
if (service != null) {
// update the persistent values
RootBeanDefinition beanDefinition = new RootBeanDefinition(this.beanDefinition);
updatePersistentValues(service, beanDefinition);
this.beanDefinition = beanDefinition;
}
} finally {
this.service = null;
if (applicationContext != null) {
applicationContext.close();
applicationContext = null;
}
}
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Set getPropertyNames() {
if (persistentProperties != null) {
return persistentProperties;
} else {
return Collections.EMPTY_SET;
}
}
public Object getProperty(String propertyName) {
if (persistentProperties == null || !persistentProperties.contains(propertyName)) {
throw new IllegalArgumentException("Property is not persistent:" +
" propertyName=" + propertyName +
", serviceType: " + beanDefinition.getBeanClassName());
}
PropertyValue propertyValue = beanDefinition.getPropertyValues().getPropertyValue(propertyName);
if (propertyValue != null) {
return propertyValue.getValue();
}
if (dynamicProperties.containsKey(propertyName)) {
return dynamicProperties.get(propertyValue);
}
return null;
}
public void setProperty(String propertyName, Object persistentValue) {
if (persistentProperties == null || !persistentProperties.contains(propertyName)) {
throw new IllegalArgumentException("Property is not persistent:" +
" propertyName=" + propertyName +
", serviceType: " + beanDefinition.getBeanClassName());
}
if (dynamicProperties.containsKey(propertyName)) {
dynamicProperties.put(propertyName, persistentValue);
return;
}
beanDefinition.getPropertyValues().removePropertyValue(propertyName);
beanDefinition.getPropertyValues().addPropertyValue(propertyName, persistentValue);
}
private void updatePersistentValues(Object service, RootBeanDefinition beanDefinition) {
Map getters = new HashMap();
Method[] methods = service.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
String methodName = method.getName();
if (Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) {
if (method.getParameterTypes().length == 0 && method.getReturnType() != Void.TYPE) {
if (methodName.length() > 3 && methodName.startsWith("get") && !methodName.equals("getClass")) {
String propertyName = fixPropertyName(methodName.substring(3));
getters.put(propertyName, method);
} else if (methodName.length() > 2 && methodName.startsWith("is")) {
String propertyName = fixPropertyName(methodName.substring(2));
getters.put(propertyName, method);
}
}
}
}
for (Iterator iterator = persistentProperties.iterator(); iterator.hasNext();) {
String propertyName = (String) iterator.next();
if (dynamicProperties.containsKey(propertyName)) {
Object value = getCurrentValue(getters, service, propertyName, dynamicProperties.get(propertyName));
dynamicProperties.put(propertyName, value);
} else {
// get the new property value
Object value = null;
PropertyValue propertyValue = beanDefinition.getPropertyValues().getPropertyValue(propertyName);
if (propertyValue != null) {
value = propertyValue.getValue();
}
value = getCurrentValue(getters, service, propertyName, value);
// update the property value
beanDefinition.getPropertyValues().removePropertyValue(propertyName);
if (value != null) {
beanDefinition.getPropertyValues().addPropertyValue(propertyName, value);
}
}
}
}
private Object getCurrentValue(Map getters, Object service, String propertyName, Object defaultValue) {
Object value = defaultValue;
try {
Method getter = (Method) getters.get(propertyName);
if (getter != null) {
value = getter.invoke(service, null);
}
} catch (Throwable throwable) {
while (throwable instanceof InvocationTargetException) {
throwable = ((InvocationTargetException) throwable).getTargetException();
}
throw new RuntimeException("Problem while obtaining the currennt persistent value of property: " +
"propertyName=" + propertyName +
", serviceType: " + beanDefinition.getBeanClassName(),
throwable);
}
// we should never get a bean definition holder
// we don't support them sice it is not serizlizable
if (value instanceof BeanDefinitionHolder) {
throw new IllegalArgumentException("Got a bean definition holder");
}
// turn proxies back into the factory bean
// the factory bean is serizlizable
if (value instanceof FactoryBeanProvider) {
value = ((FactoryBeanProvider) value).getFactoryBean();
} else {
Object proxyData = proxyManager.getProxyData(value);
if (proxyData instanceof FactoryBeanProvider) {
value = ((FactoryBeanProvider) proxyData).getFactoryBean();
} else if (proxyData instanceof FactoryBean) {
value = proxyData;
}
}
return value;
}
private static String fixPropertyName(String propertyName) {
if (Character.isUpperCase(propertyName.charAt(0))) {
return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
}
return propertyName;
}
}