1 /******************************************************************************* 2 * Copyright (c) 2005, 2016 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 15 package org.eclipse.osgi.internal.hookregistry; 16 17 import java.io.IOException; 18 import java.io.InputStream; 19 import java.net.URL; 20 import java.util.ArrayList; 21 import java.util.Collections; 22 import java.util.Enumeration; 23 import java.util.List; 24 import java.util.Properties; 25 import org.eclipse.osgi.framework.log.FrameworkLogEntry; 26 import org.eclipse.osgi.internal.cds.CDSHookConfigurator; 27 import org.eclipse.osgi.internal.framework.EquinoxConfiguration; 28 import org.eclipse.osgi.internal.framework.EquinoxContainer; 29 import org.eclipse.osgi.internal.hooks.DevClassLoadingHook; 30 import org.eclipse.osgi.internal.hooks.EclipseLazyStarter; 31 import org.eclipse.osgi.internal.signedcontent.SignedBundleHook; 32 import org.eclipse.osgi.internal.weaving.WeavingHookConfigurator; 33 import org.eclipse.osgi.util.ManifestElement; 34 35 /** 36 * The hook registry is used to store all the hooks which are 37 * configured by the hook configurators. 38 * @see HookConfigurator 39 */ 40 public final class HookRegistry { 41 /** 42 * The hook configurators properties file ("hookconfigurators.properties") <p> 43 * A framework extension may supply a hook configurators properties file to specify a 44 * list of hook configurators. 45 * @see #HOOK_CONFIGURATORS 46 */ 47 public static final String HOOK_CONFIGURATORS_FILE = "hookconfigurators.properties"; //$NON-NLS-1$ 48 49 /** 50 * The hook configurators property key ("hookconfigurators.properties") used in 51 * a hook configurators properties file to specify a comma separated list of fully 52 * qualified hook configurator classes. 53 */ 54 public static final String HOOK_CONFIGURATORS = "hook.configurators"; //$NON-NLS-1$ 55 56 /** 57 * A system property ("osgi.hook.configurators.include") used to add additional 58 * hook configurators. This is helpful for configuring optional hook configurators. 59 */ 60 public static final String PROP_HOOK_CONFIGURATORS_INCLUDE = "osgi.hook.configurators.include"; //$NON-NLS-1$ 61 62 /** 63 * A system property ("osgi.hook.configurators.exclude") used to exclude 64 * any hook configurators. This is helpful for disabling hook 65 * configurators that is specified in hook configurator properties files. 66 */ 67 public static final String PROP_HOOK_CONFIGURATORS_EXCLUDE = "osgi.hook.configurators.exclude"; //$NON-NLS-1$ 68 69 /** 70 * A system property ("osgi.hook.configurators") used to specify the list 71 * of hook configurators. If this property is set then the list of configurators 72 * specified will be the only configurators used. 73 */ 74 public static final String PROP_HOOK_CONFIGURATORS = "osgi.hook.configurators"; //$NON-NLS-1$ 75 76 private static final String BUILTIN_HOOKS = "builtin.hooks"; //$NON-NLS-1$ 77 78 private final EquinoxContainer container; 79 private volatile boolean initialized = false; 80 private final List<ClassLoaderHook> classLoaderHooks = new ArrayList<>(); 81 private final List<ClassLoaderHook> classLoaderHooksRO = Collections.unmodifiableList(classLoaderHooks); 82 private final List<StorageHookFactory<?, ?, ?>> storageHookFactories = new ArrayList<>(); 83 private final List<StorageHookFactory<?, ?, ?>> storageHookFactoriesRO = Collections.unmodifiableList(storageHookFactories); 84 private final List<BundleFileWrapperFactoryHook> bundleFileWrapperFactoryHooks = new ArrayList<>(); 85 private final List<BundleFileWrapperFactoryHook> bundleFileWrapperFactoryHooksRO = Collections.unmodifiableList(bundleFileWrapperFactoryHooks); 86 private final List<ActivatorHookFactory> activatorHookFactories = new ArrayList<>(); 87 private final List<ActivatorHookFactory> activatorHookFactoriesRO = Collections.unmodifiableList(activatorHookFactories); 88 HookRegistry(EquinoxContainer container)89 public HookRegistry(EquinoxContainer container) { 90 this.container = container; 91 } 92 93 /** 94 * Initializes the hook configurators. The following steps are used to initialize the hook configurators. <p> 95 * 1. Get a list of hook configurators from all hook configurators properties files on the classpath, 96 * add this list to the overall list of hook configurators, remove duplicates. <p> 97 * 2. Get a list of hook configurators from the ("osgi.hook.configurators.include") system property 98 * and add this list to the overall list of hook configurators, remove duplicates. <p> 99 * 3. Get a list of hook configurators from the ("osgi.hook.configurators.exclude") system property 100 * and remove this list from the overall list of hook configurators. <p> 101 * 4. Load each hook configurator class, create a new instance, then call the {@link HookConfigurator#addHooks(HookRegistry)} method <p> 102 * 5. Set this HookRegistry object to read only to prevent any other hooks from being added. <p> 103 */ initialize()104 public void initialize() { 105 List<String> configurators = new ArrayList<>(5); 106 List<FrameworkLogEntry> errors = new ArrayList<>(0); // optimistic that no errors will occur 107 mergeFileHookConfigurators(configurators, errors); 108 mergePropertyHookConfigurators(configurators); 109 synchronized (this) { 110 addClassLoaderHook(new DevClassLoadingHook(container.getConfiguration())); 111 addClassLoaderHook(new EclipseLazyStarter(container)); 112 addClassLoaderHook(new WeavingHookConfigurator(container)); 113 configurators.add(SignedBundleHook.class.getName()); 114 configurators.add(CDSHookConfigurator.class.getName()); 115 loadConfigurators(configurators, errors); 116 // set to read-only 117 initialized = true; 118 } 119 for (FrameworkLogEntry error : errors) { 120 container.getLogServices().getFrameworkLog().log(error); 121 } 122 } 123 mergeFileHookConfigurators(List<String> configuratorList, List<FrameworkLogEntry> errors)124 private void mergeFileHookConfigurators(List<String> configuratorList, List<FrameworkLogEntry> errors) { 125 ClassLoader cl = getClass().getClassLoader(); 126 // get all hook configurators files in your classloader delegation 127 Enumeration<URL> hookConfigurators; 128 try { 129 hookConfigurators = cl != null ? cl.getResources(HookRegistry.HOOK_CONFIGURATORS_FILE) : ClassLoader.getSystemResources(HookRegistry.HOOK_CONFIGURATORS_FILE); 130 } catch (IOException e) { 131 errors.add(new FrameworkLogEntry(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, 0, "getResources error on " + HookRegistry.HOOK_CONFIGURATORS_FILE, 0, e, null)); //$NON-NLS-1$ 132 return; 133 } 134 int curBuiltin = 0; 135 while (hookConfigurators.hasMoreElements()) { 136 URL url = hookConfigurators.nextElement(); 137 InputStream input = null; 138 try { 139 // check each file for a hook.configurators property 140 Properties configuratorProps = new Properties(); 141 input = url.openStream(); 142 configuratorProps.load(input); 143 String hooksValue = configuratorProps.getProperty(HOOK_CONFIGURATORS); 144 if (hooksValue == null) 145 continue; 146 boolean builtin = Boolean.valueOf(configuratorProps.getProperty(BUILTIN_HOOKS)).booleanValue(); 147 String[] configurators = ManifestElement.getArrayFromList(hooksValue, ","); //$NON-NLS-1$ 148 for (String configurator : configurators) { 149 if (!configuratorList.contains(configurator)) { 150 if (builtin) { 151 configuratorList.add(curBuiltin++, configurator); 152 } else { 153 configuratorList.add(configurator); 154 } 155 } 156 } 157 } catch (IOException e) { 158 errors.add(new FrameworkLogEntry(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, 0, "error loading: " + url.toExternalForm(), 0, e, null)); //$NON-NLS-1$ 159 // ignore and continue to next URL 160 } finally { 161 if (input != null) 162 try { 163 input.close(); 164 } catch (IOException e) { 165 // do nothing 166 } 167 } 168 } 169 } 170 mergePropertyHookConfigurators(List<String> configuratorList)171 private void mergePropertyHookConfigurators(List<String> configuratorList) { 172 // see if there is a configurators list 173 String[] configurators = ManifestElement.getArrayFromList(container.getConfiguration().getConfiguration(HookRegistry.PROP_HOOK_CONFIGURATORS), ","); //$NON-NLS-1$ 174 if (configurators.length > 0) { 175 configuratorList.clear(); // clear the list, we are only going to use the configurators from the list 176 for (String configurator : configurators) { 177 if (!configuratorList.contains(configurator)) { 178 configuratorList.add(configurator); 179 } 180 } 181 return; // don't do anything else 182 } 183 // Make sure the configurators from the include property are in the list 184 String[] includeConfigurators = ManifestElement.getArrayFromList(container.getConfiguration().getConfiguration(HookRegistry.PROP_HOOK_CONFIGURATORS_INCLUDE), ","); //$NON-NLS-1$ 185 for (String includeConfigurator : includeConfigurators) { 186 if (!configuratorList.contains(includeConfigurator)) { 187 configuratorList.add(includeConfigurator); 188 } 189 } 190 // Make sure the configurators from the exclude property are no in the list 191 String[] excludeHooks = ManifestElement.getArrayFromList(container.getConfiguration().getConfiguration(HookRegistry.PROP_HOOK_CONFIGURATORS_EXCLUDE), ","); //$NON-NLS-1$ 192 for (String excludeHook : excludeHooks) { 193 configuratorList.remove(excludeHook); 194 } 195 } 196 loadConfigurators(List<String> configurators, List<FrameworkLogEntry> errors)197 private void loadConfigurators(List<String> configurators, List<FrameworkLogEntry> errors) { 198 for (String hookName : configurators) { 199 try { 200 Class<?> clazz = Class.forName(hookName); 201 HookConfigurator configurator = (HookConfigurator) clazz.getConstructor().newInstance(); 202 configurator.addHooks(this); 203 } catch (Throwable t) { 204 // We expect the follow exeptions may happen; but we need to catch all here 205 // ClassNotFoundException 206 // IllegalAccessException 207 // InstantiationException 208 // ClassCastException 209 errors.add(new FrameworkLogEntry(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, 0, "error loading hook: " + hookName, 0, t, null)); //$NON-NLS-1$ 210 } 211 } 212 } 213 214 /** 215 * Returns the list of configured class loading hooks. 216 * @return the list of configured class loading hooks. 217 */ getClassLoaderHooks()218 public List<ClassLoaderHook> getClassLoaderHooks() { 219 return classLoaderHooksRO; 220 } 221 222 /** 223 * Returns the list of configured storage hooks. 224 * @return the list of configured storage hooks. 225 */ getStorageHookFactories()226 public List<StorageHookFactory<?, ?, ?>> getStorageHookFactories() { 227 return storageHookFactoriesRO; 228 } 229 230 /** 231 * Returns the configured bundle file wrapper factories 232 * @return the configured bundle file wrapper factories 233 */ getBundleFileWrapperFactoryHooks()234 public List<BundleFileWrapperFactoryHook> getBundleFileWrapperFactoryHooks() { 235 return bundleFileWrapperFactoryHooksRO; 236 } 237 238 /** 239 * Returns the configured activator hook factories 240 * @return the configured activator hook factories 241 */ getActivatorHookFactories()242 public List<ActivatorHookFactory> getActivatorHookFactories() { 243 return activatorHookFactoriesRO; 244 } 245 add(H hook, List<H> hooks)246 private <H> void add(H hook, List<H> hooks) { 247 if (initialized) 248 throw new IllegalStateException("Cannot add hooks dynamically."); //$NON-NLS-1$ 249 hooks.add(hook); 250 } 251 252 /** 253 * Adds a class loader hook to this hook registry. 254 * @param classLoaderHook a class loading hook object. 255 */ addClassLoaderHook(ClassLoaderHook classLoaderHook)256 public void addClassLoaderHook(ClassLoaderHook classLoaderHook) { 257 add(classLoaderHook, classLoaderHooks); 258 } 259 260 /** 261 * Adds a storage hook to this hook registry. 262 * @param storageHookFactory a storage hook object. 263 */ addStorageHookFactory(StorageHookFactory<?, ?, ?> storageHookFactory)264 public void addStorageHookFactory(StorageHookFactory<?, ?, ?> storageHookFactory) { 265 add(storageHookFactory, storageHookFactories); 266 } 267 268 /** 269 * Adds a bundle file wrapper factory for this hook registry 270 * @param factory a bundle file wrapper factory object. 271 */ addBundleFileWrapperFactoryHook(BundleFileWrapperFactoryHook factory)272 public void addBundleFileWrapperFactoryHook(BundleFileWrapperFactoryHook factory) { 273 add(factory, bundleFileWrapperFactoryHooks); 274 } 275 276 /** 277 * Adds an activator hook factory. The activators created by this factory will be started and stopped 278 * when the system bundle is started and stopped. 279 * @param activatorHookFactory the activator hook factory. 280 */ addActivatorHookFactory(ActivatorHookFactory activatorHookFactory)281 public void addActivatorHookFactory(ActivatorHookFactory activatorHookFactory) { 282 add(activatorHookFactory, activatorHookFactories); 283 } 284 285 /** 286 * Returns the configuration associated with this hook registry. 287 * @return the configuration associated with this hook registry. 288 */ getConfiguration()289 public EquinoxConfiguration getConfiguration() { 290 return container.getConfiguration(); 291 } 292 293 /** 294 * Returns the equinox container associated with this hook registry. 295 * @return the equinox container associated with this hook registry. 296 */ getContainer()297 public EquinoxContainer getContainer() { 298 return container; 299 } 300 } 301