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 (&quot;hookconfigurators.properties&quot;) <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 (&quot;hookconfigurators.properties&quot;) 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 (&quot;osgi.hook.configurators.include&quot;) 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 (&quot;osgi.hook.configurators.exclude&quot;) 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 (&quot;osgi.hook.configurators&quot;) 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 (&quot;osgi.hook.configurators.include&quot;) 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 (&quot;osgi.hook.configurators.exclude&quot;) 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