| | |
| | | */ |
| | | package org.opends.server.admin; |
| | | |
| | | |
| | | |
| | | import static com.forgerock.opendj.ldap.AdminMessages.*; |
| | | import static com.forgerock.opendj.ldap.ExtensionMessages.*; |
| | | import static com.forgerock.opendj.util.StaticUtils.*; |
| | | import static com.forgerock.opendj.util.Validator.*; |
| | | |
| | | import java.io.ByteArrayOutputStream; |
| | |
| | | import java.util.jar.Manifest; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.admin.meta.RootCfgDefn; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.util.DynamicConstants; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import com.forgerock.opendj.ldap.AdminMessages; |
| | | |
| | | /** |
| | | * Manages the class loader which should be used for loading |
| | | * configuration definition classes and associated extensions. |
| | | * Manages the class loader which should be used for loading configuration |
| | | * definition classes and associated extensions. |
| | | * <p> |
| | | * For extensions which define their own extended configuration |
| | | * definitions, the class loader will make sure that the configuration |
| | | * definition classes are loaded and initialized. |
| | | * For extensions which define their own extended configuration definitions, the |
| | | * class loader will make sure that the configuration definition classes are |
| | | * loaded and initialized. |
| | | * <p> |
| | | * Initially the class loader provider is disabled, and calls to the |
| | | * {@link #getClassLoader()} will return the system default class |
| | | * loader. |
| | | * {@link #getClassLoader()} will return the system default class loader. |
| | | * <p> |
| | | * Applications <b>MUST NOT</b> maintain persistent references to the |
| | | * class loader as it can change at run-time. |
| | | * Applications <b>MUST NOT</b> maintain persistent references to the class |
| | | * loader as it can change at run-time. |
| | | */ |
| | | public final class ClassLoaderProvider { |
| | | |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | /** |
| | | * Private URLClassLoader implementation. This is only required so |
| | | * that we can provide access to the addURL method. |
| | | */ |
| | | private static final class MyURLClassLoader extends URLClassLoader { |
| | | private static final LocalizedLogger adminLogger = LocalizedLogger.getLocalizedLogger(AdminMessages.resourceName()); |
| | | private static final Logger debugLogger = LoggerFactory.getLogger(ClassLoaderProvider.class); |
| | | |
| | | /** |
| | | * Create a class loader with the default parent class loader. |
| | | * Private URLClassLoader implementation. This is only required so that we |
| | | * can provide access to the addURL method. |
| | | */ |
| | | public MyURLClassLoader() { |
| | | super(new URL[0]); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Create a class loader with the provided parent class loader. |
| | | * |
| | | * @param parent |
| | | * The parent class loader. |
| | | */ |
| | | public MyURLClassLoader(ClassLoader parent) { |
| | | super(new URL[0], parent); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Add a Jar file to this class loader. |
| | | * |
| | | * @param jarFile |
| | | * The name of the Jar file. |
| | | * @throws MalformedURLException |
| | | * If a protocol handler for the URL could not be found, |
| | | * or if some other error occurred while constructing |
| | | * the URL. |
| | | * @throws SecurityException |
| | | * If a required system property value cannot be |
| | | * accessed. |
| | | */ |
| | | public void addJarFile(File jarFile) throws SecurityException, |
| | | MalformedURLException { |
| | | addURL(jarFile.toURI().toURL()); |
| | | } |
| | | |
| | | } |
| | | |
| | | // The name of the manifest file listing the core configuration |
| | | // definition classes. |
| | | private static final String CORE_MANIFEST = "core.manifest"; |
| | | |
| | | // The name of the manifest file listing a extension's configuration |
| | | // definition classes. |
| | | private static final String EXTENSION_MANIFEST = "extension.manifest"; |
| | | |
| | | // The name of the lib directory. |
| | | private static final String LIB_DIR = "lib"; |
| | | |
| | | // The name of the extensions directory. |
| | | private static final String EXTENSIONS_DIR = "extensions"; |
| | | |
| | | // The singleton instance. |
| | | private static final ClassLoaderProvider INSTANCE = new ClassLoaderProvider(); |
| | | |
| | | // Attribute name in jar's MANIFEST corresponding to the revision number. |
| | | private static final String REVISION_NUMBER = "Revision-Number"; |
| | | |
| | | // The attribute names for build information is name, version and revision |
| | | // number |
| | | private static final String[] BUILD_INFORMATION_ATTRIBUTE_NAMES = |
| | | new String[]{Attributes.Name.EXTENSION_NAME.toString(), |
| | | Attributes.Name.IMPLEMENTATION_VERSION.toString(), |
| | | REVISION_NUMBER}; |
| | | |
| | | |
| | | /** |
| | | * Get the single application wide class loader provider instance. |
| | | * |
| | | * @return Returns the single application wide class loader provider |
| | | * instance. |
| | | */ |
| | | public static ClassLoaderProvider getInstance() { |
| | | return INSTANCE; |
| | | } |
| | | |
| | | // Set of registered Jar files. |
| | | private Set<File> jarFiles = new HashSet<File>(); |
| | | |
| | | // Underlying class loader used to load classes and resources (null |
| | | // if disabled). |
| | | // |
| | | // We contain a reference to the URLClassLoader rather than |
| | | // sub-class it so that it is possible to replace the loader at |
| | | // run-time. For example, when removing or replacing extension Jar |
| | | // files (the URLClassLoader only supports adding new |
| | | // URLs, not removal). |
| | | private MyURLClassLoader loader = null; |
| | | |
| | | |
| | | |
| | | // Private constructor. |
| | | private ClassLoaderProvider() { |
| | | // No implementation required. |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Add the named extensions to this class loader provider. |
| | | * |
| | | * @param extensions |
| | | * The names of the extensions to be loaded. The names |
| | | * should not contain any path elements and must be located |
| | | * within the extensions folder. |
| | | * @throws InitializationException |
| | | * If one of the extensions could not be loaded and |
| | | * initialized. |
| | | * @throws IllegalStateException |
| | | * If this class loader provider is disabled. |
| | | * @throws IllegalArgumentException |
| | | * If one of the extension names was not a single relative |
| | | * path name element or was an absolute path. |
| | | */ |
| | | public synchronized void addExtension(String... extensions) |
| | | throws InitializationException, IllegalStateException, |
| | | IllegalArgumentException { |
| | | ensureNotNull(extensions); |
| | | |
| | | if (loader == null) { |
| | | throw new IllegalStateException( |
| | | "Class loader provider is disabled."); |
| | | } |
| | | |
| | | File libPath = new File(DirectoryServer.getInstanceRoot(), LIB_DIR); |
| | | File extensionsPath = new File(libPath, EXTENSIONS_DIR); |
| | | |
| | | ArrayList<File> files = new ArrayList<File>(extensions.length); |
| | | for (String extension : extensions) { |
| | | File file = new File(extensionsPath, extension); |
| | | |
| | | // For security reasons we need to make sure that the file name |
| | | // passed in did not contain any path elements and names a file |
| | | // in the extensions folder. |
| | | |
| | | // Can handle potential null parent. |
| | | if (!extensionsPath.equals(file.getParentFile())) { |
| | | throw new IllegalArgumentException("Illegal file name: " |
| | | + extension); |
| | | } |
| | | |
| | | // The file is valid. |
| | | files.add(file); |
| | | } |
| | | |
| | | // Add the extensions. |
| | | addExtension(files.toArray(new File[files.size()])); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Disable this class loader provider and removed any registered |
| | | * extensions. |
| | | * |
| | | * @throws IllegalStateException |
| | | * If this class loader provider is already disabled. |
| | | */ |
| | | public synchronized void disable() throws IllegalStateException { |
| | | if (loader == null) { |
| | | throw new IllegalStateException( |
| | | "Class loader provider already disabled."); |
| | | } |
| | | loader = null; |
| | | jarFiles = new HashSet<File>(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Enable this class loader provider using the application's |
| | | * class loader as the parent class loader. |
| | | * |
| | | * @throws InitializationException |
| | | * If the class loader provider could not initialize |
| | | * successfully. |
| | | * @throws IllegalStateException |
| | | * If this class loader provider is already enabled. |
| | | */ |
| | | public synchronized void enable() throws InitializationException, |
| | | IllegalStateException { |
| | | enable(RootCfgDefn.class.getClassLoader()); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Enable this class loader provider using the provided parent class |
| | | * loader. |
| | | * |
| | | * @param parent |
| | | * The parent class loader. |
| | | * @throws InitializationException |
| | | * If the class loader provider could not initialize |
| | | * successfully. |
| | | * @throws IllegalStateException |
| | | * If this class loader provider is already enabled. |
| | | */ |
| | | public synchronized void enable(ClassLoader parent) |
| | | throws InitializationException, IllegalStateException { |
| | | if (loader != null) { |
| | | throw new IllegalStateException( |
| | | "Class loader provider already enabled."); |
| | | } |
| | | |
| | | if (parent != null) { |
| | | loader = new MyURLClassLoader(parent); |
| | | } else { |
| | | loader = new MyURLClassLoader(); |
| | | } |
| | | |
| | | // Forcefully load all configuration definition classes in |
| | | // OpenDS.jar. |
| | | initializeCoreComponents(); |
| | | |
| | | // Put extensions jars into the class loader and load all |
| | | // configuration definition classes in that they contain. |
| | | // First load the extension from the install directory, then |
| | | // from the instance directory. |
| | | File libDir ; |
| | | File installExtensionsPath ; |
| | | File instanceExtensionsPath ; |
| | | |
| | | |
| | | // load install dir extension |
| | | libDir = new File(DirectoryServer.getServerRoot(), LIB_DIR); |
| | | try |
| | | { |
| | | installExtensionsPath = |
| | | new File(libDir, EXTENSIONS_DIR).getCanonicalFile(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | installExtensionsPath = new File(libDir, EXTENSIONS_DIR); |
| | | } |
| | | initializeAllExtensions(installExtensionsPath); |
| | | |
| | | // load instance dir extension |
| | | libDir = new File(DirectoryServer.getInstanceRoot(),LIB_DIR); |
| | | try |
| | | { |
| | | instanceExtensionsPath = |
| | | new File(libDir, EXTENSIONS_DIR).getCanonicalFile(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | instanceExtensionsPath = new File(libDir, EXTENSIONS_DIR); |
| | | } |
| | | if (! installExtensionsPath.getAbsolutePath().equals( |
| | | instanceExtensionsPath.getAbsolutePath())) |
| | | { |
| | | initializeAllExtensions(instanceExtensionsPath); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Gets the class loader which should be used for loading classes |
| | | * and resources. When this class loader provider is disabled, the |
| | | * system default class loader will be returned by default. |
| | | * <p> |
| | | * Applications <b>MUST NOT</b> maintain persistent references to |
| | | * the class loader as it can change at run-time. |
| | | * |
| | | * @return Returns the class loader which should be used for loading |
| | | * classes and resources. |
| | | */ |
| | | public synchronized ClassLoader getClassLoader() { |
| | | if (loader != null) { |
| | | return loader; |
| | | } else { |
| | | return ClassLoader.getSystemClassLoader(); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether this class loader provider is enabled. |
| | | * |
| | | * @return Returns <code>true</code> if this class loader provider |
| | | * is enabled. |
| | | */ |
| | | public synchronized boolean isEnabled() { |
| | | return loader != null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Add the named extensions to this class loader. |
| | | * |
| | | * @param extensions |
| | | * The names of the extensions to be loaded. |
| | | * @throws InitializationException |
| | | * If one of the extensions could not be loaded and |
| | | * initialized. |
| | | */ |
| | | private synchronized void addExtension(File... extensions) |
| | | throws InitializationException { |
| | | // First add the Jar files to the class loader. |
| | | List<JarFile> jars = new LinkedList<JarFile>(); |
| | | for (File extension : extensions) { |
| | | if (jarFiles.contains(extension)) { |
| | | // Skip this file as it is already loaded. |
| | | continue; |
| | | } |
| | | |
| | | // Attempt to load it. |
| | | jars.add(loadJarFile(extension)); |
| | | |
| | | // Register the Jar file with the class loader. |
| | | try { |
| | | loader.addJarFile(extension); |
| | | } catch (Exception e) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | LocalizableMessage message = ERR_ADMIN_CANNOT_OPEN_JAR_FILE. |
| | | get(extension.getName(), extension.getParent(), |
| | | stackTraceToSingleLineString(e)); |
| | | throw new InitializationException(message); |
| | | } |
| | | jarFiles.add(extension); |
| | | } |
| | | |
| | | // Now forcefully load the configuration definition classes. |
| | | for (JarFile jar : jars) { |
| | | initializeExtension(jar); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Prints out all information about extensions. |
| | | * |
| | | * @return a String instance representing all information about extensions; |
| | | * <code>null</code> if there is no information available. |
| | | */ |
| | | public String printExtensionInformation() { |
| | | File extensionsPath = |
| | | new File(new StringBuilder(DirectoryServer.getServerRoot()). |
| | | append(File.separator). |
| | | append(LIB_DIR). |
| | | append(File.separator). |
| | | append(EXTENSIONS_DIR). |
| | | toString()); |
| | | |
| | | if (!extensionsPath.exists() || !extensionsPath.isDirectory()) { |
| | | // no extensions' directory |
| | | return null; |
| | | } |
| | | |
| | | File[] extensions = extensionsPath.listFiles(new FileFilter(){ |
| | | public boolean accept(File pathname) { |
| | | // only files with names ending with ".jar" |
| | | return pathname.isFile() && pathname.getName().endsWith(".jar"); |
| | | } |
| | | }); |
| | | |
| | | if ( extensions.length == 0 ) { |
| | | return null; |
| | | } |
| | | |
| | | ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| | | PrintStream ps = new PrintStream(baos); |
| | | // prints: |
| | | // -- |
| | | // Name Build number Revision number |
| | | ps.printf("--%s %-20s %-20s %-20s%s", |
| | | EOL, |
| | | "Name", |
| | | "Build number", |
| | | "Revision number", |
| | | EOL); |
| | | |
| | | for(File extension : extensions) { |
| | | // retrieve MANIFEST entry and display name, build number and revision |
| | | // number |
| | | try { |
| | | JarFile jarFile = new JarFile(extension); |
| | | JarEntry entry = jarFile.getJarEntry("admin/" + EXTENSION_MANIFEST); |
| | | if (entry == null) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | String[] information = getBuildInformation(jarFile); |
| | | |
| | | ps.append("Extension: "); |
| | | boolean addBlank = false; |
| | | for(String name : information) { |
| | | if ( addBlank ) { |
| | | ps.append(addBlank ? " " : ""); // add blank if not first append |
| | | } else { |
| | | addBlank = true; |
| | | } |
| | | |
| | | ps.printf("%-20s", name); |
| | | } |
| | | ps.append(EOL); |
| | | } catch(Exception e) { |
| | | // ignore extra information for this extension |
| | | } |
| | | } |
| | | |
| | | return baos.toString(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns a String array with the following information : |
| | | * <br>index 0: the name of the extension. |
| | | * <br>index 1: the build number of the extension. |
| | | * <br>index 2: the revision number of the extension. |
| | | * |
| | | * @param extension the jar file of the extension |
| | | * @return a String array containing the name, the build number and the |
| | | * revision number of the extension given in argument |
| | | * @throws java.io.IOException thrown if the jar file has been closed. |
| | | */ |
| | | private String[] getBuildInformation(JarFile extension) throws IOException { |
| | | String[] result = new String[3]; |
| | | |
| | | // retrieve MANIFEST entry and display name, version and revision |
| | | Manifest manifest = extension.getManifest(); |
| | | |
| | | if ( manifest != null ) { |
| | | Attributes attributes = manifest.getMainAttributes(); |
| | | |
| | | int index = 0; |
| | | for(String name : BUILD_INFORMATION_ATTRIBUTE_NAMES) { |
| | | String value = attributes.getValue(name); |
| | | if ( value == null ) { |
| | | value = "<unknown>"; |
| | | } |
| | | result[index++] = value; |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Put extensions jars into the class loader and load all |
| | | * configuration definition classes in that they contain. |
| | | * @param extensionsPath Indicates where extensions are located. |
| | | * |
| | | * @throws InitializationException |
| | | * If the extensions folder could not be accessed or if a |
| | | * extension jar file could not be accessed or if one of |
| | | * the configuration definition classes could not be |
| | | * initialized. |
| | | */ |
| | | private void initializeAllExtensions(File extensionsPath) |
| | | throws InitializationException { |
| | | |
| | | try { |
| | | if (!extensionsPath.exists()) { |
| | | // The extensions directory does not exist. This is not a |
| | | // critical problem. |
| | | LocalizableMessage message = ERR_ADMIN_NO_EXTENSIONS_DIR.get( |
| | | String.valueOf(extensionsPath)); |
| | | logError(message); |
| | | return; |
| | | } |
| | | |
| | | if (!extensionsPath.isDirectory()) { |
| | | // The extensions directory is not a directory. This is more |
| | | // critical. |
| | | LocalizableMessage message = |
| | | ERR_ADMIN_EXTENSIONS_DIR_NOT_DIRECTORY.get( |
| | | String.valueOf(extensionsPath)); |
| | | throw new InitializationException(message); |
| | | } |
| | | |
| | | // Get each extension file name. |
| | | FileFilter filter = new FileFilter() { |
| | | private static final class MyURLClassLoader extends URLClassLoader { |
| | | |
| | | /** |
| | | * Must be a Jar file. |
| | | * Create a class loader with the default parent class loader. |
| | | */ |
| | | public boolean accept(File pathname) { |
| | | if (!pathname.isFile()) { |
| | | return false; |
| | | } |
| | | |
| | | String name = pathname.getName(); |
| | | return name.endsWith(".jar"); |
| | | public MyURLClassLoader() { |
| | | super(new URL[0]); |
| | | } |
| | | |
| | | }; |
| | | |
| | | // Add and initialize the extensions. |
| | | addExtension(extensionsPath.listFiles(filter)); |
| | | } catch (InitializationException e) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | throw e; |
| | | } catch (Exception e) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | LocalizableMessage message = ERR_ADMIN_EXTENSIONS_CANNOT_LIST_FILES.get( |
| | | String.valueOf(extensionsPath), stackTraceToSingleLineString(e)); |
| | | throw new InitializationException(message, e); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Make sure all core configuration definitions are loaded. |
| | | * |
| | | * @throws InitializationException |
| | | * If the core manifest file could not be read or if one |
| | | * of the configuration definition classes could not be |
| | | * initialized. |
| | | */ |
| | | private void initializeCoreComponents() |
| | | throws InitializationException { |
| | | InputStream is = RootCfgDefn.class.getResourceAsStream("/admin/" |
| | | + CORE_MANIFEST); |
| | | |
| | | if (is == null) { |
| | | LocalizableMessage message = ERR_ADMIN_CANNOT_FIND_CORE_MANIFEST.get(CORE_MANIFEST); |
| | | throw new InitializationException(message); |
| | | } |
| | | |
| | | try { |
| | | loadDefinitionClasses(is); |
| | | } catch (InitializationException e) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | LocalizableMessage message = ERR_CLASS_LOADER_CANNOT_LOAD_CORE.get(CORE_MANIFEST, |
| | | stackTraceToSingleLineString(e)); |
| | | throw new InitializationException(message); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Make sure all the configuration definition classes in a extension |
| | | * are loaded. |
| | | * |
| | | * @param jarFile |
| | | * The extension's Jar file. |
| | | * @throws InitializationException |
| | | * If the extension jar file could not be accessed or if |
| | | * one of the configuration definition classes could not |
| | | * be initialized. |
| | | */ |
| | | private void initializeExtension(JarFile jarFile) |
| | | throws InitializationException { |
| | | JarEntry entry = jarFile.getJarEntry("admin/" |
| | | + EXTENSION_MANIFEST); |
| | | if (entry != null) { |
| | | InputStream is; |
| | | try { |
| | | is = jarFile.getInputStream(entry); |
| | | } catch (Exception e) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | /** |
| | | * Create a class loader with the provided parent class loader. |
| | | * |
| | | * @param parent |
| | | * The parent class loader. |
| | | */ |
| | | public MyURLClassLoader(ClassLoader parent) { |
| | | super(new URL[0], parent); |
| | | } |
| | | |
| | | LocalizableMessage message = ERR_ADMIN_CANNOT_READ_EXTENSION_MANIFEST.get( |
| | | EXTENSION_MANIFEST, jarFile.getName(), |
| | | stackTraceToSingleLineString(e)); |
| | | throw new InitializationException(message); |
| | | } |
| | | |
| | | try { |
| | | loadDefinitionClasses(is); |
| | | } catch (InitializationException e) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | /** |
| | | * Add a Jar file to this class loader. |
| | | * |
| | | * @param jarFile |
| | | * The name of the Jar file. |
| | | * @throws MalformedURLException |
| | | * If a protocol handler for the URL could not be found, or |
| | | * if some other error occurred while constructing the URL. |
| | | * @throws SecurityException |
| | | * If a required system property value cannot be accessed. |
| | | */ |
| | | public void addJarFile(File jarFile) throws SecurityException, MalformedURLException { |
| | | addURL(jarFile.toURI().toURL()); |
| | | } |
| | | |
| | | LocalizableMessage message = ERR_CLASS_LOADER_CANNOT_LOAD_EXTENSION.get(jarFile |
| | | .getName(), EXTENSION_MANIFEST, stackTraceToSingleLineString(e)); |
| | | throw new InitializationException(message); |
| | | } |
| | | try { |
| | | // Log build information of extensions in the error log |
| | | String[] information = getBuildInformation(jarFile); |
| | | logError( |
| | | NOTE_LOG_EXTENSION_INFORMATION. |
| | | get(jarFile.getName(), |
| | | information[1], |
| | | information[2])); |
| | | } catch(Exception e) { |
| | | // Do not log information for that extension |
| | | } |
| | | } |
| | | } |
| | | |
| | | // The name of the manifest file listing the core configuration |
| | | // definition classes. |
| | | private static final String CORE_MANIFEST = "core.manifest"; |
| | | |
| | | // The name of the manifest file listing a extension's configuration |
| | | // definition classes. |
| | | private static final String EXTENSION_MANIFEST = "extension.manifest"; |
| | | |
| | | /** |
| | | * Forcefully load configuration definition classes named in a |
| | | * manifest file. |
| | | * |
| | | * @param is |
| | | * The manifest file input stream. |
| | | * @throws InitializationException |
| | | * If the definition classes could not be loaded and |
| | | * initialized. |
| | | */ |
| | | private void loadDefinitionClasses(InputStream is) |
| | | throws InitializationException { |
| | | BufferedReader reader = new BufferedReader(new InputStreamReader( |
| | | is)); |
| | | List<AbstractManagedObjectDefinition<?, ?>> definitions = |
| | | new LinkedList<AbstractManagedObjectDefinition<?,?>>(); |
| | | while (true) { |
| | | String className; |
| | | try { |
| | | className = reader.readLine(); |
| | | } catch (IOException e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_READ_MANIFEST_FILE.get(String |
| | | .valueOf(e.getLocalizableMessage())); |
| | | throw new InitializationException(msg, e); |
| | | } |
| | | // The name of the lib directory. |
| | | private static final String LIB_DIR = "lib"; |
| | | |
| | | // Break out when the end of the manifest is reached. |
| | | if (className == null) { |
| | | break; |
| | | } |
| | | // The name of the extensions directory. |
| | | private static final String EXTENSIONS_DIR = "extensions"; |
| | | |
| | | // Skip blank lines. |
| | | className = className.trim(); |
| | | if (className.length() == 0) { |
| | | continue; |
| | | } |
| | | // The singleton instance. |
| | | private static final ClassLoaderProvider INSTANCE = new ClassLoaderProvider(); |
| | | |
| | | // Skip lines beginning with #. |
| | | if (className.startsWith("#")) { |
| | | continue; |
| | | } |
| | | // Attribute name in jar's MANIFEST corresponding to the revision number. |
| | | private static final String REVISION_NUMBER = "Revision-Number"; |
| | | |
| | | TRACER.debugLocalizableMessage(DebugLogLevel.INFO, "Loading class " + className); |
| | | // The attribute names for build information is name, version and revision |
| | | // number |
| | | private static final String[] BUILD_INFORMATION_ATTRIBUTE_NAMES = new String[] { |
| | | Attributes.Name.EXTENSION_NAME.toString(), Attributes.Name.IMPLEMENTATION_VERSION.toString(), |
| | | REVISION_NUMBER }; |
| | | |
| | | // Load the class and get an instance of it if it is a definition. |
| | | Class<?> theClass; |
| | | try { |
| | | theClass = Class.forName(className, true, loader); |
| | | } catch (Exception e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_LOAD_CLASS.get(className, String |
| | | .valueOf(e.getLocalizableMessage())); |
| | | throw new InitializationException(msg, e); |
| | | } |
| | | if (AbstractManagedObjectDefinition.class.isAssignableFrom(theClass)) { |
| | | // We need to instantiate it using its getInstance() static method. |
| | | Method method; |
| | | /** |
| | | * Get the single application wide class loader provider instance. |
| | | * |
| | | * @return Returns the single application wide class loader provider |
| | | * instance. |
| | | */ |
| | | public static ClassLoaderProvider getInstance() { |
| | | return INSTANCE; |
| | | } |
| | | |
| | | // Set of registered Jar files. |
| | | private Set<File> jarFiles = new HashSet<File>(); |
| | | |
| | | // Underlying class loader used to load classes and resources (null |
| | | // if disabled). |
| | | // |
| | | // We contain a reference to the URLClassLoader rather than |
| | | // sub-class it so that it is possible to replace the loader at |
| | | // run-time. For example, when removing or replacing extension Jar |
| | | // files (the URLClassLoader only supports adding new |
| | | // URLs, not removal). |
| | | private MyURLClassLoader loader = null; |
| | | |
| | | // Private constructor. |
| | | private ClassLoaderProvider() { |
| | | // No implementation required. |
| | | } |
| | | |
| | | /** |
| | | * Add the named extensions to this class loader provider. |
| | | * |
| | | * @param extensions |
| | | * The names of the extensions to be loaded. The names should not |
| | | * contain any path elements and must be located within the |
| | | * extensions folder. |
| | | * @throws InitializationException |
| | | * If one of the extensions could not be loaded and initialized. |
| | | * @throws IllegalStateException |
| | | * If this class loader provider is disabled. |
| | | * @throws IllegalArgumentException |
| | | * If one of the extension names was not a single relative path |
| | | * name element or was an absolute path. |
| | | */ |
| | | public synchronized void addExtension(String... extensions) throws InitializationException, IllegalStateException, |
| | | IllegalArgumentException { |
| | | ensureNotNull(extensions); |
| | | |
| | | if (loader == null) { |
| | | throw new IllegalStateException("Class loader provider is disabled."); |
| | | } |
| | | |
| | | File libPath = new File(DirectoryServer.getInstanceRoot(), LIB_DIR); |
| | | File extensionsPath = new File(libPath, EXTENSIONS_DIR); |
| | | |
| | | ArrayList<File> files = new ArrayList<File>(extensions.length); |
| | | for (String extension : extensions) { |
| | | File file = new File(extensionsPath, extension); |
| | | |
| | | // For security reasons we need to make sure that the file name |
| | | // passed in did not contain any path elements and names a file |
| | | // in the extensions folder. |
| | | |
| | | // Can handle potential null parent. |
| | | if (!extensionsPath.equals(file.getParentFile())) { |
| | | throw new IllegalArgumentException("Illegal file name: " + extension); |
| | | } |
| | | |
| | | // The file is valid. |
| | | files.add(file); |
| | | } |
| | | |
| | | // Add the extensions. |
| | | addExtension(files.toArray(new File[files.size()])); |
| | | } |
| | | |
| | | /** |
| | | * Disable this class loader provider and removed any registered extensions. |
| | | * |
| | | * @throws IllegalStateException |
| | | * If this class loader provider is already disabled. |
| | | */ |
| | | public synchronized void disable() throws IllegalStateException { |
| | | if (loader == null) { |
| | | throw new IllegalStateException("Class loader provider already disabled."); |
| | | } |
| | | loader = null; |
| | | jarFiles = new HashSet<File>(); |
| | | } |
| | | |
| | | /** |
| | | * Enable this class loader provider using the application's class loader as |
| | | * the parent class loader. |
| | | * |
| | | * @throws InitializationException |
| | | * If the class loader provider could not initialize |
| | | * successfully. |
| | | * @throws IllegalStateException |
| | | * If this class loader provider is already enabled. |
| | | */ |
| | | public synchronized void enable() throws InitializationException, IllegalStateException { |
| | | enable(RootCfgDefn.class.getClassLoader()); |
| | | } |
| | | |
| | | /** |
| | | * Enable this class loader provider using the provided parent class loader. |
| | | * |
| | | * @param parent |
| | | * The parent class loader. |
| | | * @throws InitializationException |
| | | * If the class loader provider could not initialize |
| | | * successfully. |
| | | * @throws IllegalStateException |
| | | * If this class loader provider is already enabled. |
| | | */ |
| | | public synchronized void enable(ClassLoader parent) throws InitializationException, IllegalStateException { |
| | | if (loader != null) { |
| | | throw new IllegalStateException("Class loader provider already enabled."); |
| | | } |
| | | |
| | | if (parent != null) { |
| | | loader = new MyURLClassLoader(parent); |
| | | } else { |
| | | loader = new MyURLClassLoader(); |
| | | } |
| | | |
| | | // Forcefully load all configuration definition classes in |
| | | // OpenDS.jar. |
| | | initializeCoreComponents(); |
| | | |
| | | // Put extensions jars into the class loader and load all |
| | | // configuration definition classes in that they contain. |
| | | // First load the extension from the install directory, then |
| | | // from the instance directory. |
| | | File libDir; |
| | | File installExtensionsPath; |
| | | File instanceExtensionsPath; |
| | | |
| | | // load install dir extension |
| | | libDir = new File(DirectoryServer.getServerRoot(), LIB_DIR); |
| | | try { |
| | | method = theClass.getMethod("getInstance"); |
| | | installExtensionsPath = new File(libDir, EXTENSIONS_DIR).getCanonicalFile(); |
| | | } catch (Exception e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_FIND_GET_INSTANCE_METHOD.get( |
| | | className, String.valueOf(e.getLocalizableMessage())); |
| | | throw new InitializationException(msg, e); |
| | | installExtensionsPath = new File(libDir, EXTENSIONS_DIR); |
| | | } |
| | | initializeAllExtensions(installExtensionsPath); |
| | | |
| | | // Get the definition instance. |
| | | AbstractManagedObjectDefinition<?, ?> d; |
| | | // load instance dir extension |
| | | libDir = new File(DirectoryServer.getInstanceRoot(), LIB_DIR); |
| | | try { |
| | | d = (AbstractManagedObjectDefinition<?, ?>) method.invoke(null); |
| | | instanceExtensionsPath = new File(libDir, EXTENSIONS_DIR).getCanonicalFile(); |
| | | } catch (Exception e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_INVOKE_GET_INSTANCE_METHOD.get( |
| | | className, String.valueOf(e.getLocalizableMessage())); |
| | | throw new InitializationException(msg, e); |
| | | instanceExtensionsPath = new File(libDir, EXTENSIONS_DIR); |
| | | } |
| | | definitions.add(d); |
| | | } |
| | | if (!installExtensionsPath.getAbsolutePath().equals(instanceExtensionsPath.getAbsolutePath())) { |
| | | initializeAllExtensions(instanceExtensionsPath); |
| | | } |
| | | } |
| | | |
| | | // Initialize any definitions that were loaded. |
| | | for (AbstractManagedObjectDefinition<?, ?> d : definitions) { |
| | | try { |
| | | d.initialize(); |
| | | } catch (Exception e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_INITIALIZE_DEFN.get(d.getName(), |
| | | d.getClass().getName(), String.valueOf(e.getLocalizableMessage())); |
| | | throw new InitializationException(msg, e); |
| | | } |
| | | /** |
| | | * Gets the class loader which should be used for loading classes and |
| | | * resources. When this class loader provider is disabled, the system |
| | | * default class loader will be returned by default. |
| | | * <p> |
| | | * Applications <b>MUST NOT</b> maintain persistent references to the class |
| | | * loader as it can change at run-time. |
| | | * |
| | | * @return Returns the class loader which should be used for loading classes |
| | | * and resources. |
| | | */ |
| | | public synchronized ClassLoader getClassLoader() { |
| | | if (loader != null) { |
| | | return loader; |
| | | } else { |
| | | return ClassLoader.getSystemClassLoader(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Load the named Jar file. |
| | | * |
| | | * @param jar |
| | | * The name of the Jar file to load. |
| | | * @return Returns the loaded Jar file. |
| | | * @throws InitializationException |
| | | * If the Jar file could not be loaded. |
| | | */ |
| | | private JarFile loadJarFile(File jar) |
| | | throws InitializationException { |
| | | JarFile jarFile; |
| | | |
| | | try { |
| | | // Load the extension jar file. |
| | | jarFile = new JarFile(jar); |
| | | } catch (Exception e) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | LocalizableMessage message = ERR_ADMIN_CANNOT_OPEN_JAR_FILE.get( |
| | | jar.getName(), jar.getParent(), stackTraceToSingleLineString(e)); |
| | | throw new InitializationException(message); |
| | | /** |
| | | * Indicates whether this class loader provider is enabled. |
| | | * |
| | | * @return Returns <code>true</code> if this class loader provider is |
| | | * enabled. |
| | | */ |
| | | public synchronized boolean isEnabled() { |
| | | return loader != null; |
| | | } |
| | | return jarFile; |
| | | } |
| | | |
| | | /** |
| | | * Add the named extensions to this class loader. |
| | | * |
| | | * @param extensions |
| | | * The names of the extensions to be loaded. |
| | | * @throws InitializationException |
| | | * If one of the extensions could not be loaded and initialized. |
| | | */ |
| | | private synchronized void addExtension(File... extensions) throws InitializationException { |
| | | // First add the Jar files to the class loader. |
| | | List<JarFile> jars = new LinkedList<JarFile>(); |
| | | for (File extension : extensions) { |
| | | if (jarFiles.contains(extension)) { |
| | | // Skip this file as it is already loaded. |
| | | continue; |
| | | } |
| | | |
| | | // Attempt to load it. |
| | | jars.add(loadJarFile(extension)); |
| | | |
| | | // Register the Jar file with the class loader. |
| | | try { |
| | | loader.addJarFile(extension); |
| | | } catch (Exception e) { |
| | | debugLogger.trace("Unable to register the jar file with the class loader", e); |
| | | LocalizableMessage message = ERR_ADMIN_CANNOT_OPEN_JAR_FILE.get(extension.getName(), |
| | | extension.getParent(), stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD)); |
| | | throw new InitializationException(message); |
| | | } |
| | | jarFiles.add(extension); |
| | | } |
| | | |
| | | // Now forcefully load the configuration definition classes. |
| | | for (JarFile jar : jars) { |
| | | initializeExtension(jar); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Prints out all information about extensions. |
| | | * |
| | | * @return a String instance representing all information about extensions; |
| | | * <code>null</code> if there is no information available. |
| | | */ |
| | | public String printExtensionInformation() { |
| | | File extensionsPath = new File(new StringBuilder(DirectoryServer.getServerRoot()).append(File.separator) |
| | | .append(LIB_DIR).append(File.separator).append(EXTENSIONS_DIR).toString()); |
| | | |
| | | if (!extensionsPath.exists() || !extensionsPath.isDirectory()) { |
| | | // no extensions' directory |
| | | return null; |
| | | } |
| | | |
| | | File[] extensions = extensionsPath.listFiles(new FileFilter() { |
| | | public boolean accept(File pathname) { |
| | | // only files with names ending with ".jar" |
| | | return pathname.isFile() && pathname.getName().endsWith(".jar"); |
| | | } |
| | | }); |
| | | |
| | | if (extensions.length == 0) { |
| | | return null; |
| | | } |
| | | |
| | | ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| | | PrintStream ps = new PrintStream(baos); |
| | | // prints: |
| | | // -- |
| | | // Name Build number Revision number |
| | | ps.printf("--%s %-20s %-20s %-20s%s", EOL, "Name", "Build number", "Revision number", EOL); |
| | | |
| | | for (File extension : extensions) { |
| | | // retrieve MANIFEST entry and display name, build number and |
| | | // revision |
| | | // number |
| | | try { |
| | | JarFile jarFile = new JarFile(extension); |
| | | JarEntry entry = jarFile.getJarEntry("admin/" + EXTENSION_MANIFEST); |
| | | if (entry == null) { |
| | | continue; |
| | | } |
| | | |
| | | String[] information = getBuildInformation(jarFile); |
| | | |
| | | ps.append("Extension: "); |
| | | boolean addBlank = false; |
| | | for (String name : information) { |
| | | if (addBlank) { |
| | | ps.append(addBlank ? " " : ""); // add blank if not |
| | | // first append |
| | | } else { |
| | | addBlank = true; |
| | | } |
| | | |
| | | ps.printf("%-20s", name); |
| | | } |
| | | ps.append(EOL); |
| | | } catch (Exception e) { |
| | | // ignore extra information for this extension |
| | | } |
| | | } |
| | | |
| | | return baos.toString(); |
| | | } |
| | | |
| | | /** |
| | | * Returns a String array with the following information : <br> |
| | | * index 0: the name of the extension. <br> |
| | | * index 1: the build number of the extension. <br> |
| | | * index 2: the revision number of the extension. |
| | | * |
| | | * @param extension |
| | | * the jar file of the extension |
| | | * @return a String array containing the name, the build number and the |
| | | * revision number of the extension given in argument |
| | | * @throws java.io.IOException |
| | | * thrown if the jar file has been closed. |
| | | */ |
| | | private String[] getBuildInformation(JarFile extension) throws IOException { |
| | | String[] result = new String[3]; |
| | | |
| | | // retrieve MANIFEST entry and display name, version and revision |
| | | Manifest manifest = extension.getManifest(); |
| | | |
| | | if (manifest != null) { |
| | | Attributes attributes = manifest.getMainAttributes(); |
| | | |
| | | int index = 0; |
| | | for (String name : BUILD_INFORMATION_ATTRIBUTE_NAMES) { |
| | | String value = attributes.getValue(name); |
| | | if (value == null) { |
| | | value = "<unknown>"; |
| | | } |
| | | result[index++] = value; |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Put extensions jars into the class loader and load all configuration |
| | | * definition classes in that they contain. |
| | | * |
| | | * @param extensionsPath |
| | | * Indicates where extensions are located. |
| | | * @throws InitializationException |
| | | * If the extensions folder could not be accessed or if a |
| | | * extension jar file could not be accessed or if one of the |
| | | * configuration definition classes could not be initialized. |
| | | */ |
| | | private void initializeAllExtensions(File extensionsPath) throws InitializationException { |
| | | |
| | | try { |
| | | if (!extensionsPath.exists()) { |
| | | // The extensions directory does not exist. This is not a |
| | | // critical problem. |
| | | adminLogger.error(ERR_ADMIN_NO_EXTENSIONS_DIR, String.valueOf(extensionsPath)); |
| | | return; |
| | | } |
| | | |
| | | if (!extensionsPath.isDirectory()) { |
| | | // The extensions directory is not a directory. This is more |
| | | // critical. |
| | | LocalizableMessage message = ERR_ADMIN_EXTENSIONS_DIR_NOT_DIRECTORY.get(String.valueOf(extensionsPath)); |
| | | throw new InitializationException(message); |
| | | } |
| | | |
| | | // Get each extension file name. |
| | | FileFilter filter = new FileFilter() { |
| | | |
| | | /** |
| | | * Must be a Jar file. |
| | | */ |
| | | public boolean accept(File pathname) { |
| | | if (!pathname.isFile()) { |
| | | return false; |
| | | } |
| | | |
| | | String name = pathname.getName(); |
| | | return name.endsWith(".jar"); |
| | | } |
| | | |
| | | }; |
| | | |
| | | // Add and initialize the extensions. |
| | | addExtension(extensionsPath.listFiles(filter)); |
| | | } catch (InitializationException e) { |
| | | debugLogger.trace("Unable to initialize all extensions", e); |
| | | throw e; |
| | | } catch (Exception e) { |
| | | debugLogger.trace("Unable to initialize all extensions", e); |
| | | LocalizableMessage message = ERR_ADMIN_EXTENSIONS_CANNOT_LIST_FILES.get(String.valueOf(extensionsPath), |
| | | stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD)); |
| | | throw new InitializationException(message, e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Make sure all core configuration definitions are loaded. |
| | | * |
| | | * @throws InitializationException |
| | | * If the core manifest file could not be read or if one of the |
| | | * configuration definition classes could not be initialized. |
| | | */ |
| | | private void initializeCoreComponents() throws InitializationException { |
| | | InputStream is = RootCfgDefn.class.getResourceAsStream("/admin/" + CORE_MANIFEST); |
| | | |
| | | if (is == null) { |
| | | LocalizableMessage message = ERR_ADMIN_CANNOT_FIND_CORE_MANIFEST.get(CORE_MANIFEST); |
| | | throw new InitializationException(message); |
| | | } |
| | | |
| | | try { |
| | | loadDefinitionClasses(is); |
| | | } catch (InitializationException e) { |
| | | debugLogger.trace("Unable to initialize core components", e); |
| | | LocalizableMessage message = ERR_CLASS_LOADER_CANNOT_LOAD_CORE.get(CORE_MANIFEST, |
| | | stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD)); |
| | | throw new InitializationException(message); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Make sure all the configuration definition classes in a extension are |
| | | * loaded. |
| | | * |
| | | * @param jarFile |
| | | * The extension's Jar file. |
| | | * @throws InitializationException |
| | | * If the extension jar file could not be accessed or if one of |
| | | * the configuration definition classes could not be |
| | | * initialized. |
| | | */ |
| | | private void initializeExtension(JarFile jarFile) throws InitializationException { |
| | | JarEntry entry = jarFile.getJarEntry("admin/" + EXTENSION_MANIFEST); |
| | | if (entry != null) { |
| | | InputStream is; |
| | | try { |
| | | is = jarFile.getInputStream(entry); |
| | | } catch (Exception e) { |
| | | debugLogger.trace("Unable to get input stream from jar", e); |
| | | LocalizableMessage message = ERR_ADMIN_CANNOT_READ_EXTENSION_MANIFEST.get(EXTENSION_MANIFEST, |
| | | jarFile.getName(), stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD)); |
| | | throw new InitializationException(message); |
| | | } |
| | | |
| | | try { |
| | | loadDefinitionClasses(is); |
| | | } catch (InitializationException e) { |
| | | debugLogger.trace("Unable to load classes from input stream", e); |
| | | LocalizableMessage message = ERR_CLASS_LOADER_CANNOT_LOAD_EXTENSION.get(jarFile.getName(), |
| | | EXTENSION_MANIFEST, stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD)); |
| | | throw new InitializationException(message); |
| | | } |
| | | try { |
| | | // Log build information of extensions in the error log |
| | | String[] information = getBuildInformation(jarFile); |
| | | LocalizableMessage message = NOTE_LOG_EXTENSION_INFORMATION.get(jarFile.getName(), information[1], |
| | | information[2]); |
| | | LocalizedLogger.getLocalizedLogger(message.resourceName()).error(message); |
| | | } catch (Exception e) { |
| | | // Do not log information for that extension |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Forcefully load configuration definition classes named in a manifest |
| | | * file. |
| | | * |
| | | * @param is |
| | | * The manifest file input stream. |
| | | * @throws InitializationException |
| | | * If the definition classes could not be loaded and |
| | | * initialized. |
| | | */ |
| | | private void loadDefinitionClasses(InputStream is) throws InitializationException { |
| | | BufferedReader reader = new BufferedReader(new InputStreamReader(is)); |
| | | List<AbstractManagedObjectDefinition<?, ?>> definitions = new LinkedList<AbstractManagedObjectDefinition<?, ?>>(); |
| | | while (true) { |
| | | String className; |
| | | try { |
| | | className = reader.readLine(); |
| | | } catch (IOException e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_READ_MANIFEST_FILE.get(String.valueOf(e.getMessage())); |
| | | throw new InitializationException(msg, e); |
| | | } |
| | | |
| | | // Break out when the end of the manifest is reached. |
| | | if (className == null) { |
| | | break; |
| | | } |
| | | |
| | | // Skip blank lines. |
| | | className = className.trim(); |
| | | if (className.length() == 0) { |
| | | continue; |
| | | } |
| | | |
| | | // Skip lines beginning with #. |
| | | if (className.startsWith("#")) { |
| | | continue; |
| | | } |
| | | |
| | | debugLogger.trace("Loading class " + className); |
| | | |
| | | // Load the class and get an instance of it if it is a definition. |
| | | Class<?> theClass; |
| | | try { |
| | | theClass = Class.forName(className, true, loader); |
| | | } catch (Exception e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_LOAD_CLASS.get(className, |
| | | String.valueOf(e.getMessage())); |
| | | throw new InitializationException(msg, e); |
| | | } |
| | | if (AbstractManagedObjectDefinition.class.isAssignableFrom(theClass)) { |
| | | // We need to instantiate it using its getInstance() static |
| | | // method. |
| | | Method method; |
| | | try { |
| | | method = theClass.getMethod("getInstance"); |
| | | } catch (Exception e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_FIND_GET_INSTANCE_METHOD.get(className, |
| | | String.valueOf(e.getMessage())); |
| | | throw new InitializationException(msg, e); |
| | | } |
| | | |
| | | // Get the definition instance. |
| | | AbstractManagedObjectDefinition<?, ?> d; |
| | | try { |
| | | d = (AbstractManagedObjectDefinition<?, ?>) method.invoke(null); |
| | | } catch (Exception e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_INVOKE_GET_INSTANCE_METHOD.get(className, |
| | | String.valueOf(e.getMessage())); |
| | | throw new InitializationException(msg, e); |
| | | } |
| | | definitions.add(d); |
| | | } |
| | | } |
| | | |
| | | // Initialize any definitions that were loaded. |
| | | for (AbstractManagedObjectDefinition<?, ?> d : definitions) { |
| | | try { |
| | | d.initialize(); |
| | | } catch (Exception e) { |
| | | LocalizableMessage msg = ERR_CLASS_LOADER_CANNOT_INITIALIZE_DEFN.get(d.getName(), d.getClass() |
| | | .getName(), String.valueOf(e.getMessage())); |
| | | throw new InitializationException(msg, e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Load the named Jar file. |
| | | * |
| | | * @param jar |
| | | * The name of the Jar file to load. |
| | | * @return Returns the loaded Jar file. |
| | | * @throws InitializationException |
| | | * If the Jar file could not be loaded. |
| | | */ |
| | | private JarFile loadJarFile(File jar) throws InitializationException { |
| | | JarFile jarFile; |
| | | |
| | | try { |
| | | // Load the extension jar file. |
| | | jarFile = new JarFile(jar); |
| | | } catch (Exception e) { |
| | | debugLogger.trace("Unable to load jar file: " + jar, e); |
| | | |
| | | LocalizableMessage message = ERR_ADMIN_CANNOT_OPEN_JAR_FILE.get(jar.getName(), jar.getParent(), |
| | | stackTraceToSingleLineString(e, DynamicConstants.DEBUG_BUILD)); |
| | | throw new InitializationException(message); |
| | | } |
| | | return jarFile; |
| | | } |
| | | |
| | | } |