Java tutorial
/* * Copyright 2015-2016 Michael Kocherov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.develorium.metracer.asm; import java.io.*; import org.objectweb.asm.*; import org.objectweb.asm.commons.*; public class MetracerClassWriter extends ClassWriter { private ClassLoader classLoader; public MetracerClassWriter(ClassReader theReader, ClassLoader theLoader) { super(theReader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); classLoader = theLoader; } // Need to override in order to overcome class loaders isolation of modern app servers // Code is based on a test from an ASM framework and based on behaviour Javassist protected String getCommonSuperClass(String theType1, String theType2) { try { ClassReader info1 = typeInfo(theType1); ClassReader info2 = typeInfo(theType2); if ((info1.getAccess() & Opcodes.ACC_INTERFACE) != 0) { if (typeImplements(theType2, info2, theType1)) { return theType1; } if ((info2.getAccess() & Opcodes.ACC_INTERFACE) != 0) { if (typeImplements(theType1, info1, theType2)) { return theType2; } } return "java/lang/Object"; } if ((info2.getAccess() & Opcodes.ACC_INTERFACE) != 0) { if (typeImplements(theType1, info1, theType2)) { return theType2; } else { return "java/lang/Object"; } } StringBuilder b1 = typeAncestors(theType1, info1); StringBuilder b2 = typeAncestors(theType2, info2); String result = "java/lang/Object"; int end1 = b1.length(); int end2 = b2.length(); while (true) { int start1 = b1.lastIndexOf(";", end1 - 1); int start2 = b2.lastIndexOf(";", end2 - 1); if (start1 != -1 && start2 != -1 && end1 - start1 == end2 - start2) { String p1 = b1.substring(start1 + 1, end1); String p2 = b2.substring(start2 + 1, end2); if (p1.equals(p2)) { result = p1; end1 = start1; end2 = start2; } else { return result; } } else { return result; } } } catch (IOException e) { throw new RuntimeException(e.toString()); } } private ClassReader typeInfo(final String theType) throws IOException { StringBuilder visitedLoaders = new StringBuilder(); ClassLoader loader = classLoader; while (loader != null) { if (visitedLoaders.length() > 0) visitedLoaders.append(", "); visitedLoaders.append(loader.toString()); InputStream is = loader.getResourceAsStream(theType + ".class"); if (is != null) { try { return new ClassReader(is); } finally { is.close(); } } loader = loader.getParent(); } if (visitedLoaders.length() == 0) visitedLoaders.append("<empty>"); throw new IOException(String.format("Failed to open %1$s in all known class loaders: %2$s", theType, visitedLoaders.toString())); } private boolean typeImplements(String theType, ClassReader theReader, String theInterface) throws IOException { while (!"java/lang/Object".equals(theType)) { String[] itfs = theReader.getInterfaces(); for (int i = 0; i < itfs.length; ++i) { if (itfs[i].equals(theInterface)) { return true; } } for (int i = 0; i < itfs.length; ++i) { if (typeImplements(itfs[i], typeInfo(itfs[i]), theInterface)) { return true; } } theType = theReader.getSuperName(); theReader = typeInfo(theType); } return false; } private StringBuilder typeAncestors(String theType, ClassReader theReader) throws IOException { StringBuilder b = new StringBuilder(); while (!"java/lang/Object".equals(theType)) { b.append(';').append(theType); theType = theReader.getSuperName(); theReader = typeInfo(theType); } return b; } }