001 /* ============================================================
002 * JRobin : Pure java implementation of RRDTool's functionality
003 * ============================================================
004 *
005 * Project Info: http://www.jrobin.org
006 * Project Lead: Sasa Markovic (saxon@jrobin.org);
007 *
008 * (C) Copyright 2003-2005, by Sasa Markovic.
009 *
010 * Developers: Sasa Markovic (saxon@jrobin.org)
011 *
012 *
013 * This library is free software; you can redistribute it and/or modify it under the terms
014 * of the GNU Lesser General Public License as published by the Free Software Foundation;
015 * either version 2.1 of the License, or (at your option) any later version.
016 *
017 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
018 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
019 * See the GNU Lesser General Public License for more details.
020 *
021 * You should have received a copy of the GNU Lesser General Public License along with this
022 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
023 * Boston, MA 02111-1307, USA.
024 */
025
026 package org.jrobin.core;
027
028 import java.io.File;
029 import java.io.IOException;
030 import java.util.List;
031 import java.util.LinkedList;
032 import java.util.Arrays;
033
034 /**
035 * <p>Class used to perform various complex operations on RRD files. Use an instance of the
036 * RrdToolkit class to:</p>
037 * <ul>
038 * <li>add datasource to a RRD file.
039 * <li>add archive to a RRD file.
040 * <li>remove datasource from a RRD file.
041 * <li>remove archive from a RRD file.
042 * </ul>
043 * <p>All these operations can be performed on the copy of the original RRD file, or on the
044 * original file itself (with possible backup file creation)</p>
045 * <p/>
046 * <p><b><u>IMPORTANT</u></b>: NEVER use methods found in this class on 'live' RRD files
047 * (files which are currently in use).</p>
048 */
049 public class RrdToolkit {
050 /**
051 * Creates a new RRD file with one more datasource in it. RRD file is created based on the
052 * existing one (the original RRD file is not modified at all). All data from
053 * the original RRD file is copied to the new one.
054 *
055 * @param sourcePath path to a RRD file to import data from (will not be modified)
056 * @param destPath path to a new RRD file (will be created)
057 * @param newDatasource Datasource definition to be added to the new RRD file
058 * @throws IOException Thrown in case of I/O error
059 * @throws RrdException Thrown in case of JRobin specific error
060 */
061 public static void addDatasource(String sourcePath, String destPath, DsDef newDatasource)
062 throws IOException, RrdException {
063 if (Util.sameFilePath(sourcePath, destPath)) {
064 throw new RrdException("Source and destination paths are the same");
065 }
066 RrdDb rrdSource = new RrdDb(sourcePath);
067 try {
068 RrdDef rrdDef = rrdSource.getRrdDef();
069 rrdDef.setPath(destPath);
070 rrdDef.addDatasource(newDatasource);
071 RrdDb rrdDest = new RrdDb(rrdDef);
072 try {
073 rrdSource.copyStateTo(rrdDest);
074 }
075 finally {
076 rrdDest.close();
077 }
078 }
079 finally {
080 rrdSource.close();
081 }
082 }
083
084 /**
085 * <p>Adds one more datasource to a RRD file.</p>
086 * <p>WARNING: This method is potentialy dangerous! It will modify your RRD file.
087 * It is highly recommended to preserve the original RRD file (<i>saveBackup</i>
088 * should be set to <code>true</code>). The backup file will be created in the same
089 * directory as the original one with <code>.bak</code> extension added to the
090 * original name.</p>
091 * <p>Before applying this method, be sure that the specified RRD file is not in use
092 * (not open)</p>
093 *
094 * @param sourcePath path to a RRD file to add datasource to.
095 * @param newDatasource Datasource definition to be added to the RRD file
096 * @param saveBackup true, if backup of the original file should be created;
097 * false, otherwise
098 * @throws IOException Thrown in case of I/O error
099 * @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