/*
* 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, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* 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 and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser 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.plugins.jbossas5;
import static org.rhq.plugins.jbossas5.AbstractManagedDeploymentDiscoveryComponent.getDeploymentNamesForType;
import static org.rhq.plugins.jbossas5.AbstractManagedDeploymentDiscoveryComponent.getManagementView;
import java.io.File;
import java.net.URI;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.deployers.spi.management.KnownDeploymentTypes;
import org.jboss.deployers.spi.management.ManagementView;
import org.jboss.deployers.spi.management.deploy.DeploymentManager;
import org.jboss.deployers.spi.management.deploy.DeploymentProgress;
import org.jboss.deployers.spi.management.deploy.DeploymentStatus;
import org.jboss.deployers.spi.management.deploy.ProgressEvent;
import org.jboss.deployers.spi.management.deploy.ProgressListener;
import org.jboss.managed.api.DeploymentState;
import org.jboss.managed.api.ManagedDeployment;
import org.jboss.profileservice.spi.NoSuchDeploymentException;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
import org.rhq.plugins.jbossas5.connection.ProfileServiceConnection;
import org.rhq.plugins.jbossas5.util.DeploymentUtils;
/**
* ResourceComponent for managing ManagedDeployments (EARs, WARs, etc.).
*
* @author Mark Spritzler
* @author Ian Springer
*/
public abstract class AbstractManagedDeploymentComponent extends AbstractManagedComponent implements MeasurementFacet,
OperationFacet, ProgressListener {
private static final Log LOG = LogFactory.getLog(AbstractManagedDeploymentComponent.class);
/**
* @deprecated as of 4.13. No longer used
*/
@Deprecated
public static final String DEPLOYMENT_NAME_PROPERTY = "deploymentName";
public static final String DEPLOYMENT_KEY_PROPERTY = "deploymentKey";
public static final String DEPLOYMENT_TYPE_NAME_PROPERTY = "deploymentTypeName";
public static final String EXTENSION_PROPERTY = "extension";
private static final boolean IS_WINDOWS = (File.separatorChar == '\\');
/**
* The name of the ManagedDeployment (e.g.: vfszip:/C:/opt/jboss-6.0.0.Final/server/default/deploy/foo.war).
* @deprecated as of 4.13. Use {@link #getDeploymentName()} instead.
*/
@Deprecated
protected volatile String deploymentName;
/**
* The type of the ManagedDeployment (e.g. war).
*/
protected KnownDeploymentTypes deploymentType;
/**
* The absolute path of the deployment file (e.g.: C:/opt/jboss-6.0.0.Final/server/default/deploy/foo.war).
*/
protected File deploymentFile;
private String deploymentKey;
// ----------- ResourceComponent Implementation ------------
@Override
public void start(ResourceContext<ProfileServiceComponent<?>> resourceContext) throws Exception {
super.start(resourceContext);
Configuration pluginConfig = getResourceContext().getPluginConfiguration();
deploymentKey = pluginConfig.getSimpleValue(DEPLOYMENT_KEY_PROPERTY);
deploymentFile = getDeploymentFile(deploymentKey);
deploymentName = lookupDeploymentName();
String deploymentTypeName = pluginConfig.getSimple(DEPLOYMENT_TYPE_NAME_PROPERTY).getStringValue();
deploymentType = KnownDeploymentTypes.valueOf(deploymentTypeName);
try {
getManagedDeployment();
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.warn("Could not start deployment for [" + deploymentKey
+ "] no longer exists. It may have been deleted from the filesystem external to RHQ.");
}
}
if (LOG.isTraceEnabled()) {
LOG.trace("Started ResourceComponent for " + getResourceDescription() + ", managing " + deploymentType
+ " deployment '" + deploymentKey + "' with path '" + deploymentFile + "'.");
}
}
@Override
public void stop() {
super.stop();
deploymentName = null;
deploymentType = null;
deploymentFile = null;
deploymentKey = null;
}
private synchronized String lookupDeploymentName() {
ManagementView managementView = getManagementView(getConnection());
if (managementView == null) {
return null;
}
Set<String> deploymentNames = getDeploymentNamesForType(managementView, getResourceContext().getResourceType());
for (String deploymentName : deploymentNames) {
if (deploymentKey.equals(URI.create(deploymentName).getPath())) {
return deploymentName;
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Did not find deployment with key [" + deploymentKey + "]");
}
return null;
}
@Override
public AvailabilityType getAvailability() {
DeploymentState deploymentState;
try {
deploymentState = getManagedDeployment(false).getDeploymentState();
} catch (NoSuchDeploymentException e) {
LOG.warn(deploymentType + " deployment '" + deploymentKey + "' not found. Cause: "
+ e.getLocalizedMessage());
deploymentName = null;
return AvailabilityType.DOWN;
} catch (Throwable t) {
if (LOG.isDebugEnabled()) {
LOG.debug("Could not get deployment state for " + deploymentType + " deployment '" + deploymentKey
+ "', cause: ", t);
}
deploymentName = null;
return AvailabilityType.DOWN;
}
if (deploymentState == DeploymentState.STARTED) {
return AvailabilityType.UP;
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(deploymentType + " deployment '" + deploymentKey + "' was not running, state was: "
+ deploymentState);
}
return AvailabilityType.DOWN;
}
}
// ------------ MeasurementFacet Implementation ------------
@Override
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> requests) throws Exception {
if (deploymentType == KnownDeploymentTypes.JavaEEWebApplication) {
WarMeasurementFacetDelegate warMeasurementFacetDelegate = new WarMeasurementFacetDelegate(this);
warMeasurementFacetDelegate.getValues(report, requests);
}
}
// ------------ OperationFacet Implementation ------------
@Override
public OperationResult invokeOperation(String name, Configuration parameters) throws Exception {
ProfileServiceConnection connection = getConnection();
if (connection == null) {
OperationResult result = new OperationResult();
result.setErrorMessage("No profile service connection available");
return result;
}
String deploymentName = getDeploymentName();
if (deploymentName == null) {
OperationResult result = new OperationResult();
result.setErrorMessage("Did not find deployment with key [" + deploymentKey + "]");
return result;
}
DeploymentManager deploymentManager = connection.getDeploymentManager();
DeploymentProgress progress;
if (name.equals("start")) {
//FIXME: This is a workaround until JOPR-309 will be fixed.
if (getAvailability() != AvailabilityType.UP) {
progress = deploymentManager.start(deploymentName);
} else {
LOG.warn("Operation '" + name + "' on " + getResourceDescription()
+ " failed because the Resource is already started.");
OperationResult result = new OperationResult();
result.setErrorMessage(deploymentFile.getName() + " is already started.");
return result;
}
} else if (name.equals("stop")) {
progress = deploymentManager.stop(deploymentName);
} else if (name.equals("restart")) {
progress = deploymentManager.stop(deploymentName);
DeploymentUtils.run(progress);
// Still try to start, even if stop fails (maybe the app wasn't running to begin with).
progress = deploymentManager.start(deploymentName);
} else {
throw new UnsupportedOperationException(name);
}
DeploymentStatus status = DeploymentUtils.run(progress);
if (LOG.isDebugEnabled()) {
LOG.debug("Operation '" + name + "' on " + getResourceDescription() + " returned status [" + status + "].");
}
if (status.isFailed()) {
throw status.getFailure();
}
return new OperationResult();
}
// ------------ ProgressListener implementation -------------
@Override
public void progressEvent(ProgressEvent event) {
LOG.debug(event);
}
// -------------------------------------------------------------
public String getDeploymentName() {
if (deploymentName == null) {
deploymentName = lookupDeploymentName();
}
return deploymentName;
}
public KnownDeploymentTypes getDeploymentType() {
return deploymentType;
}
public final String getDeploymentKey() {
return deploymentKey;
}
protected ManagedDeployment getManagedDeployment() throws NoSuchDeploymentException {
return getManagedDeployment(true);
}
protected ManagedDeployment getManagedDeployment(boolean forceLoad) throws NoSuchDeploymentException {
ManagementView managementView = getConnection().getManagementView();
if (forceLoad) {
managementView.load();
}
String deploymentName = getDeploymentName();
if (deploymentName == null) {
throw new NoSuchDeploymentException("Did not find deployment with key [" + deploymentKey + "]");
}
return managementView.getDeployment(deploymentName);
}
private File getDeploymentFile(String deploymentKey) {
// Under Windows, the deploymentKey will look like:
// /C:/opt/jboss-6.0.0.Final/server/default/deploy/foo.ear/
// Java considers the path with the leading slash to be valid and equivalent to the same path with the
// leading slash removed, but the leading slash is unnecessary and ugly, so excise it.
if (IS_WINDOWS && deploymentKey.charAt(0) == '/') {
deploymentKey = deploymentKey.substring(1);
}
return new File(deploymentKey);
}
}