/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008-2009 Sun Microsystems, Inc. * Portions copyright 2012-2015 ForgeRock AS. */ package org.forgerock.opendj.config; import static com.forgerock.opendj.ldap.config.AdminMessages.*; import static com.forgerock.opendj.ldap.config.ExtensionMessages.NOTE_LOG_EXTENSION_INFORMATION; import static com.forgerock.opendj.util.StaticUtils.EOL; import static com.forgerock.opendj.util.StaticUtils.stackTraceToSingleLineString; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.config.server.ConfigException; import org.forgerock.opendj.server.config.meta.RootCfgDefn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.forgerock.opendj.ldap.config.AdminMessages; /** * This class is responsible for managing the configuration framework including: *
* Initially the configuration framework is disabled, and calls to the {@link #getClassLoader()} * will return the system default class loader. *
* Applications MUST NOT maintain persistent references to the class loader as it can change at run-time.
*/
public final class ConfigurationFramework {
/**
* Private URLClassLoader implementation. This is only required so that we can provide access to the addURL method.
*/
private static final class MyURLClassLoader extends URLClassLoader {
/**
* Create a class loader with the default parent class loader.
*/
public MyURLClassLoader() {
super(new URL[0]);
}
/**
* Create a class loader with the provided parent class loader.
*
* @param parent
* The parent class loader.
*/
public MyURLClassLoader(final 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(final File jarFile) throws MalformedURLException {
addURL(jarFile.toURI().toURL());
}
}
private static final String MANIFEST =
"/META-INF/services/org.forgerock.opendj.config.AbstractManagedObjectDefinition";
private static final LocalizedLogger adminLogger = LocalizedLogger
.getLocalizedLogger(AdminMessages.resourceName());
private static final Logger debugLogger = LoggerFactory.getLogger(ConfigurationFramework.class);
/** 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 ConfigurationFramework INSTANCE = new ConfigurationFramework();
/** 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 };
/**
* Returns the single application wide configuration framework instance.
*
* @return The single application wide configuration framework instance.
*/
public static ConfigurationFramework getInstance() {
return INSTANCE;
}
/** Set of registered Jar files. */
private Set
* 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;
private boolean isClient = true;
private String installPath;
private String instancePath;
private ClassLoader parent;
/** Private constructor. */
private ConfigurationFramework() {
// No implementation required.
}
/**
* Returns the class loader which should be used for loading classes and resources. When this configuration
* framework is disabled, the system default class loader will be returned by default.
*
* Applications MUST NOT 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();
}
}
/**
* Initializes the configuration framework using the application's class loader as the parent class loader,
* and the current working directory as the install and instance path.
*
* @return The configuration framework.
* @throws ConfigException
* If the configuration framework could not initialize successfully.
* @throws IllegalStateException
* If the configuration framework has already been initialized.
*/
public ConfigurationFramework initialize() throws ConfigException {
return initialize(null);
}
/**
* Initializes the configuration framework using the application's class loader
* as the parent class loader, and the provided install/instance path.
*
* @param installAndInstancePath
* The path where application binaries and data are located.
* @return The configuration framework.
* @throws ConfigException
* If the configuration framework could not initialize successfully.
* @throws IllegalStateException
* If the configuration framework has already been initialized.
*/
public ConfigurationFramework initialize(final String installAndInstancePath)
throws ConfigException {
return initialize(installAndInstancePath, installAndInstancePath);
}
/**
* Initializes the configuration framework using the application's class loader
* as the parent class loader, and the provided install and instance paths.
*
* @param installPath
* The path where application binaries are located.
* @param instancePath
* The path where application data are located.
* @return The configuration framework.
* @throws ConfigException
* If the configuration framework could not initialize successfully.
* @throws IllegalStateException
* If the configuration framework has already been initialized.
*/
public ConfigurationFramework initialize(final String installPath, final String instancePath)
throws ConfigException {
return initialize(installPath, instancePath, RootCfgDefn.class.getClassLoader());
}
/**
* Initializes the configuration framework using the provided parent class
* loader and install and instance paths.
*
* @param installPath
* The path where application binaries are located.
* @param instancePath
* The path where application data are located.
* @param parent
* The parent class loader.
* @return The configuration framework.
* @throws ConfigException
* If the configuration framework could not initialize successfully.
* @throws IllegalStateException
* If the configuration framework has already been initialized.
*/
public synchronized ConfigurationFramework initialize(final String installPath,
final String instancePath, final ClassLoader parent) throws ConfigException {
if (loader != null) {
throw new IllegalStateException("configuration framework already initialized.");
}
this.installPath = installPath == null ? System.getenv("INSTALL_ROOT") : installPath;
if (instancePath != null) {
this.instancePath = instancePath;
} else {
this.instancePath = System.getenv("INSTANCE_ROOT") != null ? System.getenv("INSTANCE_ROOT")
: this.installPath;
}
this.parent = parent;
initialize0();
return this;
}
/**
* Returns {@code true} if the configuration framework is being used within
* a client application. Client applications will perform less property
* value validation than server applications because they do not have
* resources available such as the server schema.
*
* @return {@code true} if the configuration framework is being used within a client application.
*/
public boolean isClient() {
return isClient;
}
/**
* Returns {@code true} if the configuration framework has been initialized.
*
* @return {@code true} if the configuration framework has been initialized.
*/
public synchronized boolean isInitialized() {
return loader != null;
}
/**
* Prints out all information about extensions.
*
* @return A string representing all information about extensions;
* null if there is no information available.
*/
public String printExtensionInformation() {
final File extensionsPath = buildExtensionPath(installPath);
final List
* index 0: the name of the extension.
* index 1: the build number of the extension.
* 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(final JarFile extension) throws IOException {
final String[] result = new String[3];
// retrieve MANIFEST entry and display name, version and revision
final Manifest manifest = extension.getManifest();
if (manifest != null) {
final Attributes attributes = manifest.getMainAttributes();
int index = 0;
for (final String name : BUILD_INFORMATION_ATTRIBUTE_NAMES) {
String value = attributes.getValue(name);
if (value == null) {
value = "