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

org.glassfish.admin.rest.generator.ASMClassWriter Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-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 org.glassfish.admin.rest.generator;

import com.sun.enterprise.util.SystemPropertyConstants;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.glassfish.admin.rest.RestLogging;
import org.glassfish.admin.rest.utils.ResourceUtil;
import org.glassfish.hk2.api.ServiceLocator;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

/**
 * @author Ludovic Champenois
 */
public class ASMClassWriter implements ClassWriter, Opcodes {
    private static final String INJECTOR_FIELD = "serviceLocator";
    private static final String FORNAME_INJECTOR_TYPE = "Lorg/glassfish/hk2/api/ServiceLocator;";
    private static final String INTERFACE_INJECTOR_TYPE = "org/glassfish/hk2/api/ServiceLocator";
    private static final String CREATE_AND_INITIALIZE = "createAndInitialize";
    private static final String CREATE_AND_INITIALIZE_SIG = "(Ljava/lang/Class;)Ljava/lang/Object;";
    
    private static final String PATH_CLASS_NAME = "Ljakarta/ws/rs/Path;";

    private org.objectweb.asm.ClassWriter cw = new org.objectweb.asm.ClassWriter(0);
    private String className;
    private ServiceLocator habitat;
    private final String generatedPath;
    private Map generatedMethods = new HashMap();
  //  private String baseClassName;
  //  private String resourcePath;

    public ASMClassWriter(ServiceLocator habitat, String generatedPath, String className, String baseClassName, String resourcePath) {
        this.habitat = habitat;
        this.className = className;
        this.generatedPath = generatedPath;
   //     this.baseClassName = baseClassName;
   //     this.resourcePath = resourcePath;
        if (baseClassName.contains("TemplateCommand")) { //constructor is created in createCommandResourceConstructor
            return;
        }
        if (baseClassName.indexOf('.') != -1) {
            baseClassName = baseClassName.replace('.', '/');
        } else {
            baseClassName = "org/glassfish/admin/rest/resources/" + baseClassName;
        }
        cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, generatedPath + className , null,
                 baseClassName , null);

