/* * 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.brooklyn.util.core.task.ssh; import java.util.Arrays; import java.util.concurrent.Callable; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.api.mgmt.TaskWrapper; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.internal.ssh.SshTool; import org.apache.brooklyn.util.core.task.TaskBuilder; import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; /** As {@link ProcessTaskWrapper}, but putting a file on the remote machine */ @Beta public class SshPutTaskWrapper extends SshPutTaskStub implements TaskWrapper<Void> { private static final Logger log = LoggerFactory.getLogger(SshPutTaskWrapper.class); private final Task<Void> task; protected Integer exitCodeOfCopy = null; protected Exception exception = null; protected boolean successful = false; // package private as only AbstractSshTaskFactory should invoke SshPutTaskWrapper(SshPutTaskFactory constructor) { super(constructor); TaskBuilder<Void> tb = TaskBuilder.<Void>builder().dynamic(false).displayName(getSummary()); task = tb.body(new SshPutJob()).build(); } @Override public Task<Void> asTask() { return getTask(); } @Override public Task<Void> getTask() { return task; } // TODO: // verify // copyAsRoot // owner // lastModificationDate - see {@link #PROP_LAST_MODIFICATION_DATE}; not supported by all SshTool implementations // lastAccessDate - see {@link #PROP_LAST_ACCESS_DATE}; not supported by all SshTool implementations private class SshPutJob implements Callable<Void> { @Override public Void call() throws Exception { try { Preconditions.checkNotNull(getMachine(), "machine"); String remoteFile = getRemoteFile(); if (createDirectory) { String remoteDir = remoteFile; int exitCodeOfCreate = -1; try { int li = remoteDir.lastIndexOf("/"); if (li>=0) { remoteDir = remoteDir.substring(0, li+1); exitCodeOfCreate = getMachine().execCommands("creating directory for "+getSummary(), Arrays.asList("mkdir -p "+remoteDir)); } else { // nothing to create exitCodeOfCreate = 0; } } catch (Exception e) { if (log.isDebugEnabled()) log.debug("SSH put "+getRemoteFile()+" (create dir, in task "+getSummary()+") to "+getMachine()+" threw exception: "+e); exception = e; } if (exception!=null || !((Integer)0).equals(exitCodeOfCreate)) { if (!allowFailure) { if (exception != null) { throw new IllegalStateException(getSummary()+" (creating dir "+remoteDir+" for SSH put task) ended with exception, in "+Tasks.current()+": "+exception, exception); } if (exitCodeOfCreate!=0) { exception = new IllegalStateException(getSummary()+" (creating dir "+remoteDir+" SSH put task) ended with exit code "+exitCodeOfCreate+", in "+Tasks.current()); throw exception; } } // not successful, but allowed return null; } } ConfigBag config = ConfigBag.newInstanceCopying(getConfig()); if (permissions!=null) config.put(SshTool.PROP_PERMISSIONS, permissions); exitCodeOfCopy = getMachine().copyTo(config.getAllConfig(), contents.get(), remoteFile); if (log.isDebugEnabled()) log.debug("SSH put "+getRemoteFile()+" (task "+getSummary()+") to "+getMachine()+" completed with exit code "+exitCodeOfCopy); } catch (Exception e) { if (log.isDebugEnabled()) log.debug("SSH put "+getRemoteFile()+" (task "+getSummary()+") to "+getMachine()+" threw exception: "+e); exception = e; } if (exception!=null || !((Integer)0).equals(exitCodeOfCopy)) { if (!allowFailure) { if (exception != null) { throw new IllegalStateException(getSummary()+" (SSH put task) ended with exception, in "+Tasks.current()+": "+exception, exception); } if (exitCodeOfCopy!=0) { exception = new IllegalStateException(getSummary()+" (SSH put task) ended with exit code "+exitCodeOfCopy+", in "+Tasks.current()); throw exception; } } // not successful, but allowed return null; } // TODO verify successful = (exception==null && ((Integer)0).equals(exitCodeOfCopy)); return null; } } @Override public String toString() { return super.toString()+"["+task+"]"; } /** blocks, throwing if there was an exception */ public Void get() { return getTask().getUnchecked(); } /** returns the exit code from the copy, 0 on success; * null if it has not completed or threw exception * (not sure if this is ever a non-zero integer or if it is meaningful) * <p> * most callers will want the simpler {@link #isSuccessful()} */ public Integer getExitCode() { return exitCodeOfCopy; } /** returns any exception encountered in the operation */ public Exception getException() { return exception; } /** blocks until the task completes; does not throw */ public SshPutTaskWrapper block() { getTask().blockUntilEnded(); return this; } /** true iff the ssh job has completed (with or without failure) */ public boolean isDone() { return getTask().isDone(); } /** true iff the scp has completed successfully; guaranteed to be set before {@link #isDone()} or {@link #block()} are satisfied */ public boolean isSuccessful() { return successful; } }