001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: welker $' 006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 007 * '$Revision: 24234 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.actor.ssh; 031 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.kepler.ssh.ExecException; 035import org.kepler.ssh.ExecFactory; 036import org.kepler.ssh.ExecInterface; 037 038import ptolemy.actor.TypedAtomicActor; 039import ptolemy.actor.TypedIOPort; 040import ptolemy.actor.parameters.PortParameter; 041import ptolemy.data.BooleanToken; 042import ptolemy.data.StringToken; 043import ptolemy.data.expr.Parameter; 044import ptolemy.data.type.BaseType; 045import ptolemy.kernel.CompositeEntity; 046import ptolemy.kernel.util.IllegalActionException; 047import ptolemy.kernel.util.NameDuplicationException; 048 049////////////////////////////////////////////////////////////////////////// 050//// FileRemover 051/** 052 * <p> 053 * Connects to a remote host using Ssh protocol (or does nothing for the local 054 * host) and deletes files/directories matching a given mask. 055 * 056 * </p> 057 * <p> 058 * This actor uses the org.kepler.ssh package for longlasting connections. If 059 * the host is empty string or equals "local", the Java Runtime will be used for 060 * local execution instead of ssh. 061 * 062 * </p> 063 * <p> 064 * The input should define: 065 * <ul> 066 * <li>the target machine, either "" or "local" to denote the local machine to 067 * be used by Java I/O commands, OR "[user@]host[:port]" to denote a remote 068 * machine to be used by an ssh connection.</li> 069 * <li>the file mask to be deleted (given as String).</li> 070 * <li>The flag 'recursive' should be set if you want to delete directories.</li> 071 * <li>The flag 'allowMask' should be set if you want to use wildcards.</li> 072 * </ul> 073 * 074 * </p> 075 * <p> 076 * A file mask can contain wildcards in the path expressions as well as in the 077 * file name part. E.g. /path/d*r/../sub??/./f*.txt is a valid expression. In 078 * case of remote operations, the command will be actually the 'rm -rf' command 079 * (without -r if 'recursive' is not set). 080 * 081 * </p> 082 * <p> 083 * A relative path in the file mask is relative to the home directory in case of 084 * remote operations and relative to the current directory in case of local 085 * operations. 086 * 087 * </p> 088 * <p> 089 * Note, that symbolic links will also be deleted, but if they are referring to 090 * a directory, they are not followed. This is how 'rm -rf' works and the local 091 * version implements the same behaviour. 092 * 093 * </p> 094 * <p> 095 * This actor produces a Boolean token on 'succ' port. TRUE indicates successful 096 * operation, while false indicates an error. The actor also produces a String 097 * token on the 'error' port; an empty string on success, internal error 098 * messages on failure. 099 * </p> 100 * 101 * @author Norbert Podhorszki 102 * @version $Revision: 24234 $ 103 * @category.name remote 104 * @category.name connection 105 * @category.name file operation 106 */ 107 108public class FileRemover extends TypedAtomicActor { 109 110 /** 111 * Construct an FileRemover actor with the given container and name. Create 112 * the parameters, initialize their values. 113 * 114 * @param container 115 * The container. 116 * @param name 117 * The name of this actor. 118 * @exception IllegalActionException 119 * If the entity cannot be contained by the proposed 120 * container. 121 * @exception NameDuplicationException 122 * If the container already has an actor with this name. 123 */ 124 public FileRemover(CompositeEntity container, String name) 125 throws NameDuplicationException, IllegalActionException { 126 super(container, name); 127 128 /* 129 * Input ports 130 */ 131 132 // target machine 133 target = new PortParameter(this, "target", new StringToken( 134 "[user@]host[:port]")); 135 new Parameter(target.getPort(), "_showName", BooleanToken.TRUE); 136 137 // file mask 138 mask = new PortParameter(this, "mask", new StringToken("none.txt")); 139 new Parameter(mask.getPort(), "_showName", BooleanToken.TRUE); 140 141 // recursive parameter 142 recursive = new Parameter(this, "recursive", new BooleanToken(false)); 143 recursive.setTypeEquals(BaseType.BOOLEAN); 144 145 // allowMask parameter 146 allowMask = new Parameter(this, "allowMask", new BooleanToken(false)); 147 allowMask.setTypeEquals(BaseType.BOOLEAN); 148 149 /* 150 * Output ports 151 */ 152 153 succ = new TypedIOPort(this, "succ", false, true); 154 succ.setTypeEquals(BaseType.BOOLEAN); 155 new Parameter(succ, "_showName", BooleanToken.TRUE); 156 157 error = new TypedIOPort(this, "error", false, true); 158 error.setTypeEquals(BaseType.STRING); 159 new Parameter(error, "_showName", BooleanToken.TRUE); 160 161 } 162 163 // //////////////// Public ports and parameters /////////////////////// 164 165 /** 166 * Target in user@host:port format. If user is not provided, the local 167 * username will be used. If port is not provided, the default port 22 will 168 * be applied. If host is "local" or empty string, the path is handled as 169 * local path. 170 */ 171 public PortParameter target; 172 173 /** 174 * File mask as String. Path expressions are allowed. 175 */ 176 public PortParameter mask; 177 178 /** 179 * The flag of successful removal. It will be true if ALL matched files and 180 * directories are deleted. If 'recursive' is not set and directories are 181 * also matched, the value will be false. Note, that files will be still 182 * removed in the latter case. It is a port of type Boolean token. 183 */ 184 public TypedIOPort succ; 185 186 /** 187 * The string representation of all the errors that happened during the 188 * execution of the actor, if there are any. A port of type String token. 189 */ 190 public TypedIOPort error; 191 192 /** 193 * Specifying whether directories can be removed recursively. 194 */ 195 public Parameter recursive; 196 197 /** 198 * Specifying whether wildcards (* and ?) are allowed in the mask. 199 */ 200 public Parameter allowMask; 201 202 // ///////////////////////////////////////////////////////////////// 203 // // public methods //// 204 205 /** 206 * Perform copying. 207 * 208 * @exception IllegalActionException 209 * If it is thrown by the send() method sending out the 210 * token. 211 */ 212 public void fire() throws IllegalActionException { 213 super.fire(); 214 215 // get inputs 216 target.update(); 217 String strTarget = ((StringToken) target.getToken()).stringValue() 218 .trim(); 219 220 mask.update(); 221 String strMask = ((StringToken) mask.getToken()).stringValue().trim(); 222 223 boolean recursiveFlag = ((BooleanToken) recursive.getToken()) 224 .booleanValue(); 225 boolean allowFlag = ((BooleanToken) allowMask.getToken()) 226 .booleanValue(); 227 228 229 // execute the file removal command 230 boolean result = false; 231 try { 232 // select the appropriate execution setting for the current source and 233 // target 234 ExecInterface execObj = ExecFactory.getExecObject(strTarget); 235 result = execObj.deleteFile(strMask, recursiveFlag, allowFlag); 236 237 } catch (ExecException e) { 238 String errText = new String("Error at execution:\n" 239 + e.getMessage()); 240 241 log.error(errText); 242 succ.send(0, new BooleanToken(false)); 243 error.send(0, new StringToken(errText)); 244 return; 245 } 246 247 // report 248 if (result) { 249 // finally, good news can be reported 250 succ.send(0, new BooleanToken(true)); 251 error.send(0, new StringToken("")); 252 } else { 253 String errText = new String( 254 "Some file(s) were not deleted for unknown reasons on host " 255 + strTarget + " with mask " + strMask 256 + " with flags recursive=" + recursiveFlag 257 + " and allowMask=" + allowFlag); 258 log.warn(errText); 259 succ.send(0, new BooleanToken(false)); 260 error.send(0, new StringToken(errText)); 261 } 262 263 } // end-method fire() 264 265 private static final Log log = LogFactory.getLog(FileRemover.class 266 .getName()); 267 private static final boolean isDebugging = log.isDebugEnabled(); 268}