        if (resourcePath != null) {
            RestLogging.restLogger.log(Level.FINE, "Creating resource with path {0} (1)", resourcePath);
            AnnotationVisitor av0 = cw.visitAnnotation(PATH_CLASS_NAME, true);
            av0.visit("value", "/" + resourcePath + "/");
            av0.visitEnd();
        }


        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL,  baseClassName , "", "()V");
        mv.visitInsn(RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();


    }

    @Override
    public void createCustomResourceMapping(String resourceClassName, String mappingPath) {

        //gen in custom package!
        String completeName = "org/glassfish/admin/rest/resources/custom/" + resourceClassName;
        String baseClassName = generatedPath + className;
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get" + resourceClassName, "()L" + completeName + ";", null, null);

        RestLogging.restLogger.log(Level.FINE, "Creating resource with path {0} (2)", mappingPath);
        AnnotationVisitor av0 = mv.visitAnnotation(PATH_CLASS_NAME, true);
        av0.visit("value", mappingPath + "/");
        av0.visitEnd();

        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, baseClassName, INJECTOR_FIELD, FORNAME_INJECTOR_TYPE);
        mv.visitLdcInsn(Type.getType("L" + completeName + ";"));
        mv.visitMethodInsn(INVOKEINTERFACE, INTERFACE_INJECTOR_TYPE,
                CREATE_AND_INITIALIZE, CREATE_AND_INITIALIZE_SIG);
        mv.visitTypeInsn(CHECKCAST, completeName);
        mv.visitVarInsn(ASTORE, 1);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKEVIRTUAL, baseClassName, "getEntity", "()Lorg/jvnet/hk2/config/Dom;");
        mv.visitMethodInsn(INVOKEVIRTUAL, completeName, "setEntity", "(Lorg/jvnet/hk2/config/Dom;)V");
        mv.visitVarInsn(ALOAD, 1);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(2, 2);
        mv.visitEnd();

    }

    @Override
    public void createGetCommandResourcePaths(List commandMetaData) {


        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getCommandResourcesPaths", "()[[Ljava/lang/String;", null, null);
        mv.visitCode();
        int size = 0;
        for (CommandResourceMetaData metaData : commandMetaData) {
            if (ResourceUtil.commandIsPresent(habitat, metaData.command)){
                size++;
            }
        }
        mv.visitIntInsn(BIPUSH, size);   //11 number of entries
        mv.visitTypeInsn(ANEWARRAY, "[Ljava/lang/String;");  //first outer array
        int index= -1;
        for (CommandResourceMetaData metaData : commandMetaData) {
            if (ResourceUtil.commandIsPresent(habitat, metaData.command)){
            index++;
            switch (index){//inner array has 3 strings,
                case 0: mv.visitInsn(DUP);mv.visitInsn(ICONST_0);mv.visitInsn(ICONST_3);break;
                case 1: mv.visitInsn(DUP);mv.visitInsn(ICONST_1);mv.visitInsn(ICONST_3);break;
                case 2: mv.visitInsn(DUP);mv.visitInsn(ICONST_2);mv.visitInsn(ICONST_3);break;
                case 3: mv.visitInsn(DUP);mv.visitInsn(ICONST_3);mv.visitInsn(ICONST_3);break;
                case 4: mv.visitInsn(DUP);mv.visitInsn(ICONST_4);mv.visitInsn(ICONST_3);break;
                case 5: mv.visitInsn(DUP);mv.visitInsn(ICONST_5);mv.visitInsn(ICONST_3);break;
                default: mv.visitInsn(DUP);mv.visitIntInsn(BIPUSH, index);mv.visitInsn(ICONST_3);break;  //6 and bigger is DIFFERENT!!!
            } //switch


            mv.visitTypeInsn(ANEWARRAY, "java/lang/String");  //inner array

            mv.visitInsn(DUP);mv.visitInsn(ICONST_0);
            mv.visitLdcInsn(metaData.resourcePath);
            mv.visitInsn(AASTORE);mv.visitInsn(DUP);mv.visitInsn(ICONST_1);
            mv.visitLdcInsn(metaData.httpMethod);
            mv.visitInsn(AASTORE);mv.visitInsn(DUP);mv.visitInsn(ICONST_2);
            mv.visitLdcInsn(metaData.command);
            mv.visitInsn(AASTORE);mv.visitInsn(AASTORE);
          }

        } //for

        mv.visitInsn(ARETURN);
        mv.visitMaxs(7, 1);
        mv.visitEnd();

    }

    @Override
    public void createGetCommandResource(String commandResourceClassName, String resourcePath) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get" + commandResourceClassName, "()L" + generatedPath + commandResourceClassName + ";", null, null);

        RestLogging.restLogger.log(Level.FINE, "Creating resource with path {0} (3)", resourcePath);
        AnnotationVisitor av0 = mv.visitAnnotation(PATH_CLASS_NAME, true);
        av0.visit("value", resourcePath + "/");
        av0.visitEnd();

        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, generatedPath + className, INJECTOR_FIELD, FORNAME_INJECTOR_TYPE);
        mv.visitLdcInsn(Type.getType("L" + generatedPath + commandResourceClassName + ";"));
        mv.visitMethodInsn(INVOKEINTERFACE, INTERFACE_INJECTOR_TYPE,
                CREATE_AND_INITIALIZE, CREATE_AND_INITIALIZE_SIG);
        mv.visitTypeInsn(CHECKCAST, generatedPath + commandResourceClassName);
        mv.visitVarInsn(ASTORE, 1);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(2, 2);
        mv.visitEnd();
    }

    @Override
    public void createCommandResourceConstructor(String commandResourceClassName, String commandName,
            String httpMethod, boolean linkedToParent,
            CommandResourceMetaData.ParameterMetaData[] commandParams,
            String commandDisplayName,
            String commandAction) {

        String baseClassName = "";
        switch (httpMethod) {
            case "GET":
                baseClassName = "org/glassfish/admin/rest/resources/TemplateCommandGetResource";
                break;
            case "DELETE":
                baseClassName = "org/glassfish/admin/rest/resources/TemplateCommandDeleteResource";
                break;
            case "POST":
                baseClassName = "org/glassfish/admin/rest/resources/TemplateCommandPostResource";
                break;
            default:
                throw new GeneratorException("Invalid httpMethod specified: " + httpMethod);
        }
        boolean isget = (httpMethod.equals("GET"));


        cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, generatedPath + commandResourceClassName, null,
                baseClassName, null);
   //     cw.visitInnerClass(generatedPath + commandResourceClassName +"$1", null, null, 0);
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitLdcInsn(commandResourceClassName);
        mv.visitLdcInsn(commandName);
        mv.visitLdcInsn(httpMethod);
        if (!isget) {
            mv.visitLdcInsn(commandAction);
            mv.visitLdcInsn(commandDisplayName);
        }
        if (linkedToParent == true) {
            mv.visitInsn(ICONST_1);
        } else {
            mv.visitInsn(ICONST_0);
        }

