All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.ejb.codegen.EjbOptionalIntfGenerator Maven / Gradle / Ivy

There is a newer version: 6.2024.6
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-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-2020] [Payara Foundation and/or affiliates]

package com.sun.ejb.codegen;

import com.sun.enterprise.container.common.spi.util.IndirectlySerializable;
import com.sun.enterprise.container.common.spi.util.SerializableObjectFactory;
import com.sun.ejb.spi.container.OptionalLocalInterfaceProvider;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.ReflectPermission;
import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.io.Serializable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.Permission;

import com.sun.enterprise.deployment.util.TypeUtil;
import org.glassfish.api.deployment.ResourceClassLoader;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import com.sun.ejb.containers.ClassSupplier;

public class EjbOptionalIntfGenerator
    implements Opcodes {

    private static final int INTF_FLAGS = ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES;

    private static final String DELEGATE_FIELD_NAME = "__ejb31_delegate";

    private static final Class[] emptyClassArray = new Class[] {};

    private Map classMap = new HashMap<>();

    private final ClassLoader loader;

    private ProtectionDomain protectionDomain;

    public EjbOptionalIntfGenerator(ClassLoader loader) {
        this.loader = loader;
    }

    public Class loadClass(final String name) throws Exception {
        Class clz = null;

        try {
            clz = loader.loadClass(name);
        } catch (ClassNotFoundException cnfe) {
            final byte[] classData = (byte[]) classMap.get(name);
            if (classData != null) {

                clz = (Class) java.security.AccessController.doPrivileged(
                        new java.security.PrivilegedAction() {
                    public java.lang.Object run() {
                        return makeClass(name, classData, protectionDomain, loader);
                    }
                }
                );
            }
        }

        if (clz == null) {

            throw new ClassNotFoundException(name);
        }

        return clz;
    }

    public Class loadClass(final String ejbClass, final String name, ClassSupplier generator)
        throws Exception
    {
        Class clz = null;

        try {
            clz = loader.loadClass(name);
        } catch (ClassNotFoundException cnfe) {

            final byte[] classData = generator.get();
            if (classData != null) {

                clz = (Class) java.security.AccessController.doPrivileged(
                        new java.security.PrivilegedAction() {
                    public java.lang.Object run() {
                        return makeClass(ejbClass, name, classData, protectionDomain, loader);
                    }
                }
                );
            }
        }

        if (clz == null) {

            throw new ClassNotFoundException(name);
        }

        return clz;       
    }

    public byte[] generateOptionalLocalInterface(Class ejbClass, String intfClassName)
        throws Exception {

        return generateInterface(ejbClass, intfClassName, Serializable.class);
    }

    public byte[] generateInterface(Class ejbClass, String intfClassName, final Class... interfaces) throws Exception {
        String[] interfaceNames = new String[interfaces.length];
        for (int i = 0; i < interfaces.length; i++) {
            interfaceNames[i] = Type.getType(interfaces[i]).getInternalName();
        }

        if( protectionDomain == null ) {
            protectionDomain = ejbClass.getProtectionDomain();
        }

        ClassWriter cw = new ClassWriter(INTF_FLAGS);

//        ClassVisitor tv = (_debug)
//                ? new TraceClassVisitor(cw, new PrintWriter(System.out)) : cw;
        ClassVisitor tv = cw;
        String intfInternalName = intfClassName.replace('.', '/');
        tv.visit(V1_1, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
                intfInternalName, null,
                Type.getType(Object.class).getInternalName(), 
                interfaceNames );

        for (java.lang.reflect.Method m : ejbClass.getMethods()) {
            if (qualifiedAsBeanMethod(m)) {
                generateInterfaceMethod(tv, m);
            }
        }

        tv.visitEnd();

        byte[] classData = cw.toByteArray();
        classMap.put(intfClassName, classData);
        return classData;
    }
    
    /**
     * Determines if a method from a bean class can be considered as a business
     * method for EJB of no-interface view.
     * @param m a public method
     * @return true if m can be included as a bean business method.
     */
    private boolean qualifiedAsBeanMethod(java.lang.reflect.Method m) {
        if (m.getDeclaringClass() == Object.class) {
            return false;
        }
        int mod = m.getModifiers();
        return !Modifier.isStatic(mod) && !Modifier.isFinal(mod);
    }

    private boolean hasSameSignatureAsExisting(java.lang.reflect.Method toMatch,
                                               Set methods) {
        boolean sameSignature = false;
        for(java.lang.reflect.Method m : methods) {
            if( TypeUtil.sameMethodSignature(m, toMatch) ) {
                sameSignature = true;
                break;
            }
        }
        return sameSignature;
    }

    public byte[] generateOptionalLocalInterfaceSubClass(
            Class superClass,
            String subClassName,
            Class delegateClass) throws Exception {

        return generateSubclass(
                superClass,
                subClassName,
                delegateClass,
                IndirectlySerializable.class
        );
    }

    public byte[] generateSubclass(
            Class superClass,
            String subClassName,
            Class delegateClass,
            Class... interfaces) throws Exception {


        if( protectionDomain == null ) {
            protectionDomain = superClass.getProtectionDomain();
        }

        ClassWriter cw = new ClassWriter(INTF_FLAGS);

       ClassVisitor tv = cw;
//        ClassVisitor tv = (_debug)
//                ? new TraceClassVisitor(cw, new PrintWriter(System.out)) : cw;
        
        String[] interfaceNames = new String[interfaces.length + 1];
        interfaceNames[0] = OptionalLocalInterfaceProvider.class.getName().replace('.', '/');
        for (int i = 0; i < interfaces.length; i++) {
            interfaceNames[i+1] = interfaces[i].getName().replace('.', '/');
        }

        tv.visit(V1_1, ACC_PUBLIC, subClassName.replace('.', '/'), null,
                Type.getType(superClass).getInternalName(), interfaceNames);

        String fldDesc = Type.getDescriptor(delegateClass);
        FieldVisitor fv = tv.visitField(ACC_PRIVATE, DELEGATE_FIELD_NAME,
                fldDesc, null, null);
        fv.visitEnd();

	// Generate constructor. The EJB spec only allows no-arg constructors, but
	// JSR 299 added requirements that allow a single constructor to define
	// parameters injected by CDI.
	{

	    Constructor[] ctors = superClass.getConstructors();
	    Constructor ctorWithParams = null;
	    for(Constructor ctor : ctors) {
		if(ctor.getParameterTypes().length == 0) {
                    ctorWithParams = null;    //exists the no-arg ctor, use it
                    break;
                } else if(ctorWithParams == null) {
		    ctorWithParams = ctor;
		}
	    }

	    MethodVisitor cv = tv.visitMethod(ACC_PUBLIC, "", "()V", null, null);
	    cv.visitVarInsn(ALOAD, 0);
	    String paramTypeString = "()V";
	    // if void, only one param (implicit 'this' param)
	    int maxValue = 1;
	    if( ctorWithParams != null ) {
		Class[] paramTypes = ctorWithParams.getParameterTypes();
		for(int i = 0; i < paramTypes.length; i++) {
		    cv.visitInsn(ACONST_NULL);
		}
		paramTypeString = Type.getConstructorDescriptor(ctorWithParams);
		// num params + one for 'this' pointer
		maxValue = paramTypes.length + 1;
	    }
	    cv.visitMethodInsn(INVOKESPECIAL,  Type.getType(superClass).getInternalName(), "",
			   paramTypeString);
	    cv.visitInsn(RETURN);
	    cv.visitMaxs(maxValue, 1);
	}

        generateSetDelegateMethod(tv, delegateClass, subClassName);

        for (Class anInterface : interfaces) {

            // dblevins: Don't think we need this special case.
            // Should be covered by letting generateBeanMethod
            // handle the methods on IndirectlySerializable.
            //
            // Not sure where the related tests are to verify.
            if (anInterface.equals(IndirectlySerializable.class)) {
                generateGetSerializableObjectFactoryMethod(tv, fldDesc, subClassName.replace('.', '/'));
                continue;
            }

            for (java.lang.reflect.Method method : anInterface.getMethods()) {
                generateBeanMethod(tv, subClassName, method, delegateClass);
            }
        }


        Set allMethods = new HashSet();
        
        for (java.lang.reflect.Method m : superClass.getMethods()) {
            if (qualifiedAsBeanMethod(m)) {
                generateBeanMethod(tv, subClassName, m, delegateClass);
            }
        }
        
        for (Class clz = superClass; clz != Object.class; clz = clz.getSuperclass()) {
            java.lang.reflect.Method[] beanMethods = clz.getDeclaredMethods();
            for (java.lang.reflect.Method mth : beanMethods) {
                if( !hasSameSignatureAsExisting(mth, allMethods)) {
                    int modifiers = mth.getModifiers();
                    boolean isPublic = Modifier.isPublic(modifiers);
                    boolean isPrivate = Modifier.isPrivate(modifiers);
                    boolean isProtected = Modifier.isProtected(modifiers);
                    boolean isPackage = !isPublic && !isPrivate && !isProtected;

                    boolean isStatic = Modifier.isStatic(modifiers);

                    if( (isPackage || isProtected) && !isStatic ) {
                        generateNonAccessibleMethod(tv, mth);
                    }                    
                    allMethods.add(mth);
                }
            }
        }

        // add toString() method if it was not overridden
        java.lang.reflect.Method mth = Object.class.getDeclaredMethod("toString");
        if( !hasSameSignatureAsExisting(mth, allMethods)) {
                        //generateBeanMethod(tv, subClassName, mth, delegateClass);
            generateToStringBeanMethod(tv, superClass);
        }

        tv.visitEnd();

        byte[] classData = cw.toByteArray();
        classMap.put(subClassName, classData);
        return classData;
    }


    private static void generateInterfaceMethod(ClassVisitor cv, java.lang.reflect.Method m)
        throws Exception {

        String methodName = m.getName();
        Type returnType = Type.getReturnType(m);
        Type[] argTypes = Type.getArgumentTypes(m);

        Method asmMethod = new Method(methodName, returnType, argTypes);
        GeneratorAdapter cg = new GeneratorAdapter(ACC_PUBLIC + ACC_ABSTRACT,
                asmMethod, null, getExceptionTypes(m), cv);
        cg.endMethod();

    }

    private static void generateBeanMethod(
            ClassVisitor cv,
            String subClassName,
            java.lang.reflect.Method m,
            Class delegateClass) {

        String methodName = m.getName();
        Type returnType = Type.getReturnType(m);
        Type[] argTypes = Type.getArgumentTypes(m);
        Method asmMethod = new Method(methodName, returnType, argTypes);

        GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, asmMethod, null,
                getExceptionTypes(m), cv);
        mg.loadThis();
        mg.visitFieldInsn(GETFIELD, subClassName.replace('.', '/'),
                DELEGATE_FIELD_NAME, Type.getType(delegateClass).getDescriptor());
        mg.loadArgs();
        mg.invokeInterface(Type.getType(delegateClass), asmMethod);
        mg.returnValue();
        mg.endMethod();

    }

    private static void generateToStringBeanMethod(
            ClassVisitor cv,
            Class superClass) {

        String toStringMethodName = "toString";
        String toStringMethodDescriptor = "()Ljava/lang/String;";
        String stringBuilder = "java/lang/StringBuilder";

        MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, toStringMethodName, toStringMethodDescriptor, null, null);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitTypeInsn(NEW, stringBuilder);
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, stringBuilder, "", "()V");
        mv.visitLdcInsn(superClass.getName() + "@");
        mv.visitMethodInsn(INVOKEVIRTUAL, stringBuilder, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I");
        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "toHexString", "(I)Ljava/lang/String;");
        mv.visitMethodInsn(INVOKEVIRTUAL, stringBuilder, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
        mv.visitMethodInsn(INVOKEVIRTUAL, stringBuilder, toStringMethodName, toStringMethodDescriptor);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(2, 1);

    }

    private static void generateNonAccessibleMethod(
            ClassVisitor cv,
            java.lang.reflect.Method m
    ) {

        String methodName = m.getName();
        Type returnType = Type.getReturnType(m);
        Type[] argTypes = Type.getArgumentTypes(m);
        Method asmMethod = new Method(methodName, returnType, argTypes);

        // Only called for non-static Protected or Package access
        int access =  ACC_PUBLIC;

        GeneratorAdapter mg = new GeneratorAdapter(access, asmMethod, null,
                getExceptionTypes(m), cv);

        mg.throwException(Type.getType(jakarta.ejb.EJBException.class),
                "Illegal non-business method access on no-interface view");
        
        mg.returnValue();
        
        mg.endMethod();

    }

    private static void generateGetSerializableObjectFactoryMethod(ClassVisitor classVisitor,
                                                                   String fieldDesc,
                                                                   String classDesc) {

        MethodVisitor cv = classVisitor.visitMethod(ACC_PUBLIC, "getSerializableObjectFactory",
                "()L" + SerializableObjectFactory.class.getName().replace('.', '/') + ";", null, new String[] { "java/io/IOException" });
        cv.visitVarInsn(ALOAD, 0);
        cv.visitFieldInsn(GETFIELD, classDesc, DELEGATE_FIELD_NAME, fieldDesc);
        cv.visitTypeInsn(CHECKCAST, IndirectlySerializable.class.getName().replace('.', '/'));
        cv.visitMethodInsn(INVOKEINTERFACE,
                IndirectlySerializable.class.getName().replace('.', '/'), "getSerializableObjectFactory",
                "()L" + SerializableObjectFactory.class.getName().replace('.', '/') + ";");
        cv.visitInsn(ARETURN);
        cv.visitMaxs(1, 1);

        
    }


    private static Type[] getExceptionTypes(java.lang.reflect.Method m) {
        Class[] exceptions = m.getExceptionTypes();
        Type[] eTypes = new Type[exceptions.length];
        for (int i=0; i() {
	    public java.lang.reflect.Method run() {
		try {
		    java.lang.reflect.Method meth = ClassLoader.class.getDeclaredMethod(
			"defineClass", String.class,
			byte[].class, int.class, int.class,
			ProtectionDomain.class ) ;
		    meth.setAccessible( true ) ;
		    return meth ;
		} catch (Exception exc) {
		    throw new RuntimeException(
			"Could not find defineClass method!", exc ) ;
		}
	    }
	}
    ) ;

    private static final Permission accessControlPermission =
	    new ReflectPermission( "suppressAccessChecks" ) ;

    // This requires a permission check
    private Class makeClass(
            String ejbClass,
            String name,
            byte[] def,
            ProtectionDomain pd,
            ClassLoader loader) {

        SecurityManager sman = System.getSecurityManager();
        if (sman != null) {
            sman.checkPermission(accessControlPermission);
        }

        if (loader instanceof ResourceClassLoader) {
            ResourceClassLoader classLoader = (ResourceClassLoader) loader;
            return classLoader.addGeneratedResourceEntry(ejbClass, name, def, pd);
        } else {
            try {
                return (Class) defineClassMethod.invoke(loader,
                        name, def, 0, def.length, pd);
            } catch (Exception exc) {
                throw new RuntimeException("Could not invoke defineClass!",
                        exc);
            }
        }
    }

    // This requires a permission check
    private Class makeClass(
            String name,
            byte[] def,
            ProtectionDomain pd,
            ClassLoader loader) {

        SecurityManager sman = System.getSecurityManager();
        if (sman != null) {
            sman.checkPermission(accessControlPermission);
        }

        try {
            return (Class) defineClassMethod.invoke(loader,
                    name, def, 0, def.length, pd);
        } catch (Exception exc) {
            throw new RuntimeException("Could not invoke defineClass!",
                    exc);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy