Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: a5f5a656ef1a8dddd4d5ef1b6d3187471005b1c7 (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
/*******************************************************************************
 * Copyright (c) 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.connect;

import static org.eclipse.osgi.internal.framework.EquinoxContainer.sneakyThrow;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.osgi.container.Module;
import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent;
import org.eclipse.osgi.container.ModuleRevisionBuilder;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
import org.eclipse.osgi.internal.framework.EquinoxContainer.ConnectModules;
import org.eclipse.osgi.internal.hookregistry.ActivatorHookFactory;
import org.eclipse.osgi.internal.hookregistry.ClassLoaderHook;
import org.eclipse.osgi.internal.hookregistry.HookConfigurator;
import org.eclipse.osgi.internal.hookregistry.HookRegistry;
import org.eclipse.osgi.internal.hookregistry.StorageHookFactory;
import org.eclipse.osgi.internal.hookregistry.StorageHookFactory.StorageHook;
import org.eclipse.osgi.internal.loader.BundleLoader;
import org.eclipse.osgi.internal.loader.ModuleClassLoader;
import org.eclipse.osgi.storage.BundleInfo.Generation;
import org.eclipse.osgi.storage.bundlefile.BundleFile;
import org.eclipse.osgi.storage.bundlefile.BundleFileWrapperChain;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.connect.ConnectContent;
import org.osgi.framework.connect.ConnectModule;
import org.osgi.framework.connect.ModuleConnector;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.IdentityNamespace;

public class ConnectHookConfigurator implements HookConfigurator {
	static final Collection<String> CONNECT_TAG_NAMESPACES = new ArrayList<>(Arrays.asList(
			BundleNamespace.BUNDLE_NAMESPACE, HostNamespace.HOST_NAMESPACE, IdentityNamespace.IDENTITY_NAMESPACE));

	@Override
	public void addHooks(final HookRegistry hookRegistry) {
		final ConnectModules connectModules = hookRegistry.getContainer().getConnectModules();
		ModuleConnector moduleConnector = connectModules.getModuleConnector();

		hookRegistry.addStorageHookFactory(new StorageHookFactory<Object, Object, StorageHook<Object, Object>>() {
			@Override
			protected StorageHook<Object, Object> createStorageHook(Generation generation) {
				final ConnectModule m = connectModules.getConnectModule(generation.getBundleInfo().getLocation());

				return new StorageHook<Object, Object>(generation, this.getClass()) {
					boolean hasModule = false;

					@Override
					public void save(Object saveContext, DataOutputStream os) throws IOException {
						os.writeBoolean(m != null);
					}

					@Override
					public void load(Object loadContext, DataInputStream is) throws IOException {
						hasModule = is.readBoolean();
					}

					@Override
					public void validate() throws IllegalStateException {
						// make sure we have the module still from the factory
						if (hasModule && m == null) {
							throw new IllegalStateException("Connect Factory no longer has the module at locataion: " + generation.getBundleInfo().getLocation()); //$NON-NLS-1$
						}
					}

					@Override
					public ModuleRevisionBuilder adaptModuleRevisionBuilder(ModuleEvent operation, Module origin, ModuleRevisionBuilder builder) {
						if (m != null) {
							CONNECT_TAG_NAMESPACES.stream().map(builder::getCapabilities).flatMap(List::stream)
									.forEach(c -> c.getAttributes().compute(IdentityNamespace.CAPABILITY_TAGS_ATTRIBUTE,
											(k, v) -> {
												if (v == null) {
													return Collections.singletonList(ConnectContent.TAG_OSGI_CONNECT);
												}
												if (v instanceof List) {
													@SuppressWarnings({ "unchecked", "rawtypes" })
													List<String> l = new ArrayList<>((List) v);
													l.add(ConnectContent.TAG_OSGI_CONNECT);
													return Collections.unmodifiableList(l);
												}
												// should not get here, but just recover
												return Arrays.asList(v, ConnectContent.TAG_OSGI_CONNECT);
											}));
							return builder;
						}
						return null;
					}
				};
			}

			@Override
			public URLConnection handleContentConnection(Module module, String location, InputStream in) {
				if (in != null) {
					// Do not call ModuleConnector method connect when input stream is non null.
					return null;
				}
				if (location == null) {
					location = module.getLocation();
				}
				try {
					ConnectModule m = connectModules.connect(location);
					if (m != null) {
						return ConnectInputStream.URL_CONNECTION_INSTANCE;
					}
				} catch (IllegalStateException e) {
					if (e.getCause() instanceof BundleException) {
						sneakyThrow(e.getCause());
					}
				}
				return null;
			}
		});

		if (moduleConnector == null) {
			return;
		}

		hookRegistry.addClassLoaderHook(new ClassLoaderHook() {
			@Override
			public ModuleClassLoader createClassLoader(ClassLoader parent, EquinoxConfiguration configuration, BundleLoader delegate, Generation generation) {
				ConnectModule m = connectModules.getConnectModule(generation.getBundleInfo().getLocation());
				if (m != null) {
					BundleFile bundlefile = generation.getBundleFile();
					if (bundlefile instanceof BundleFileWrapperChain) {
						BundleFileWrapperChain chain = (BundleFileWrapperChain) bundlefile;
						while (chain.getNext() != null) {
							chain = chain.getNext();
						}
						bundlefile = chain.getBundleFile();
					}
					if (bundlefile instanceof ConnectBundleFile) {
						return ((ConnectBundleFile) bundlefile).getClassLoader().map((l) //
						-> new DelegatingConnectClassLoader(parent, configuration, delegate, generation, l)).orElse(null);
					}
				}
				return null;
			}
		});

		hookRegistry.addActivatorHookFactory(new ActivatorHookFactory() {

			@Override
			public BundleActivator createActivator() {
				final List<BundleActivator> activators = new ArrayList<>();
				moduleConnector.newBundleActivator().ifPresent((a) -> activators.add(a));
				return new BundleActivator() {
					@Override
					public void start(BundleContext context) throws Exception {
						for (BundleActivator activator : activators) {
							activator.start(context);
						}
					}

					@Override
					public void stop(BundleContext context) throws Exception {
						for (BundleActivator activator : activators) {
							activator.stop(context);
						}
					}
				};
			}
		});
	}
}

Back to the top