/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
//Portions Copyright [2016] [Payara Foundation]
package org.glassfish.flashlight.impl.core;
/**
* @author Mahesh Kannan
* Date: Nov 8, 2009
* Fixed a bunch of FindBugs problem, 2/2012, Byron Nevins
*/
import com.sun.enterprise.util.LocalStringManagerImpl;
import org.glassfish.flashlight.FlashlightLoggerInfo;
import org.glassfish.hk2.external.org.objectweb.asm.*;
import java.security.*;
import java.io.InputStream;
import java.security.ProtectionDomain;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.concurrent.atomic.AtomicInteger;
public class ProviderSubClassImplGenerator {
private static final Logger logger = FlashlightLoggerInfo.getLogger();
public final static LocalStringManagerImpl localStrings =
new LocalStringManagerImpl(ProviderSubClassImplGenerator.class);
private String invokerId;
private Class providerClazz;
private static AtomicInteger counter = new AtomicInteger();
public ProviderSubClassImplGenerator(Class providerClazz, String invokerId) {
this.providerClazz = providerClazz;
this.invokerId = invokerId;
}
public <T> Class<T> generateAndDefineClass(final Class<T> providerClazz, String invokerId) {
int id = counter.incrementAndGet();
String providerClassName = providerClazz.getName().replace('.', '/');
String generatedClassName = providerClassName + invokerId + "_" + id;
byte[] provClassData = null;
try {
InputStream is = providerClazz.getClassLoader().getResourceAsStream(providerClassName + ".class");
int sz = is.available();
provClassData = new byte[sz];
int index = 0;
while (index < sz) {
int r = is.read(provClassData, index, sz - index);
if (r > 0) {
index += r;
}
}
}
catch (Exception ex) {
return null;
}
ClassReader cr = new ClassReader(provClassData);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES + ClassWriter.COMPUTE_MAXS);
byte[] classData = null;
ProbeProviderSubClassGenerator sgen = new ProbeProviderSubClassGenerator(cw,
invokerId, "_" + id);
cr.accept(sgen, 0);
classData = cw.toByteArray();
ProtectionDomain pd = providerClazz.getProtectionDomain();
SubClassLoader scl = createSubClassLoader(providerClazz);
if(scl == null)
return null;
try {
String gcName = scl.defineClass(generatedClassName, classData, pd);
if (logger.isLoggable(Level.FINE))
logger.fine("**** DEFINE CLASS SUCCEEDED for " + gcName + "," + generatedClassName);
return (Class<T>) scl.loadClass(gcName);
}
catch (Throwable ex) {
ex.printStackTrace();
}
return null;
}
/**
* Byron Nevins Feb 2012 FindBugs fix
* Hide this ugly access control code in this method
* @return the SubClassLoader or null if error(s)
*/
private SubClassLoader createSubClassLoader(final Class theClass) {
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<SubClassLoader>() {
@Override
public SubClassLoader run() throws Exception {
return new SubClassLoader(theClass.getClassLoader());
}
});
}
catch (Exception e) {
return null;
}
}
static class SubClassLoader
extends ClassLoader {
SubClassLoader(ClassLoader cl) {
super(cl);
}
String defineClass(String className, byte[] data, ProtectionDomain pd)
throws Exception {
className = className.replace('/', '.');
super.defineClass(className, data, 0, data.length, pd);
return className;
}
}
private static class ProbeProviderSubClassGenerator
extends ClassVisitor {
String superClassName;
String token;
String id;
ProbeProviderSubClassGenerator(ClassVisitor cv, String token, String id) {
super(Opcodes.ASM5, cv);
this.id = id;
this.token = token;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.superClassName = name;
super.visit(version, access, name + token + id, signature, name, interfaces);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
AnnotationVisitor delegate = super.visitAnnotation(desc, visible);
if ("Lorg/glassfish/external/probe/provider/annotations/ProbeProvider;".equals(desc)) {
return new ProbeProviderAnnotationVisitor(delegate, token);
}
else {
return delegate;
}
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] strings) {
if ("<init>".equals(name) && desc.equals("()V")) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, strings);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassName, "<init>", desc);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
return null;
}
else {
return super.visitMethod(access, name, desc, signature, strings);
}
}
}
private static class ProbeProviderAnnotationVisitor
extends AnnotationVisitor {
private AnnotationVisitor delegate;
private String token;
ProbeProviderAnnotationVisitor(AnnotationVisitor delegate, String token) {
super(Opcodes.ASM5);
this.delegate = delegate;
this.token = token;
}
@Override
public void visit(String attrName, Object value) {
delegate.visit(attrName, ("probeProviderName".equals(attrName) ? value + token : value));
}
@Override
public void visitEnum(String s, String s1, String s2) {
delegate.visitEnum(s, s1, s2);
}
@Override
public AnnotationVisitor visitAnnotation(String s, String s1) {
return delegate.visitAnnotation(s, s1);
}
@Override
public AnnotationVisitor visitArray(String s) {
return delegate.visitArray(s);
}
@Override
public void visitEnd() {
delegate.visitEnd();
}
}
}