Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: f9d881dd465a9e28ae48d711fa53537dac7d3968 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/*******************************************************************************
 * Copyright (c) 2005, 2016 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.osgi.internal.hookregistry;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
import org.eclipse.osgi.internal.framework.EquinoxContainer;
import org.eclipse.osgi.internal.hooks.DevClassLoadingHook;
import org.eclipse.osgi.internal.hooks.EclipseLazyStarter;
import org.eclipse.osgi.internal.signedcontent.SignedBundleHook;
import org.eclipse.osgi.internal.weaving.WeavingHookConfigurator;
import org.eclipse.osgi.util.ManifestElement;

/**
 * The hook registry is used to store all the hooks which are
 * configured by the hook configurators.
 * @see HookConfigurator
 */
public final class HookRegistry {
	/**
	 * The hook configurators properties file (&quot;hookconfigurators.properties&quot;) <p>
	 * A framework extension may supply a hook configurators properties file to specify a 
	 * list of hook configurators.
	 * @see #HOOK_CONFIGURATORS
	 */
	public static final String HOOK_CONFIGURATORS_FILE = "hookconfigurators.properties"; //$NON-NLS-1$

	/**
	 * The hook configurators property key (&quot;hookconfigurators.properties&quot;) used in 
	 * a hook configurators properties file to specify a comma separated list of fully 
	 * qualified hook configurator classes.
	 */
	public static final String HOOK_CONFIGURATORS = "hook.configurators"; //$NON-NLS-1$

	/**
	 * A system property (&quot;osgi.hook.configurators.include&quot;) used to add additional
	 * hook configurators.  This is helpful for configuring optional hook configurators.
	 */
	public static final String PROP_HOOK_CONFIGURATORS_INCLUDE = "osgi.hook.configurators.include"; //$NON-NLS-1$

	/**
	 * A system property (&quot;osgi.hook.configurators.exclude&quot;) used to exclude 
	 * any hook configurators.  This is helpful for disabling hook
	 * configurators that is specified in hook configurator properties files.
	 */
	public static final String PROP_HOOK_CONFIGURATORS_EXCLUDE = "osgi.hook.configurators.exclude"; //$NON-NLS-1$

	/**
	 * A system property (&quot;osgi.hook.configurators&quot;) used to specify the list
	 * of hook configurators.  If this property is set then the list of configurators 
	 * specified will be the only configurators used.
	 */
	public static final String PROP_HOOK_CONFIGURATORS = "osgi.hook.configurators"; //$NON-NLS-1$

	private static final String BUILTIN_HOOKS = "builtin.hooks"; //$NON-NLS-1$

	private final EquinoxContainer container;
	private volatile boolean initialized = false;
	private final List<ClassLoaderHook> classLoaderHooks = new ArrayList<>();
	private final List<ClassLoaderHook> classLoaderHooksRO = Collections.unmodifiableList(classLoaderHooks);
	private final List<StorageHookFactory<?, ?, ?>> storageHookFactories = new ArrayList<>();
	private final List<StorageHookFactory<?, ?, ?>> storageHookFactoriesRO = Collections.unmodifiableList(storageHookFactories);
	private final List<BundleFileWrapperFactoryHook> bundleFileWrapperFactoryHooks = new ArrayList<>();
	private final List<BundleFileWrapperFactoryHook> bundleFileWrapperFactoryHooksRO = Collections.unmodifiableList(bundleFileWrapperFactoryHooks);
	private final List<ActivatorHookFactory> activatorHookFactories = new ArrayList<>();
	private final List<ActivatorHookFactory> activatorHookFactoriesRO = Collections.unmodifiableList(activatorHookFactories);

	public HookRegistry(EquinoxContainer container) {
		this.container = container;
	}

