/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.airavata.gfac.impl; import org.apache.airavata.common.utils.AiravataUtils; import org.apache.airavata.credential.store.store.CredentialStoreException; import org.apache.airavata.gfac.core.GFacEngine; import org.apache.airavata.gfac.core.GFacException; import org.apache.airavata.gfac.core.GFacUtils; import org.apache.airavata.gfac.core.context.ProcessContext; import org.apache.airavata.model.commons.ErrorModel; import org.apache.airavata.model.status.ProcessState; import org.apache.airavata.model.status.ProcessStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.PrintWriter; import java.io.StringWriter; import java.text.MessageFormat; import java.util.List; public class GFacWorker implements Runnable { private static final Logger log = LoggerFactory.getLogger(GFacWorker.class); private GFacEngine engine; private ProcessContext processContext; private String processId; private String gatewayId; private String tokenId; private boolean continueTaskFlow = false; /** * This will be called by monitoring service. */ public GFacWorker(ProcessContext processContext) throws GFacException { if (processContext == null) { throw new GFacException("Worker must initialize with valid processContext, Process context is null"); } this.processId = processContext.getProcessId(); this.gatewayId = processContext.getGatewayId(); this.tokenId = processContext.getTokenId(); engine = Factory.getGFacEngine(); this.processContext = processContext; continueTaskFlow = true; } /** * This constructor will be called when new or recovery request comes. */ public GFacWorker(String processId, String gatewayId, String tokenId) throws GFacException, CredentialStoreException { this.processId = processId; this.gatewayId = gatewayId; this.tokenId = tokenId; engine = Factory.getGFacEngine(); this.processContext = engine.populateProcessContext(processId, gatewayId, tokenId); Factory.getGfacContext().addProcess(this.processContext); } @Override public void run() { try { ProcessState processState = processContext.getProcessState(); switch (processState) { case CREATED: case VALIDATED: case STARTED: executeProcess(); break; case PRE_PROCESSING: case CONFIGURING_WORKSPACE: case INPUT_DATA_STAGING: case EXECUTING: case MONITORING: case OUTPUT_DATA_STAGING: case POST_PROCESSING: if (continueTaskFlow) { continueTaskExecution(); } else { recoverProcess(); } break; case COMPLETED: completeProcess(); break; case CANCELLING: cancelProcess(); break; case CANCELED: // TODO - implement cancel scenario break; case FAILED: // TODO - implement failed scenario break; default: throw new GFacException("process Id : " + processId + " Couldn't identify process type"); } if (processContext.isCancel()) { processState = processContext.getProcessState(); switch (processState) { case MONITORING: case EXECUTING: // don't send ack if the process is in MONITORING or EXECUTING states, wait until cancel email comes to airavata break; case CANCELLING: cancelProcess(); break; default: sendAck(); Factory.getGfacContext().removeProcess(processContext.getProcessId()); break; } } } catch (GFacException e) { log.error("GFac Worker throws an exception", e); ProcessStatus status = new ProcessStatus(ProcessState.FAILED); status.setReason(e.getMessage()); status.setTimeOfStateChange(AiravataUtils.getCurrentTimestamp().getTime()); processContext.setProcessStatus(status); StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); ErrorModel errorModel = new ErrorModel(); errorModel.setUserFriendlyMessage("GFac Worker throws an exception"); errorModel.setActualErrorMessage(errors.toString()); errorModel.setCreationTime(AiravataUtils.getCurrentTimestamp().getTime()); try { GFacUtils.saveAndPublishProcessStatus(processContext); GFacUtils.saveExperimentError(processContext, errorModel); GFacUtils.saveProcessError(processContext, errorModel); } catch (GFacException e1) { log.error("expId: {}, processId: {} :- Couldn't save and publish process status {}", processContext .getExperimentId(), processContext.getProcessId(), processContext.getProcessState()); } sendAck(); } } private void cancelProcess() throws GFacException { // do cleanup works before cancel the process. ProcessStatus processStatus = new ProcessStatus(ProcessState.CANCELED); processStatus.setReason("Process cancellation has been triggered"); processContext.setProcessStatus(processStatus); GFacUtils.saveAndPublishProcessStatus(processContext); sendAck(); Factory.getGfacContext().removeProcess(processContext.getProcessId()); } private void completeProcess() throws GFacException { ProcessStatus status = new ProcessStatus(ProcessState.COMPLETED); status.setTimeOfStateChange(AiravataUtils.getCurrentTimestamp().getTime()); processContext.setProcessStatus(status); GFacUtils.saveAndPublishProcessStatus(processContext); sendAck(); Factory.getGfacContext().removeProcess(processContext.getProcessId()); } private void continueTaskExecution() throws GFacException { // checkpoint if (processContext.isInterrupted()) { return; } processContext.setPauseTaskExecution(false); List<String> taskExecutionOrder = processContext.getTaskExecutionOrder(); String currentExecutingTaskId = processContext.getCurrentExecutingTaskId(); boolean found = false; String nextTaskId = null; for (String taskId : taskExecutionOrder) { if (!found) { if (taskId.equalsIgnoreCase(currentExecutingTaskId)) { found = true; } continue; } else { nextTaskId = taskId; break; } } if (nextTaskId != null) { engine.continueProcess(processContext, nextTaskId); }else { processContext.setComplete(true); } // checkpoint if (processContext.isInterrupted()) { return; } if (processContext.isComplete()) { completeProcess(); } } private void recoverProcess() throws GFacException { engine.recoverProcess(processContext); if (processContext.isInterrupted()) { return; } if (processContext.isComplete()) { completeProcess(); } } private void executeProcess() throws GFacException { // checkpoint if (processContext.isInterrupted()) { return; } engine.executeProcess(processContext); // checkpoint if (processContext.isInterrupted()) { return; } if (processContext.isComplete()) { completeProcess(); } } private void sendAck() { // this ensure, gfac doesn't send ack more than once for a process. which cause to remove gfac rabbitmq consumer from rabbitmq server. if (!processContext.isAcknowledge()) { try { long processDeliveryTag = GFacUtils.getProcessDeliveryTag(processContext.getCuratorClient(), processContext.getExperimentId(), processId); Factory.getProcessLaunchSubscriber().sendAck(processDeliveryTag); processContext.setAcknowledge(true); log.info("expId: {}, processId: {} :- Sent ack for deliveryTag {}", processContext.getExperimentId(), processId, processDeliveryTag); } catch (Exception e1) { processContext.setAcknowledge(false); String format = MessageFormat.format("expId: {0}, processId: {1} :- Couldn't send ack for deliveryTag ", processContext .getExperimentId(), processId); log.error(format, e1); } } else { log.info("expId: {}, processId: {} :- already acknowledged ", processContext.getExperimentId(), processId); } } }