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.io; 031 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.kepler.io.DirectoryListing; 035import org.kepler.io.FileInfo; 036import org.kepler.ssh.ExecException; 037 038import ptolemy.actor.TypedAtomicActor; 039import ptolemy.actor.TypedIOPort; 040import ptolemy.actor.parameters.PortParameter; 041import ptolemy.data.ArrayToken; 042import ptolemy.data.BooleanToken; 043import ptolemy.data.LongToken; 044import ptolemy.data.RecordToken; 045import ptolemy.data.StringToken; 046import ptolemy.data.Token; 047import ptolemy.data.expr.Parameter; 048import ptolemy.data.type.ArrayType; 049import ptolemy.data.type.BaseType; 050import ptolemy.data.type.RecordType; 051import ptolemy.data.type.Type; 052import ptolemy.kernel.CompositeEntity; 053import ptolemy.kernel.util.IllegalActionException; 054import ptolemy.kernel.util.NameDuplicationException; 055 056////////////////////////////////////////////////////////////////////////// 057//// SshDirectoryList 058 059/** 060 * <p> 061 * <b>Obsolete actor.</b> Use SshDirectoryList-v1.1 instead. 062 * </p> 063 * <p> 064 * Actor for directory listing. 065 * <ul> 066 * <li>local or remote dir (using ssh)</li> 067 * <li>get new files from previous call</li> 068 * </ul> 069 * 070 * </p> 071 * <p> 072 * This actor uses org.kepler.io.DirectoryListing class to get file listing 073 * 074 * </p> 075 * <p> 076 * The initial input should be: 077 * <ul> 078 * <li>the target machine, either null, "" or "local" to denote the local 079 * machine to be used by Java I/O commands, OR "[user@]host[:port]" to denote a 080 * remote machine to be used by an ssh connection.</li> 081 * <li>the directory to be listed as String</li> 082 * <li>the file mask for listing files according to the pattern only, "" means 083 * all files</li> 084 * </ul> 085 * 086 * </p> 087 * <p> 088 * The actor takes the target and directory arguments only once! After that it 089 * works with that setting at each fire. The mask can be reset to something else 090 * for each firing but in such a case, the previous listings are deleted (as if 091 * starting fresh). 092 * 093 * </p> 094 * <p> 095 * Depending on the boolean parameter 'newFilesOnly' there are two different 096 * behavior: <br/> 097 * If 'newFilesOnly' is set (default), for each trigger, the file list is 098 * updated, and the array of 'new' files will be the output. 'New' means the 099 * difference between the current and the previous listings. <br/> 100 * If 'newFilesOnly' is not set, for each trigger, the whole list is returned. 101 * This can be used for watching a specific file and look for changes in size or 102 * access time of the file. 103 * 104 * </p> 105 * <p> 106 * Depending on the boolean parameter 'checkSizeAndDate' the meaning 'new' is 107 * handled as following: <br/> 108 * If 'checkSizeAndDate' is set, modified files are also listed besides brand 109 * new files. I.e., a new file is that is not found in previous listing OR the 110 * size or date of the file has changed between the previous and current 111 * listings. <br/> 112 * If 'checkSizeAndDate' is not set (default), only brand new files are listed. 113 * I.e. only the file names are checked, and file modifications have no effect. 114 * 115 * </p> 116 * <p> 117 * The files are listed in the following format: Each element in the array will 118 * be a RecordToken: {name=String, size=long, date=long}, where the size is 119 * given in bytes, the date is given in UTC seconds. The resolution depends on 120 * the 'ls -l' output, i.e. at most a minute, and for old files a day. In the 121 * local case, it depends on the resolution of Java and the local OS, usually 122 * less than a millisecond. 123 * 124 * </p> 125 * <p> 126 * On error, an empty string is returned. 127 * 128 * </p> 129 * <p> 130 * Note: even if you pass a token to the mask port parameter, you must pass a 131 * token to the trigger port as well anyway. 132 * </p> 133 * 134 * @author Norbert Podhorszki 135 * @version $Id: SshDirectoryList.java 24234 2010-05-06 05:21:26Z welker $ 136 * @since Ptolemy II 5.0.1 137 */ 138public class SshDirectoryList extends TypedAtomicActor { 139 /** 140 * Construct an actor with the given container and name. 141 * 142 * @param container 143 * The container. 144 * @param name 145 * The name of this actor. 146 * @exception IllegalActionException 147 * If the actor cannot be contained by the proposed 148 * container. 149 * @exception NameDuplicationException 150 * If the container already has an actor with this name. 151 */ 152 public SshDirectoryList(CompositeEntity container, String name) 153 throws NameDuplicationException, IllegalActionException { 154 super(container, name); 155 156 // Uncomment the next line to see debugging statements 157 // addDebugListener(new ptolemy.kernel.util.StreamListener()); 158 159 // target selects the machine where the directory is to be accessed 160 target = new PortParameter(this, "target", new StringToken( 161 "[ local | [user@]host[:port] ]")); 162 new Parameter(target.getPort(), "_showName", BooleanToken.TRUE); 163 164 // dir is the path to the directory to be listed on the target machine 165 dir = new PortParameter(this, "dir", new StringToken("/path/to/dir")); 166 new Parameter(dir.getPort(), "_showName", BooleanToken.TRUE); 167 168 // mask is the file mask for the files to be listed 169 mask = new PortParameter(this, "mask", new StringToken("*")); 170 new Parameter(mask.getPort(), "_showName", BooleanToken.TRUE); 171 172 // a trigger to read it again 173 trigger = new TypedIOPort(this, "trigger", true, false); 174 trigger.setTypeEquals(BaseType.UNKNOWN); 175 new Parameter(trigger, "_showName", BooleanToken.FALSE); 176 177 // new files only or all? 178 newFilesOnly = new Parameter(this, "newFilesOnly", new BooleanToken( 179 true)); 180 newFilesOnly.setTypeEquals(BaseType.BOOLEAN); 181 182 // new files only or all? 183 checkSizeAndDate = new Parameter(this, "checkSizeAndDate", 184 new BooleanToken(false)); 185 checkSizeAndDate.setTypeEquals(BaseType.BOOLEAN); 186 187 // the output: an array of filenames 188 newFiles = new TypedIOPort(this, "newFiles", false, true); 189 String[] labels = { "name", "size", "date" }; 190 Type[] ctypes = { BaseType.STRING, BaseType.LONG, BaseType.LONG }; 191 _etype = new RecordType(labels, ctypes); 192 newFiles.setTypeEquals(new ArrayType(_etype)); 193 // newFiles.setTypeAtMost(new RecordType(new String[0], new Type[0])); 194 // newFiles.setTypeEquals(new ArrayType(BaseType.UNKNOWN)); 195 // newFiles.setTypeEquals(new BaseType.UNKNOWN); 196 new Parameter(newFiles, "_showName", BooleanToken.FALSE); 197 } 198 199 /*********************************************************** 200 * ports and parameters 201 */ 202 203 /** 204 * The machine to be used at job submission. It should be null, "" or 205 * "local" for the local machine or [user@]host[:port] to denote a remote 206 * machine accessible with ssh. 207 * 208 * This parameter is read once at initialize. 209 */ 210 public PortParameter target; 211 212 /** 213 * The path to the directory to be read on the target machines. This 214 * parameter is read once at initialize. 215 */ 216 public PortParameter dir; 217 218 /** 219 * The file mask for listing only such files. This parameter is read once at 220 * initialize. 221 */ 222 public PortParameter mask; 223 224 /** 225 * The trigger port to do the reading. This port is an output port of type 226 * ArrayToken. 227 */ 228 public TypedIOPort trigger; 229 230 /** 231 * Specifying whether the output should contain only new files or all. 'New' 232 * means the difference in the current and previous listing. 233 */ 234 public Parameter newFilesOnly; 235 236 /** 237 * Specifying whether the output should contain modified files as well, or 238 * only brand new files. 239 */ 240 public Parameter checkSizeAndDate; 241 242 /** 243 * The output: array of new files since the previous read. This port is an 244 * output port of type ArrayToken. Each element is a RecordToken of 245 * {name=String, size=long, date=long} 246 */ 247 public TypedIOPort newFiles; 248 249 /*********************************************************** 250 * public methods 251 */ 252 253 /** 254 * initialize() runs once before first exec 255 * 256 * @exception IllegalActionException 257 * If the parent class throws it. 258 */ 259 public void initialize() throws IllegalActionException { 260 super.initialize(); 261 _firstFire = true; 262 } 263 264 /** 265 * fire 266 * 267 * @exception IllegalActionException 268 */ 269 public void fire() throws IllegalActionException { 270 super.fire(); 271 272 // consume the trigger token 273 trigger.get(0); 274 275 FileInfo[] _newFiles = null; 276 if (_firstFire) { 277 // update PortParameters 278 target.update(); 279 dir.update(); 280 mask.update(); 281 282 _target = ((StringToken) target.getToken()).stringValue(); 283 _dir = ((StringToken) dir.getToken()).stringValue(); 284 _mask = ((StringToken) mask.getToken()).stringValue(); 285 _prevmask = _mask; 286 _newOnly = ((BooleanToken) newFilesOnly.getToken()).booleanValue(); 287 _checkModifications = ((BooleanToken) checkSizeAndDate.getToken()) 288 .booleanValue(); 289 290 if (isDebugging) 291 log.debug("Create DirectoryListing object: " + "target = " 292 + _target + "; dir = " + _dir + "; filemask = " + _mask 293 + "; newFilesOnly = " + _newOnly 294 + "; checkSizeAndDate = " + _checkModifications); 295 296 String masks[] = new String[1]; 297 masks[0] = _mask; 298 _dl = new DirectoryListing(_target, _dir, masks); 299 300 _firstFire = false; 301 } else { 302 // mask can be reset 303 mask.update(); 304 _mask = ((StringToken) mask.getToken()).stringValue(); 305 if (!_mask.equals(_prevmask)) { 306 String masks[] = new String[1]; 307 masks[0] = _mask; 308 _dl.setMask(masks); 309 _prevmask = _mask; 310 } 311 } 312 313 try { 314 // list directory 315 int n = _dl.list(); 316 if (isDebugging) 317 log.debug("Number of total files = " + n); 318 if (n > 0) { 319 // get new files 320 if (_newOnly) 321 _newFiles = _dl.getNewFiles(_checkModifications); 322 else 323 _newFiles = _dl.getList(); 324 325 if (isDebugging || _newFiles.length > 0) 326 log.info("Number of " + (_newOnly ? "NEW" : "") 327 + " files = " + _newFiles.length); 328 } 329 330 } catch (ExecException ex) { 331 log.error("SshDirectoryList error at remote directory reading. " 332 + ex); 333 } 334 335 // create the result 336 ArrayToken at; 337 if (_newFiles != null && _newFiles.length > 0) { 338 RecordToken[] rt = new RecordToken[_newFiles.length]; 339 for (int i = 0; i < _newFiles.length; i++) { 340 rt[i] = createRecordToken(_newFiles[i]); 341 // newFiles.send(0, rt[i]); 342 } 343 at = new ArrayToken(_etype, rt); 344 } 345 // else at = ArrayToken.NIL; 346 else 347 at = new ArrayToken(_etype, new RecordToken[0]); 348 349 newFiles.send(0, at); 350 } 351 352 /* 353 * Create one RecordToken of format {name=String, size=long, date=long} from 354 * the FileInfo struct. 355 */ 356 private RecordToken createRecordToken(FileInfo fi) { 357 String[] labels = { "name", "size", "date" }; 358 Token[] values = new Token[3]; 359 values[0] = new StringToken(fi.getName()); 360 values[1] = new LongToken(fi.getSize()); 361 values[2] = new LongToken(fi.getDate()); 362 RecordToken rt = null; 363 try { 364 rt = new RecordToken(labels, values); 365 } catch (IllegalActionException ex) { 366 log 367 .error("SshDirectoryList: Error at creating a record token for fileinfo: " 368 + fi 369 + "\nlabels = " 370 + labels 371 + "\nvalues = " 372 + values); 373 } 374 return rt; 375 } 376 377 private boolean _firstFire; 378 private String _target; 379 private String _dir; 380 private String _mask; 381 private String _prevmask; 382 private boolean _newOnly; 383 private boolean _checkModifications; 384 private DirectoryListing _dl; 385 private Type _etype; 386 387 private static final Log log = LogFactory.getLog(SshDirectoryList.class 388 .getName()); 389 private static final boolean isDebugging = log.isDebugEnabled(); 390 391}