/*
* RHQ Management Platform
* Copyright (C) 2005-2014 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.core.pc.inventory;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.clientapi.agent.PluginContainerException;
import org.rhq.core.clientapi.agent.inventory.CreateResourceRequest;
import org.rhq.core.clientapi.agent.inventory.CreateResourceResponse;
import org.rhq.core.clientapi.agent.inventory.DeleteResourceRequest;
import org.rhq.core.clientapi.agent.inventory.DeleteResourceResponse;
import org.rhq.core.clientapi.agent.inventory.ResourceFactoryAgentService;
import org.rhq.core.clientapi.agent.metadata.PluginMetadataManager;
import org.rhq.core.clientapi.server.inventory.ResourceFactoryServerService;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pc.ContainerService;
import org.rhq.core.pc.PluginContainer;
import org.rhq.core.pc.PluginContainerConfiguration;
import org.rhq.core.pc.ServerServices;
import org.rhq.core.pc.agent.AgentService;
import org.rhq.core.pc.agent.AgentServiceStreamRemoter;
import org.rhq.core.pc.plugin.PluginManager;
import org.rhq.core.pc.util.ComponentUtil;
import org.rhq.core.pc.util.FacetLockType;
import org.rhq.core.pc.util.LoggingThreadFactory;
import org.rhq.core.pluginapi.inventory.CreateChildResourceFacet;
import org.rhq.core.pluginapi.inventory.CreateResourceReport;
import org.rhq.core.pluginapi.inventory.DeleteResourceFacet;
/**
* Plugin container manager that is responsible for handling resource factory operations (create and delete resource).
* This manager implements the remoted <code>ResourceFactoryAgentService</code> interface.
*
* @author Jason Dobies
*/
public class ResourceFactoryManager extends AgentService implements ContainerService, ResourceFactoryAgentService {
private static final Log log = LogFactory.getLog(ResourceFactoryManager.class);
// This used to be a single value fixed at 60 seconds. But create and delete actions can very well exceed 1 minute
// depending on the type of resource being created, or perhaps graceful shutdown of a resource being deleted. So,
// allow the timeout value to be overriden by editing rhq-agent-env.sh with new -D settings. Also, create separate
// timeouts for create and delete, as their execution times really are not related. The properties are set in
// milliseconds:
// rhq.agent.plugins.facet.create-child-resource.timeout
// rhq.agent.plugins.facet.delete-resource.timeout
//
// The default is still 60s. Note that increasing this value affects all types across all plugins and should not
// be raised unless a successful create or delete action requires the higher limit. Realize that while a
// create or delete is in progress that the resource is write-locked, so no other actions can take place (like
// metric collection).
//
private static final int FACET_CREATE_TIMEOUT;
private static final int FACET_DELETE_TIMEOUT;
static {
int timeout;
try {
timeout = Integer.parseInt(System.getProperty("rhq.agent.plugins.facet.create-child-resource.timeout",
"60000"));
} catch (Throwable t) {
timeout = 60 * 1000;
}
FACET_CREATE_TIMEOUT = timeout;
try {
timeout = Integer.parseInt(System.getProperty("rhq.agent.plugins.facet.delete-resource.timeout", "60000"));
} catch (Throwable t) {
timeout = 60 * 1000;
}
FACET_DELETE_TIMEOUT = timeout;
}
// Attributes --------------------------------------------
/**
* Configuration elements for the running of this manager.
*/
private final PluginContainerConfiguration configuration;
/**
* Executor service used to perform tasks.
*/
private final ExecutorService executor;
/**
* Handle to the metadata manager.
*/
private final PluginMetadataManager metadataManager;
// Constructors --------------------------------------------
/**
* Creates a new <code>ResourceFactoryManager</code> and initializes it as a remoted object.
*/
public ResourceFactoryManager(PluginContainerConfiguration configuration, AgentServiceStreamRemoter streamRemoter, PluginManager pluginManager) {
super(ResourceFactoryAgentService.class, streamRemoter);
log.debug("Initializing...");
this.configuration = configuration;
this.metadataManager = pluginManager.getMetadataManager();
// Initialize thread pool for executing tasks
int corePoolSize = configuration.getResourceFactoryCoreThreadPoolSize();
int keepAliveTime = configuration.getResourceFactoryKeepAliveTime();
int maxPoolSize = configuration.getResourceFactoryMaxThreadPoolSize();
executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(10000), new LoggingThreadFactory("ResourceFactory.executor", true));
}
public void shutdown() {
// TODO (ips, 04/30/12): Is it safe to pass true here to interrupt executing threads?
PluginContainer.shutdownExecutorService(executor, false);
}
// ResourceFactoryAgentService Implementation --------------------------------------------
@SuppressWarnings("unchecked")
public CreateResourceResponse executeCreateResourceImmediately(CreateResourceRequest request)
throws PluginContainerException {
// Load the actual resource type instance to be passed to the facet
ResourceType resourceType = metadataManager.getType(request.getResourceTypeName(), request.getPluginName());
if (resourceType == null) {
throw new PluginContainerException("Could not retrieve resource type for request: " + request);
}
String creationType = (request.getResourceConfiguration() != null) ? "configuration" : "package";
{
log.debug("Creating " + creationType + "-based resource of type '" + request.getResourceTypeName()
+ "' and with parent with id " + request.getParentResourceId() + "...");
}
// Create the report to send the plugin
CreateResourceReport report = new CreateResourceReport(request.getResourceName(), resourceType, request
.getPluginConfiguration(), request.getResourceConfiguration(), request.getPackageDetails());
// Execute the create against the plugin
CreateChildResourceFacet facet = getCreateChildResourceFacet(request.getParentResourceId(), request
.getTimeout());
CreateResourceRunner runner = new CreateResourceRunner(this, request.getParentResourceId(), facet, request
.getRequestId(), report, configuration.isInsideAgent());
CreateResourceResponse response;
try {
response = (CreateResourceResponse) executor.submit((Callable) runner).get();
} catch (Exception e) {
throw new PluginContainerException("Error during create resource callable", e);
}
return response;
}
public void createResource(CreateResourceRequest request) throws PluginContainerException {
// Load the actual resource type instance to be passed to the facet
ResourceType resourceType = metadataManager.getType(request.getResourceTypeName(), request.getPluginName());
if (resourceType == null) {
throw new PluginContainerException("Could not retrieve resource type for request: " + request);
}
String creationType = (request.getResourceConfiguration() != null) ? "configuration" : "package";
{
log.debug("Creating " + creationType + "-based resource of type '" + request.getResourceTypeName()
+ "' and with parent with id " + request.getParentResourceId() + "...");
}
// Create the report to send the plugin
CreateResourceReport report = new CreateResourceReport(request.getResourceName(), resourceType, request
.getPluginConfiguration(), request.getResourceConfiguration(), request.getPackageDetails());
// Execute the create against the plugin
CreateChildResourceFacet facet = getCreateChildResourceFacet(request.getParentResourceId(), request
.getTimeout());
CreateResourceRunner runner = new CreateResourceRunner(this, request.getParentResourceId(), facet, request
.getRequestId(), report, configuration.isInsideAgent());
executor.submit((Runnable) runner);
}
@SuppressWarnings("unchecked")
public DeleteResourceResponse executeDeleteResourceImmediately(DeleteResourceRequest request)
throws PluginContainerException {
int resourceId = request.getResourceId();
DeleteResourceFacet facet = getDeleteResourceFacet(resourceId);
DeleteResourceRunner runner = new DeleteResourceRunner(this, facet, request.getRequestId(), resourceId);
DeleteResourceResponse response;
try {
response = (DeleteResourceResponse) executor.submit((Callable) runner).get();
} catch (Exception e) {
throw new PluginContainerException("Error occurred in delete resource thread", e);
}
return response;
}
public void deleteResource(DeleteResourceRequest request) throws PluginContainerException {
int resourceId = request.getResourceId();
DeleteResourceFacet facet = getDeleteResourceFacet(resourceId);
DeleteResourceRunner runner = new DeleteResourceRunner(this, facet, request.getRequestId(), resourceId);
executor.submit((Runnable) runner);
}
// Package --------------------------------------------
/**
* Returns the server service implementation used to notify of a task completion.
*
* @return server service if one is registered; <code>null</code> otherwise
*/
ResourceFactoryServerService getServerService() {
ServerServices serverServices = configuration.getServerServices();
if (serverServices == null) {
return null;
}
ResourceFactoryServerService resourceFactoryServerService = serverServices.getResourceFactoryServerService();
return resourceFactoryServerService;
}
// Private --------------------------------------------
/**
* Returns the component that should be used to delete the resource in the given request.
*
* @param resourceId identifies the resource for which to retrieve the facet
*
* @return component used to delete the resource described by the request
*
* @throws PluginContainerException if the resource component required to delete the resource does not implement the
* correct facet
*/
private DeleteResourceFacet getDeleteResourceFacet(int resourceId) throws PluginContainerException {
DeleteResourceFacet facet = ComponentUtil.getComponent(resourceId, DeleteResourceFacet.class,
FacetLockType.WRITE, FACET_DELETE_TIMEOUT, false, true, false);
return facet;
}
/**
* Returns the component that should be used to create the resource in the given request.
*
* @param parentResourceId identifies the parent under which the new resource will be created
* @param timeout the agent side timeout for the resource creation. if null or unusable use FACET_CREATE_TIMEOUT.
*
* @return component used to create the resource
*
* @throws PluginContainerException if the resource component required to create the resource does not implement the
* correct facet
*/
private CreateChildResourceFacet getCreateChildResourceFacet(int parentResourceId, Integer timeout)
throws PluginContainerException {
int createTimeout = (null == timeout || timeout < 1) ? FACET_CREATE_TIMEOUT : timeout;
CreateChildResourceFacet facet = ComponentUtil.getComponent(parentResourceId, CreateChildResourceFacet.class,
FacetLockType.WRITE, createTimeout, false, true, false);
return facet;
}
}