Copyright (c) 2003, 2019 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2003, 2019 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.osgi.internal.framework; import static org.osgi.framework.Constants.FRAMEWORK_LANGUAGE; import static org.osgi.framework.Constants.FRAMEWORK_OS_NAME; import static org.osgi.framework.Constants.FRAMEWORK_OS_VERSION; import static org.osgi.framework.Constants.FRAMEWORK_PROCESSOR; import static org.osgi.framework.Constants.FRAMEWORK_STORAGE_CLEAN; import static org.osgi.framework.Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT; import static org.osgi.framework.Constants.FRAMEWORK_UUID; import static org.osgi.framework.Constants.FRAMEWORK_VENDOR; import static org.osgi.framework.Constants.SUPPORTS_FRAMEWORK_EXTENSION; import static org.osgi.framework.Constants.SUPPORTS_FRAMEWORK_FRAGMENT; import static org.osgi.framework.Constants.SUPPORTS_FRAMEWORK_REQUIREBUNDLE; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.JarURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; import java.util.StringTokenizer; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.internal.adaptor.ConsoleManager; import org.eclipse.osgi.framework.log.FrameworkLogEntry; import org.eclipse.osgi.internal.debug.Debug; import org.eclipse.osgi.internal.debug.FrameworkDebugOptions; import org.eclipse.osgi.internal.hookregistry.HookRegistry; import org.eclipse.osgi.internal.location.EquinoxLocations; import org.eclipse.osgi.internal.location.LocationHelper; import org.eclipse.osgi.internal.log.EquinoxLogServices; import org.eclipse.osgi.internal.messages.Msg; import org.eclipse.osgi.service.datalocation.Location; import org.eclipse.osgi.service.debug.DebugOptions; import org.eclipse.osgi.service.environment.Constants; import org.eclipse.osgi.service.environment.EnvironmentInfo; import org.eclipse.osgi.util.ManifestElement; import org.eclipse.osgi.util.NLS; import org.osgi.framework.Version;
Internal class.
/** * Internal class. */
public class EquinoxConfiguration implements EnvironmentInfo { private static final String CONFIG_FILE = "config.ini"; //$NON-NLS-1$ // While we recognize the SunOS operating system, we change // this internally to be Solaris. private static final String INTERNAL_OS_SUNOS = "SunOS"; //$NON-NLS-1$ private static final String INTERNAL_OS_LINUX = "Linux"; //$NON-NLS-1$ private static final String INTERNAL_OS_MACOSX = "Mac OS"; //$NON-NLS-1$ private static final String INTERNAL_OS_AIX = "AIX"; //$NON-NLS-1$ private static final String INTERNAL_OS_HPUX = "HP-UX"; //$NON-NLS-1$ private static final String INTERNAL_OS_QNX = "QNX"; //$NON-NLS-1$ private static final String INTERNAL_OS_OS400 = "OS/400"; //$NON-NLS-1$ private static final String INTERNAL_OS_OS390 = "OS/390"; //$NON-NLS-1$ private static final String INTERNAL_OS_ZOS = "z/OS"; //$NON-NLS-1$ private static final String INTERNAL_OS_FREEBSD = "FreeBSD"; //$NON-NLS-1$ // While we recognize the i386 architecture, we change // this internally to be x86. private static final String INTERNAL_ARCH_I386 = "i386"; //$NON-NLS-1$ // While we recognize the amd64 architecture, we change // this internally to be x86_64. private static final String INTERNAL_AMD64 = "amd64"; //$NON-NLS-1$ public static final String VARIABLE_DELIM_STRING = "$"; //$NON-NLS-1$ public static final char VARIABLE_DELIM_CHAR = '$'; private final ConfigValues configValues; private final Debug debug; private final DebugOptions debugOptions; private final HookRegistry hookRegistry; private final AliasMapper aliasMapper = new AliasMapper(); private final EquinoxLocations equinoxLocations; private volatile String[] allArgs; private volatile String[] frameworkArgs; private volatile String[] appArgs; // dev mode fields private final boolean inDevelopmentMode; private final File devLocation; private final Object devMonitor = new Object(); private String[] devDefaultClasspath; private Dictionary<String, String> devProperties = null; // timestamp for the dev.properties file private long devLastModified = 0; public final boolean contextBootDelegation; public final boolean compatibilityBootDelegation; public final boolean compatibilityLazyTriggerOnFailLoad; public final List<String> LIB_EXTENSIONS; public final List<String> ECLIPSE_LIB_VARIANTS; public final boolean COPY_NATIVES; public final List<String> ECLIPSE_NL_JAR_VARIANTS; public final boolean DEFINE_PACKAGE_ATTRIBUTES; public final boolean BUNDLE_SET_TCCL; public final int BSN_VERSION; public static final int BSN_VERSION_SINGLE = 1; public static final int BSN_VERSION_MULTIPLE = 2; public static final int BSN_VERSION_MANAGED = 3; public final boolean throwErrorOnFailedStart; public final boolean CLASS_CERTIFICATE; public final boolean PARALLEL_CAPABLE; private final Map<Throwable, Integer> exceptions = new LinkedHashMap<>(0); // JVM os.arch property name public static final String PROP_JVM_OS_ARCH = "os.arch"; //$NON-NLS-1$ // JVM os.name property name public static final String PROP_JVM_OS_NAME = "os.name"; //$NON-NLS-1$ // JVM os.version property name public static final String PROP_JVM_OS_VERSION = "os.version"; //$NON-NLS-1$ public static final String PROP_JVM_SPEC_VERSION = "java.specification.version"; //$NON-NLS-1$ public static final String PROP_JVM_SPEC_NAME = "java.specification.name"; //$NON-NLS-1$ public static final String PROP_SETPERMS_CMD = "osgi.filepermissions.command"; //$NON-NLS-1$ public static final String PROP_DEBUG = "osgi.debug"; //$NON-NLS-1$ public static final String PROP_DEBUG_VERBOSE = "osgi.debug.verbose"; //$NON-NLS-1$ public static final String PROP_DEV = "osgi.dev"; //$NON-NLS-1$ public static final String PROP_CLEAN = "osgi.clean"; //$NON-NLS-1$ public static final String PROP_USE_SYSTEM_PROPERTIES = "osgi.framework.useSystemProperties"; //$NON-NLS-1$ public static final String PROP_FRAMEWORK = "osgi.framework"; //$NON-NLS-1$ public static final String ECLIPSE_FRAMEWORK_VENDOR = "Eclipse"; //$NON-NLS-1$ public static final String PROP_OSGI_JAVA_PROFILE = "osgi.java.profile"; //$NON-NLS-1$ public static final String PROP_OSGI_JAVA_PROFILE_NAME = "osgi.java.profile.name"; //$NON-NLS-1$ // OSGi java profile bootdelegation; used to indicate how the org.osgi.framework.bootdelegation // property defined in the java profile should be processed, (ingnore, override, none). default is ignore public static final String PROP_OSGI_JAVA_PROFILE_BOOTDELEGATION = "osgi.java.profile.bootdelegation"; //$NON-NLS-1$ // indicates that the org.osgi.framework.bootdelegation in the java profile should be ingored public static final String PROP_OSGI_BOOTDELEGATION_IGNORE = "ignore"; //$NON-NLS-1$ // indicates that the org.osgi.framework.bootdelegation in the java profile should override the system property public static final String PROP_OSGI_BOOTDELEGATION_OVERRIDE = "override"; //$NON-NLS-1$ // indicates that the org.osgi.framework.bootdelegation in the java profile AND the system properties should be ignored public static final String PROP_OSGI_BOOTDELEGATION_NONE = "none"; //$NON-NLS-1$ public static final String PROP_CONTEXT_BOOTDELEGATION = "osgi.context.bootdelegation"; //$NON-NLS-1$ public static final String PROP_COMPATIBILITY_BOOTDELEGATION = "osgi.compatibility.bootdelegation"; //$NON-NLS-1$ public static final String PROP_DS_DELAYED_KEEPINSTANCES = "ds.delayed.keepInstances"; //$NON-NLS-1$ public static final String PROP_COMPATIBILITY_ERROR_FAILED_START = "osgi.compatibility.errorOnFailedStart"; //$NON-NLS-1$ public static final String PROP_COMPATIBILITY_START_LAZY = "osgi.compatibility.eagerStart.LazyActivation"; //$NON-NLS-1$ public static final String PROP_COMPATIBILITY_START_LAZY_ON_FAIL_CLASSLOAD = "osgi.compatibility.trigger.lazyActivation.onFailedClassLoad"; //$NON-NLS-1$ public static final String PROP_OSGI_OS = "osgi.os"; //$NON-NLS-1$ public static final String PROP_OSGI_WS = "osgi.ws"; //$NON-NLS-1$ public static final String PROP_OSGI_ARCH = "osgi.arch"; //$NON-NLS-1$ public static final String PROP_OSGI_NL = "osgi.nl"; //$NON-NLS-1$ public static final String PROP_OSGI_NL_USER = "osgi.nl.user"; //$NON-NLS-1$ public static final String PROP_ROOT_LOCALE = "equinox.root.locale"; //$NON-NLS-1$ public static final String PROP_PARENT_CLASSLOADER = "osgi.parentClassloader"; //$NON-NLS-1$ // A parent classloader type that specifies the framework classlaoder public static final String PARENT_CLASSLOADER_FWK = "fwk"; //$NON-NLS-1$ // System property used to set the context classloader parent classloader type (ccl is the default) public static final String PROP_CONTEXTCLASSLOADER_PARENT = "osgi.contextClassLoaderParent"; //$NON-NLS-1$ public static final String CONTEXTCLASSLOADER_PARENT_APP = "app"; //$NON-NLS-1$ public static final String CONTEXTCLASSLOADER_PARENT_EXT = "ext"; //$NON-NLS-1$ public static final String CONTEXTCLASSLOADER_PARENT_BOOT = "boot"; //$NON-NLS-1$ public static final String CONTEXTCLASSLOADER_PARENT_FWK = "fwk"; //$NON-NLS-1$ public static final String PROP_FRAMEWORK_LIBRARY_EXTENSIONS = "osgi.framework.library.extensions"; //$NON-NLS-1$ public static final String PROP_COPY_NATIVES = "osgi.classloader.copy.natives"; //$NON-NLS-1$ public static final String PROP_DEFINE_PACKAGES = "osgi.classloader.define.packages"; //$NON-NLS-1$ public static final String PROP_BUNDLE_SETTCCL = "eclipse.bundle.setTCCL"; //$NON-NLS-1$ public static final String PROP_EQUINOX_SECURITY = "eclipse.security"; //$NON-NLS-1$ public static final String PROP_FILE_LIMIT = "osgi.bundlefile.limit"; //$NON-NLS-1$ public final static String PROP_CLASS_CERTIFICATE_SUPPORT = "osgi.support.class.certificate"; //$NON-NLS-1$ public final static String PROP_CLASS_LOADER_TYPE = "osgi.classloader.type"; //$NON-NLS-1$ public final static String CLASS_LOADER_TYPE_PARALLEL = "parallel"; //$NON-NLS-1$ public static final String PROP_FORCED_RESTART = "osgi.forcedRestart"; //$NON-NLS-1$ public static final String PROP_IGNORE_USER_CONFIGURATION = "eclipse.ignoreUserConfiguration"; //$NON-NLS-1$ public static final String PROPERTY_STRICT_BUNDLE_ENTRY_PATH = "osgi.strictBundleEntryPath";//$NON-NLS-1$ public static final String PROP_CHECK_CONFIGURATION = "osgi.checkConfiguration"; //$NON-NLS-1$ private final boolean inCheckConfigurationMode; public static final String DEFAULT_STATE_SAVE_DELAY_INTERVAL = "30000"; //$NON-NLS-1$ public static final String PROP_STATE_SAVE_DELAY_INTERVAL = "eclipse.stateSaveDelayInterval"; //$NON-NLS-1$ public static final String PROP_MODULE_LOCK_TIMEOUT = "osgi.module.lock.timeout"; //$NON-NLS-1$ public static final String PROP_MODULE_AUTO_START_ON_RESOLVE = "osgi.module.auto.start.on.resolve"; //$NON-NLS-1$ public static final String PROP_ALLOW_RESTRICTED_PROVIDES = "osgi.equinox.allow.restricted.provides"; //$NON-NLS-1$ public static final String PROP_LOG_HISTORY_MAX = "equinox.log.history.max"; //$NON-NLS-1$ @Deprecated public static final String PROP_RESOLVER_THREAD_COUNT = "equinox.resolver.thead.count"; //$NON-NLS-1$ public static final String PROP_EQUINOX_RESOLVER_THREAD_COUNT = "equinox.resolver.thread.count"; //$NON-NLS-1$ public static final String PROP_EQUINOX_START_LEVEL_THREAD_COUNT = "equinox.start.level.thread.count"; //$NON-NLS-1$ public static final String PROP_EQUINOX_START_LEVEL_RESTRICT_PARALLEL = "equinox.start.level.restrict.parallel"; //$NON-NLS-1$ public static final String PROP_RESOLVER_REVISION_BATCH_SIZE = "equinox.resolver.revision.batch.size"; //$NON-NLS-1$ public static final String PROP_RESOLVER_BATCH_TIMEOUT = "equinox.resolver.batch.timeout"; //$NON-NLS-1$ public static final String PROP_SYSTEM_PROVIDE_HEADER = "equinox.system.provide.header"; //$NON-NLS-1$ public static final String SYSTEM_PROVIDE_HEADER_ORIGINAL = "original"; //$NON-NLS-1$ public static final String SYSTEM_PROVIDE_HEADER_SYSTEM = "system"; //$NON-NLS-1$ public static final String SYSTEM_PROVIDE_HEADER_SYSTEM_EXTRA = "system.extra"; //$NON-NLS-1$ public static final String PROP_DEFAULT_SUFFIX = ".default"; //$NON-NLS-1$ public static final Collection<String> PROP_WITH_ECLIPSE_STARTER_DEFAULTS = Collections.unmodifiableList(Arrays.asList(PROP_COMPATIBILITY_BOOTDELEGATION, PROP_DS_DELAYED_KEEPINSTANCES)); public static final String PROP_INIT_UUID = "equinox.init.uuid"; //$NON-NLS-1$ public static final String PROP_ACTIVE_THREAD_TYPE = "osgi.framework.activeThreadType"; //$NON-NLS-1$ public static final String ACTIVE_THREAD_TYPE_NORMAL = "normal"; //$NON-NLS-1$ public static final String PROP_GOSH_ARGS = "gosh.args"; //$NON-NLS-1$ public static final class ConfigValues {
Value of localConfig properties that should be considered null and for which access should not fall back to System.getProperty(String). The instance must be compared by identity (==, not equals) and must not be leaked outside this class.
/** * Value of {@link #localConfig} properties that should be considered * <code>null</code> and for which access should not fall back to * {@link System#getProperty(String)}. * The instance must be compared by identity (==, not equals) and must not * be leaked outside this class. */
private final static String NULL_CONFIG = new String("org.eclipse.equinox.configuration.null.value"); //$NON-NLS-1$ private final static Collection<String> populateInitConfig = Arrays.asList(PROP_OSGI_ARCH, PROP_OSGI_OS, PROP_OSGI_WS, PROP_OSGI_NL, FRAMEWORK_OS_NAME, FRAMEWORK_OS_VERSION, FRAMEWORK_PROCESSOR, FRAMEWORK_LANGUAGE); private final boolean useSystemProperties; private final Map<String, Object> initialConfig; private final Properties localConfig; private final Map<Throwable, Integer> exceptions; public ConfigValues(Map<String, ?> initialConfiguration, Map<Throwable, Integer> exceptions) { this.exceptions = exceptions; this.initialConfig = initialConfiguration == null ? new HashMap<String, Object>(0) : new HashMap<>(initialConfiguration); Object useSystemPropsValue = initialConfig.get(PROP_USE_SYSTEM_PROPERTIES); this.useSystemProperties = useSystemPropsValue == null ? false : Boolean.parseBoolean(useSystemPropsValue.toString()); Properties tempConfiguration = useSystemProperties ? EquinoxContainer.secureAction.getProperties() : new Properties(); // do this the hard way to handle null values for (Map.Entry<String, ?> initialEntry : this.initialConfig.entrySet()) { if (initialEntry.getValue() == null) { if (useSystemProperties) { tempConfiguration.remove(initialEntry.getKey()); } else { tempConfiguration.put(initialEntry.getKey(), NULL_CONFIG); } } else { tempConfiguration.put(initialEntry.getKey(), initialEntry.getValue()); } } localConfig = useSystemProperties ? null : tempConfiguration; } void loadConfigIni(URL configIni) { if (configIni != null) { mergeConfiguration(loadProperties(configIni)); } } void finalizeValues() { // populate initConfig after loading the configIni for (String initialKey : populateInitConfig) { String value = getConfiguration(initialKey); if (value != null) { this.initialConfig.put(initialKey, value); } } // look for special case EclipseStarter defaults for (String keyWithEclipseStarterDefault : PROP_WITH_ECLIPSE_STARTER_DEFAULTS) { String currentValue = getConfiguration(keyWithEclipseStarterDefault); if (currentValue == null) { String defaultValue = getConfiguration(keyWithEclipseStarterDefault + PROP_DEFAULT_SUFFIX); if (defaultValue != null) { setConfiguration(keyWithEclipseStarterDefault, defaultValue); } } } // set other defaults if (FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT.equals(getConfiguration(FRAMEWORK_STORAGE_CLEAN))) { setConfiguration(PROP_CLEAN, "true"); //$NON-NLS-1$ } if (getConfiguration(PROP_STATE_SAVE_DELAY_INTERVAL) == null) // Property not specified. Use the default. setConfiguration(PROP_STATE_SAVE_DELAY_INTERVAL, DEFAULT_STATE_SAVE_DELAY_INTERVAL); try { // Verify type compatibility. Long.parseLong(getConfiguration(PROP_STATE_SAVE_DELAY_INTERVAL)); } catch (NumberFormatException e) { exceptions.put(e, FrameworkLogEntry.ERROR); // The specified value is not type compatible. Use the default. setConfiguration(PROP_STATE_SAVE_DELAY_INTERVAL, DEFAULT_STATE_SAVE_DELAY_INTERVAL); } if (getConfiguration(PROP_GOSH_ARGS) == null) { String consoleProp = getConfiguration(ConsoleManager.PROP_CONSOLE); consoleProp = consoleProp == null ? null : consoleProp.trim(); if (consoleProp == null || consoleProp.length() > 0) { // no -console was specified or it has specified none or a port for telnet; // need to make sure the gogo shell does not create an interactive console on standard in/out setConfiguration(PROP_GOSH_ARGS, "--nointeractive"); //$NON-NLS-1$ } else { // Need to make sure we don't shutdown the framework if no console is around (bug 362412) setConfiguration(PROP_GOSH_ARGS, "--noshutdown"); //$NON-NLS-1$ } } } private void mergeConfiguration(Properties source) { for (Enumeration<?> e = source.keys(); e.hasMoreElements();) { String key = (String) e.nextElement(); String value = source.getProperty(key); if (getConfiguration(key) == null) { setProperty(key, value); initialConfig.put(key, value); } else { initialConfig.put(key, getConfiguration(key)); } } } private Properties loadProperties(URL location) { Properties result = new Properties(); if (location == null) return result; try { InputStream in = LocationHelper.getStream(location); try { result.load(in); } finally { in.close(); } } catch (FileNotFoundException e) { // TODO probably should log, but the common case for non-eclipse // environments would be to not have a config.ini ... } catch (IOException e) { // We'll just use the defaults for everything but log the exception on reading exceptions.put(e, FrameworkLogEntry.WARNING); } return substituteVars(result); } private Properties substituteVars(Properties result) { if (result == null) { //nothing todo. return null; } for (Enumeration<Object> eKeys = result.keys(); eKeys.hasMoreElements();) { Object key = eKeys.nextElement(); if (key instanceof String) { String value = result.getProperty((String) key); if (value != null) result.put(key, substituteVars(value, true)); } } return result; } public String substituteVars(String path, boolean preserveDelimiters) { StringBuilder buf = new StringBuilder(path.length()); StringTokenizer st = new StringTokenizer(path, VARIABLE_DELIM_STRING, true); boolean varStarted = false; // indicates we are processing a var subtitute String var = null; // the current var key while (st.hasMoreElements()) { String tok = st.nextToken(); if (VARIABLE_DELIM_STRING.equals(tok)) { if (!varStarted) { varStarted = true; // we found the start of a var var = ""; //$NON-NLS-1$ } else { // we have found the end of a var String prop = null; // get the value of the var from system properties if (var != null && var.length() > 0) prop = getProperty(var); if (prop == null) { try { // try using the System.getenv method if it exists (bug 126921) Method getenv = System.class.getMethod("getenv", new Class[] {String.class}); //$NON-NLS-1$ prop = (String) getenv.invoke(null, new Object[] {var}); } catch (Throwable t) { // do nothing; // on 1.4 VMs this throws an error // on J2ME this method does not exist } } if (prop != null) { // found a value; use it buf.append(prop); } else { // could not find a value append the var if (preserveDelimiters) { buf.append(VARIABLE_DELIM_CHAR); } buf.append(var == null ? "" : var); //$NON-NLS-1$ if (preserveDelimiters) { buf.append(VARIABLE_DELIM_CHAR); } } varStarted = false; var = null; } } else { if (!varStarted) buf.append(tok); // the token is not part of a var else var = tok; // the token is the var key; save the key to process when we find the end token } } if (var != null) // found a case of $var at the end of the path with no trailing $; just append it as is. buf.append(VARIABLE_DELIM_CHAR).append(var); return buf.toString(); } public String getProperty(String key) { // have to access configuration directly instead of using getConfiguration to get access to NULL_CONFIG values String result = internalGet(key); if (result == NULL_CONFIG) { return null; } return result == null ? EquinoxContainer.secureAction.getProperty(key) : result; } public String setProperty(String key, String value) { if (value == null) { return clearConfiguration(key); } return setConfiguration(key, value); } public String getConfiguration(String key) { String result = internalGet(key); return result == NULL_CONFIG ? null : result; } private String internalGet(String key) { if (useSystemProperties) { return EquinoxContainer.secureAction.getProperty(key); } return localConfig.getProperty(key); } public String getConfiguration(String key, String defaultValue) { String result = getConfiguration(key); return result == null ? defaultValue : result; } public String setConfiguration(String key, String value) { Object result = internalPut(key, value); return result instanceof String && result != NULL_CONFIG ? (String) result : null; } public String clearConfiguration(String key) { Object result = internalRemove(key); if (!useSystemProperties) { internalPut(key, NULL_CONFIG); } return result instanceof String && result != NULL_CONFIG ? (String) result : null; } private Object internalPut(String key, String value) { if (useSystemProperties) { return EquinoxContainer.secureAction.getProperties().put(key, value); } return localConfig.put(key, value); } private Object internalRemove(String key) { if (useSystemProperties) { return EquinoxContainer.secureAction.getProperties().remove(key); } return localConfig.remove(key); } public Map<String, String> getConfiguration() { Properties props = useSystemProperties ? EquinoxContainer.secureAction.getProperties() : localConfig; // must sync on props to avoid concurrent modification exception synchronized (props) { Map<String, String> result = new HashMap<>(props.size()); for (Object key : props.keySet()) { if (key instanceof String) { String skey = (String) key; String sValue = props.getProperty(skey); if (sValue != NULL_CONFIG) { result.put(skey, sValue); } } } return result; } } public Map<String, Object> getInitialConfig() { return Collections.unmodifiableMap(initialConfig); } } EquinoxConfiguration(Map<String, ?> initialConfiguration, HookRegistry hookRegistry) { this.hookRegistry = hookRegistry; // Care must be taken to bootstrap of the config values properly // A separate class is used to hold the configuration maps so that we can pass them // to the EquionxLocations before the EquinoxConfiguration has been fully constructed this.configValues = new ConfigValues(initialConfiguration, exceptions); // We need to initialize some properties always before constructing the EquinoxLocations initializeProperties(); // At this point we do not know if we want to debug locations because we have not determined if osgi.debug is set yet // We use an AttomicBoolean to hold the setting so we can set it after the config.ini has been loaded AtomicBoolean debugLocations = new AtomicBoolean(); this.equinoxLocations = new EquinoxLocations(this.configValues, this.hookRegistry.getContainer(), debugLocations, exceptions); this.configValues.loadConfigIni(getConfigIni(equinoxLocations, false)); this.configValues.loadConfigIni(getConfigIni(equinoxLocations, true)); this.configValues.finalizeValues(); this.debugOptions = new FrameworkDebugOptions(this); this.debug = new Debug(this.debugOptions); // finally we know if locations should be debugged. debugLocations.set(debug.DEBUG_LOCATION); // Finally we can set all our variables according to the configuration values String osgiDev = getConfiguration(PROP_DEV); File f = null; boolean devMode = false; if (osgiDev != null) { try { devMode = true; URL location = new URL(osgiDev); if ("file".equals(location.getProtocol())) { //$NON-NLS-1$ f = LocationHelper.decodePath(new File(location.getPath())); devLastModified = f.lastModified(); } // Check the osgi.dev property to see if dev classpath entries have been defined. try { loadDevProperties(LocationHelper.getStream(location)); devMode = true; } catch (IOException e) { exceptions.put(e, FrameworkLogEntry.ERROR); } } catch (MalformedURLException e) { devDefaultClasspath = getArrayFromList(osgiDev); } } inDevelopmentMode = devMode; devLocation = f; contextBootDelegation = "true".equals(getConfiguration(PROP_CONTEXT_BOOTDELEGATION, "true")); //$NON-NLS-1$ //$NON-NLS-2$ compatibilityBootDelegation = "true".equals(getConfiguration(PROP_COMPATIBILITY_BOOTDELEGATION)); //$NON-NLS-1$ compatibilityLazyTriggerOnFailLoad = "true".equals(getConfiguration(PROP_COMPATIBILITY_START_LAZY_ON_FAIL_CLASSLOAD)); //$NON-NLS-1$ COPY_NATIVES = Boolean.valueOf(getConfiguration(PROP_COPY_NATIVES)).booleanValue(); String[] libExtensions = ManifestElement.getArrayFromList(getConfiguration(EquinoxConfiguration.PROP_FRAMEWORK_LIBRARY_EXTENSIONS, getConfiguration(org.osgi.framework.Constants.FRAMEWORK_LIBRARY_EXTENSIONS, getOSLibraryExtDefaults())), ","); //$NON-NLS-1$ for (int i = 0; i < libExtensions.length; i++) if (libExtensions[i].length() > 0 && libExtensions[i].charAt(0) != '.') libExtensions[i] = '.' + libExtensions[i]; LIB_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(libExtensions)); ECLIPSE_LIB_VARIANTS = buildEclipseLibraryVariants(getWS(), getOS(), getOSArch(), getNL()); ECLIPSE_NL_JAR_VARIANTS = buildNLJarVariants(getNL()); DEFINE_PACKAGE_ATTRIBUTES = !"noattributes".equals(getConfiguration(PROP_DEFINE_PACKAGES)); //$NON-NLS-1$ String bsnVersion = getConfiguration(org.osgi.framework.Constants.FRAMEWORK_BSNVERSION); if (org.osgi.framework.Constants.FRAMEWORK_BSNVERSION_SINGLE.equals(bsnVersion)) { BSN_VERSION = BSN_VERSION_SINGLE; } else if (org.osgi.framework.Constants.FRAMEWORK_BSNVERSION_MULTIPLE.equals(bsnVersion)) { BSN_VERSION = BSN_VERSION_MULTIPLE; } else { BSN_VERSION = BSN_VERSION_MANAGED; } BUNDLE_SET_TCCL = "true".equals(getConfiguration(PROP_BUNDLE_SETTCCL, "true")); //$NON-NLS-1$ //$NON-NLS-2$ throwErrorOnFailedStart = "true".equals(getConfiguration(PROP_COMPATIBILITY_ERROR_FAILED_START, "true")); //$NON-NLS-1$//$NON-NLS-2$ CLASS_CERTIFICATE = Boolean.valueOf(getConfiguration(PROP_CLASS_CERTIFICATE_SUPPORT, "true")).booleanValue(); //$NON-NLS-1$ PARALLEL_CAPABLE = CLASS_LOADER_TYPE_PARALLEL.equals(getConfiguration(PROP_CLASS_LOADER_TYPE)); // A specified osgi.dev property but unspecified osgi.checkConfiguration // property implies osgi.checkConfiguration = true. inCheckConfigurationMode = Boolean.valueOf(getConfiguration(PROP_CHECK_CONFIGURATION, Boolean.toString(devMode))); // Must ensure the check configuration property is set if in osgi.dev mode (bug 443340) if (inCheckConfigurationMode && getConfiguration(PROP_CHECK_CONFIGURATION) == null) { setConfiguration(PROP_CHECK_CONFIGURATION, "true"); //$NON-NLS-1$ } } private URL getConfigIni(EquinoxLocations locations, boolean parent) { if (Boolean.TRUE.toString().equals(getConfiguration(EquinoxConfiguration.PROP_IGNORE_USER_CONFIGURATION))) return null; Location configArea = locations.getConfigurationLocation(); if (configArea != null && parent) { configArea = configArea.getParentLocation(); } if (configArea == null) { return null; } try { return new URL(configArea.getURL().toExternalForm() + CONFIG_FILE); } catch (MalformedURLException e) { // its ok. This should never happen } return null; } public Map<String, Object> getInitialConfig() { return this.configValues.getInitialConfig(); } private static List<String> buildEclipseLibraryVariants(String ws, String os, String arch, String nl) { List<String> result = new ArrayList<>(); result.add("ws/" + ws + "/"); //$NON-NLS-1$ //$NON-NLS-2$ result.add("os/" + os + "/" + arch + "/"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ result.add("os/" + os + "/"); //$NON-NLS-1$ //$NON-NLS-2$ nl = nl.replace('_', '/'); while (nl.length() > 0) { result.add("nl/" + nl + "/"); //$NON-NLS-1$ //$NON-NLS-2$ int i = nl.lastIndexOf('/'); nl = (i < 0) ? "" : nl.substring(0, i); //$NON-NLS-1$ } result.add(""); //$NON-NLS-1$ return Collections.unmodifiableList(result); } private static List<String> buildNLJarVariants(String nl) { List<String> result = new ArrayList<>(); nl = nl.replace('_', '/'); while (nl.length() > 0) { result.add("nl/" + nl + "/"); //$NON-NLS-1$ //$NON-NLS-2$ int i = nl.lastIndexOf('/'); nl = (i < 0) ? "" : nl.substring(0, i); //$NON-NLS-1$ } result.add(""); //$NON-NLS-1$ return result; } private static String getOSLibraryExtDefaults() { // Some OSes have multiple library extensions // We should provide defaults to the known ones // For example Mac OS X uses dylib and jnilib (bug 380350) String os = EquinoxContainer.secureAction.getProperty(EquinoxConfiguration.PROP_JVM_OS_NAME); return os == null || !os.startsWith("Mac OS") ? null : "dylib,jnilib"; //$NON-NLS-1$ //$NON-NLS-2$ } public boolean inCheckConfigurationMode() { return inCheckConfigurationMode; } @Override public boolean inDevelopmentMode() { return inDevelopmentMode; } @Override public boolean inDebugMode() { return debugOptions.isDebugEnabled(); } @Override public String[] getCommandLineArgs() { return allArgs; } @Override public String[] getFrameworkArgs() { return frameworkArgs; } @Override public String[] getNonFrameworkArgs() { return appArgs; } @Override public String getOSArch() { return getConfiguration(PROP_OSGI_ARCH); } @Override public String getNL() { return getConfiguration(PROP_OSGI_NL); } @Override public String getOS() { return getConfiguration(PROP_OSGI_OS); } @Override public String getWS() { return getConfiguration(PROP_OSGI_WS); } public void setAllArgs(String[] allArgs) { // do not check if this is set already to allow arguments to change when multiple applications are launched this.allArgs = allArgs; } public void setAppArgs(String[] appArgs) { // do not check if this is set already to allow arguments to change when multiple applications are launched this.appArgs = appArgs; } public void setFrameworkArgs(String[] frameworkArgs) { // do not check if this is set already to allow arguments to change when multiple applications are launched this.frameworkArgs = frameworkArgs; } public static String guessWS(String osName) { // setup default values for known OSes if nothing was specified if (osName.equals(Constants.OS_WIN32)) return Constants.WS_WIN32; if (osName.equals(Constants.OS_LINUX)) return Constants.WS_GTK; if (osName.equals(Constants.OS_FREEBSD)) return Constants.WS_GTK; if (osName.equals(Constants.OS_MACOSX)) return Constants.WS_COCOA; if (osName.equals(Constants.OS_HPUX)) return Constants.WS_MOTIF; if (osName.equals(Constants.OS_AIX)) return Constants.WS_MOTIF; if (osName.equals(Constants.OS_SOLARIS)) return Constants.WS_GTK; if (osName.equals(Constants.OS_QNX)) return Constants.WS_PHOTON; return Constants.WS_UNKNOWN; } public static String guessOS(String osName) { // check to see if the OS name is "Windows 98" or some other // flavour which should be converted to win32. if (osName.regionMatches(true, 0, Constants.OS_WIN32, 0, 3)) return Constants.OS_WIN32; // EXCEPTION: All mappings of SunOS convert to Solaris if (osName.equalsIgnoreCase(INTERNAL_OS_SUNOS)) return Constants.OS_SOLARIS; if (osName.equalsIgnoreCase(INTERNAL_OS_LINUX)) return Constants.OS_LINUX; if (osName.equalsIgnoreCase(INTERNAL_OS_QNX)) return Constants.OS_QNX; if (osName.equalsIgnoreCase(INTERNAL_OS_AIX)) return Constants.OS_AIX; if (osName.equalsIgnoreCase(INTERNAL_OS_HPUX)) return Constants.OS_HPUX; if (osName.equalsIgnoreCase(INTERNAL_OS_OS400)) return Constants.OS_OS400; if (osName.equalsIgnoreCase(INTERNAL_OS_OS390)) return Constants.OS_OS390; if (osName.equalsIgnoreCase(INTERNAL_OS_ZOS)) return Constants.OS_ZOS; if (osName.equalsIgnoreCase(INTERNAL_OS_FREEBSD)) return Constants.OS_FREEBSD; // os.name on Mac OS can be either Mac OS or Mac OS X if (osName.regionMatches(true, 0, INTERNAL_OS_MACOSX, 0, INTERNAL_OS_MACOSX.length())) return Constants.OS_MACOSX; return Constants.OS_UNKNOWN; } public String getConfiguration(String key) { return this.configValues.getConfiguration(key); } public String getConfiguration(String key, String defaultValue) { return this.configValues.getConfiguration(key, defaultValue); } public String setConfiguration(String key, String value) { return this.configValues.setConfiguration(key, value); } public String clearConfiguration(String key) { return this.configValues.clearConfiguration(key); } public Map<String, String> getConfiguration() { return this.configValues.getConfiguration(); } public Debug getDebug() { return this.debug; } public DebugOptions getDebugOptions() { return this.debugOptions; } public HookRegistry getHookRegistry() { return hookRegistry; } @Override public String getProperty(String key) { return this.configValues.getProperty(key); } @Override public String setProperty(String key, String value) { return this.configValues.setProperty(key, value); } public AliasMapper getAliasMapper() { return aliasMapper; } /* * Updates the dev classpath if the file containing the entries have changed */ private void updateDevProperties() { if (devLocation == null) return; synchronized (devMonitor) { if (devLocation.lastModified() == devLastModified) return; try { loadDevProperties(new FileInputStream(devLocation)); } catch (FileNotFoundException e) { return; } devLastModified = devLocation.lastModified(); } } private static String[] getDevClassPath(String id, Dictionary<String, String> properties, String[] defaultClasspath) { String[] result = null; if (id != null && properties != null) { String entry = properties.get(id); if (entry != null) result = getArrayFromList(entry); } if (result == null) result = defaultClasspath; return result; }
Returns a list of classpath elements for the specified bundle symbolic name.
Params:
  • id – a bundle symbolic name to get the development classpath for
  • properties – a Dictionary of properties to use or null if the default develoment classpath properties should be used
Returns:a list of development classpath elements
/** * Returns a list of classpath elements for the specified bundle symbolic name. * @param id a bundle symbolic name to get the development classpath for * @param properties a Dictionary of properties to use or <code>null</code> if * the default develoment classpath properties should be used * @return a list of development classpath elements */
public String[] getDevClassPath(String id, Dictionary<String, String> properties) { if (properties == null) { synchronized (devMonitor) { updateDevProperties(); return getDevClassPath(id, devProperties, devDefaultClasspath); } } return getDevClassPath(id, properties, getArrayFromList(properties.get("*"))); //$NON-NLS-1$ }
Returns a list of classpath elements for the specified bundle symbolic name.
Params:
  • id – a bundle symbolic name to get the development classpath for
Returns:a list of development classpath elements
/** * Returns a list of classpath elements for the specified bundle symbolic name. * @param id a bundle symbolic name to get the development classpath for * @return a list of development classpath elements */
public String[] getDevClassPath(String id) { return getDevClassPath(id, null); } private static String[] getArrayFromList(String prop) { return ManifestElement.getArrayFromList(prop, ","); //$NON-NLS-1$ } /* * Load the given input stream into a dictionary */ private void loadDevProperties(InputStream input) { Properties props = new Properties(); try { props.load(input); } catch (IOException e) { exceptions.put(e, FrameworkLogEntry.ERROR); } finally { if (input != null) try { input.close(); } catch (IOException e) { // tried our best } } @SuppressWarnings({"unchecked", "rawtypes"}) Dictionary<String, String> result = (Dictionary) props; synchronized (devMonitor) { devProperties = result; if (devProperties != null) devDefaultClasspath = getArrayFromList(devProperties.get("*")); //$NON-NLS-1$ } } private void initializeProperties() { // initialize some framework properties that must always be set AND cannot be set with config.ini if (getConfiguration(PROP_FRAMEWORK) == null || getConfiguration(EquinoxLocations.PROP_INSTALL_AREA) == null) { ProtectionDomain pd = EquinoxConfiguration.class.getProtectionDomain(); CodeSource cs = pd == null ? null : pd.getCodeSource(); URL url = cs == null ? null : cs.getLocation(); if (url == null) { IOException cause = null; // try to determine by loading a resource we know we have URL java6Profile = EquinoxConfiguration.class.getResource("/JavaSE-1.6.profile"); //$NON-NLS-1$ if (java6Profile != null && "jar".equals(java6Profile.getProtocol())) { //$NON-NLS-1$ try { url = ((JarURLConnection) java6Profile.openConnection()).getJarFileURL(); } catch (IOException e) { cause = e; } } if (url == null) { throw new IllegalArgumentException(NLS.bind(Msg.ECLIPSE_STARTUP_PROPS_NOT_SET, PROP_FRAMEWORK + ", " + EquinoxLocations.PROP_INSTALL_AREA), cause); //$NON-NLS-1$ } } // allow props to be preset if (getConfiguration(PROP_FRAMEWORK) == null) { String externalForm = getFrameworkPath(url.toExternalForm(), false); setConfiguration(PROP_FRAMEWORK, externalForm); } if (getConfiguration(EquinoxLocations.PROP_INSTALL_AREA) == null) { String filePart = getFrameworkPath(url.getPath(), true); setConfiguration(EquinoxLocations.PROP_INSTALL_AREA, filePart); } } // always decode these properties setConfiguration(PROP_FRAMEWORK, LocationHelper.decode(getConfiguration(PROP_FRAMEWORK), true)); setConfiguration(EquinoxLocations.PROP_INSTALL_AREA, LocationHelper.decode(getConfiguration(EquinoxLocations.PROP_INSTALL_AREA), true)); setConfiguration(FRAMEWORK_VENDOR, ECLIPSE_FRAMEWORK_VENDOR); String value = getConfiguration(FRAMEWORK_PROCESSOR); if (value == null) { value = EquinoxContainer.secureAction.getProperty(PROP_JVM_OS_ARCH); if (value != null) { setConfiguration(FRAMEWORK_PROCESSOR, aliasMapper.getCanonicalProcessor(value)); } } value = getConfiguration(FRAMEWORK_OS_NAME); if (value == null) { value = EquinoxContainer.secureAction.getProperty(PROP_JVM_OS_NAME); if (value != null) { setConfiguration(FRAMEWORK_OS_NAME, aliasMapper.getCanonicalOSName(value)); } } value = getConfiguration(FRAMEWORK_OS_VERSION); if (value == null) { value = EquinoxContainer.secureAction.getProperty(PROP_JVM_OS_VERSION); if (value != null) { // only use the value upto the first space int space = value.indexOf(' '); if (space > 0) { value = value.substring(0, space); } // fix up cases where the os version does not make a valid Version string. int major = 0, minor = 0, micro = 0; String qualifier = ""; //$NON-NLS-1$ try { StringTokenizer st = new StringTokenizer(value, ".", true); //$NON-NLS-1$ major = parseVersionInt(st.nextToken()); if (st.hasMoreTokens()) { st.nextToken(); // consume delimiter minor = parseVersionInt(st.nextToken()); if (st.hasMoreTokens()) { st.nextToken(); // consume delimiter micro = parseVersionInt(st.nextToken()); if (st.hasMoreTokens()) { st.nextToken(); // consume delimiter qualifier = st.nextToken(); } } } } catch (NoSuchElementException e) { // ignore, use the values parsed so far } try { value = new Version(major, minor, micro, qualifier).toString(); } catch (IllegalArgumentException e) { // must be an invalid qualifier; just ignore it value = new Version(major, minor, micro).toString(); } setConfiguration(FRAMEWORK_OS_VERSION, value); } } value = getConfiguration(FRAMEWORK_LANGUAGE); if (value == null) // set the value of the framework language property setConfiguration(FRAMEWORK_LANGUAGE, Locale.getDefault().getLanguage()); // set the support properties for fragments and require-bundle (bug 173090) setConfiguration(SUPPORTS_FRAMEWORK_FRAGMENT, "true"); //$NON-NLS-1$ setConfiguration(SUPPORTS_FRAMEWORK_REQUIREBUNDLE, "true"); //$NON-NLS-1$ setConfiguration(SUPPORTS_FRAMEWORK_EXTENSION, "true"); //$NON-NLS-1$ /* * Initializes the execution context for this run of the platform. The context * includes information about the locale, operating system and window system. * * NOTE: The OS, WS, and ARCH values should never be null. The executable should * be setting these values and therefore this code path is obsolete for Eclipse * when run from the executable. */ // if the user didn't set the locale with a command line argument then use the default. String nlValue = getConfiguration(PROP_OSGI_NL); if (nlValue != null) { Locale userLocale = toLocale(nlValue, Locale.getDefault()); Locale.setDefault(userLocale); // TODO what the heck is this for?? why not just use osgi.nl setConfiguration(PROP_OSGI_NL_USER, nlValue); } nlValue = Locale.getDefault().toString(); setConfiguration(PROP_OSGI_NL, nlValue); // if the user didn't set the operating system with a command line // argument then use the default. String osValue = getConfiguration(PROP_OSGI_OS); if (osValue == null) { osValue = guessOS(EquinoxContainer.secureAction.getProperty(PROP_JVM_OS_NAME)); setConfiguration(PROP_OSGI_OS, osValue); } // if the user didn't set the window system with a command line // argument then use the default. String wsValue = getConfiguration(PROP_OSGI_WS); if (wsValue == null) { wsValue = guessWS(osValue); setConfiguration(PROP_OSGI_WS, wsValue); } // if the user didn't set the system architecture with a command line // argument then use the default. String archValue = getConfiguration(PROP_OSGI_ARCH); if (archValue == null) { String name = EquinoxContainer.secureAction.getProperty(PROP_JVM_OS_ARCH); // Map i386 architecture to x86 if (name.equalsIgnoreCase(INTERNAL_ARCH_I386)) archValue = Constants.ARCH_X86; // Map amd64 architecture to x86_64 else if (name.equalsIgnoreCase(INTERNAL_AMD64)) archValue = Constants.ARCH_X86_64; else archValue = name; setConfiguration(PROP_OSGI_ARCH, archValue); } // set the initial UUID so that it is set as soon as possible String uuid = UUID.randomUUID().toString(); setConfiguration(FRAMEWORK_UUID, uuid); } private static String getFrameworkPath(String path, boolean parent) { if (File.separatorChar == '\\') { // in case on windows the \ is used path = path.replace('\\', '/'); } // TODO is there a better way? // this is a hack to get the framework to launch from a self-hosted eclipse instance // we assume the code source will end in the path org.eclipse.osgi/bin/ if (path.endsWith("org.eclipse.osgi/bin/")) { //$NON-NLS-1$ path = path.substring(0, path.length() - "bin/".length()); //$NON-NLS-1$ } if (parent) { int lastSlash = path.lastIndexOf('/'); return lastSlash == -1 ? "/" : path.substring(0, lastSlash); //$NON-NLS-1$ } return path; } private static int parseVersionInt(String value) { try { return Integer.parseInt(value); } catch (NumberFormatException e) { // try up to the first non-number char StringBuilder sb = new StringBuilder(value.length()); char[] chars = value.toCharArray(); for (int i = 0; i < chars.length; i++) { if (!Character.isDigit(chars[i])) break; sb.append(chars[i]); } if (sb.length() > 0) return Integer.parseInt(sb.toString()); return 0; } } public String substituteVars(String path) { return substituteVars(path, false); } public String substituteVars(String path, boolean preserveDelimiters) { return this.configValues.substituteVars(path, preserveDelimiters); }

Converts a String to a Locale.

This method takes the string format of a locale and creates the locale object from it.

This method validates the input strictly. The language code must be lowercase. The country code must be uppercase. The separator must be an underscore. The length must be correct.

This method is inspired by org.apache.commons.lang.LocaleUtils.toLocale(String) by fixing the parsing error for uncommon Locales like having a language and a variant code but no country code, or a Locale that only consists of a country code.

Params:
  • str – the locale String to convert
  • defaultLocale – the Locale that should be returned in case of an invalid Locale String
Returns:a Locale that matches the specified locale String. If the given input String is null or can not be parsed because of an invalid format, the given default Locale will be returned.
/** * <p> * Converts a String to a Locale. * </p> * * <p> * This method takes the string format of a locale and creates the locale object from it. * </p> * * <p> * This method validates the input strictly. The language code must be lowercase. The country * code must be uppercase. The separator must be an underscore. The length must be correct. * </p> * * <p> * This method is inspired by <code>org.apache.commons.lang.LocaleUtils.toLocale(String)</code> * by fixing the parsing error for uncommon Locales like having a language and a variant code * but no country code, or a Locale that only consists of a country code. * </p> * * @param str * the locale String to convert * @param defaultLocale * the Locale that should be returned in case of an invalid Locale String * @return a Locale that matches the specified locale String. If the given input String is * <code>null</code> or can not be parsed because of an invalid format, the given * default {@link Locale} will be returned. */
public static Locale toLocale(String str, Locale defaultLocale) { if (str == null) { System.err.println("Given locale String is null - Default Locale will be used instead."); //$NON-NLS-1$ return defaultLocale; } String language = ""; //$NON-NLS-1$ String country = ""; //$NON-NLS-1$ String variant = ""; //$NON-NLS-1$ String[] localeParts = str.split("_"); //$NON-NLS-1$ if (localeParts.length == 0 || localeParts.length > 3 || (localeParts.length == 1 && localeParts[0].length() == 0)) { System.err.println(NLS.bind(Msg.error_badNL, str)); return defaultLocale; } if (localeParts[0].length() > 0 && !localeParts[0].matches("[a-zA-Z]{2,8}")) { //$NON-NLS-1$ System.err.println(NLS.bind(Msg.error_badNL, str)); return defaultLocale; } language = localeParts[0]; if (localeParts.length > 1) { if (localeParts[1].length() > 0 && !localeParts[1].matches("[a-zA-Z]{2}|[0-9]{3}")) { //$NON-NLS-1$ if (language.length() > 0) { System.err.println(NLS.bind(Msg.error_badNL_language, str)); return new Locale(language); } System.err.println(NLS.bind(Msg.error_badNL, str)); return defaultLocale; } country = localeParts[1]; } if (localeParts.length == 3) { if (localeParts[2].length() == 0) { System.err.println(NLS.bind(Msg.error_badNL_language_country, str)); return new Locale(language, country); } variant = localeParts[2]; } return new Locale(language, country, variant); } public EquinoxLocations getEquinoxLocations() { return equinoxLocations; } void logMessages(EquinoxLogServices logServices) { for (Map.Entry<Throwable, Integer> exception : exceptions.entrySet()) { logServices.log(EquinoxContainer.NAME, exception.getValue(), exception.getKey().getMessage(), exception.getKey()); } exceptions.clear(); } }