/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.service;
import static org.jboss.msc.value.Values.cached;
import java.beans.PropertyEditor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.jboss.as.server.deployment.Attachments;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.server.deployment.reflect.ClassReflectionIndex;
import org.jboss.as.server.deployment.reflect.DeploymentReflectionIndex;
import org.jboss.as.service.component.ServiceComponentInstantiator;
import org.jboss.as.service.descriptor.JBossServiceAttributeConfig;
import org.jboss.as.service.descriptor.JBossServiceAttributeConfig.Inject;
import org.jboss.as.service.descriptor.JBossServiceAttributeConfig.ValueFactory;
import org.jboss.as.service.descriptor.JBossServiceAttributeConfig.ValueFactoryParameter;
import org.jboss.as.service.descriptor.JBossServiceConfig;
import org.jboss.as.service.descriptor.JBossServiceConstructorConfig;
import org.jboss.as.service.descriptor.JBossServiceConstructorConfig.Argument;
import org.jboss.as.service.descriptor.JBossServiceDependencyConfig;
import org.jboss.as.service.descriptor.JBossServiceDependencyListConfig;
import org.jboss.as.service.descriptor.JBossServiceXmlDescriptor;
import org.jboss.as.service.logging.SarLogger;
import org.jboss.common.beans.property.finder.PropertyEditorFinder;
import org.jboss.modules.Module;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.inject.MethodInjector;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.value.ImmediateValue;
import org.jboss.msc.value.MethodValue;
import org.jboss.msc.value.Value;
import org.jboss.msc.value.Values;
import org.wildfly.security.manager.WildFlySecurityManager;
/**
* DeploymentUnit processor responsible for taking JBossServiceXmlDescriptor configuration and creating the
* corresponding services.
*
* @author John E. Bailey
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
* @author Eduardo Martins
*/
public class ParsedServiceDeploymentProcessor implements DeploymentUnitProcessor {
private final ServiceName mbeanServerServiceName;
ParsedServiceDeploymentProcessor(ServiceName mbeanServerServiceName) {
this.mbeanServerServiceName = mbeanServerServiceName;
}
/**
* Process a deployment for JbossService configuration. Will install a {@code JBossService} for each configured service.
*
* @param phaseContext the deployment unit context
* @throws DeploymentUnitProcessingException
*/
@Override
public void deploy(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
final JBossServiceXmlDescriptor serviceXmlDescriptor = deploymentUnit.getAttachment(JBossServiceXmlDescriptor.ATTACHMENT_KEY);
if (serviceXmlDescriptor == null) {
// Skip deployments without a service xml descriptor
return;
}
// assert module
final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
if (module == null)
throw SarLogger.ROOT_LOGGER.failedToGetAttachment("module", deploymentUnit);
// assert reflection index
final DeploymentReflectionIndex reflectionIndex = deploymentUnit.getAttachment(Attachments.REFLECTION_INDEX);
if (reflectionIndex == null)
throw SarLogger.ROOT_LOGGER.failedToGetAttachment("reflection index", deploymentUnit);
// install services
final ClassLoader classLoader = module.getClassLoader();
final List<JBossServiceConfig> serviceConfigs = serviceXmlDescriptor.getServiceConfigs();
final ServiceTarget target = phaseContext.getServiceTarget();
final Map<String,ServiceComponentInstantiator> serviceComponents = deploymentUnit.getAttachment(ServiceAttachments.SERVICE_COMPONENT_INSTANTIATORS);
for (final JBossServiceConfig serviceConfig : serviceConfigs) {
addServices(target, serviceConfig, classLoader, reflectionIndex, serviceComponents != null ? serviceComponents.get(serviceConfig.getName()) : null, phaseContext);
}
}
@Override
public void undeploy(final DeploymentUnit context) {
}
private void addServices(final ServiceTarget target, final JBossServiceConfig mBeanConfig, final ClassLoader classLoader, final DeploymentReflectionIndex index, ServiceComponentInstantiator componentInstantiator, final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final String mBeanClassName = mBeanConfig.getCode();
final List<ClassReflectionIndex> mBeanClassHierarchy = ReflectionUtils.getClassHierarchy(mBeanClassName, index, classLoader);
final Object mBeanInstance = newInstance(mBeanConfig, mBeanClassHierarchy, classLoader);
final String mBeanName = mBeanConfig.getName();
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
final MBeanServices mBeanServices = new MBeanServices(mBeanName, mBeanInstance, mBeanClassHierarchy, target, componentInstantiator, deploymentUnit.getAttachmentList(org.jboss.as.ee.component.Attachments.WEB_SETUP_ACTIONS), classLoader, mbeanServerServiceName);
final JBossServiceDependencyConfig[] dependencyConfigs = mBeanConfig.getDependencyConfigs();
addDependencies(dependencyConfigs, mBeanClassHierarchy, mBeanServices);
final JBossServiceDependencyListConfig[] dependencyListConfigs = mBeanConfig.getDependencyConfigLists();
addDependencyLists(dependencyListConfigs, mBeanClassHierarchy, mBeanServices);
final JBossServiceAttributeConfig[] attributeConfigs = mBeanConfig.getAttributeConfigs();
addAttributes(attributeConfigs, mBeanClassHierarchy, mBeanServices, classLoader);
// register all mBean related services
mBeanServices.install();
}
private void addDependencies(final JBossServiceDependencyConfig[] dependencyConfigs, final List<ClassReflectionIndex> mBeanClassHierarchy, final MBeanServices mBeanServices) throws DeploymentUnitProcessingException {
if (dependencyConfigs != null) {
final Service<Object> createDestroyService = mBeanServices.getCreateDestroyService();
for (final JBossServiceDependencyConfig dependencyConfig : dependencyConfigs) {
final String optionalAttributeName = dependencyConfig.getOptionalAttributeName();
if(optionalAttributeName != null){
final Injector<Object> injector = getOptionalAttributeInjector(optionalAttributeName, mBeanClassHierarchy, createDestroyService);
final ObjectName dependencyObjectName = createDependencyObjectName(dependencyConfig.getDependencyName());
final ImmediateValue<ObjectName> dependencyNameValue = new ImmediateValue<ObjectName>(dependencyObjectName);
mBeanServices.addInjectionValue(injector, dependencyNameValue);
}
mBeanServices.addDependency(dependencyConfig.getDependencyName());
}
}
}
private void addDependencyLists(final JBossServiceDependencyListConfig[] dependencyListConfigs, final List<ClassReflectionIndex> mBeanClassHierarchy, final MBeanServices mBeanServices) throws DeploymentUnitProcessingException {
if(dependencyListConfigs != null){
final Service<Object> createDestroyService = mBeanServices.getCreateDestroyService();
for(final JBossServiceDependencyListConfig dependencyListConfig: dependencyListConfigs) {
final List<ObjectName> dependencyObjectNames = new ArrayList<ObjectName>(dependencyListConfig.getDependencyConfigs().length);
for(final JBossServiceDependencyConfig dependencyConfig: dependencyListConfig.getDependencyConfigs()){
final String dependencyName = dependencyConfig.getDependencyName();
mBeanServices.addDependency(dependencyName);
final ObjectName dependencyObjectName = createDependencyObjectName(dependencyName);
dependencyObjectNames.add(dependencyObjectName);
}
final String optionalAttributeName = dependencyListConfig.getOptionalAttributeName();
if(optionalAttributeName != null){
final Injector<Object> injector = getOptionalAttributeInjector(optionalAttributeName, mBeanClassHierarchy, createDestroyService);
final ImmediateValue<List<ObjectName>> dependencyNamesValue = new ImmediateValue<List<ObjectName>>(dependencyObjectNames);
mBeanServices.addInjectionValue(injector, dependencyNamesValue);
}
}
}
}
private void addAttributes(final JBossServiceAttributeConfig[] attributeConfigs, final List<ClassReflectionIndex> mBeanClassHierarchy, final MBeanServices mBeanServices, final ClassLoader classLoader) throws DeploymentUnitProcessingException {
if (attributeConfigs != null) {
final Service<Object> createDestroyService = mBeanServices.getCreateDestroyService();
for (final JBossServiceAttributeConfig attributeConfig : attributeConfigs) {
final String propertyName = attributeConfig.getName();
final Inject injectConfig = attributeConfig.getInject();
final ValueFactory valueFactoryConfig = attributeConfig.getValueFactory();
if (injectConfig != null) {
final Value<?> value = getValue(injectConfig);
final Injector<Object> injector = getPropertyInjector(propertyName, mBeanClassHierarchy, createDestroyService, value);
mBeanServices.addAttribute(injectConfig.getBeanName(), injector);
} else if (valueFactoryConfig != null) {
final Value<?> value = getValue(valueFactoryConfig, classLoader);
final Injector<Object> injector = getPropertyInjector(propertyName, mBeanClassHierarchy, createDestroyService, value);
mBeanServices.addAttribute(valueFactoryConfig.getBeanName(), injector);
} else {
final Value<?> value = getValue(attributeConfig, mBeanClassHierarchy);
final Injector<Object> injector = getPropertyInjector(propertyName, mBeanClassHierarchy, createDestroyService, Values.injectedValue());
mBeanServices.addInjectionValue(injector, value);
}
}
}
}
private ObjectName createDependencyObjectName(final String dependencyName) throws DeploymentUnitProcessingException {
try {
return new ObjectName(dependencyName);
} catch(MalformedObjectNameException exception){
throw SarLogger.ROOT_LOGGER.malformedDependencyName(exception, dependencyName);
}
}
private static Injector<Object> getOptionalAttributeInjector(final String attributeName, final List<ClassReflectionIndex> mBeanClassHierarchy, final Service<Object> service) {
return getPropertyInjector(attributeName, mBeanClassHierarchy, service, Values.injectedValue());
}
private static Value<?> getValue(final Inject injectConfig) {
final String propertyName = injectConfig.getPropertyName();
Value<?> valueToInject = Values.injectedValue();
if (propertyName != null) {
final Value<Method> methodValue = new InjectedBeanMethodValue(Values.injectedValue(), new InjectedBeanMethodValue.MethodFinder() {
@Override
public Method find(Class<?> clazz) {
return ReflectionUtils.getGetter(clazz, propertyName);
}
});
valueToInject = cached(new MethodValue<Object>(methodValue, valueToInject, Values.<Object>emptyList()));
}
return valueToInject;
}
private static Value<?> getValue(final ValueFactory valueFactory, final ClassLoader classLoader) throws DeploymentUnitProcessingException {
final String methodName = valueFactory.getMethodName();
final ValueFactoryParameter[] parameters = valueFactory.getParameters();
final List<Class<?>> paramTypes = new ArrayList<Class<?>>(parameters.length);
final List<Value<?>> paramValues = new ArrayList<Value<?>>(parameters.length);
for (ValueFactoryParameter parameter : parameters) {
final Class<?> attributeTypeValue = ReflectionUtils.getClass(parameter.getType(), classLoader);
paramTypes.add(attributeTypeValue);
paramValues.add(new ImmediateValue<Object>(newValue(attributeTypeValue, parameter.getValue())));
}
final Value<Method> methodValue = new InjectedBeanMethodValue(Values.injectedValue(), new InjectedBeanMethodValue.MethodFinder() {
@Override
public Method find(Class<?> clazz) {
return ReflectionUtils.getMethod(clazz, methodName, paramTypes.toArray(new Class<?>[0]));
}
});
return cached(new MethodValue<Object>(methodValue, Values.injectedValue(), paramValues));
}
private static Value<?> getValue(final JBossServiceAttributeConfig attributeConfig, final List<ClassReflectionIndex> mBeanClassHierarchy) {
final String attributeName = attributeConfig.getName();
final Method setterMethod = ReflectionUtils.getSetter(mBeanClassHierarchy, attributeName);
final Class<?> setterType = setterMethod.getParameterTypes()[0];
return new ImmediateValue<Object>(newValue(setterType, attributeConfig.getValue()));
}
private static Object newInstance(final JBossServiceConfig serviceConfig, final List<ClassReflectionIndex> mBeanClassHierarchy, final ClassLoader deploymentClassLoader) throws DeploymentUnitProcessingException {
// set TCCL so that the MBean instantiation happens in the deployment's classloader
final ClassLoader oldTCCL = WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(deploymentClassLoader);
try {
final JBossServiceConstructorConfig constructorConfig = serviceConfig.getConstructorConfig();
final int paramCount = constructorConfig != null ? constructorConfig.getArguments().length : 0;
final Class<?>[] types = new Class<?>[paramCount];
final Object[] params = new Object[paramCount];
if (constructorConfig != null) {
final Argument[] arguments = constructorConfig.getArguments();
for (int i = 0; i < paramCount; i++) {
final Argument argument = arguments[i];
types[i] = ReflectionUtils.getClass(argument.getType(), deploymentClassLoader);
params[i] = newValue(ReflectionUtils.getClass(argument.getType(), deploymentClassLoader), argument.getValue());
}
}
final Constructor<?> constructor = mBeanClassHierarchy.get(0).getConstructor(types);
if(constructor == null){
throw SarLogger.ROOT_LOGGER.defaultConstructorNotFound(mBeanClassHierarchy.get(0).getIndexedClass());
}
final Object mBeanInstance = ReflectionUtils.newInstance(constructor, params);
return mBeanInstance;
} finally {
// switch back the TCCL
WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(oldTCCL);
}
}
private static Injector<Object> getPropertyInjector(final String propertyName, final List<ClassReflectionIndex> mBeanClassHierarchy, final Service<?> service, final Value<?> value) {
final Method setterMethod = ReflectionUtils.getSetter(mBeanClassHierarchy, propertyName);
return new MethodInjector<Object>(setterMethod, service, Values.nullValue(), Collections.singletonList(value));
}
private static Object newValue(final Class<?> type, final String value) {
final PropertyEditor editor = PropertyEditorFinder.getInstance().find(type);
if (editor == null) {
SarLogger.ROOT_LOGGER.propertyNotFound(type);
return null;
}
editor.setAsText(value);
return editor.getValue();
}
}