/* * RHQ Management Platform * Copyright (C) 2005-2008 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.core.pc.operation; import java.util.HashMap; import java.util.Map; import org.rhq.core.clientapi.server.operation.OperationServerService; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.domain.operation.OperationDefinition; import org.rhq.core.pluginapi.operation.OperationContext; import org.rhq.core.pluginapi.operation.OperationServices; import org.rhq.core.pluginapi.operation.OperationServicesResult; import org.rhq.core.pluginapi.operation.OperationServicesResultCode; import org.rhq.core.util.StringUtil; import org.rhq.core.util.exception.ExceptionPackage; /** * Bridge between the plugin's calls back into the PC from an agent-side plugin and the {@link OperationManager} itself. * * @author Jason Dobies */ public class OperationServicesAdapter implements OperationServices, OperationServerService { /** * Reference to the plugin's operation manager, which will do the actual work of invoking an operation. */ private OperationManager operationManager; /** * The call to invoke the operation will block on this object until the response comes back from the * OperationManager. */ private final Object callbackLock = new Object(); /** * When an operation is completed, its result will be put in this map. When the thread that called the operation * wakes up, it will check this map for its job ID. If it is in here, it will know the job has finished and return * to the caller. If the job ID is not in this map, the operation is still going on and the thread will go back to * sleep. */ private Map<String, OperationServicesResult> completedJobs = new HashMap<String, OperationServicesResult>(); public OperationServicesAdapter(OperationManager operationManager) { this.operationManager = operationManager; } public OperationServicesResult invokeOperation(OperationContext context, String operationName, Configuration operationParameters, long timeout) { if (timeout < 1) { throw new IllegalArgumentException("timeout must be greater than zero"); } OperationContextImpl contextImpl = (OperationContextImpl) context; // OperationManager will check the parameter configuration for the timeout, so if the plugin specified a // timeout, stuff it in the parameter config (making it if it doesn't exist) if (timeout > 0) { if (operationParameters == null) { operationParameters = new Configuration(); } operationParameters.put(new PropertySimple(OperationDefinition.TIMEOUT_PARAM_NAME, timeout)); } String jobId = "OperationServicesAdapter." + System.currentTimeMillis(); synchronized (callbackLock) { try { operationManager.invokeOperation(jobId, contextImpl.getResourceId(), operationName, operationParameters, this); // If the plugin executes multiple operations at the same time, the notify may be indicating a different // job has completed than the one being waited on. while (!completedJobs.containsKey(jobId)) { callbackLock.wait(); } } catch (Exception e) { OperationServicesResult result = new OperationServicesResult(OperationServicesResultCode.FAILURE); result.setErrorStackTrace(StringUtil.getStackTrace(e)); return result; } } OperationServicesResult result = completedJobs.get(jobId); completedJobs.remove(jobId); return result; } public void operationSucceeded(String jobId, Configuration result, long invocationTime, long completionTime) { OperationServicesResult operationServicesResult = new OperationServicesResult( OperationServicesResultCode.SUCCESS); operationServicesResult.setComplexResults(result); completedJobs.put(jobId, operationServicesResult); synchronized (callbackLock) { callbackLock.notifyAll(); } } public void operationFailed(String jobId, Configuration result, ExceptionPackage error, long invocationTime, long completionTime) { OperationServicesResult operationServicesResult = new OperationServicesResult( OperationServicesResultCode.FAILURE); operationServicesResult.setComplexResults(result); operationServicesResult.setErrorStackTrace(error.getStackTraceString()); completedJobs.put(jobId, operationServicesResult); synchronized (callbackLock) { callbackLock.notifyAll(); } } public void operationCanceled(String jobId, Configuration result, ExceptionPackage error, long invocationTime, long canceledTime) { OperationServicesResult operationServicesResult = new OperationServicesResult( OperationServicesResultCode.CANCELED); operationServicesResult.setComplexResults(result); operationServicesResult.setErrorStackTrace(error.getStackTraceString()); completedJobs.put(jobId, operationServicesResult); synchronized (callbackLock) { callbackLock.notifyAll(); } } public void operationTimedOut(String jobId, long invocationTime, long timeoutTime) { OperationServicesResult operationServicesResult = new OperationServicesResult( OperationServicesResultCode.TIMED_OUT); completedJobs.put(jobId, operationServicesResult); synchronized (callbackLock) { callbackLock.notifyAll(); } } }