Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/cdsadapter/com.ibm.cds/src/com/ibm/cds/CDSHookImpls.java
12927 views
1
/*******************************************************************************
2
* Copyright (c) 2006, 2018 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
23
package com.ibm.cds;
24
25
import java.io.IOException;
26
import java.util.ArrayList;
27
import java.util.Arrays;
28
29
import org.eclipse.osgi.internal.hookregistry.BundleFileWrapperFactoryHook;
30
import org.eclipse.osgi.internal.hookregistry.ClassLoaderHook;
31
import org.eclipse.osgi.internal.loader.ModuleClassLoader;
32
import org.eclipse.osgi.internal.loader.classpath.ClasspathEntry;
33
import org.eclipse.osgi.internal.loader.classpath.ClasspathManager;
34
import org.eclipse.osgi.storage.BundleInfo.Generation;
35
import org.eclipse.osgi.storage.bundlefile.BundleEntry;
36
import org.eclipse.osgi.storage.bundlefile.BundleFile;
37
import org.eclipse.osgi.storage.bundlefile.BundleFileWrapper;
38
import org.eclipse.osgi.storage.bundlefile.BundleFileWrapperChain;
39
40
import com.ibm.oti.shared.HelperAlreadyDefinedException;
41
import com.ibm.oti.shared.Shared;
42
import com.ibm.oti.shared.SharedClassHelperFactory;
43
import com.ibm.oti.shared.SharedClassURLHelper;
44
45
public class CDSHookImpls extends ClassLoaderHook implements BundleFileWrapperFactoryHook {
46
private static SharedClassHelperFactory factory = Shared.getSharedClassHelperFactory();
47
private static java.lang.reflect.Method minimizeMethod = null;
48
private static boolean hasMinimizeMethod = true; /* Assume true to begin with */
49
50
// With Equinox bug 226038 (v3.4), the framework will now pass an instance
51
// of BundleFileWrapperChain rather than the wrapped BundleFile. This is
52
// so that multiple wrapping hooks can each wrap the BundleFile and all
53
// wrappers are accessible.
54
//
55
// The Wrapper chain will look like below:
56
// WrapperChain -> Wrapper<N> -> WrapperChain -> CDSBundleFile -> WrapperChain -> BundleFile
57
//
58
private static CDSBundleFile getCDSBundleFile(BundleFile bundleFile) {
59
CDSBundleFile cdsBundleFile = null;
60
61
if (bundleFile instanceof BundleFileWrapperChain) {
62
// Equinox > 3.4
63
BundleFile wrapped = null;
64
do {
65
wrapped = ((BundleFileWrapperChain) bundleFile).getWrapped();
66
if (wrapped instanceof CDSBundleFile) {
67
cdsBundleFile = (CDSBundleFile) wrapped;
68
break;
69
}
70
71
//Go to next wrapper chain.
72
bundleFile = ((BundleFileWrapperChain) bundleFile).getNext();
73
} while (wrapped != null);
74
}
75
return cdsBundleFile;
76
}
77
78
79
public void recordClassDefine(String name, Class clazz,
80
byte[] classbytes, ClasspathEntry classpathEntry,
81
BundleEntry entry, ClasspathManager manager) { // only attempt to record the class define if:
82
// 1) the class was found (clazz != null)
83
// 2) the class has the magic class number CAFEBABE indicating a real class
84
// 3) the bundle file for the classpath entry is of type CDSBundleFile
85
// 4) class bytes is same as passed to weaving hook i.e. weaving hook did not modify the class bytes
86
if ((null == clazz) || (false == hasMagicClassNumber(classbytes)) || (null == getCDSBundleFile(classpathEntry.getBundleFile()))) {
87
return;
88
}
89
try {
90
// check if weaving hook modified the class bytes
91
byte originalClassBytes[] = entry.getBytes();
92
if (originalClassBytes != classbytes) {
93
// weaving hook has potentially modified the class bytes
94
boolean modified = false;
95
if (originalClassBytes.length == classbytes.length) {
96
// do a byte-by-byte comparison
97
modified = !Arrays.equals(classbytes, originalClassBytes);
98
} else {
99
modified = true;
100
}
101
if (modified) {
102
// Class bytes have been modified by weaving hooks.
103
// Such classes need to be stored as Orphans, so skip the call to storeSharedClass()
104
return;
105
}
106
}
107
} catch (IOException e) {
108
// this should never happen, but in case it does, its safe to return
109
return;
110
}
111
112
CDSBundleFile cdsFile = getCDSBundleFile(classpathEntry.getBundleFile());
113
114
if (null == cdsFile.getURL()) {
115
// something went wrong trying to determine the url to the real bundle file
116
return;
117
}
118
119
// look for the urlHelper; if it does not exist then we are not sharing for this class loader
120
SharedClassURLHelper urlHelper = cdsFile.getURLHelper();
121
if (urlHelper == null) {
122
// this should never happen but just in case get the helper from the base host bundle file.
123
CDSBundleFile hostBundleFile = getCDSBundleFile(manager.getGeneration().getBundleFile());
124
if (null != hostBundleFile) {
125
// try getting the helper from the host base cdsFile
126
urlHelper = hostBundleFile.getURLHelper();
127
}
128
129
if (null != urlHelper) {
130
cdsFile.setURLHelper(urlHelper);
131
}
132
}
133
if (null != urlHelper) {
134
// store the class in the cache
135
urlHelper.storeSharedClass(cdsFile.getURL(), clazz);
136
cdsFile.setPrimed(true);
137
}
138
}
139
140
/* Calling setMinimizeUpdateChecks() on the urlHelper tells it to only check the plugin jar for updates
141
* once on startup. This removes the need to "prime" plugins by always cacheing the first class from the jar.
142
*
143
* Java5 does not have a runMinimizeUpdateChecks method, but Java6 does. The text below explains why.
144
*
145
* Java6 has an improved jar update detection mechanism which is event-driven and listens for
146
* real jar open and close events. It will check jar timestamps on every class-load for closed jars (when
147
* loading cached classes from those jars) and not check them if it knows the jars are open.
148
*
149
* Java5 didn't know about jar open/close events so simply assumed that the first class to be stored by
150
* a plugin implied that its jar was opened indefinitely. This is why it helps to "prime" a plugin when
151
* running under Java5 - by storing a class, the jar is opened and the JVM stops checking its timestamp
152
* which results in faster startup.
153
*
154
* While the Java6 behaviour is more correct (it will pick up changes if a jar is closed after having been opened),
155
* if the jars are not opened or "primed", then it will perform constant checks on their timestamps which hurts startup times.
156
* This is why setMinimizeUpdateChecks was introduced - it's a means of saying to the urlHelper - regardless of
157
* whether my container(s) is open or closed, I only want you to check it once for updates.
158
*
159
* The consequence of this is that less file handles are open on startup in Java6.
160
*
161
* This has been written in such a way that this adaptor will continue to work exactly the same with Java5, but
162
* will adapt its behaviour when used with Java6 to do the right thing.
163
*/
164
private boolean runMinimizeMethod(SharedClassURLHelper urlHelper) {
165
if (hasMinimizeMethod && (urlHelper != null)) {
166
if (minimizeMethod == null) {
167
hasMinimizeMethod = false; /* Assume failure - prove success below */
168
try {
169
Class c = urlHelper.getClass();
170
/* Not supported in the Java6 GA, but will be in SR1 + */
171
java.lang.reflect.Field isSupported = c.getField("MINIMIZE_ENABLED");
172
if (isSupported != null) { /* Field doesn't exist in Java5 */
173
if (isSupported.getBoolean(urlHelper)) {
174
minimizeMethod = c.getMethod("setMinimizeUpdateChecks", null);
175
hasMinimizeMethod = true;
176
}
177
}
178
} catch (Exception e) {
179
/* hasMinimizeMethod will be false and we won't try this again */
180
}
181
}
182
if (minimizeMethod != null) {
183
try {
184
minimizeMethod.invoke(urlHelper, null);
185
return true;
186
} catch (Exception e) {
187
hasMinimizeMethod = false;
188
}
189
}
190
}
191
return false;
192
}
193
194
private boolean hasMagicClassNumber(byte[] classbytes) {
195
if (classbytes == null || classbytes.length < 4)
196
return false;
197
// TODO maybe there is a better way to do this? I'm not sure why I had to AND each byte with the value I was checking ...
198
return (classbytes[0] & 0xCA) == 0xCA && (classbytes[1] & 0xFE) == 0xFE && (classbytes[2] & 0xBA) == 0xBA && (classbytes[3] & 0xBE) == 0xBE;
199
}
200
201
public void classLoaderCreated(ModuleClassLoader classLoader) {
202
// try to get the url helper for this class loader
203
if (factory == null) {
204
return;
205
}
206
CDSBundleFile hostFile = null;
207
try {
208
SharedClassURLHelper urlHelper = factory.getURLHelper(classLoader);
209
boolean minimizeSucceeded = runMinimizeMethod(urlHelper);
210
// set the url helper for the host base CDSBundleFile
211
hostFile = getCDSBundleFile(classLoader.getClasspathManager().getGeneration().getBundleFile());
212
if (hostFile != null) {
213
hostFile.setURLHelper(urlHelper);
214
if (minimizeSucceeded) {
215
/* In Java6, there is no longer a requirement to "prime" plugins */
216
hostFile.setPrimed(true);
217
}
218
}
219
} catch (HelperAlreadyDefinedException e) {
220
// We should never get here.
221
// If we do, we simply won't share for this ClassLoader
222
}
223
}
224
225
public boolean addClassPathEntry(ArrayList cpEntries,
226
String cp, ClasspathManager hostmanager, Generation sourceGeneration) {
227
CDSBundleFile hostFile = getCDSBundleFile(hostmanager.getGeneration().getBundleFile());
228
CDSBundleFile sourceFile = getCDSBundleFile(sourceGeneration.getBundleFile());
229
if ((hostFile != sourceFile) && (null != hostFile) && (null != sourceFile)) {
230
// set the helper that got set on the host base bundle file in initializedClassLoader
231
SharedClassURLHelper urlHelper = hostFile.getURLHelper();
232
sourceFile.setURLHelper(urlHelper);
233
}
234
235
return false;
236
}
237
238
//////////////// BundleFileWrapperFactoryHook //////////////
239
public BundleFileWrapper wrapBundleFile(BundleFile bundleFile, Generation generation, boolean base) {
240
// wrap the real bundle file for purposes of loading shared classes.
241
CDSBundleFile newBundleFile;
242
if (!base && generation.getBundleInfo().getBundleId() != 0) {
243
// initialize the urlHelper from the base one.
244
SharedClassURLHelper urlHelper = null;
245
BundleFile baseFile = generation.getBundleFile();
246
if ((baseFile = getCDSBundleFile(baseFile)) != null) {
247
urlHelper = ((CDSBundleFile) baseFile).getURLHelper();
248
}
249
newBundleFile = new CDSBundleFile(bundleFile, urlHelper);
250
} else {
251
newBundleFile = new CDSBundleFile(bundleFile);
252
}
253
254
return newBundleFile;
255
}
256
}
257
258