	/**
	 * Initializes the hook configurators.  The following steps are used to initialize the hook configurators. <p>
	 * 1. Get a list of hook configurators from all hook configurators properties files on the classpath, 
	 *    add this list to the overall list of hook configurators, remove duplicates. <p>
	 * 2. Get a list of hook configurators from the (&quot;osgi.hook.configurators.include&quot;) system property 
	 *    and add this list to the overall list of hook configurators, remove duplicates. <p>
	 * 3. Get a list of hook configurators from the (&quot;osgi.hook.configurators.exclude&quot;) system property
	 *    and remove this list from the overall list of hook configurators. <p>
	 * 4. Load each hook configurator class, create a new instance, then call the {@link HookConfigurator#addHooks(HookRegistry)} method <p>
	 * 5. Set this HookRegistry object to read only to prevent any other hooks from being added. <p>
	 */
	public void initialize() {
		List<String> configurators = new ArrayList<>(5);
		List<FrameworkLogEntry> errors = new ArrayList<>(0); // optimistic that no errors will occur
		mergeFileHookConfigurators(configurators, errors);
		mergePropertyHookConfigurators(configurators);
		synchronized (this) {
			addClassLoaderHook(new DevClassLoadingHook(container.getConfiguration()));
			addClassLoaderHook(new EclipseLazyStarter(container));
			addClassLoaderHook(new WeavingHookConfigurator(container));
			configurators.add(SignedBundleHook.class.getName());
			loadConfigurators(configurators, errors);
			// set to read-only
			initialized = true;
		}
		for (FrameworkLogEntry error : errors) {
			container.getLogServices().getFrameworkLog().log(error);
		}
	}

	private void mergeFileHookConfigurators(List<String> configuratorList, List<FrameworkLogEntry> errors) {
		ClassLoader cl = getClass().getClassLoader();
		// get all hook configurators files in your classloader delegation
		Enumeration<URL> hookConfigurators;
		try {
			hookConfigurators = cl != null ? cl.getResources(HookRegistry.HOOK_CONFIGURATORS_FILE) : ClassLoader.getSystemResources(HookRegistry.HOOK_CONFIGURATORS_FILE);
		} catch (IOException e) {
			errors.add(new FrameworkLogEntry(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, 0, "getResources error on " + HookRegistry.HOOK_CONFIGURATORS_FILE, 0, e, null)); //$NON-NLS-1$
			return;
		}
		int curBuiltin = 0;
		while (hookConfigurators.hasMoreElements()) {
			URL url = hookConfigurators.nextElement();
			InputStream input = null;
			try {
				// check each file for a hook.configurators property
				Properties configuratorProps = new Properties();
				input = url.openStream();
				configuratorProps.load(input);
				String hooksValue = configuratorProps.getProperty(HOOK_CONFIGURATORS);
				if (hooksValue == null)
					continue;
				boolean builtin = Boolean.valueOf(configuratorProps.getProperty(BUILTIN_HOOKS)).booleanValue();
				String[] configurators = ManifestElement.getArrayFromList(hooksValue, ","); //$NON-NLS-1$
				for (int i = 0; i < configurators.length; i++)
					if (!configuratorList.contains(configurators[i])) {
						if (builtin) // make sure the built-in configurators are listed first (bug 170881)
							configuratorList.add(curBuiltin++, configurators[i]);
						else
							configuratorList.add(configurators[i]);
					}
			} catch (IOException e) {
				errors.add(new FrameworkLogEntry(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, 0, "error loading: " + url.toExternalForm(), 0, e, null)); //$NON-NLS-1$
				// ignore and continue to next URL
			} finally {
				if (input != null)
					try {
						input.close();
					} catch (IOException e) {
						// do nothing
					}
			}
		}
	}

	private void mergePropertyHookConfigurators(List<String> configuratorList) {
		// see if there is a configurators list
		String[] configurators = ManifestElement.getArrayFromList(container.getConfiguration().getConfiguration(HookRegistry.PROP_HOOK_CONFIGURATORS), ","); //$NON-NLS-1$
		if (configurators.length > 0) {
			configuratorList.clear(); // clear the list, we are only going to use the configurators from the list
			for (int i = 0; i < configurators.length; i++)
				if (!configuratorList.contains(configurators[i]))
					configuratorList.add(configurators[i]);
			return; // don't do anything else
		}
		// Make sure the configurators from the include property are in the list
		String[] includeConfigurators = ManifestElement.getArrayFromList(container.getConfiguration().getConfiguration(HookRegistry.PROP_HOOK_CONFIGURATORS_INCLUDE), ","); //$NON-NLS-1$
		for (int i = 0; i < includeConfigurators.length; i++)
			if (!configuratorList.contains(includeConfigurators[i]))
				configuratorList.add(includeConfigurators[i]);
		// Make sure the configurators from the exclude property are no in the list
		String[] excludeHooks = ManifestElement.getArrayFromList(container.getConfiguration().getConfiguration(HookRegistry.PROP_HOOK_CONFIGURATORS_EXCLUDE), ","); //$NON-NLS-1$
		for (int i = 0; i < excludeHooks.length; i++)
			configuratorList.remove(excludeHooks[i]);
	}

	private void loadConfigurators(List<String> configurators, List<FrameworkLogEntry> errors) {
		for (Iterator<String> iHooks = configurators.iterator(); iHooks.hasNext();) {
			String hookName = iHooks.next();
			try {
				Class<?> clazz = Class.forName(hookName);
				HookConfigurator configurator = (HookConfigurator) clazz.newInstance();
				configurator.addHooks(this);
			} catch (Throwable t) {
				// We expect the follow exeptions may happen; but we need to catch all here
				// ClassNotFoundException
				// IllegalAccessException
				// InstantiationException
				// ClassCastException
				errors.add(new FrameworkLogEntry(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, 0, "error loading hook: " + hookName, 0, t, null)); //$NON-NLS-1$
			}
		}
	}

	/**
	 * Returns the list of configured class loading hooks.
	 * @return the list of configured class loading hooks.
	 */
	public List<ClassLoaderHook> getClassLoaderHooks() {
		return classLoaderHooksRO;
	}

	/**
	 * Returns the list of configured storage hooks.
	 * @return the list of configured storage hooks.
	 */
	public List<StorageHookFactory<?, ?, ?>> getStorageHookFactories() {
		return storageHookFactoriesRO;
	}

	/**
	 * Returns the configured bundle file wrapper factories
	 * @return the configured bundle file wrapper factories
	 */
	public List<BundleFileWrapperFactoryHook> getBundleFileWrapperFactoryHooks() {
		return bundleFileWrapperFactoryHooksRO;
	}

	/**
	 * Returns the configured activator hook factories
	 * @return the configured activator hook factories
	 */
	public List<ActivatorHookFactory> getActivatorHookFactories() {
		return activatorHookFactoriesRO;
	}

	private <H> void add(H hook, List<H> hooks) {
		if (initialized)
			throw new IllegalStateException("Cannot add hooks dynamically."); //$NON-NLS-1$
		hooks.add(hook);
	}

	/**
	 * Adds a class loader hook to this hook registry.
	 * @param classLoaderHook a class loading hook object.
	 */
	public void addClassLoaderHook(ClassLoaderHook classLoaderHook) {
		add(classLoaderHook, classLoaderHooks);
	}

	/**
	 * Adds a storage hook to this hook registry.
	 * @param storageHookFactory a storage hook object.
	 */
	public void addStorageHookFactory(StorageHookFactory<?, ?, ?> storageHookFactory) {
		add(storageHookFactory, storageHookFactories);
	}

	/**
	 * Adds a bundle file wrapper factory for this hook registry
	 * @param factory a bundle file wrapper factory object.
	 */
	public void addBundleFileWrapperFactoryHook(BundleFileWrapperFactoryHook factory) {
		add(factory, bundleFileWrapperFactoryHooks);
	}

	/**
	 * Adds an activator hook factory.  The activators created by this factory will be started and stopped
	 * when the system bundle is started and stopped.
	 * @param activatorHookFactory the activator hook factory.
	 */
	public void addActivatorHookFactory(ActivatorHookFactory activatorHookFactory) {
		add(activatorHookFactory, activatorHookFactories);
	}

	/**
	 * Returns the configuration associated with this hook registry.
	 * @return the configuration associated with this hook registry.
	 */
	public EquinoxConfiguration getConfiguration() {
		return container.getConfiguration();
	}

	/**
	 * Returns the equinox container associated with this hook registry.
	 * @return the equinox container associated with this hook registry.
	 */
	public EquinoxContainer getContainer() {
		return container;
	}
}

Back to the top