1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard; 22 23 import proguard.classfile.ClassConstants; 24 import proguard.classfile.util.ClassUtil; 25 import proguard.util.ListUtil; 26 27 import java.io.*; 28 import java.util.*; 29 30 31 /** 32 * This class writes ProGuard configurations to a file. 33 * 34 * @author Eric Lafortune 35 */ 36 public class ConfigurationWriter 37 { 38 private static final String[] KEEP_OPTIONS = new String[] 39 { 40 ConfigurationConstants.KEEP_OPTION, 41 ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION, 42 ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION 43 }; 44 45 46 private final PrintWriter writer; 47 private File baseDir; 48 49 50 /** 51 * Creates a new ConfigurationWriter for the given file name. 52 */ ConfigurationWriter(File configurationFile)53 public ConfigurationWriter(File configurationFile) throws IOException 54 { 55 this(new PrintWriter(new FileWriter(configurationFile))); 56 57 baseDir = configurationFile.getParentFile(); 58 } 59 60 61 /** 62 * Creates a new ConfigurationWriter for the given OutputStream. 63 */ ConfigurationWriter(OutputStream outputStream)64 public ConfigurationWriter(OutputStream outputStream) throws IOException 65 { 66 this(new PrintWriter(outputStream)); 67 } 68 69 70 /** 71 * Creates a new ConfigurationWriter for the given PrintWriter. 72 */ ConfigurationWriter(PrintWriter writer)73 public ConfigurationWriter(PrintWriter writer) throws IOException 74 { 75 this.writer = writer; 76 } 77 78 79 /** 80 * Closes this ConfigurationWriter. 81 */ close()82 public void close() throws IOException 83 { 84 writer.close(); 85 } 86 87 88 /** 89 * Writes the given configuration. 90 * @param configuration the configuration that is to be written out. 91 * @throws IOException if an IO error occurs while writing the configuration. 92 */ write(Configuration configuration)93 public void write(Configuration configuration) throws IOException 94 { 95 // Write the program class path (input and output entries). 96 writeJarOptions(ConfigurationConstants.INJARS_OPTION, 97 ConfigurationConstants.OUTJARS_OPTION, 98 configuration.programJars); 99 writer.println(); 100 101 // Write the library class path (output entries only). 102 writeJarOptions(ConfigurationConstants.LIBRARYJARS_OPTION, 103 ConfigurationConstants.LIBRARYJARS_OPTION, 104 configuration.libraryJars); 105 writer.println(); 106 107 // Android-added: Write value of -systemjars option to configuration file. 108 // Write the system class path (output entries only). 109 writeJarOptions(ConfigurationConstants.SYSTEMJARS_OPTION, 110 ConfigurationConstants.SYSTEMJARS_OPTION, 111 configuration.systemJars); 112 writer.println(); 113 114 // Write the other options. 115 writeOption(ConfigurationConstants.SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION, configuration.skipNonPublicLibraryClasses); 116 writeOption(ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION, !configuration.skipNonPublicLibraryClassMembers); 117 writeOption(ConfigurationConstants.KEEP_DIRECTORIES_OPTION, configuration.keepDirectories); 118 writeOption(ConfigurationConstants.TARGET_OPTION, ClassUtil.externalClassVersion(configuration.targetClassVersion)); 119 writeOption(ConfigurationConstants.FORCE_PROCESSING_OPTION, configuration.lastModified == Long.MAX_VALUE); 120 121 writeOption(ConfigurationConstants.DONT_SHRINK_OPTION, !configuration.shrink); 122 writeOption(ConfigurationConstants.PRINT_USAGE_OPTION, configuration.printUsage); 123 124 writeOption(ConfigurationConstants.DONT_OPTIMIZE_OPTION, !configuration.optimize); 125 writeOption(ConfigurationConstants.OPTIMIZATIONS, configuration.optimizations); 126 writeOption(ConfigurationConstants.OPTIMIZATION_PASSES, configuration.optimizationPasses); 127 writeOption(ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION, configuration.allowAccessModification); 128 writeOption(ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION, configuration.mergeInterfacesAggressively); 129 130 writeOption(ConfigurationConstants.DONT_OBFUSCATE_OPTION, !configuration.obfuscate); 131 writeOption(ConfigurationConstants.PRINT_MAPPING_OPTION, configuration.printMapping); 132 writeOption(ConfigurationConstants.APPLY_MAPPING_OPTION, configuration.applyMapping); 133 writeOption(ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION, configuration.obfuscationDictionary); 134 writeOption(ConfigurationConstants.CLASS_OBFUSCATION_DICTIONARY_OPTION, configuration.classObfuscationDictionary); 135 writeOption(ConfigurationConstants.PACKAGE_OBFUSCATION_DICTIONARY_OPTION, configuration.packageObfuscationDictionary); 136 writeOption(ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION, configuration.overloadAggressively); 137 writeOption(ConfigurationConstants.USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION, configuration.useUniqueClassMemberNames); 138 writeOption(ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION, !configuration.useMixedCaseClassNames); 139 writeOption(ConfigurationConstants.KEEP_PACKAGE_NAMES_OPTION, configuration.keepPackageNames, true); 140 writeOption(ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION, configuration.flattenPackageHierarchy, true); 141 writeOption(ConfigurationConstants.REPACKAGE_CLASSES_OPTION, configuration.repackageClasses, true); 142 writeOption(ConfigurationConstants.KEEP_ATTRIBUTES_OPTION, configuration.keepAttributes); 143 writeOption(ConfigurationConstants.KEEP_PARAMETER_NAMES_OPTION, configuration.keepParameterNames); 144 writeOption(ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION, configuration.newSourceFileAttribute); 145 writeOption(ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION, configuration.adaptClassStrings, true); 146 writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION, configuration.adaptResourceFileNames); 147 writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION, configuration.adaptResourceFileContents); 148 149 writeOption(ConfigurationConstants.DONT_PREVERIFY_OPTION, !configuration.preverify); 150 writeOption(ConfigurationConstants.MICRO_EDITION_OPTION, configuration.microEdition); 151 152 writeOption(ConfigurationConstants.VERBOSE_OPTION, configuration.verbose); 153 writeOption(ConfigurationConstants.DONT_NOTE_OPTION, configuration.note, true); 154 writeOption(ConfigurationConstants.DONT_WARN_OPTION, configuration.warn, true); 155 writeOption(ConfigurationConstants.IGNORE_WARNINGS_OPTION, configuration.ignoreWarnings); 156 writeOption(ConfigurationConstants.PRINT_CONFIGURATION_OPTION, configuration.printConfiguration); 157 writeOption(ConfigurationConstants.DUMP_OPTION, configuration.dump); 158 159 writeOption(ConfigurationConstants.PRINT_SEEDS_OPTION, configuration.printSeeds); 160 writer.println(); 161 162 // Write the "why are you keeping" options. 163 writeOptions(ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION, configuration.whyAreYouKeeping); 164 165 // Write the keep options. 166 writeOptions(KEEP_OPTIONS, configuration.keep); 167 168 // Write the "no side effect methods" options. 169 writeOptions(ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION, configuration.assumeNoSideEffects); 170 171 if (writer.checkError()) 172 { 173 throw new IOException("Can't write configuration"); 174 } 175 } 176 177 writeJarOptions(String inputEntryOptionName, String outputEntryOptionName, ClassPath classPath)178 private void writeJarOptions(String inputEntryOptionName, 179 String outputEntryOptionName, 180 ClassPath classPath) 181 { 182 if (classPath != null) 183 { 184 for (int index = 0; index < classPath.size(); index++) 185 { 186 ClassPathEntry entry = classPath.get(index); 187 String optionName = entry.isOutput() ? 188 outputEntryOptionName : 189 inputEntryOptionName; 190 191 writer.print(optionName); 192 writer.print(' '); 193 writer.print(relativeFileName(entry.getFile())); 194 195 // Append the filters, if any. 196 boolean filtered = false; 197 198 // For backward compatibility, the aar and apk filters come 199 // first. 200 filtered = writeFilter(filtered, entry.getAarFilter()); 201 filtered = writeFilter(filtered, entry.getApkFilter()); 202 filtered = writeFilter(filtered, entry.getZipFilter()); 203 filtered = writeFilter(filtered, entry.getEarFilter()); 204 filtered = writeFilter(filtered, entry.getWarFilter()); 205 filtered = writeFilter(filtered, entry.getJarFilter()); 206 filtered = writeFilter(filtered, entry.getFilter()); 207 208 if (filtered) 209 { 210 writer.print(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD); 211 } 212 213 writer.println(); 214 } 215 } 216 } 217 218 writeFilter(boolean filtered, List filter)219 private boolean writeFilter(boolean filtered, List filter) 220 { 221 if (filtered) 222 { 223 writer.print(ConfigurationConstants.SEPARATOR_KEYWORD); 224 } 225 226 if (filter != null) 227 { 228 if (!filtered) 229 { 230 writer.print(ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD); 231 } 232 233 writer.print(ListUtil.commaSeparatedString(filter, true)); 234 235 filtered = true; 236 } 237 238 return filtered; 239 } 240 241 writeOption(String optionName, boolean flag)242 private void writeOption(String optionName, boolean flag) 243 { 244 if (flag) 245 { 246 writer.println(optionName); 247 } 248 } 249 250 writeOption(String optionName, int argument)251 private void writeOption(String optionName, int argument) 252 { 253 if (argument != 1) 254 { 255 writer.print(optionName); 256 writer.print(' '); 257 writer.println(argument); 258 } 259 } 260 261 writeOption(String optionName, List arguments)262 private void writeOption(String optionName, List arguments) 263 { 264 writeOption(optionName, arguments, false); 265 } 266 267 writeOption(String optionName, List arguments, boolean replaceInternalClassNames)268 private void writeOption(String optionName, 269 List arguments, 270 boolean replaceInternalClassNames) 271 { 272 if (arguments != null) 273 { 274 if (arguments.isEmpty()) 275 { 276 writer.println(optionName); 277 } 278 else 279 { 280 if (replaceInternalClassNames) 281 { 282 arguments = externalClassNames(arguments); 283 } 284 285 writer.print(optionName); 286 writer.print(' '); 287 writer.println(ListUtil.commaSeparatedString(arguments, true)); 288 } 289 } 290 } 291 292 writeOption(String optionName, String arguments)293 private void writeOption(String optionName, String arguments) 294 { 295 writeOption(optionName, arguments, false); 296 } 297 298 writeOption(String optionName, String arguments, boolean replaceInternalClassNames)299 private void writeOption(String optionName, 300 String arguments, 301 boolean replaceInternalClassNames) 302 { 303 if (arguments != null) 304 { 305 if (replaceInternalClassNames) 306 { 307 arguments = ClassUtil.externalClassName(arguments); 308 } 309 310 writer.print(optionName); 311 writer.print(' '); 312 writer.println(quotedString(arguments)); 313 } 314 } 315 316 writeOption(String optionName, File file)317 private void writeOption(String optionName, File file) 318 { 319 if (file != null) 320 { 321 if (file.getPath().length() > 0) 322 { 323 writer.print(optionName); 324 writer.print(' '); 325 writer.println(relativeFileName(file)); 326 } 327 else 328 { 329 writer.println(optionName); 330 } 331 } 332 } 333 334 writeOptions(String[] optionNames, List keepClassSpecifications)335 private void writeOptions(String[] optionNames, 336 List keepClassSpecifications) 337 { 338 if (keepClassSpecifications != null) 339 { 340 for (int index = 0; index < keepClassSpecifications.size(); index++) 341 { 342 writeOption(optionNames, (KeepClassSpecification)keepClassSpecifications.get(index)); 343 } 344 } 345 } 346 347 writeOption(String[] optionNames, KeepClassSpecification keepClassSpecification)348 private void writeOption(String[] optionNames, 349 KeepClassSpecification keepClassSpecification) 350 { 351 // Compose the option name. 352 String optionName = optionNames[keepClassSpecification.markConditionally ? 2 : 353 keepClassSpecification.markClasses ? 0 : 354 1]; 355 356 if (keepClassSpecification.markDescriptorClasses) 357 { 358 optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + 359 ConfigurationConstants.INCLUDE_DESCRIPTOR_CLASSES_SUBOPTION; 360 } 361 362 if (keepClassSpecification.allowShrinking) 363 { 364 optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + 365 ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION; 366 } 367 368 if (keepClassSpecification.allowOptimization) 369 { 370 optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + 371 ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION; 372 } 373 374 if (keepClassSpecification.allowObfuscation) 375 { 376 optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD + 377 ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION; 378 } 379 380 // Write out the option with the proper class specification. 381 writeOption(optionName, keepClassSpecification); 382 } 383 384 writeOptions(String optionName, List classSpecifications)385 private void writeOptions(String optionName, 386 List classSpecifications) 387 { 388 if (classSpecifications != null) 389 { 390 for (int index = 0; index < classSpecifications.size(); index++) 391 { 392 writeOption(optionName, (ClassSpecification)classSpecifications.get(index)); 393 } 394 } 395 } 396 397 writeOption(String optionName, ClassSpecification classSpecification)398 private void writeOption(String optionName, 399 ClassSpecification classSpecification) 400 { 401 writer.println(); 402 403 // Write out the comments for this option. 404 writeComments(classSpecification.comments); 405 406 writer.print(optionName); 407 writer.print(' '); 408 409 // Write out the required annotation, if any. 410 if (classSpecification.annotationType != null) 411 { 412 writer.print(ConfigurationConstants.ANNOTATION_KEYWORD); 413 writer.print(ClassUtil.externalType(classSpecification.annotationType)); 414 writer.print(' '); 415 } 416 417 // Write out the class access flags. 418 writer.print(ClassUtil.externalClassAccessFlags(classSpecification.requiredUnsetAccessFlags, 419 ConfigurationConstants.NEGATOR_KEYWORD)); 420 421 writer.print(ClassUtil.externalClassAccessFlags(classSpecification.requiredSetAccessFlags)); 422 423 // Write out the class keyword, if we didn't write the interface 424 // keyword earlier. 425 if (((classSpecification.requiredSetAccessFlags | 426 classSpecification.requiredUnsetAccessFlags) & 427 (ClassConstants.ACC_INTERFACE | 428 ClassConstants.ACC_ENUM)) == 0) 429 { 430 writer.print(ConfigurationConstants.CLASS_KEYWORD); 431 } 432 433 writer.print(' '); 434 435 // Write out the class name. 436 writer.print(classSpecification.className != null ? 437 ClassUtil.externalClassName(classSpecification.className) : 438 ConfigurationConstants.ANY_CLASS_KEYWORD); 439 440 // Write out the extends template, if any. 441 if (classSpecification.extendsAnnotationType != null || 442 classSpecification.extendsClassName != null) 443 { 444 writer.print(' '); 445 writer.print(ConfigurationConstants.EXTENDS_KEYWORD); 446 writer.print(' '); 447 448 // Write out the required extends annotation, if any. 449 if (classSpecification.extendsAnnotationType != null) 450 { 451 writer.print(ConfigurationConstants.ANNOTATION_KEYWORD); 452 writer.print(ClassUtil.externalType(classSpecification.extendsAnnotationType)); 453 writer.print(' '); 454 } 455 456 // Write out the extended class name. 457 writer.print(classSpecification.extendsClassName != null ? 458 ClassUtil.externalClassName(classSpecification.extendsClassName) : 459 ConfigurationConstants.ANY_CLASS_KEYWORD); 460 } 461 462 // Write out the keep field and keep method options, if any. 463 if (classSpecification.fieldSpecifications != null || 464 classSpecification.methodSpecifications != null) 465 { 466 writer.print(' '); 467 writer.println(ConfigurationConstants.OPEN_KEYWORD); 468 469 writeFieldSpecification( classSpecification.fieldSpecifications); 470 writeMethodSpecification(classSpecification.methodSpecifications); 471 472 writer.println(ConfigurationConstants.CLOSE_KEYWORD); 473 } 474 else 475 { 476 writer.println(); 477 } 478 } 479 480 481 writeComments(String comments)482 private void writeComments(String comments) 483 { 484 if (comments != null) 485 { 486 int index = 0; 487 while (index < comments.length()) 488 { 489 int breakIndex = comments.indexOf('\n', index); 490 if (breakIndex < 0) 491 { 492 breakIndex = comments.length(); 493 } 494 495 writer.print('#'); 496 497 if (comments.charAt(index) != ' ') 498 { 499 writer.print(' '); 500 } 501 502 writer.println(comments.substring(index, breakIndex)); 503 504 index = breakIndex + 1; 505 } 506 } 507 } 508 509 writeFieldSpecification(List memberSpecifications)510 private void writeFieldSpecification(List memberSpecifications) 511 { 512 if (memberSpecifications != null) 513 { 514 for (int index = 0; index < memberSpecifications.size(); index++) 515 { 516 MemberSpecification memberSpecification = 517 (MemberSpecification)memberSpecifications.get(index); 518 519 writer.print(" "); 520 521 // Write out the required annotation, if any. 522 if (memberSpecification.annotationType != null) 523 { 524 writer.print(ConfigurationConstants.ANNOTATION_KEYWORD); 525 writer.println(ClassUtil.externalType(memberSpecification.annotationType)); 526 writer.print(" "); 527 } 528 529 // Write out the field access flags. 530 writer.print(ClassUtil.externalFieldAccessFlags(memberSpecification.requiredUnsetAccessFlags, 531 ConfigurationConstants.NEGATOR_KEYWORD)); 532 533 writer.print(ClassUtil.externalFieldAccessFlags(memberSpecification.requiredSetAccessFlags)); 534 535 // Write out the field name and descriptor. 536 String name = memberSpecification.name; 537 String descriptor = memberSpecification.descriptor; 538 539 writer.print(descriptor == null ? name == null ? 540 ConfigurationConstants.ANY_FIELD_KEYWORD : 541 ConfigurationConstants.ANY_TYPE_KEYWORD + ' ' + name : 542 ClassUtil.externalFullFieldDescription(0, 543 name == null ? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD : name, 544 descriptor)); 545 546 writer.println(ConfigurationConstants.SEPARATOR_KEYWORD); 547 } 548 } 549 } 550 551 writeMethodSpecification(List memberSpecifications)552 private void writeMethodSpecification(List memberSpecifications) 553 { 554 if (memberSpecifications != null) 555 { 556 for (int index = 0; index < memberSpecifications.size(); index++) 557 { 558 MemberSpecification memberSpecification = 559 (MemberSpecification)memberSpecifications.get(index); 560 561 writer.print(" "); 562 563 // Write out the required annotation, if any. 564 if (memberSpecification.annotationType != null) 565 { 566 writer.print(ConfigurationConstants.ANNOTATION_KEYWORD); 567 writer.println(ClassUtil.externalType(memberSpecification.annotationType)); 568 writer.print(" "); 569 } 570 571 // Write out the method access flags. 572 writer.print(ClassUtil.externalMethodAccessFlags(memberSpecification.requiredUnsetAccessFlags, 573 ConfigurationConstants.NEGATOR_KEYWORD)); 574 575 writer.print(ClassUtil.externalMethodAccessFlags(memberSpecification.requiredSetAccessFlags)); 576 577 // Write out the method name and descriptor. 578 String name = memberSpecification.name; 579 String descriptor = memberSpecification.descriptor; 580 581 writer.print(descriptor == null ? name == null ? 582 ConfigurationConstants.ANY_METHOD_KEYWORD : 583 ConfigurationConstants.ANY_TYPE_KEYWORD + ' ' + name + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + ConfigurationConstants.ANY_ARGUMENTS_KEYWORD + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD : 584 ClassUtil.externalFullMethodDescription(ClassConstants.METHOD_NAME_INIT, 585 0, 586 name == null ? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD : name, 587 descriptor)); 588 589 writer.println(ConfigurationConstants.SEPARATOR_KEYWORD); 590 } 591 } 592 } 593 594 595 /** 596 * Returns a list with external versions of the given list of internal 597 * class names. 598 */ externalClassNames(List internalClassNames)599 private List externalClassNames(List internalClassNames) 600 { 601 List externalClassNames = new ArrayList(internalClassNames.size()); 602 603 for (int index = 0; index < internalClassNames.size(); index++) 604 { 605 externalClassNames.add(ClassUtil.externalClassName((String)internalClassNames.get(index))); 606 } 607 608 return externalClassNames; 609 } 610 611 612 /** 613 * Returns a relative file name of the given file, if possible. 614 * The file name is also quoted, if necessary. 615 */ relativeFileName(File file)616 private String relativeFileName(File file) 617 { 618 String fileName = file.getAbsolutePath(); 619 620 // See if we can convert the file name into a relative file name. 621 if (baseDir != null) 622 { 623 String baseDirName = baseDir.getAbsolutePath() + File.separator; 624 if (fileName.startsWith(baseDirName)) 625 { 626 fileName = fileName.substring(baseDirName.length()); 627 } 628 } 629 630 return quotedString(fileName); 631 } 632 633 634 /** 635 * Returns a quoted version of the given string, if necessary. 636 */ quotedString(String string)637 private String quotedString(String string) 638 { 639 return string.length() == 0 || 640 string.indexOf(' ') >= 0 || 641 string.indexOf('@') >= 0 || 642 string.indexOf('{') >= 0 || 643 string.indexOf('}') >= 0 || 644 string.indexOf('(') >= 0 || 645 string.indexOf(')') >= 0 || 646 string.indexOf(':') >= 0 || 647 string.indexOf(';') >= 0 || 648 string.indexOf(',') >= 0 ? ("'" + string + "'") : 649 ( string ); 650 } 651 652 653 /** 654 * A main method for testing configuration writing. 655 */ main(String[] args)656 public static void main(String[] args) { 657 try 658 { 659 ConfigurationWriter writer = new ConfigurationWriter(new File(args[0])); 660 661 writer.write(new Configuration()); 662 } 663 catch (Exception ex) 664 { 665 ex.printStackTrace(); 666 } 667 } 668 } 669