Path: blob/master/cdsadapter/com.ibm.cds/src/com/ibm/cds/CDSHookImpls.java
12927 views
/*******************************************************************************1* Copyright (c) 2006, 2018 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* 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-exception20*******************************************************************************/2122package com.ibm.cds;2324import java.io.IOException;25import java.util.ArrayList;26import java.util.Arrays;2728import org.eclipse.osgi.internal.hookregistry.BundleFileWrapperFactoryHook;29import org.eclipse.osgi.internal.hookregistry.ClassLoaderHook;30import org.eclipse.osgi.internal.loader.ModuleClassLoader;31import org.eclipse.osgi.internal.loader.classpath.ClasspathEntry;32import org.eclipse.osgi.internal.loader.classpath.ClasspathManager;33import org.eclipse.osgi.storage.BundleInfo.Generation;34import org.eclipse.osgi.storage.bundlefile.BundleEntry;35import org.eclipse.osgi.storage.bundlefile.BundleFile;36import org.eclipse.osgi.storage.bundlefile.BundleFileWrapper;37import org.eclipse.osgi.storage.bundlefile.BundleFileWrapperChain;3839import com.ibm.oti.shared.HelperAlreadyDefinedException;40import com.ibm.oti.shared.Shared;41import com.ibm.oti.shared.SharedClassHelperFactory;42import com.ibm.oti.shared.SharedClassURLHelper;4344public class CDSHookImpls extends ClassLoaderHook implements BundleFileWrapperFactoryHook {45private static SharedClassHelperFactory factory = Shared.getSharedClassHelperFactory();46private static java.lang.reflect.Method minimizeMethod = null;47private static boolean hasMinimizeMethod = true; /* Assume true to begin with */4849// With Equinox bug 226038 (v3.4), the framework will now pass an instance50// of BundleFileWrapperChain rather than the wrapped BundleFile. This is51// so that multiple wrapping hooks can each wrap the BundleFile and all52// wrappers are accessible.53//54// The Wrapper chain will look like below:55// WrapperChain -> Wrapper<N> -> WrapperChain -> CDSBundleFile -> WrapperChain -> BundleFile56//57private static CDSBundleFile getCDSBundleFile(BundleFile bundleFile) {58CDSBundleFile cdsBundleFile = null;5960if (bundleFile instanceof BundleFileWrapperChain) {61// Equinox > 3.462BundleFile wrapped = null;63do {64wrapped = ((BundleFileWrapperChain) bundleFile).getWrapped();65if (wrapped instanceof CDSBundleFile) {66cdsBundleFile = (CDSBundleFile) wrapped;67break;68}6970//Go to next wrapper chain.71bundleFile = ((BundleFileWrapperChain) bundleFile).getNext();72} while (wrapped != null);73}74return cdsBundleFile;75}767778public void recordClassDefine(String name, Class clazz,79byte[] classbytes, ClasspathEntry classpathEntry,80BundleEntry entry, ClasspathManager manager) { // only attempt to record the class define if:81// 1) the class was found (clazz != null)82// 2) the class has the magic class number CAFEBABE indicating a real class83// 3) the bundle file for the classpath entry is of type CDSBundleFile84// 4) class bytes is same as passed to weaving hook i.e. weaving hook did not modify the class bytes85if ((null == clazz) || (false == hasMagicClassNumber(classbytes)) || (null == getCDSBundleFile(classpathEntry.getBundleFile()))) {86return;87}88try {89// check if weaving hook modified the class bytes90byte originalClassBytes[] = entry.getBytes();91if (originalClassBytes != classbytes) {92// weaving hook has potentially modified the class bytes93boolean modified = false;94if (originalClassBytes.length == classbytes.length) {95// do a byte-by-byte comparison96modified = !Arrays.equals(classbytes, originalClassBytes);97} else {98modified = true;99}100if (modified) {101// Class bytes have been modified by weaving hooks.102// Such classes need to be stored as Orphans, so skip the call to storeSharedClass()103return;104}105}106} catch (IOException e) {107// this should never happen, but in case it does, its safe to return108return;109}110111CDSBundleFile cdsFile = getCDSBundleFile(classpathEntry.getBundleFile());112113if (null == cdsFile.getURL()) {114// something went wrong trying to determine the url to the real bundle file115return;116}117118// look for the urlHelper; if it does not exist then we are not sharing for this class loader119SharedClassURLHelper urlHelper = cdsFile.getURLHelper();120if (urlHelper == null) {121// this should never happen but just in case get the helper from the base host bundle file.122CDSBundleFile hostBundleFile = getCDSBundleFile(manager.getGeneration().getBundleFile());123if (null != hostBundleFile) {124// try getting the helper from the host base cdsFile125urlHelper = hostBundleFile.getURLHelper();126}127128if (null != urlHelper) {129cdsFile.setURLHelper(urlHelper);130}131}132if (null != urlHelper) {133// store the class in the cache134urlHelper.storeSharedClass(cdsFile.getURL(), clazz);135cdsFile.setPrimed(true);136}137}138139/* Calling setMinimizeUpdateChecks() on the urlHelper tells it to only check the plugin jar for updates140* once on startup. This removes the need to "prime" plugins by always cacheing the first class from the jar.141*142* Java5 does not have a runMinimizeUpdateChecks method, but Java6 does. The text below explains why.143*144* Java6 has an improved jar update detection mechanism which is event-driven and listens for145* real jar open and close events. It will check jar timestamps on every class-load for closed jars (when146* loading cached classes from those jars) and not check them if it knows the jars are open.147*148* Java5 didn't know about jar open/close events so simply assumed that the first class to be stored by149* a plugin implied that its jar was opened indefinitely. This is why it helps to "prime" a plugin when150* running under Java5 - by storing a class, the jar is opened and the JVM stops checking its timestamp151* which results in faster startup.152*153* While the Java6 behaviour is more correct (it will pick up changes if a jar is closed after having been opened),154* if the jars are not opened or "primed", then it will perform constant checks on their timestamps which hurts startup times.155* This is why setMinimizeUpdateChecks was introduced - it's a means of saying to the urlHelper - regardless of156* whether my container(s) is open or closed, I only want you to check it once for updates.157*158* The consequence of this is that less file handles are open on startup in Java6.159*160* This has been written in such a way that this adaptor will continue to work exactly the same with Java5, but161* will adapt its behaviour when used with Java6 to do the right thing.162*/163private boolean runMinimizeMethod(SharedClassURLHelper urlHelper) {164if (hasMinimizeMethod && (urlHelper != null)) {165if (minimizeMethod == null) {166hasMinimizeMethod = false; /* Assume failure - prove success below */167try {168Class c = urlHelper.getClass();169/* Not supported in the Java6 GA, but will be in SR1 + */170java.lang.reflect.Field isSupported = c.getField("MINIMIZE_ENABLED");171if (isSupported != null) { /* Field doesn't exist in Java5 */172if (isSupported.getBoolean(urlHelper)) {173minimizeMethod = c.getMethod("setMinimizeUpdateChecks", null);174hasMinimizeMethod = true;175}176}177} catch (Exception e) {178/* hasMinimizeMethod will be false and we won't try this again */179}180}181if (minimizeMethod != null) {182try {183minimizeMethod.invoke(urlHelper, null);184return true;185} catch (Exception e) {186hasMinimizeMethod = false;187}188}189}190return false;191}192193private boolean hasMagicClassNumber(byte[] classbytes) {194if (classbytes == null || classbytes.length < 4)195return false;196// 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 ...197return (classbytes[0] & 0xCA) == 0xCA && (classbytes[1] & 0xFE) == 0xFE && (classbytes[2] & 0xBA) == 0xBA && (classbytes[3] & 0xBE) == 0xBE;198}199200public void classLoaderCreated(ModuleClassLoader classLoader) {201// try to get the url helper for this class loader202if (factory == null) {203return;204}205CDSBundleFile hostFile = null;206try {207SharedClassURLHelper urlHelper = factory.getURLHelper(classLoader);208boolean minimizeSucceeded = runMinimizeMethod(urlHelper);209// set the url helper for the host base CDSBundleFile210hostFile = getCDSBundleFile(classLoader.getClasspathManager().getGeneration().getBundleFile());211if (hostFile != null) {212hostFile.setURLHelper(urlHelper);213if (minimizeSucceeded) {214/* In Java6, there is no longer a requirement to "prime" plugins */215hostFile.setPrimed(true);216}217}218} catch (HelperAlreadyDefinedException e) {219// We should never get here.220// If we do, we simply won't share for this ClassLoader221}222}223224public boolean addClassPathEntry(ArrayList cpEntries,225String cp, ClasspathManager hostmanager, Generation sourceGeneration) {226CDSBundleFile hostFile = getCDSBundleFile(hostmanager.getGeneration().getBundleFile());227CDSBundleFile sourceFile = getCDSBundleFile(sourceGeneration.getBundleFile());228if ((hostFile != sourceFile) && (null != hostFile) && (null != sourceFile)) {229// set the helper that got set on the host base bundle file in initializedClassLoader230SharedClassURLHelper urlHelper = hostFile.getURLHelper();231sourceFile.setURLHelper(urlHelper);232}233234return false;235}236237//////////////// BundleFileWrapperFactoryHook //////////////238public BundleFileWrapper wrapBundleFile(BundleFile bundleFile, Generation generation, boolean base) {239// wrap the real bundle file for purposes of loading shared classes.240CDSBundleFile newBundleFile;241if (!base && generation.getBundleInfo().getBundleId() != 0) {242// initialize the urlHelper from the base one.243SharedClassURLHelper urlHelper = null;244BundleFile baseFile = generation.getBundleFile();245if ((baseFile = getCDSBundleFile(baseFile)) != null) {246urlHelper = ((CDSBundleFile) baseFile).getURLHelper();247}248newBundleFile = new CDSBundleFile(bundleFile, urlHelper);249} else {250newBundleFile = new CDSBundleFile(bundleFile);251}252253return newBundleFile;254}255}256257258