001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008-2009 Sun Microsystems, Inc. 025 * Portions copyright 2012-2015 ForgeRock AS. 026 */ 027package org.forgerock.opendj.config; 028 029import static com.forgerock.opendj.ldap.AdminMessages.*; 030import static com.forgerock.opendj.ldap.ExtensionMessages.NOTE_LOG_EXTENSION_INFORMATION; 031import static com.forgerock.opendj.util.StaticUtils.EOL; 032import static com.forgerock.opendj.util.StaticUtils.stackTraceToSingleLineString; 033 034import java.io.BufferedReader; 035import java.io.ByteArrayOutputStream; 036import java.io.File; 037import java.io.FileFilter; 038import java.io.IOException; 039import java.io.InputStream; 040import java.io.InputStreamReader; 041import java.io.PrintStream; 042import java.lang.reflect.Method; 043import java.net.MalformedURLException; 044import java.net.URL; 045import java.net.URLClassLoader; 046import java.util.ArrayList; 047import java.util.HashSet; 048import java.util.LinkedList; 049import java.util.List; 050import java.util.Set; 051import java.util.jar.Attributes; 052import java.util.jar.JarEntry; 053import java.util.jar.JarFile; 054import java.util.jar.Manifest; 055 056import org.forgerock.i18n.LocalizableMessage; 057import org.forgerock.i18n.slf4j.LocalizedLogger; 058import org.forgerock.opendj.config.server.ConfigException; 059import org.forgerock.opendj.server.config.meta.RootCfgDefn; 060import org.forgerock.util.Reject; 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063 064import com.forgerock.opendj.ldap.AdminMessages; 065 066/** 067 * This class is responsible for managing the configuration framework including: 068 * <ul> 069 * <li>loading core components during application initialization 070 * <li>loading extensions during and after application initialization 071 * <li>changing the property validation strategy based on whether the 072 * application is a client or server. 073 * </ul> 074 * This class defines a class loader which will be used for loading components. 075 * For extensions which define their own extended configuration definitions, the 076 * class loader will make sure that the configuration definition classes are 077 * loaded and initialized. 078 * <p> 079 * Initially the configuration framework is disabled, and calls to the 080 * {@link #getClassLoader()} will return the system default class loader. 081 * <p> 082 * Applications <b>MUST NOT</b> maintain persistent references to the class 083 * loader as it can change at run-time. 084 */ 085public final class ConfigurationFramework { 086 /** 087 * Private URLClassLoader implementation. This is only required so that we 088 * can provide access to the addURL method. 089 */ 090 private static final class MyURLClassLoader extends URLClassLoader { 091 092 /** 093 * Create a class loader with the default parent class loader. 094 */ 095 public MyURLClassLoader() { 096 super(new URL[0]); 097 } 098 099 /** 100 * Create a class loader with the provided parent class loader. 101 * 102 * @param parent 103 * The parent class loader. 104 */ 105 public MyURLClassLoader(final ClassLoader parent) { 106 super(new URL[0], parent); 107 } 108 109 /** 110 * Add a Jar file to this class loader. 111 * 112 * @param jarFile 113 * The name of the Jar file. 114 * @throws MalformedURLException 115 * If a protocol handler for the URL could not be found, or 116 * if some other error occurred while constructing the URL. 117 * @throws SecurityException 118 * If a required system property value cannot be accessed. 119 */ 120 public void addJarFile(final File jarFile) throws MalformedURLException { 121 addURL(jarFile.toURI().toURL()); 122 } 123 124 } 125 126 private static final String MANIFEST = 127 "/META-INF/services/org.forgerock.opendj.config.AbstractManagedObjectDefinition"; 128 129 private static final LocalizedLogger adminLogger = LocalizedLogger 130 .getLocalizedLogger(AdminMessages.resourceName()); 131 private static final Logger debugLogger = LoggerFactory.getLogger(ConfigurationFramework.class); 132 133 /** The name of the lib directory. */ 134 private static final String LIB_DIR = "lib"; 135 136 /** The name of the extensions directory. */ 137 private static final String EXTENSIONS_DIR = "extensions"; 138 139 /** The singleton instance. */ 140 private static final ConfigurationFramework INSTANCE = new ConfigurationFramework(); 141 142 /** Attribute name in jar's MANIFEST corresponding to the revision number. */ 143 private static final String REVISION_NUMBER = "Revision-Number"; 144 145 /** 146 * The attribute names for build information is name, version and revision 147 * number. 148 */ 149 private static final String[] BUILD_INFORMATION_ATTRIBUTE_NAMES = new String[] { 150 Attributes.Name.EXTENSION_NAME.toString(), 151 Attributes.Name.IMPLEMENTATION_VERSION.toString(), REVISION_NUMBER }; 152 153 /** 154 * Returns the single application wide configuration framework instance. 155 * 156 * @return The single application wide configuration framework instance. 157 */ 158 public static ConfigurationFramework getInstance() { 159 return INSTANCE; 160 } 161 162 /** Set of registered Jar files. */ 163 private Set<File> jarFiles = new HashSet<>(); 164 165 /** 166 * Underlying class loader used to load classes and resources (null 167 * if disabled). 168 * <p> 169 * We contain a reference to the URLClassLoader rather than 170 * sub-class it so that it is possible to replace the loader at 171 * run-time. For example, when removing or replacing extension Jar 172 * files (the URLClassLoader only supports adding new URLs, not removal). 173 */ 174 private MyURLClassLoader loader; 175 176 private boolean isClient = true; 177 private String installPath; 178 private String instancePath; 179 private ClassLoader parent; 180 181 /** Private constructor. */ 182 private ConfigurationFramework() { 183 // No implementation required. 184 } 185 186 /** 187 * Loads the named extensions into the configuration framework. 188 * 189 * @param extensions 190 * The names of the extensions to be loaded. The names should not 191 * contain any path elements and must be located within the 192 * extensions folder. 193 * @throws ConfigException 194 * If one of the extensions could not be loaded and initialized. 195 * @throws IllegalStateException 196 * If the configuration framework has not yet been initialized. 197 * @throws IllegalArgumentException 198 * If one of the extension names was not a single relative path 199 * name element or was an absolute path. 200 */ 201 public synchronized void addExtension(final String... extensions) throws ConfigException { 202 Reject.ifNull(extensions); 203 ensureInitialized(); 204 205 final File libPath = new File(instancePath, LIB_DIR); 206 final File extensionsPath = new File(libPath, EXTENSIONS_DIR); 207 208 final ArrayList<File> files = new ArrayList<>(extensions.length); 209 for (final String extension : extensions) { 210 final File file = new File(extensionsPath, extension); 211 212 // For security reasons we need to make sure that the file name 213 // passed in did not contain any path elements and names a file 214 // in the extensions folder. 215 216 // Can handle potential null parent. 217 if (!extensionsPath.equals(file.getParentFile())) { 218 throw new IllegalArgumentException("Illegal file name: " + extension); 219 } 220 221 // The file is valid. 222 files.add(file); 223 } 224 225 // Add the extensions. 226 addExtension(files.toArray(new File[files.size()])); 227 } 228 229 /** 230 * Returns the class loader which should be used for loading classes and 231 * resources. When this configuration framework is disabled, the system 232 * default class loader will be returned by default. 233 * <p> 234 * Applications <b>MUST NOT</b> maintain persistent references to the class 235 * loader as it can change at run-time. 236 * 237 * @return Returns the class loader which should be used for loading classes 238 * and resources. 239 */ 240 public synchronized ClassLoader getClassLoader() { 241 if (loader != null) { 242 return loader; 243 } else { 244 return ClassLoader.getSystemClassLoader(); 245 } 246 } 247 248 /** 249 * Initializes the configuration framework using the application's class 250 * loader as the parent class loader, and the current working directory as 251 * the install and instance path. 252 * 253 * @return The configuration framework. 254 * @throws ConfigException 255 * If the configuration framework could not initialize 256 * successfully. 257 * @throws IllegalStateException 258 * If the configuration framework has already been initialized. 259 */ 260 public ConfigurationFramework initialize() throws ConfigException { 261 return initialize(null); 262 } 263 264 /** 265 * Initializes the configuration framework using the application's class 266 * loader as the parent class loader, and the provided install/instance 267 * path. 268 * 269 * @param installAndInstancePath 270 * The path where application binaries and data are located. 271 * @return The configuration framework. 272 * @throws ConfigException 273 * If the configuration framework could not initialize 274 * successfully. 275 * @throws IllegalStateException 276 * If the configuration framework has already been initialized. 277 */ 278 public ConfigurationFramework initialize(final String installAndInstancePath) 279 throws ConfigException { 280 return initialize(installAndInstancePath, installAndInstancePath); 281 } 282 283 /** 284 * Initializes the configuration framework using the application's class 285 * loader as the parent class loader, and the provided install and instance 286 * paths. 287 * 288 * @param installPath 289 * The path where application binaries are located. 290 * @param instancePath 291 * The path where application data are located. 292 * @return The configuration framework. 293 * @throws ConfigException 294 * If the configuration framework could not initialize 295 * successfully. 296 * @throws IllegalStateException 297 * If the configuration framework has already been initialized. 298 */ 299 public ConfigurationFramework initialize(final String installPath, final String instancePath) 300 throws ConfigException { 301 return initialize(installPath, instancePath, RootCfgDefn.class.getClassLoader()); 302 } 303 304 /** 305 * Initializes the configuration framework using the provided parent class 306 * loader and install and instance paths. 307 * 308 * @param installPath 309 * The path where application binaries are located. 310 * @param instancePath 311 * The path where application data are located. 312 * @param parent 313 * The parent class loader. 314 * @return The configuration framework. 315 * @throws ConfigException 316 * If the configuration framework could not initialize 317 * successfully. 318 * @throws IllegalStateException 319 * If the configuration framework has already been initialized. 320 */ 321 public synchronized ConfigurationFramework initialize(final String installPath, 322 final String instancePath, final ClassLoader parent) throws ConfigException { 323 if (loader != null) { 324 throw new IllegalStateException("configuration framework already initialized."); 325 } 326 this.installPath = installPath == null ? System.getenv("INSTALL_ROOT") : installPath; 327 if (instancePath != null) { 328 this.instancePath = instancePath; 329 } else { 330 this.instancePath = System.getenv("INSTANCE_ROOT") != null ? System.getenv("INSTANCE_ROOT") 331 : this.installPath; 332 } 333 this.parent = parent; 334 initialize0(); 335 return this; 336 } 337 338 /** 339 * Returns {@code true} if the configuration framework is being used within 340 * a client application. Client applications will perform less property 341 * value validation than server applications because they do not have 342 * resources available such as the server schema. 343 * 344 * @return {@code true} if the configuration framework is being used within 345 * a client application. 346 */ 347 public boolean isClient() { 348 return isClient; 349 } 350 351 /** 352 * Returns {@code true} if the configuration framework has been initialized. 353 * 354 * @return {@code true} if the configuration framework has been initialized. 355 */ 356 public synchronized boolean isInitialized() { 357 return loader != null; 358 } 359 360 /** 361 * Prints out all information about extensions. 362 * 363 * @return A string representing all information about extensions; 364 * <code>null</code> if there is no information available. 365 */ 366 public String printExtensionInformation() { 367 final File extensionsPath = 368 new File(installPath + File.separator + LIB_DIR + File.separator + EXTENSIONS_DIR); 369 370 if (!extensionsPath.exists() || !extensionsPath.isDirectory()) { 371 // no extensions' directory 372 return null; 373 } 374 375 final File[] extensions = extensionsPath.listFiles(new FileFilter() { 376 @Override 377 public boolean accept(final File pathname) { 378 // only files with names ending with ".jar" 379 return pathname.isFile() && pathname.getName().endsWith(".jar"); 380 } 381 }); 382 383 if (extensions.length == 0) { 384 return null; 385 } 386 387 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 388 final PrintStream ps = new PrintStream(baos); 389 // prints: 390 // -- 391 // Name Build number Revision number 392 ps.printf("--%s %-20s %-20s %-20s%s", EOL, "Name", "Build number", 393 "Revision number", EOL); 394 395 for (final File extension : extensions) { 396 // retrieve MANIFEST entry and display name, build number and 397 // revision number 398 try { 399 final JarFile jarFile = new JarFile(extension); 400 final JarEntry entry = jarFile.getJarEntry(MANIFEST); 401 if (entry == null) { 402 continue; 403 } 404 405 final String[] information = getBuildInformation(jarFile); 406 407 ps.append("Extension: "); 408 boolean addBlank = false; 409 for (final String name : information) { 410 if (addBlank) { 411 ps.append(addBlank ? " " : ""); // add blank if not 412 // first append 413 } else { 414 addBlank = true; 415 } 416 417 ps.printf("%-20s", name); 418 } 419 ps.append(EOL); 420 } catch (final Exception e) { 421 // ignore extra information for this extension 422 } 423 } 424 425 return baos.toString(); 426 } 427 428 /** 429 * Reloads the configuration framework. 430 * 431 * @throws ConfigException 432 * If the configuration framework could not initialize 433 * successfully. 434 * @throws IllegalStateException 435 * If the configuration framework has not yet been initialized. 436 */ 437 public synchronized void reload() throws ConfigException { 438 ensureInitialized(); 439 loader = null; 440 jarFiles = new HashSet<>(); 441 initialize0(); 442 } 443 444 /** 445 * Specifies whether or not the configuration framework is being used within 446 * a client application. Client applications will perform less property 447 * value validation than server applications because they do not have 448 * resources available such as the server schema. 449 * 450 * @param isClient 451 * {@code true} if the configuration framework is being used 452 * within a client application. 453 * @return The configuration framework. 454 */ 455 public ConfigurationFramework setIsClient(final boolean isClient) { 456 this.isClient = isClient; 457 return this; 458 } 459 460 private void addExtension(final File... extensions) throws ConfigException { 461 // First add the Jar files to the class loader. 462 final List<JarFile> jars = new LinkedList<>(); 463 for (final File extension : extensions) { 464 if (jarFiles.contains(extension)) { 465 // Skip this file as it is already loaded. 466 continue; 467 } 468 469 // Attempt to load it. 470 jars.add(loadJarFile(extension)); 471 472 // Register the Jar file with the class loader. 473 try { 474 loader.addJarFile(extension); 475 } catch (final Exception e) { 476 debugLogger.trace("Unable to register the jar file with the class loader", e); 477 final LocalizableMessage message = 478 ERR_ADMIN_CANNOT_OPEN_JAR_FILE.get(extension.getName(), extension 479 .getParent(), stackTraceToSingleLineString(e, true)); 480 throw new ConfigException(message); 481 } 482 jarFiles.add(extension); 483 } 484 485 // Now forcefully load the configuration definition classes. 486 for (final JarFile jar : jars) { 487 initializeExtension(jar); 488 } 489 } 490 491 private void ensureInitialized() { 492 if (loader == null) { 493 throw new IllegalStateException("configuration framework is disabled."); 494 } 495 } 496 497 /** 498 * Returns a String array with the following information : <br> 499 * index 0: the name of the extension. <br> 500 * index 1: the build number of the extension. <br> 501 * index 2: the revision number of the extension. 502 * 503 * @param extension 504 * the jar file of the extension 505 * @return a String array containing the name, the build number and the 506 * revision number of the extension given in argument 507 * @throws java.io.IOException 508 * thrown if the jar file has been closed. 509 */ 510 private String[] getBuildInformation(final JarFile extension) throws IOException { 511 final String[] result = new String[3]; 512 513 // retrieve MANIFEST entry and display name, version and revision 514 final Manifest manifest = extension.getManifest(); 515 516 if (manifest != null) { 517 final Attributes attributes = manifest.getMainAttributes(); 518 519 int index = 0; 520 for (final String name : BUILD_INFORMATION_ATTRIBUTE_NAMES) { 521 String value = attributes.getValue(name); 522 if (value == null) { 523 value = "<unknown>"; 524 } 525 result[index++] = value; 526 } 527 } 528 529 return result; 530 } 531 532 private void initialize0() throws ConfigException { 533 if (parent != null) { 534 loader = new MyURLClassLoader(parent); 535 } else { 536 loader = new MyURLClassLoader(); 537 } 538 539 // Forcefully load all configuration definition classes in 540 // OpenDS.jar. 541 initializeCoreComponents(); 542 543 // Put extensions jars into the class loader and load all 544 // configuration definition classes in that they contain. 545 // First load the extension from the install directory, then 546 // from the instance directory. 547 File libDir; 548 File installExtensionsPath; 549 File instanceExtensionsPath; 550 551 // load install dir extension 552 libDir = new File(installPath, LIB_DIR); 553 try { 554 installExtensionsPath = new File(libDir, EXTENSIONS_DIR).getCanonicalFile(); 555 } catch (final Exception e) { 556 installExtensionsPath = new File(libDir, EXTENSIONS_DIR); 557 } 558 initializeAllExtensions(installExtensionsPath); 559 560 // load instance dir extension 561 libDir = new File(instancePath, LIB_DIR); 562 try { 563 instanceExtensionsPath = new File(libDir, EXTENSIONS_DIR).getCanonicalFile(); 564 } catch (final Exception e) { 565 instanceExtensionsPath = new File(libDir, EXTENSIONS_DIR); 566 } 567 if (!installExtensionsPath.getAbsolutePath().equals( 568 instanceExtensionsPath.getAbsolutePath())) { 569 initializeAllExtensions(instanceExtensionsPath); 570 } 571 } 572 573 /** 574 * Put extensions jars into the class loader and load all configuration 575 * definition classes in that they contain. 576 * 577 * @param extensionsPath 578 * Indicates where extensions are located. 579 * @throws ConfigException 580 * If the extensions folder could not be accessed or if a 581 * extension jar file could not be accessed or if one of the 582 * configuration definition classes could not be initialized. 583 */ 584 private void initializeAllExtensions(final File extensionsPath) throws ConfigException { 585 try { 586 if (!extensionsPath.exists()) { 587 // The extensions directory does not exist. This is not a 588 // critical problem. 589 adminLogger.error(ERR_ADMIN_NO_EXTENSIONS_DIR, String.valueOf(extensionsPath)); 590 return; 591 } 592 593 if (!extensionsPath.isDirectory()) { 594 // The extensions directory is not a directory. This is more 595 // critical. 596 final LocalizableMessage message = 597 ERR_ADMIN_EXTENSIONS_DIR_NOT_DIRECTORY.get(String.valueOf(extensionsPath)); 598 throw new ConfigException(message); 599 } 600 601 // Get each extension file name. 602 final FileFilter filter = new FileFilter() { 603 604 /** 605 * Must be a Jar file. 606 */ 607 @Override 608 public boolean accept(final File pathname) { 609 if (!pathname.isFile()) { 610 return false; 611 } 612 613 final String name = pathname.getName(); 614 return name.endsWith(".jar"); 615 } 616 617 }; 618 619 // Add and initialize the extensions. 620 addExtension(extensionsPath.listFiles(filter)); 621 } catch (final ConfigException e) { 622 debugLogger.trace("Unable to initialize all extensions", e); 623 throw e; 624 } catch (final Exception e) { 625 debugLogger.trace("Unable to initialize all extensions", e); 626 final LocalizableMessage message = 627 ERR_ADMIN_EXTENSIONS_CANNOT_LIST_FILES.get(String.valueOf(extensionsPath), 628 stackTraceToSingleLineString(e, true)); 629 throw new ConfigException(message, e); 630 } 631 } 632 633 /** 634 * Make sure all core configuration definitions are loaded. 635 * 636 * @throws ConfigException 637 * If the core manifest file could not be read or if one of the 638 * configuration definition classes could not be initialized. 639 */ 640 private void initializeCoreComponents() throws ConfigException { 641 final InputStream is = RootCfgDefn.class.getResourceAsStream(MANIFEST); 642 if (is == null) { 643 final LocalizableMessage message = ERR_ADMIN_CANNOT_FIND_CORE_MANIFEST.get(MANIFEST); 644 throw new ConfigException(message); 645 } 646 try { 647 loadDefinitionClasses(is); 648 } catch (final ConfigException e) { 649 debugLogger.trace("Unable to initialize core components", e); 650 final LocalizableMessage message = 651 ERR_CLASS_LOADER_CANNOT_LOAD_CORE.get(MANIFEST, stackTraceToSingleLineString(e, 652 true)); 653 throw new ConfigException(message); 654 } 655 } 656 657 /** 658 * Make sure all the configuration definition classes in a extension are 659 * loaded. 660 * 661 * @param jarFile 662 * The extension's Jar file. 663 * @throws ConfigException 664 * If the extension jar file could not be accessed or if one of 665 * the configuration definition classes could not be 666 * initialized. 667 */ 668 private void initializeExtension(final JarFile jarFile) throws ConfigException { 669 final JarEntry entry = jarFile.getJarEntry(MANIFEST); 670 if (entry != null) { 671 InputStream is; 672 try { 673 is = jarFile.getInputStream(entry); 674 } catch (final Exception e) { 675 debugLogger.trace("Unable to get input stream from jar", e); 676 final LocalizableMessage message = 677 ERR_ADMIN_CANNOT_READ_EXTENSION_MANIFEST.get(MANIFEST, jarFile.getName(), 678 stackTraceToSingleLineString(e, true)); 679 throw new ConfigException(message); 680 } 681 682 try { 683 loadDefinitionClasses(is); 684 } catch (final ConfigException e) { 685 debugLogger.trace("Unable to load classes from input stream", e); 686 final LocalizableMessage message = 687 ERR_CLASS_LOADER_CANNOT_LOAD_EXTENSION.get(jarFile.getName(), MANIFEST, 688 stackTraceToSingleLineString(e, true)); 689 throw new ConfigException(message); 690 } 691 try { 692 // Log build information of extensions in the error log 693 final String[] information = getBuildInformation(jarFile); 694 final LocalizableMessage message = 695 NOTE_LOG_EXTENSION_INFORMATION.get(jarFile.getName(), information[1], 696 information[2]); 697 LocalizedLogger.getLocalizedLogger(message.resourceName()).error(message); 698 } catch (final Exception e) { 699 // Do not log information for that extension 700 } 701 } 702 } 703 704 /** 705 * Forcefully load configuration definition classes named in a manifest 706 * file. 707 * 708 * @param is 709 * The manifest file input stream. 710 * @throws ConfigException 711 * If the definition classes could not be loaded and 712 * initialized. 713 */ 714 private void loadDefinitionClasses(final InputStream is) throws ConfigException { 715 final BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 716 final List<AbstractManagedObjectDefinition<?, ?>> definitions = new LinkedList<>(); 717 while (true) { 718 String className; 719 try { 720 className = reader.readLine(); 721 } catch (final IOException e) { 722 final LocalizableMessage msg = 723 ERR_CLASS_LOADER_CANNOT_READ_MANIFEST_FILE.get(String.valueOf(e 724 .getMessage())); 725 throw new ConfigException(msg, e); 726 } 727 728 // Break out when the end of the manifest is reached. 729 if (className == null) { 730 break; 731 } 732 733 // Skip blank lines. 734 className = className.trim(); 735 if (className.length() == 0) { 736 continue; 737 } 738 739 // Skip lines beginning with #. 740 if (className.startsWith("#")) { 741 continue; 742 } 743 744 debugLogger.trace("Loading class " + className); 745 746 // Load the class and get an instance of it if it is a definition. 747 Class<?> theClass; 748 try { 749 theClass = Class.forName(className, true, loader); 750 } catch (final Exception e) { 751 final LocalizableMessage msg = 752 ERR_CLASS_LOADER_CANNOT_LOAD_CLASS.get(className, String.valueOf(e 753 .getMessage())); 754 throw new ConfigException(msg, e); 755 } 756 if (AbstractManagedObjectDefinition.class.isAssignableFrom(theClass)) { 757 // We need to instantiate it using its getInstance() static 758 // method. 759 Method method; 760 try { 761 method = theClass.getMethod("getInstance"); 762 } catch (final Exception e) { 763 final LocalizableMessage msg = 764 ERR_CLASS_LOADER_CANNOT_FIND_GET_INSTANCE_METHOD.get(className, String 765 .valueOf(e.getMessage())); 766 throw new ConfigException(msg, e); 767 } 768 769 // Get the definition instance. 770 AbstractManagedObjectDefinition<?, ?> d; 771 try { 772 d = (AbstractManagedObjectDefinition<?, ?>) method.invoke(null); 773 } catch (final Exception e) { 774 final LocalizableMessage msg = 775 ERR_CLASS_LOADER_CANNOT_INVOKE_GET_INSTANCE_METHOD.get(className, 776 String.valueOf(e.getMessage())); 777 throw new ConfigException(msg, e); 778 } 779 definitions.add(d); 780 } 781 } 782 783 // Initialize any definitions that were loaded. 784 for (final AbstractManagedObjectDefinition<?, ?> d : definitions) { 785 try { 786 d.initialize(); 787 } catch (final Exception e) { 788 final LocalizableMessage msg = 789 ERR_CLASS_LOADER_CANNOT_INITIALIZE_DEFN.get(d.getName(), d.getClass() 790 .getName(), String.valueOf(e.getMessage())); 791 throw new ConfigException(msg, e); 792 } 793 } 794 } 795 796 private JarFile loadJarFile(final File jar) throws ConfigException { 797 try { 798 // Load the extension jar file. 799 return new JarFile(jar); 800 } catch (final Exception e) { 801 debugLogger.trace("Unable to load jar file: " + jar, e); 802 803 final LocalizableMessage message = 804 ERR_ADMIN_CANNOT_OPEN_JAR_FILE.get(jar.getName(), jar.getParent(), 805 stackTraceToSingleLineString(e, true)); 806 throw new ConfigException(message); 807 } 808 } 809 810 /** 811 * Returns the installation path. 812 * 813 * @return The installation path of this instance. 814 */ 815 public String getInstallPath() { 816 return installPath; 817 } 818 819 /** 820 * Returns the instance path. 821 * 822 * @return The instance path. 823 */ 824 public String getInstancePath() { 825 return instancePath; 826 } 827 828}