/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, 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.system.server.profileservice;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jboss.beans.info.spi.BeanInfo;
import org.jboss.beans.metadata.spi.BeanMetaData;
import org.jboss.bootstrap.spi.Bootstrap;
import org.jboss.bootstrap.spi.Server;
import org.jboss.bootstrap.spi.ServerConfig;
import org.jboss.bootstrap.spi.microcontainer.MCServer;
import org.jboss.dependency.spi.ControllerContext;
import org.jboss.dependency.spi.ControllerState;
import org.jboss.deployers.client.spi.IncompleteDeploymentException;
import org.jboss.deployers.client.spi.main.MainDeployer;
import org.jboss.deployers.spi.DeploymentException;
import org.jboss.deployers.spi.deployer.managed.ManagedDeploymentCreator;
import org.jboss.deployers.spi.management.KnownComponentTypes;
import org.jboss.deployers.spi.management.ManagementView;
import org.jboss.deployers.structure.spi.DeploymentUnit;
import org.jboss.deployers.vfs.deployer.kernel.KernelDeploymentDeployer.KernelDeploymentVisitor;
import org.jboss.kernel.Kernel;
import org.jboss.kernel.plugins.registry.BeanKernelRegistryEntry;
import org.jboss.kernel.spi.config.KernelConfigurator;
import org.jboss.kernel.spi.dependency.KernelController;
import org.jboss.kernel.spi.deployment.KernelDeployment;
import org.jboss.kernel.spi.registry.KernelRegistryEntry;
import org.jboss.kernel.spi.registry.KernelRegistryPlugin;
import org.jboss.logging.Logger;
import org.jboss.managed.api.ComponentType;
import org.jboss.managed.api.Fields;
import org.jboss.managed.api.ManagedDeployment;
import org.jboss.managed.api.ManagedObject;
import org.jboss.managed.api.ManagedOperation;
import org.jboss.managed.api.ManagedProperty;
import org.jboss.managed.api.MutableManagedObject;
import org.jboss.managed.api.annotation.ManagementComponent;
import org.jboss.managed.api.annotation.ManagementObject;
import org.jboss.managed.api.annotation.ViewUse;
import org.jboss.managed.api.factory.ManagedObjectFactory;
import org.jboss.managed.plugins.DefaultFieldsImpl;
import org.jboss.managed.plugins.ManagedComponentImpl;
import org.jboss.managed.plugins.ManagedObjectImpl;
import org.jboss.managed.plugins.ManagedOperationImpl;
import org.jboss.managed.plugins.ManagedPropertyImpl;
import org.jboss.metatype.api.types.ArrayMetaType;
import org.jboss.metatype.api.types.MetaType;
import org.jboss.metatype.api.types.SimpleMetaType;
import org.jboss.metatype.api.values.ArrayValueSupport;
import org.jboss.metatype.api.values.EnumValue;
import org.jboss.metatype.api.values.EnumValueSupport;
import org.jboss.profileservice.spi.MutableProfile;
import org.jboss.profileservice.spi.NoSuchProfileException;
import org.jboss.profileservice.spi.Profile;
import org.jboss.profileservice.spi.ProfileKey;
import org.jboss.profileservice.spi.ProfileService;
import org.jboss.profileservice.spi.types.ControllerStateMetaType;
import org.jboss.system.server.profileservice.repository.AbstractBootstrapProfileFactory;
/**
* Bootstraps the profile service
*
* @author Scott.Stark@jboss.org
* @author adrian@jboss.org
* @author <a href="mailto:emuckenh@redhat.com">Emanuel Muckenhuber</a>
* @version $Revision: 89838 $
*/
public class ProfileServiceBootstrap implements Bootstrap, KernelRegistryPlugin
{
/** The log */
private static final Logger log = Logger.getLogger(ProfileServiceBootstrap.class);
/** The root profile key. */
protected ProfileKey profileKey;
/** The loaded profiles. */
private List<ProfileKey> bootstrapProfiles = new ArrayList<ProfileKey>();
/** The server MainDeployer */
protected MainDeployer mainDeployer;
/** The server ProfileService */
protected ProfileService profileService;
/** The ManagedDeploymentCreator plugin */
private ManagedDeploymentCreator mgtDeploymentCreator = null;
/** The ManagedObjectFactory for building the bootstrap deployment ManagedObjects */
private ManagedObjectFactory mof;
/** The ManagedDeployment map for the MCServer KernelDeployments */
private Map<String, ManagedDeployment> bootstrapMDs = new HashMap<String, ManagedDeployment>();
/** The profile bootstrap factory */
private AbstractBootstrapProfileFactory profileFactory;
/** Whether we are shutdown */
private AtomicBoolean shutdown = new AtomicBoolean(false);
/** */
private Map<Object, KernelRegistryEntry> bootstrapEntries = new HashMap<Object, KernelRegistryEntry>();
/** */
private KernelConfigurator configurator;
/**
* Create a new ProfileServiceBootstrap.
*/
public ProfileServiceBootstrap()
{
}
/**
* Return the MainDeployer bean.
*
* @return the MainDeployer bean if bootstrap succeeded, null otherwise.
*/
public MainDeployer getMainDeployer()
{
return mainDeployer;
}
public void setMainDeployer(MainDeployer mainDeployer)
{
this.mainDeployer = mainDeployer;
}
public KernelConfigurator getConfigurator()
{
return configurator;
}
public void setConfigurator(KernelConfigurator configurator)
{
this.configurator = configurator;
}
/**
* Return the ProfileService bean.
*
* @return the ProfileService bean if bootstrap succeeded, null otherwise
*/
public ProfileService getProfileService()
{
return profileService;
}
public void setProfileService(ProfileService profileService)
{
this.profileService = profileService;
}
public ProfileKey getProfileKey()
{
return profileKey;
}
public void setProfileKey(ProfileKey profileKey)
{
this.profileKey = profileKey;
}
public ManagedObjectFactory getMof()
{
return mof;
}
public void setMof(ManagedObjectFactory mof)
{
this.mof = mof;
}
public ManagedDeploymentCreator getMgtDeploymentCreator()
{
return mgtDeploymentCreator;
}
public void setMgtDeploymentCreator(ManagedDeploymentCreator mgtDeploymentCreator)
{
this.mgtDeploymentCreator = mgtDeploymentCreator;
}
public AbstractBootstrapProfileFactory getBootstrapProfileFactory()
{
return profileFactory;
}
public void setBootstrapProfileFactory(AbstractBootstrapProfileFactory profileFactory)
{
this.profileFactory = profileFactory;
}
public Map<String, ManagedDeployment> getBootstrapMDs()
{
return bootstrapMDs;
}
public void setBootstrapMDs(Map<String, ManagedDeployment> bootstrapMDs)
{
this.bootstrapMDs = bootstrapMDs;
}
/**
*
*/
public void start(Server server)
throws Exception
{
shutdown.set(false);
if(profileService == null)
throw new IllegalStateException("The ProfileService has not been injected");
log.debug("Using ProfileService: " + profileService);
if(mainDeployer == null)
throw new IllegalStateException("The MainDeployer has not been injected");
log.debug("Using MainDeployer: " + mainDeployer);
// Validate that everything is ok
mainDeployer.checkComplete();
// Expose the bootstrap ManagedDeployments
initBootstrapMDs(server);
// Load the profiles
if(this.profileKey == null)
this.profileKey = new ProfileKey(server.getConfig().getServerName());
// TODO check if there is a predetermined ProfileMetaData attachment
// Map<String, Object> metaData = server.getMetaData();
// ProfileMetaData pmd = (ProfileMetaData) metaData.get(ProfileMetaData.class.getName());
// Register the profiles
Collection<Profile> bootstrapProfiles = profileFactory.createProfiles(profileKey, null);
for(Profile profile : bootstrapProfiles)
{
profileService.registerProfile(profile);
// Add to loaded profiles
if(this.profileKey.equals(profile.getKey()) == false)
this.bootstrapProfiles.add(0, profile.getKey());
}
// Activate the root profile
log.info("Loading profile: " + this.profileKey);
this.profileService.activateProfile(this.profileKey);
this.profileService.validateProfile(this.profileKey);
try
{
// Check if everything is complete
mainDeployer.checkComplete();
}
catch (IncompleteDeploymentException e)
{
log.error("Failed to load profile: " + e.getMessage());
}
catch (Exception e)
{
log.error("Failed to load profile: ", e);
}
// Enable modification checks on all mutable profiles
for(ProfileKey key : profileService.getActiveProfileKeys())
{
try
{
Profile profile = profileService.getActiveProfile(key);
if(profile.isMutable() && profile instanceof MutableProfile)
((MutableProfile) profile).enableModifiedDeploymentChecks(true);
}
catch(NoSuchProfileException ignore) { }
}
}
public void prepareShutdown(Server server)
{
shutdown.set(true);
if (mainDeployer != null)
mainDeployer.prepareShutdown();
}
public void shutdown(Server server)
{
// Disable modification checks on all mutable profiles
for(ProfileKey key : profileService.getActiveProfileKeys())
{
try
{
Profile profile = profileService.getActiveProfile(key);
if(profile.isMutable() && profile instanceof MutableProfile)
((MutableProfile) profile).enableModifiedDeploymentChecks(false);
}
catch(NoSuchProfileException ignore) { }
}
// Deactivate the root profile
try
{
// Release
if(profileService.getActiveProfileKeys().contains(profileKey))
profileService.deactivateProfile(profileKey);
}
catch(Throwable t)
{
log.warn("Error deactivating profile: " + this.profileKey, t);
}
try
{
// Unregister
if(profileService.getProfileKeys().contains(profileKey))
profileService.unregisterProfile(profileKey);
}
catch(Throwable t)
{
log.warn("Error unregistering profile: " + this.profileKey, t);
}
// Deactivate all profiles we registered
deactivateProfiles(this.bootstrapProfiles);
// Deactivate all still active profiles
deactivateProfiles(this.profileService.getActiveProfileKeys());
// Unregister all profiles at once
for(ProfileKey key : profileService.getProfileKeys())
{
try
{
profileService.unregisterProfile(key);
}
catch(Throwable t)
{
// Ignore
}
}
try
{
mainDeployer.shutdown();
}
catch (Throwable t)
{
log.warn("Error shutting down the main deployer", t);
}
}
public KernelRegistryEntry getEntry(Object name)
{
KernelRegistryEntry entry = bootstrapEntries.get(name);
return entry;
}
protected void deactivateProfiles(Collection<ProfileKey> profiles)
{
if(profiles != null && profiles.isEmpty() == false)
{
for(ProfileKey key : profiles)
{
try
{
profileService.deactivateProfile(key);
}
catch(NoSuchProfileException e)
{
// ignore
}
catch(Throwable t)
{
log.warn("Error unloading profile: " + this.profileKey, t);
}
}
}
}
/**
* Create ManagedDeployments for the MCServer KernelDeployments. This allows
* the bootstrap deployments outside of the profile service to be visible in
* the ManagementView
* @see {@link ManagementView}
*
* @param server - the Bootstrap.start Server instance. This must be an
* MCServer in order for there to be KernelDeployments available.
*/
protected void initBootstrapMDs(Server server)
{
if(mof == null || mgtDeploymentCreator == null)
{
log.warn("Skipping ManagedDeployment creation due to missing mof, mgtDeploymentCreator");
return;
}
Map<String, KernelDeployment> serverDeployments = null;
if(server instanceof MCServer)
{
// Build ManagedDeployments for the KernelDeployments
MCServer mcserver = MCServer.class.cast(server);
Kernel kernel = mcserver.getKernel();
serverDeployments = mcserver.getDeployments();
ManagedDeployment firstDeployment = null;
for(KernelDeployment kd : serverDeployments.values())
{
BootstrapDeployment deploymentUnit = new BootstrapDeployment(kd);
KernelDeploymentVisitor visitor = new KernelDeploymentVisitor();
try
{
visitor.deploy(deploymentUnit, kd);
}
catch(DeploymentException e)
{
log.debug("Failed to build ManagedDeployment for: "+kd, e);
continue;
}
/* Create minimal deployment ManagedObject. Don't use the ManagedObjectFactory
* as this will create ManagedObjects for the beans via the beansFactory
* property. We handle the beans below.
*/
Set<ManagedProperty> kdProperties = new HashSet<ManagedProperty>();
HashSet<ManagedOperation> ops = null;
ManagedObject kdMO = new ManagedObjectImpl(kd.getName(), "",
KernelDeployment.class.getName(),
kdProperties, ops, (Serializable) kd);
Map<String, ManagedObject> kdMOs = new HashMap<String, ManagedObject>();
kdMOs.put(kd.getName(), kdMO);
// Traverse the deployment components
for(DeploymentUnit compUnit : deploymentUnit.getComponents())
{
BeanMetaData bmd = compUnit.getAttachment(BeanMetaData.class);
ManagedObject bmdMO = mof.initManagedObject(bmd, compUnit.getMetaData());
if(bmdMO == null)
continue;
Map<String, Annotation> moAnns = bmdMO.getAnnotations();
ManagementObject mo = (ManagementObject) moAnns.get(ManagementObject.class.getName());
// Reset the name to the bean name rather than the attachment name
if(bmdMO instanceof MutableManagedObject)
{
MutableManagedObject mmo = (MutableManagedObject) bmdMO;
// Reset the name to the bean name if its the attachment name
if(mmo.getName().equals(mmo.getAttachmentName()))
mmo.setName(bmd.getName());
mmo.setParent(kdMO);
// Add an alias property
Set<Object> bmdAliases = bmd.getAliases();
Map<String, ManagedProperty> oldProps = mmo.getProperties();
Map<String, ManagedProperty> newProps = new HashMap<String, ManagedProperty>(oldProps);
if(bmdAliases != null && bmdAliases.size() > 0)
{
ArrayMetaType aliasType = new ArrayMetaType(SimpleMetaType.STRING, false);
DefaultFieldsImpl fields = getFields("alias", aliasType);
fields.setDescription("Aliases of the bean");
String[] aliases = new String[bmdAliases.size()];
Iterator<?> i = bmdAliases.iterator();
for(int n = 0; i.hasNext(); n++)
{
aliases[n] = i.next().toString();
}
ArrayValueSupport value = new ArrayValueSupport(aliasType, aliases);
fields.setValue(value);
ManagedPropertyImpl aliasesMP = new ManagedPropertyImpl(bmdMO, fields);
newProps.put("alias", aliasesMP);
}
// Add a state property
DefaultFieldsImpl stateFields = getFields("state", ControllerStateMetaType.TYPE);
stateFields.setViewUse(new ViewUse[]{ViewUse.STATISTIC});
EnumValue stateValue = getState(bmd.getName(), kernel);
stateFields.setValue(stateValue);
stateFields.setDescription("The bean controller state");
ManagedPropertyImpl stateMP = new ManagedPropertyImpl(mmo, stateFields);
newProps.put("state", stateMP);
// Update the properties
mmo.setProperties(newProps);
}
log.debug("Created ManagedObject: "+bmdMO+" for bean: "+bmd.getName());
kdMOs.put(bmd.getName(), bmdMO);
}
// Create the ManagedDeployment
ManagedDeployment md = mgtDeploymentCreator.build(deploymentUnit, kdMOs, null);
if(firstDeployment == null)
firstDeployment = md;
// Create the ManagedComponents
for(ManagedObject bmdMO : kdMOs.values())
{
if(bmdMO.getAttachmentName().equals(KernelDeployment.class.getName()))
continue;
ComponentType type = KnownComponentTypes.MCBean.Any.getType();
Map<String, Annotation> moAnns = bmdMO.getAnnotations();
ManagementComponent mc = (ManagementComponent) moAnns.get(ManagementComponent.class.getName());
if(mc != null)
{
type = new ComponentType(mc.type(), mc.subtype());
}
ManagedComponentImpl comp = new ManagedComponentImpl(type, md, bmdMO);
md.addComponent(bmdMO.getName(), comp);
log.debug("Created ManagedComponent("+comp.getName()+") of type: "
+type
+" for MO: "+bmdMO.getName()
+", componentName: "+bmdMO.getComponentName());
}
if(md != null)
bootstrapMDs.put(kd.getName(), md);
}
// Add other Server managed objects
if(firstDeployment != null)
{
ComponentType type = new ComponentType("MCBean", "MCServer");
ManagedObject serverMO = mof.initManagedObject(mcserver, null);
if (serverMO.getOperations() != null && serverMO.getOperations().size() == 0)
{
ManagedOperationImpl shutdown = new ManagedOperationImpl("Shutdown the server", "shutdown");
if(serverMO instanceof MutableManagedObject)
{
HashSet<ManagedOperation> ops = new HashSet<ManagedOperation>();
ops.add(shutdown);
MutableManagedObject mmo = MutableManagedObject.class.cast(serverMO);
mmo.setOperations(ops);
}
}
ManagedComponentImpl serverComp = new ManagedComponentImpl(type, firstDeployment, serverMO);
firstDeployment.addComponent("MCServer", serverComp);
try
{
BeanInfo mcserverInfo = configurator.getBeanInfo(mcserver.getClass());
BeanKernelRegistryEntry entry = new BeanKernelRegistryEntry(mcserver, mcserverInfo);
bootstrapEntries.put(serverComp.getComponentName(), entry);
}
catch(Throwable t)
{
log.error("Failed to create BeanInfo for: "+serverComp, t);
}
// ServerConfig
type = new ComponentType("MCBean", "ServerConfig");
ServerConfig config = mcserver.getConfig();
ManagedObject mo = mof.initManagedObject(config, null);
ManagedComponentImpl configComp = new ManagedComponentImpl(type, firstDeployment, mo);
firstDeployment.addComponent("ServerConfig", configComp);
log.debug("Created ManagedComponent of type: "+type+" for ServerConfig");
}
}
}
/**
* Create a DefaultFieldsImpl for the given property name and type
* @param name - the property name
* @param type - the property type
* @return return the fields implementation
*/
protected DefaultFieldsImpl getFields(String name, MetaType type)
{
DefaultFieldsImpl fields = new DefaultFieldsImpl();
fields.setMetaType(type);
fields.setName(name);
fields.setField(Fields.MAPPED_NAME, name);
fields.setMandatory(false);
return fields;
}
/**
* Get the state of a bean
*
* @param name the bean name
* @return state enum value
*/
protected EnumValue getState(Object name, Kernel kernel)
{
KernelController controller = kernel.getController();
ControllerContext context = controller.getContext(name, null);
if (context == null)
throw new IllegalStateException("Context not installed: " + name);
ControllerState state = context.getState();
return new EnumValueSupport(ControllerStateMetaType.TYPE, state.getStateString());
}
}