org.forgerock.openidm.shell.felixgogo.FelixGogoCommandsServiceGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.forgerock.openidm.shell.felixgogo.FelixGogoCommandsServiceGenerator.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 org.forgerock.openidm.shell.felixgogo;

import org.forgerock.openidm.shell.CustomCommandScope;
import org.forgerock.openidm.shell.impl.Main;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.Set;

import static org.objectweb.asm.Opcodes.*;

/**
 * Felix GoGo Commands Service class generator. It uses {@link AbstractFelixCommandsService} as super class and
 * adds public methods for each command name and in method body redirects call to original commands service
 *
 * Based on Apache License 2.0 licensed osgilab org.knowhowlab.osgi.shell.felixgogo
 */
public class FelixGogoCommandsServiceGenerator extends ClassLoader {
    /**
     * Logger.
     */
    private static final Logger logger = LoggerFactory.getLogger(FelixGogoCommandsServiceGenerator.class);

    /* *//**
         * Classes pool
         *//*
            private static final javassist.ClassPool POOL = javassist.ClassPool.getDefault();
                
            */
    /**
     * Initialization of Classes pool
     *//*
        static {
        POOL.appendClassPath(new javassist.ClassClassPath(AbstractFelixCommandsService.class));
        }*/

    private static StubClassLoader classLoader = new StubClassLoader();

    /**
     * Generate CommandProvider class and newBuilder for this class based on parameters.
     *
     * @param service  commands service
     * @param commands commands map (name=help)
     * @param suffix   unique class suffix
     * @return generated CommandProvider newBuilder
     * @throws Exception if something went wrong
     */
    public static Object generate(CustomCommandScope service, Map<String, String> commands, String suffix)
            throws Exception {
        // generate class with unique name
        //javassist.CtClass ctClass = POOL.makeClass(AbstractFelixCommandsService.class.getName() + suffix);

        try {
            ClassWriter cw = new ClassWriter(0);
            MethodVisitor mv;
            AnnotationVisitor av0;
            String className = AbstractFelixCommandsService.class.getName().replace('.', '/') + suffix;
            cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, className, null,
                    AbstractFelixCommandsService.class.getName().replace('.', '/'), null);

            //cw.visitSource("AbstractFelixCommandsServiceSample.java", null);

            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Object;)V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(10, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKESPECIAL, AbstractFelixCommandsService.class.getName().replace('.', '/'),
                    "<init>", "(Ljava/lang/Object;)V");
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLineNumber(11, l1);
            mv.visitInsn(RETURN);
            Label l2 = new Label();
            mv.visitLabel(l2);
            mv.visitLocalVariable("this", "L" + className + ";", null, l0, l2, 0);
            mv.visitLocalVariable("service", "Ljava/lang/Object;", null, l0, l2, 1);
            mv.visitMaxs(2, 2);
            mv.visitEnd();

