Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: e1f830469698deb342de47c8d06d5cf291431ce8 (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
/*******************************************************************************
 * Copyright (c) 2010, 2017 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.weaving;

import java.util.*;
import org.eclipse.osgi.internal.framework.EquinoxContainer;
import org.eclipse.osgi.internal.hookregistry.ClassLoaderHook;
import org.eclipse.osgi.internal.loader.BundleLoader;
import org.eclipse.osgi.internal.loader.ModuleClassLoader;
import org.eclipse.osgi.internal.loader.classpath.ClasspathEntry;
import org.eclipse.osgi.internal.loader.classpath.ClasspathManager;
import org.eclipse.osgi.internal.serviceregistry.ServiceRegistry;
import org.eclipse.osgi.storage.bundlefile.BundleEntry;
import org.osgi.framework.*;

public class WeavingHookConfigurator extends ClassLoaderHook {
	static class WovenClassContext {
		List<WovenClassImpl> wovenClassStack = new ArrayList<>(6);
		List<String> processClassNameStack = new ArrayList<>(6);
	}

	// holds the map of black listed hooks.  Use weak map to avoid pinning and simplify cleanup.
	private final Map<ServiceRegistration<?>, Boolean> blackList = Collections.synchronizedMap(new WeakHashMap<ServiceRegistration<?>, Boolean>());
	// holds the stack of WovenClass objects currently being used to define classes
	private final ThreadLocal<WovenClassContext> wovenClassContext = new ThreadLocal<>();

	private final EquinoxContainer container;

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

	private ServiceRegistry getRegistry() {
		return container.getServiceRegistry();
	}

	public byte[] processClass(String name, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClasspathManager manager) {
		ServiceRegistry registry = getRegistry();
		if (registry == null)
			return null; // no registry somehow we are loading classes before the registry has been created
		ModuleClassLoader classLoader = manager.getClassLoader();
		BundleLoader loader = classLoader.getBundleLoader();
		// create a woven class object and add it to the thread local stack
		WovenClassImpl wovenClass = new WovenClassImpl(name, classbytes, entry, classpathEntry, loader, container, blackList);
		WovenClassContext context = wovenClassContext.get();
		if (context == null) {
			context = new WovenClassContext();
			wovenClassContext.set(context);
		}
		context.wovenClassStack.add(wovenClass);
		// If we detect recursion for the same class name then we will not call the
		// weaving hooks for the second request to load.
		// Note that this means the actual bytes that get used to define the class
		// will not have been woven at all.
		if (!context.processClassNameStack.contains(name)) {
			context.processClassNameStack.add(name);
			// call the weaving hooks
			try {
				return wovenClass.callHooks();
			} catch (Throwable t) {
				ServiceRegistration<?> errorHook = wovenClass.getErrorHook();
				Bundle errorBundle = errorHook != null ? errorHook.getReference().getBundle() : manager.getGeneration().getRevision().getBundle();
				container.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, errorBundle, t);
				// fail hard with a class loading error
				ClassFormatError error = new ClassFormatError("Unexpected error from weaving hook."); //$NON-NLS-1$
				error.initCause(t);
				throw error;
			} finally {
				context.processClassNameStack.remove(name);
			}
		}
		return null;
	}

	public void recordClassDefine(String name, Class<?> clazz, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClasspathManager manager) {
		// here we assume the stack contans a woven class with the same name as the class we are defining.
		WovenClassContext context = wovenClassContext.get();
		if (context == null || context.wovenClassStack.size() == 0)
			return;
		WovenClassImpl wovenClass = context.wovenClassStack.remove(context.wovenClassStack.size() - 1);
		// inform the woven class about the class that was defined.
		wovenClass.setWeavingCompleted(clazz);
	}

	@Override
	public boolean isProcessClassRecursionSupported() {
		return true;
	}
}

Back to the top