1 /* ============================================================ 2 * JRobin : Pure java implementation of RRDTool's functionality 3 * ============================================================ 4 * 5 * Project Info: http://www.jrobin.org 6 * Project Lead: Sasa Markovic (saxon@jrobin.org); 7 * 8 * (C) Copyright 2003-2005, by Sasa Markovic. 9 * 10 * Developers: Sasa Markovic (saxon@jrobin.org) 11 * 12 * 13 * This library is free software; you can redistribute it and/or modify it under the terms 14 * of the GNU Lesser General Public License as published by the Free Software Foundation; 15 * either version 2.1 of the License, or (at your option) any later version. 16 * 17 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 18 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 19 * See the GNU Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public License along with this 22 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 23 * Boston, MA 02111-1307, USA. 24 */ 25 26 package org.jrobin.core; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.util.List; 31 import java.util.LinkedList; 32 import java.util.Arrays; 33 34 /** 35 * <p>Class used to perform various complex operations on RRD files. Use an instance of the 36 * RrdToolkit class to:</p> 37 * <ul> 38 * <li>add datasource to a RRD file. 39 * <li>add archive to a RRD file. 40 * <li>remove datasource from a RRD file. 41 * <li>remove archive from a RRD file. 42 * </ul> 43 * <p>All these operations can be performed on the copy of the original RRD file, or on the 44 * original file itself (with possible backup file creation)</p> 45 * <p/> 46 * <p><b><u>IMPORTANT</u></b>: NEVER use methods found in this class on 'live' RRD files 47 * (files which are currently in use).</p> 48 */ 49 public class RrdToolkit { 50 /** 51 * Creates a new RRD file with one more datasource in it. RRD file is created based on the 52 * existing one (the original RRD file is not modified at all). All data from 53 * the original RRD file is copied to the new one. 54 * 55 * @param sourcePath path to a RRD file to import data from (will not be modified) 56 * @param destPath path to a new RRD file (will be created) 57 * @param newDatasource Datasource definition to be added to the new RRD file 58 * @throws IOException Thrown in case of I/O error 59 * @throws RrdException Thrown in case of JRobin specific error 60 */ 61 public static void addDatasource(String sourcePath, String destPath, DsDef newDatasource) 62 throws IOException, RrdException { 63 if (Util.sameFilePath(sourcePath, destPath)) { 64 throw new RrdException("Source and destination paths are the same"); 65 } 66 RrdDb rrdSource = new RrdDb(sourcePath); 67 try { 68 RrdDef rrdDef = rrdSource.getRrdDef(); 69 rrdDef.setPath(destPath); 70 rrdDef.addDatasource(newDatasource); 71 RrdDb rrdDest = new RrdDb(rrdDef); 72 try { 73 rrdSource.copyStateTo(rrdDest); 74 } 75 finally { 76 rrdDest.close(); 77 } 78 } 79 finally { 80 rrdSource.close(); 81 } 82 } 83 84 /** 85 * <p>Adds one more datasource to a RRD file.</p> 86 * <p>WARNING: This method is potentialy dangerous! It will modify your RRD file. 87 * It is highly recommended to preserve the original RRD file (<i>saveBackup</i> 88 * should be set to <code>true</code>). The backup file will be created in the same 89 * directory as the original one with <code>.bak</code> extension added to the 90 * original name.</p> 91 * <p>Before applying this method, be sure that the specified RRD file is not in use 92 * (not open)</p> 93 * 94 * @param sourcePath path to a RRD file to add datasource to. 95 * @param newDatasource Datasource definition to be added to the RRD file 96 * @param saveBackup true, if backup of the original file should be created; 97 * false, otherwise 98 * @throws IOException Thrown in case of I/O error 99 * @throws RrdException Thrown in case of JRobin specific error 100 */ 101 public static void addDatasource(String sourcePath, DsDef newDatasource, boolean saveBackup) 102 throws IOException, RrdException { 103 String destPath = Util.getTmpFilename(); 104 addDatasource(sourcePath, destPath, newDatasource); 105 copyFile(destPath, sourcePath, saveBackup); 106 } 107 108 /** 109 * Creates a new RRD file with one datasource removed. RRD file is created based on the 110 * existing one (the original RRD file is not modified at all). All remaining data from 111 * the original RRD file is copied to the new one. 112 * 113 * @param sourcePath path to a RRD file to import data from (will not be modified) 114 * @param destPath path to a new RRD file (will be created) 115 * @param dsName Name of the Datasource to be removed from the new RRD file 116 * @throws IOException Thrown in case of I/O error 117 * @throws RrdException Thrown in case of JRobin specific error 118 */ 119 public static void removeDatasource(String sourcePath, String destPath, String dsName) 120 throws IOException, RrdException { 121 if (Util.sameFilePath(sourcePath, destPath)) { 122 throw new RrdException("Source and destination paths are the same"); 123 } 124 RrdDb rrdSource = new RrdDb(sourcePath); 125 try { 126 RrdDef rrdDef = rrdSource.getRrdDef(); 127 rrdDef.setPath(destPath); 128 rrdDef.removeDatasource(dsName); 129 RrdDb rrdDest = new RrdDb(rrdDef); 130 try { 131 rrdSource.copyStateTo(rrdDest); 132 } 133 finally { 134 rrdDest.close(); 135 } 136 } 137 finally { 138 rrdSource.close(); 139 } 140 } 141 142 /** 143 * <p>Removes single datasource from a RRD file.</p> 144 * <p>WARNING: This method is potentialy dangerous! It will modify your RRD file. 145 * It is highly recommended to preserve the original RRD file (<i>saveBackup</i> 146 * should be set to <code>true</code>). The backup file will be created in the same 147 * directory as the original one with <code>.bak</code> extension added to the 148 * original name.</p> 149 * <p>Before applying this method, be sure that the specified RRD file is not in use 150 * (not open)</p> 151 * 152 * @param sourcePath path to a RRD file to remove datasource from. 153 * @param dsName Name of the Datasource to be removed from the RRD file 154 * @param saveBackup true, if backup of the original file should be created; 155 * false, otherwise 156 * @throws IOException Thrown in case of I/O error 157 * @throws RrdException Thrown in case of JRobin specific error 158 */ 159 public static void removeDatasource(String sourcePath, String dsName, boolean saveBackup) 160 throws IOException, RrdException { 161 String destPath = Util.getTmpFilename(); 162 removeDatasource(sourcePath, destPath, dsName); 163 copyFile(destPath, sourcePath, saveBackup); 164 } 165 166 /** 167 * Renames single datasource in the given RRD file. 168 * 169 * @param sourcePath Path to a RRD file 170 * @param oldDsName Old datasource name 171 * @param newDsName New datasource name 172 * @throws IOException Thrown in case of I/O error 173 * @throws RrdException Thrown in case of JRobin specific error (invalid path or datasource names, 174 * for example) 175 */ 176 public static void renameDatasource(String sourcePath, String oldDsName, String newDsName) 177 throws IOException, RrdException { 178 RrdDb rrd = new RrdDb(sourcePath); 179 try { 180 if (rrd.containsDs(oldDsName)) { 181 Datasource datasource = rrd.getDatasource(oldDsName); 182 datasource.setDsName(newDsName); 183 } 184 else { 185 throw new RrdException("Could not find datasource [" + oldDsName + "] in file " + sourcePath); 186 } 187 } 188 finally { 189 rrd.close(); 190 } 191 } 192 193 /** 194 * Updates single or all datasource names in the specified RRD file 195 * by appending '!' (if not already present). Datasources with names ending with '!' 196 * will never store NaNs in RRA archives (zero value will be used instead). Might be useful 197 * from time to time 198 * 199 * @param sourcePath Path to a RRD file 200 * @param dsName Datasource name or null if you want to rename all datasources 201 * @return Number of datasources successfully renamed 202 * @throws IOException Thrown in case of I/O error 203 * @throws RrdException Thrown in case of JRobin specific error (invalid path or datasource name, 204 * for example) 205 */ 206 public static int forceZerosForNans(String sourcePath, String dsName) throws IOException, RrdException { 207 RrdDb rrd = new RrdDb(sourcePath); 208 try { 209 Datasource[] datasources; 210 if (dsName == null) { 211 datasources = rrd.getDatasources(); 212 } 213 else { 214 if (rrd.containsDs(dsName)) { 215 datasources = new Datasource[] {rrd.getDatasource(dsName)}; 216 } 217 else { 218 throw new RrdException("Could not find datasource [" + dsName + "] in file " + sourcePath); 219 } 220 } 221 int count = 0; 222 for (Datasource datasource : datasources) { 223 String currentDsName = datasource.getDsName(); 224 if (!currentDsName.endsWith(DsDef.FORCE_ZEROS_FOR_NANS_SUFFIX)) { 225 datasource.setDsName(currentDsName + DsDef.FORCE_ZEROS_FOR_NANS_SUFFIX); 226 count++; 227 } 228 } 229 return count; 230 } 231 finally { 232 rrd.close(); 233 } 234 } 235 236 /** 237 * Creates a new RRD file with one more archive in it. RRD file is created based on the 238 * existing one (the original RRD file is not modified at all). All data from 239 * the original RRD file is copied to the new one. 240 * 241 * @param sourcePath path to a RRD file to import data from (will not be modified) 242 * @param destPath path to a new RRD file (will be created) 243 * @param newArchive Archive definition to be added to the new RRD file 244 * @throws IOException Thrown in case of I/O error 245 * @throws RrdException Thrown in case of JRobin specific error 246 */ 247 public static void addArchive(String sourcePath, String destPath, ArcDef newArchive) 248 throws IOException, RrdException { 249 if (Util.sameFilePath(sourcePath, destPath)) { 250 throw new RrdException("Source and destination paths are the same"); 251 } 252 RrdDb rrdSource = new RrdDb(sourcePath); 253 try { 254 RrdDef rrdDef = rrdSource.getRrdDef(); 255 rrdDef.setPath(destPath); 256 rrdDef.addArchive(newArchive); 257 RrdDb rrdDest = new RrdDb(rrdDef); 258 try { 259 rrdSource.copyStateTo(rrdDest); 260 } 261 finally { 262 rrdDest.close(); 263 } 264 } 265 finally { 266 rrdSource.close(); 267 } 268 } 269 270 /** 271 * <p>Adds one more archive to a RRD file.</p> 272 * <p>WARNING: This method is potentialy dangerous! It will modify your RRD file. 273 * It is highly recommended to preserve the original RRD file (<i>saveBackup</i> 274 * should be set to <code>true</code>). The backup file will be created in the same 275 * directory as the original one with <code>.bak</code> extension added to the 276 * original name.</p> 277 * <p>Before applying this method, be sure that the specified RRD file is not in use 278 * (not open)</p> 279 * 280 * @param sourcePath path to a RRD file to add datasource to. 281 * @param newArchive Archive definition to be added to the RRD file 282 * @param saveBackup true, if backup of the original file should be created; 283 * false, otherwise 284 * @throws IOException Thrown in case of I/O error 285 * @throws RrdException Thrown in case of JRobin specific error 286 */ 287 public static void addArchive(String sourcePath, ArcDef newArchive, boolean saveBackup) 288 throws IOException, RrdException { 289 String destPath = Util.getTmpFilename(); 290 addArchive(sourcePath, destPath, newArchive); 291 copyFile(destPath, sourcePath, saveBackup); 292 } 293 294 /** 295 * Creates a new RRD file with one archive removed. RRD file is created based on the 296 * existing one (the original RRD file is not modified at all). All relevant data from 297 * the original RRD file is copied to the new one. 298 * 299 * @param sourcePath path to a RRD file to import data from (will not be modified) 300 * @param destPath path to a new RRD file (will be created) 301 * @param consolFun Consolidation function of Archive which should be removed 302 * @param steps Number of steps for Archive which should be removed 303 * @throws IOException Thrown in case of I/O error 304 * @throws RrdException Thrown in case of JRobin specific error 305 */ 306 public static void removeArchive(String sourcePath, String destPath, String consolFun, int steps) 307 throws IOException, RrdException { 308 if (Util.sameFilePath(sourcePath, destPath)) { 309 throw new RrdException("Source and destination paths are the same"); 310 } 311 RrdDb rrdSource = new RrdDb(sourcePath); 312 try { 313 RrdDef rrdDef = rrdSource.getRrdDef(); 314 rrdDef.setPath(destPath); 315 rrdDef.removeArchive(consolFun, steps); 316 RrdDb rrdDest = new RrdDb(rrdDef); 317 try { 318 rrdSource.copyStateTo(rrdDest); 319 } 320 finally { 321 rrdDest.close(); 322 } 323 } 324 finally { 325 rrdSource.close(); 326 } 327 } 328 329 /** 330 * <p>Removes one archive from a RRD file.</p> 331 * <p>WARNING: This method is potentialy dangerous! It will modify your RRD file. 332 * It is highly recommended to preserve the original RRD file (<i>saveBackup</i> 333 * should be set to <code>true</code>). The backup file will be created in the same 334 * directory as the original one with <code>.bak</code> extension added to the 335 * original name.</p> 336 * <p>Before applying this method, be sure that the specified RRD file is not in use 337 * (not open)</p> 338 * 339 * @param sourcePath path to a RRD file to add datasource to. 340 * @param consolFun Consolidation function of Archive which should be removed 341 * @param steps Number of steps for Archive which should be removed 342 * @param saveBackup true, if backup of the original file should be created; 343 * false, otherwise 344 * @throws IOException Thrown in case of I/O error 345 * @throws RrdException Thrown in case of JRobin specific error 346 */ 347 public static void removeArchive(String sourcePath, String consolFun, int steps, 348 boolean saveBackup) throws IOException, RrdException { 349 String destPath = Util.getTmpFilename(); 350 removeArchive(sourcePath, destPath, consolFun, steps); 351 copyFile(destPath, sourcePath, saveBackup); 352 } 353 354 private static void copyFile(String sourcePath, String destPath, boolean saveBackup) 355 throws IOException { 356 File source = new File(sourcePath); 357 File dest = new File(destPath); 358 if (saveBackup) { 359 String backupPath = getBackupPath(destPath); 360 File backup = new File(backupPath); 361 deleteFile(backup); 362 if (!dest.renameTo(backup)) { 363 throw new IOException("Could not create backup file " + backupPath); 364 } 365 } 366 deleteFile(dest); 367 if (!source.renameTo(dest)) { 368 throw new IOException("Could not create file " + destPath + " from " + sourcePath); 369 } 370 } 371 372 private static String getBackupPath(String destPath) { 373 String backupPath = destPath; 374 do { 375 backupPath += ".bak"; 376 } while (Util.fileExists(backupPath)); 377 return backupPath; 378 } 379 380 /** 381 * Sets datasource heartbeat to a new value. 382 * 383 * @param sourcePath Path to exisiting RRD file (will be updated) 384 * @param datasourceName Name of the datasource in the specified RRD file 385 * @param newHeartbeat New datasource heartbeat 386 * @throws RrdException Thrown in case of JRobin specific error 387 * @throws IOException Thrown in case of I/O error 388 */ 389 public static void setDsHeartbeat(String sourcePath, String datasourceName, 390 long newHeartbeat) throws RrdException, IOException { 391 RrdDb rrd = new RrdDb(sourcePath); 392 try { 393 Datasource ds = rrd.getDatasource(datasourceName); 394 ds.setHeartbeat(newHeartbeat); 395 } 396 finally { 397 rrd.close(); 398 } 399 } 400 401 /** 402 * Sets datasource heartbeat to a new value. 403 * 404 * @param sourcePath Path to exisiting RRD file (will be updated) 405 * @param dsIndex Index of the datasource in the specified RRD file 406 * @param newHeartbeat New datasource heartbeat 407 * @throws RrdException Thrown in case of JRobin specific error 408 * @throws IOException Thrown in case of I/O error 409 */ 410 public static void setDsHeartbeat(String sourcePath, int dsIndex, long newHeartbeat) 411 throws RrdException, IOException { 412 RrdDb rrd = new RrdDb(sourcePath); 413 try { 414 Datasource ds = rrd.getDatasource(dsIndex); 415 ds.setHeartbeat(newHeartbeat); 416 } 417 finally { 418 rrd.close(); 419 } 420 } 421 422 /** 423 * Sets datasource min value to a new value 424 * 425 * @param sourcePath Path to exisiting RRD file (will be updated) 426 * @param datasourceName Name of the datasource in the specified RRD file 427 * @param newMinValue New min value for the datasource 428 * @param filterArchivedValues set to <code>true</code> if archived values less than 429 * <code>newMinValue</code> should be set to NaN; set to false, otherwise. 430 * @throws RrdException Thrown in case of JRobin specific error 431 * @throws IOException Thrown in case of I/O error 432 */ 433 public static void setDsMinValue(String sourcePath, String datasourceName, 434 double newMinValue, boolean filterArchivedValues) throws RrdException, IOException { 435 RrdDb rrd = new RrdDb(sourcePath); 436 try { 437 Datasource ds = rrd.getDatasource(datasourceName); 438 ds.setMinValue(newMinValue, filterArchivedValues); 439 } 440 finally { 441 rrd.close(); 442 } 443 } 444 445 /** 446 * Sets datasource max value to a new value. 447 * 448 * @param sourcePath Path to exisiting RRD file (will be updated) 449 * @param datasourceName Name of the datasource in the specified RRD file 450 * @param newMaxValue New max value for the datasource 451 * @param filterArchivedValues set to <code>true</code> if archived values greater than 452 * <code>newMaxValue</code> should be set to NaN; set to false, otherwise. 453 * @throws RrdException Thrown in case of JRobin specific error 454 * @throws IOException Thrown in case of I/O error 455 */ 456 public static void setDsMaxValue(String sourcePath, String datasourceName, 457 double newMaxValue, boolean filterArchivedValues) throws RrdException, IOException { 458 RrdDb rrd = new RrdDb(sourcePath); 459 try { 460 Datasource ds = rrd.getDatasource(datasourceName); 461 ds.setMaxValue(newMaxValue, filterArchivedValues); 462 } 463 finally { 464 rrd.close(); 465 } 466 } 467 468 /** 469 * Updates valid value range for the given datasource. 470 * 471 * @param sourcePath Path to exisiting RRD file (will be updated) 472 * @param datasourceName Name of the datasource in the specified RRD file 473 * @param newMinValue New min value for the datasource 474 * @param newMaxValue New max value for the datasource 475 * @param filterArchivedValues set to <code>true</code> if archived values outside 476 * of the specified min/max range should be replaced with NaNs. 477 * @throws RrdException Thrown in case of JRobin specific error 478 * @throws IOException Thrown in case of I/O error 479 */ 480 public static void setDsMinMaxValue(String sourcePath, String datasourceName, 481 double newMinValue, double newMaxValue, boolean filterArchivedValues) 482 throws RrdException, IOException { 483 RrdDb rrd = new RrdDb(sourcePath); 484 try { 485 Datasource ds = rrd.getDatasource(datasourceName); 486 ds.setMinMaxValue(newMinValue, newMaxValue, filterArchivedValues); 487 } 488 finally { 489 rrd.close(); 490 } 491 } 492 493 /** 494 * Sets single archive's X-files factor to a new value. 495 * 496 * @param sourcePath Path to existing RRD file (will be updated) 497 * @param consolFun Consolidation function of the target archive 498 * @param steps Number of sptes of the target archive 499 * @param newXff New X-files factor for the target archive 500 * @throws RrdException Thrown in case of JRobin specific error 501 * @throws IOException Thrown in case of I/O error 502 */ 503 public static void setArcXff(String sourcePath, String consolFun, int steps, 504 double newXff) throws RrdException, IOException { 505 RrdDb rrd = new RrdDb(sourcePath); 506 try { 507 Archive arc = rrd.getArchive(consolFun, steps); 508 arc.setXff(newXff); 509 } 510 finally { 511 rrd.close(); 512 } 513 } 514 515 /** 516 * Creates new RRD file based on the existing one, but with a different 517 * size (number of rows) for a single archive. The archive to be resized 518 * is identified by its consolidation function and the number of steps. 519 * 520 * @param sourcePath Path to the source RRD file (will not be modified) 521 * @param destPath Path to the new RRD file (will be created) 522 * @param consolFun Consolidation function of the archive to be resized 523 * @param numSteps Number of steps of the archive to be resized 524 * @param newRows New archive size (number of archive rows) 525 * @throws IOException Thrown in case of I/O error 526 * @throws RrdException Thrown in case of JRobin specific error 527 */ 528 public static void resizeArchive(String sourcePath, String destPath, String consolFun, 529 int numSteps, int newRows) 530 throws IOException, RrdException { 531 if (Util.sameFilePath(sourcePath, destPath)) { 532 throw new RrdException("Source and destination paths are the same"); 533 } 534 if (newRows < 2) { 535 throw new RrdException("New arcihve size must be at least 2"); 536 } 537 RrdDb rrdSource = new RrdDb(sourcePath); 538 try { 539 RrdDef rrdDef = rrdSource.getRrdDef(); 540 ArcDef arcDef = rrdDef.findArchive(consolFun, numSteps); 541 if (arcDef.getRows() != newRows) { 542 arcDef.setRows(newRows); 543 rrdDef.setPath(destPath); 544 RrdDb rrdDest = new RrdDb(rrdDef); 545 try { 546 rrdSource.copyStateTo(rrdDest); 547 } 548 finally { 549 rrdDest.close(); 550 } 551 } 552 } 553 finally { 554 rrdSource.close(); 555 } 556 } 557 558 /** 559 * Modifies existing RRD file, by resizing its chosen archive. The archive to be resized 560 * is identified by its consolidation function and the number of steps. 561 * 562 * @param sourcePath Path to the RRD file (will be modified) 563 * @param consolFun Consolidation function of the archive to be resized 564 * @param numSteps Number of steps of the archive to be resized 565 * @param newRows New archive size (number of archive rows) 566 * @param saveBackup true, if backup of the original file should be created; 567 * false, otherwise 568 * @throws IOException Thrown in case of I/O error 569 * @throws RrdException Thrown in case of JRobin specific error 570 */ 571 public static void resizeArchive(String sourcePath, String consolFun, 572 int numSteps, int newRows, boolean saveBackup) 573 throws IOException, RrdException { 574 String destPath = Util.getTmpFilename(); 575 resizeArchive(sourcePath, destPath, consolFun, numSteps, newRows); 576 copyFile(destPath, sourcePath, saveBackup); 577 } 578 579 private static void deleteFile(File file) throws IOException { 580 if (file.exists() && !file.delete()) { 581 throw new IOException("Could not delete file: " + file.getCanonicalPath()); 582 } 583 } 584 585 /** 586 * Splits single RRD file with several datasources into a number of smaller RRD files 587 * with a single datasource in it. All archived values are preserved. If 588 * you have a RRD file named 'traffic.rrd' with two datasources, 'in' and 'out', this 589 * method will create two files (with a single datasource, in the same directory) 590 * named 'in-traffic.rrd' and 'out-traffic.rrd'. 591 * 592 * @param sourcePath Path to a RRD file with multiple datasources defined 593 * @throws IOException Thrown in case of I/O error 594 * @throws RrdException Thrown in case of JRobin specific error 595 */ 596 public static void split(String sourcePath) throws IOException, RrdException { 597 RrdDb rrdSource = new RrdDb(sourcePath); 598 try { 599 String[] dsNames = rrdSource.getDsNames(); 600 for (String dsName : dsNames) { 601 RrdDef rrdDef = rrdSource.getRrdDef(); 602 rrdDef.setPath(createSplitPath(dsName, sourcePath)); 603 rrdDef.saveSingleDatasource(dsName); 604 RrdDb rrdDest = new RrdDb(rrdDef); 605 try { 606 rrdSource.copyStateTo(rrdDest); 607 } 608 finally { 609 rrdDest.close(); 610 } 611 } 612 } 613 finally { 614 rrdSource.close(); 615 } 616 } 617 618 /** 619 * Returns list of canonical file names with the specified extension in the given directory. This 620 * method is not RRD related, but might come handy to create a quick list of all RRD files 621 * in the given directory. 622 * 623 * @param directory Source directory 624 * @param extension File extension (like ".rrd", ".jrb", ".rrd.jrb") 625 * @param resursive true if all subdirectories should be traversed for the same extension, false otherwise 626 * @return Array of sorted canonical file names with the given extension 627 * @throws IOException Thrown in case of I/O error 628 */ 629 public static String[] getCanonicalPaths(String directory, final String extension, boolean resursive) 630 throws IOException { 631 File baseDir = new File(directory); 632 if (!baseDir.isDirectory()) { 633 throw new IOException("Not a directory: " + directory); 634 } 635 List<String> fileList = new LinkedList<String>(); 636 traverseDirectory(new File(directory), extension, resursive, fileList); 637 String[] result = fileList.toArray(new String[fileList.size()]); 638 Arrays.sort(result); 639 return result; 640 } 641 642 private static void traverseDirectory(File directory, String extension, boolean recursive, List<String> list) 643 throws IOException { 644 File[] files = directory.listFiles(); 645 for (File file : files) { 646 if (file.isDirectory() && recursive) { 647 // traverse subdirectories only if recursive flag is specified 648 traverseDirectory(file, extension, recursive, list); 649 } 650 else if (file.isFile() && file.getName().endsWith(extension)) { 651 list.add(file.getCanonicalPath()); 652 } 653 } 654 } 655 656 private static String createSplitPath(String dsName, String sourcePath) { 657 File file = new File(sourcePath); 658 String newName = dsName + "-" + file.getName(); 659 String path = file.getAbsolutePath(); 660 String parentDir = path.substring(0, 1 + path.lastIndexOf(Util.getFileSeparator())); 661 return parentDir + newName; 662 } 663 } 664