            /*javassist.bytecode.ClassFile ccFile = ctClass.getClassFile();
            ccFile.setVersionToJava5();
            javassist.bytecode.ConstPool constPool = ccFile.getConstPool();
                
            // set superclass
            javassist.CtClass abstractCtClass = POOL.getCtClass(AbstractFelixCommandsService.class.getName());
            ctClass.setSuperclass(abstractCtClass);
                
            // add constructor
            javassist.CtClass serviceCtClass = POOL.getCtClass(Object.class.getName());
            javassist.CtConstructor ctConstructor = new javassist.CtConstructor(new javassist.CtClass[]{serviceCtClass},
                ctClass);
            ctConstructor.setModifiers(javassist.Modifier.PUBLIC);
            ctConstructor.setBody("super($1);");
            ctClass.addConstructor(ctConstructor);
                
            // add method for each command
            javassist.CtClass sessionCtClass = POOL.getCtClass(CommandSession.class.getName());
            javassist.CtClass stringArrayCtClass = POOL.getCtClass(String[].class.getName());*/
            Set<String> names = commands.keySet();
            for (String name : names) {
                if (isMethodAvailable(service, name)) {
                    mv = cw.visitMethod(ACC_PUBLIC, name,
                            "(Lorg/apache/felix/service/command/CommandSession;[Ljava/lang/String;)V", null, null);

                    av0 = mv.visitAnnotation("Lorg/apache/felix/service/command/Descriptor;", true);
                    av0.visit("value", commands.get(name));
                    av0.visitEnd();

                    mv.visitCode();
                    l0 = new Label();
                    mv.visitLabel(l0);
                    mv.visitLineNumber(15, l0);
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitLdcInsn(name);
                    mv.visitVarInsn(ALOAD, 1);
                    mv.visitVarInsn(ALOAD, 2);
                    mv.visitMethodInsn(INVOKEVIRTUAL, className, "runCommand",
                            "(Ljava/lang/String;Lorg/apache/felix/service/command/CommandSession;"
                                    + "[Ljava/lang/String;)V");
                    l1 = new Label();
                    mv.visitLabel(l1);
                    mv.visitLineNumber(16, l1);
                    mv.visitInsn(RETURN);
                    l2 = new Label();
                    mv.visitLabel(l2);
                    mv.visitLocalVariable("this", "L" + className + ";", null, l0, l2, 0);
                    mv.visitLocalVariable("session", "Lorg/apache/felix/service/command/CommandSession;", null, l0,
                            l2, 1);
                    mv.visitLocalVariable("args", "[Ljava/lang/String;", null, l0, l2, 2);
                    mv.visitMaxs(4, 3);
                    mv.visitEnd();

                    /*javassist.CtMethod ctMethod = new javassist.CtMethod(javassist.CtClass.voidType, name,
                        new javassist.CtClass[]{
                            sessionCtClass, stringArrayCtClass
                        }, ctClass);
                    ctMethod.setModifiers(javassist.Modifier.PUBLIC);
                    ctMethod.setBody("runCommand(\"" + name + "\", $1, $2);");
                    ctClass.addMethod(ctMethod);
                        
                    // add GoGo descriptor for this shell command
                    javassist.bytecode.AnnotationsAttribute annotationsAttribute =
                        new javassist.bytecode.AnnotationsAttribute(constPool,
                            javassist.bytecode.AnnotationsAttribute.visibleTag);
                    javassist.bytecode.annotation.Annotation annotation =
                        new javassist.bytecode.annotation.Annotation(Descriptor.class.getName(), constPool);
                    annotation.addMemberValue("value",
                        new javassist.bytecode.annotation.StringMemberValue(commands.get(name), constPool));
                    annotationsAttribute.addAnnotation(annotation);
                    ctMethod.getMethodInfo().addAttribute(annotationsAttribute);*/
                }
            }

            cw.visitEnd();
            // create new newBuilder
            /*Class<?> aClass = ctClass.toClass(FelixGogoCommandsServiceGenerator.class.getClassLoader(), null);
            */
            Class<?> aClass = classLoader.defineClass(className, cw.toByteArray());
            Constructor<?> constructor = aClass.getConstructor(Object.class);
            return constructor.newInstance(service);
        } catch (Exception e) {
            //ctClass.detach();
            throw e;
        }
    }

    private static boolean isMethodAvailable(CustomCommandScope commandsProvider, String methodName) {
        return null != Main.findCommandMethod(commandsProvider.getClass(), methodName);
    }

    /**
     * Detach generated class.
     *
     * @param suffix unique class suffix
     */
    public static void clean(String suffix) {
        /*try {
        javassist.CtClass ctClass = POOL.getCtClass(AbstractFelixCommandsService.class.getName() + suffix);
        ctClass.defrost();
        ctClass.detach();
        } catch (javassist.NotFoundException e) {
        logger.warn("Unable to clean Console Service. {}", e.getMessage(), e);
        }*/
    }

    static class StubClassLoader extends ClassLoader {
        public Class<?> defineClass(String name, byte[] b) {
            return defineClass(name.replace('/', '.'), b, 0, b.length);
        }
    }

}