//next is different based on parent
        if (!isget) {

            mv.visitMethodInsn(INVOKESPECIAL, baseClassName,
                    "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V");
        } else {
            mv.visitMethodInsn(INVOKESPECIAL, baseClassName,
                    "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V");

        }
        mv.visitInsn(RETURN);

        if (!isget) {
            mv.visitMaxs(7, 1);  //GET is 5!!!
        } else {
            mv.visitMaxs(5, 1);

        }
        mv.visitEnd();

        if (commandParams != null) {

            mv = cw.visitMethod(ACC_PROTECTED, "getCommandParams", "()Ljava/util/HashMap;", "()Ljava/util/HashMap;", null);
            mv.visitCode();
            mv.visitTypeInsn(NEW, "java/util/HashMap");
            mv.visitInsn(DUP);
            mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "", "()V");
            mv.visitVarInsn(ASTORE, 1);

            for (CommandResourceMetaData.ParameterMetaData commandParam : commandParams) {
                mv.visitVarInsn(ALOAD, 1);
                mv.visitLdcInsn(commandParam.name);
                mv.visitLdcInsn(commandParam.value);
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
                mv.visitInsn(POP);
            }


            mv.visitVarInsn(ALOAD, 1);
            mv.visitInsn(ARETURN);
            mv.visitMaxs(3, 2);
            mv.visitEnd();
        }

    }

    @Override
    public void done() {
        cw.visitEnd();
        try {
            defineClass(this.getClass(), cw.toByteArray());
            if ("true".equals(System.getenv("REST_DEBUG"))) {
                debug(className, cw.toByteArray());
            }
        } catch (Exception ex) {
            RestLogging.restLogger.log(Level.SEVERE, null, ex);
        }

    }

    @Override
    public void createGetDeleteCommand(String commandName) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getDeleteCommand", "()Ljava/lang/String;", null, null);
        mv.visitCode();
        mv.visitLdcInsn(commandName);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }

    @Override
    public void createGetPostCommand(String commandName) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getPostCommand", "()Ljava/lang/String;", null, null);
        mv.visitCode();
        mv.visitLdcInsn(commandName);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }

    @Override
    public void createGetChildResource(String path, String childResourceClassName) {
        String childClass;
        switch (childResourceClassName) {
            case "PropertiesBagResource":
                childClass = "org/glassfish/admin/rest/resources/PropertiesBagResource";
                break;
            case "MonitoredAttributeBagResource":
                childClass = "fish/payara/admin/rest/resources/MonitoredAttributeBagResource";
                break;
            case "MonitoredMetricAttributeBagResource":
                childClass = "fish/payara/admin/rest/resources/MonitoredMetricAttributeBagResource";
                break;
            default:
                childClass = generatedPath + childResourceClassName;
                break;
        }

        String methodName = "get" + childResourceClassName;
        if (childClass.equals(generatedMethods.get(methodName))) {
            return;
        }

        generatedMethods.put(methodName, childClass);
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, "()L"+ childClass + ";", null, null);

        RestLogging.restLogger.log(Level.FINE, "Creating resource with path {0} (4)", path);
        AnnotationVisitor av0 = mv.visitAnnotation(PATH_CLASS_NAME, true);
        av0.visit("value", path + "/");
        av0.visitEnd();

        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, generatedPath + className, INJECTOR_FIELD, FORNAME_INJECTOR_TYPE);
        mv.visitLdcInsn(Type.getType("L" + childClass + ";"));
        mv.visitMethodInsn(INVOKEINTERFACE, INTERFACE_INJECTOR_TYPE,
                CREATE_AND_INITIALIZE, CREATE_AND_INITIALIZE_SIG);
        mv.visitTypeInsn(CHECKCAST, childClass);
        mv.visitVarInsn(ASTORE, 1);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKEVIRTUAL, generatedPath + className, "getEntity", "()Lorg/jvnet/hk2/config/Dom;");
        mv.visitLdcInsn(path);
        mv.visitMethodInsn(INVOKEVIRTUAL, childClass, "setParentAndTagName", "(Lorg/jvnet/hk2/config/Dom;Ljava/lang/String;)V");
        mv.visitVarInsn(ALOAD, 1);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(3, 2);
        mv.visitEnd();
    }

    @Override
    public void createGetChildResourceForListResources(String keyAttributeName, String childResourceClassName) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get" + childResourceClassName , "(Ljava/lang/String;)L" + generatedPath + childResourceClassName + ";", null, null);

        RestLogging.restLogger.log(Level.FINE, "Creating resource with path {0} (5)", keyAttributeName);
        AnnotationVisitor av0 = mv.visitAnnotation(PATH_CLASS_NAME, true);
        av0.visit("value", "{" + keyAttributeName + "}/");
        av0.visitEnd();


        av0 = mv.visitParameterAnnotation(0, "Ljakarta/ws/rs/PathParam;", true);
        av0.visit("value", keyAttributeName);
        av0.visitEnd();

        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, generatedPath +"List" + childResourceClassName , INJECTOR_FIELD, FORNAME_INJECTOR_TYPE);
        mv.visitLdcInsn(Type.getType("L" + generatedPath + childResourceClassName + ";"));
        mv.visitMethodInsn(INVOKEINTERFACE, INTERFACE_INJECTOR_TYPE,
                CREATE_AND_INITIALIZE, CREATE_AND_INITIALIZE_SIG);
        mv.visitTypeInsn(CHECKCAST, generatedPath + childResourceClassName );
        mv.visitVarInsn(ASTORE, 2);
        mv.visitVarInsn(ALOAD, 2);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, generatedPath + "List" + childResourceClassName , "entity", "Ljava/util/List;");
        mv.visitVarInsn(ALOAD, 1);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, generatedPath + "List" + childResourceClassName, "tagName", "Ljava/lang/String;");
        mv.visitMethodInsn(INVOKEVIRTUAL, generatedPath + childResourceClassName , "setBeanByKey", "(Ljava/util/List;Ljava/lang/String;Ljava/lang/String;)V");
        mv.visitVarInsn(ALOAD, 2);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(4, 3);
        mv.visitEnd();
    }

    @Override
    public void createGetPostCommandForCollectionLeafResource(String postCommandName) {
        MethodVisitor mv = cw.visitMethod(ACC_PROTECTED, "getPostCommand", "()Ljava/lang/String;", null, null);
        mv.visitCode();
        mv.visitLdcInsn(postCommandName);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }

    @Override
    public void createGetDeleteCommandForCollectionLeafResource(String deleteCommandName) {
        MethodVisitor mv = cw.visitMethod(ACC_PROTECTED, "getDeleteCommand", "()Ljava/lang/String;", null, null);
        mv.visitCode();
        mv.visitLdcInsn(deleteCommandName);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }

    @Override
    public void createGetDisplayNameForCollectionLeafResource(String displayName) {
        MethodVisitor mv = cw.visitMethod(ACC_PROTECTED, "getName", "()Ljava/lang/String;", null, null);
        mv.visitCode();
        mv.visitLdcInsn(displayName);
        mv.visitInsn(ARETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }

    public byte[] getByteClass() {
        return cw.toByteArray();
    }

    public String defineClass(Class similarClass, byte[] classBytes) throws Exception {

        String generatedClassName = "org.glassfish.admin.rest.resources.generatedASM.";
        generatedClassName =  generatedClassName + className;

        byte[] byteContent = getByteClass();
        ProtectionDomain pd = similarClass.getProtectionDomain();

        java.lang.reflect.Method jm = null;
        for (java.lang.reflect.Method jm2 : ClassLoader.class.getDeclaredMethods()) {
            if (jm2.getName().equals("defineClass") && jm2.getParameterTypes().length == 5) {
                jm = jm2;
                break;
            }
        }
        if (jm==null){//should never happen, makes findbug happy
            throw new RuntimeException("cannot find method called defineclass...");
        }
        final java.lang.reflect.Method clM = jm;
        try {
            java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedExceptionAction() {

                        @Override
                        public java.lang.Object run() throws Exception {
                            if (!clM.isAccessible()) {
                                clM.setAccessible(true);
                            }
                            return null;
                        }
                    });

            RestLogging.restLogger.log(Level.FINEST, "Loading bytecode for {0}", generatedClassName);
            clM.invoke(similarClass.getClassLoader()
                    /*Thread.currentThread().getContextClassLoader()*/, generatedClassName, byteContent, 0,
                     byteContent.length, pd);

            try {
               similarClass.getClassLoader().loadClass(generatedClassName);
            } catch (ClassNotFoundException cnfEx) {
                throw new RuntimeException(cnfEx);
            }

            return generatedClassName;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }

    }

    /*
     dump bytecode in class files so that we can  decompile them to check the real content
    */
    private void debug(String clsName, byte[] classData) {


        // the path is horribly long.  Let's just write t directly into the
        // lib dir.  It is not for loading as a class but just for us humans
        // to decompile to figure out what is going on.  No need to make it even harder!
        clsName = clsName.replace('.', '/');
        clsName = clsName.replace('\\', '/'); // just in case Windows?  unlikely...
        int index = clsName.lastIndexOf('/');

        if (index >= 0) {
            clsName = clsName.substring(index + 1);
        }

        FileOutputStream fos = null;
        try {
            String rootPath = System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY)
                    + File.separator + "lib" + File.separator + "rest" + File.separator;
            File parentDir = new File(rootPath);
            if (!parentDir.exists()) {
                if (!parentDir.mkdirs()) {
                    throw new RuntimeException("Unable to create parent directory for generated class file logging");
                }
            }

            fos = new FileOutputStream(new File(parentDir, clsName + ".class"));
            fos.write(classData);
            fos.flush();
        } catch (Exception ex) {
            RestLogging.restLogger.log(Level.SEVERE, null, ex);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ex) {
                    RestLogging.restLogger.log(Level.SEVERE, null, ex);
                }
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy