org.glassfish.pfl.tf.spi.EnhancedClassDataASMImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.glassfish.pfl.tf.spi.EnhancedClassDataASMImpl.java

Source

package org.glassfish.pfl.tf.spi;

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright (c) 1997-2010,2017 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 glassfish/bootstrap/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.
 */

import org.glassfish.pfl.tf.spi.annotation.TFEnhanced;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class EnhancedClassDataASMImpl extends EnhancedClassDataBase {
    private final ClassNode currentClass;
    private static final String TFENHANCED_ANNO_NAME = Type.getInternalName(TFEnhanced.class);

    // Get Set<String> for MM annotations present on class
    private void processClassAnnotations() {
        final List<AnnotationNode> classAnnotations = currentClass.visibleAnnotations;
        if (classAnnotations != null) {
            for (AnnotationNode an : classAnnotations) {
                final String aname = Type.getType(an.desc).getInternalName();
                if (annotationNames.contains(aname)) {
                    annoNamesForClass.add(aname);
                }
            }

            List<String> acnames = new ArrayList<String>(annoNamesForClass);
            Collections.sort(acnames);

            int ctr = 0;
            for (String aname : acnames) {
                annoToHolderName.put(aname, "__$mm$__" + ctr);
                ctr++;
            }

            if (util.getDebug()) {
                util.msg("Enhancing class " + currentClass.name);
                util.msg("\tannoNamesForClass = " + annoNamesForClass);
                util.msg("\tannoToHolderName = " + annoToHolderName);
            }
        }
    }

    private Object getAttribute(AnnotationNode an, String name) {
        if (an.values != null) {
            Iterator iter = an.values.iterator();
            while (iter.hasNext()) {
                Object key = iter.next();
                Object value = iter.next();
                if (!(key instanceof String)) {
                    return null;
                }

                if (key.equals(name)) {
                    return value;
                }
            }
        }

        return null;
    }

    // Scan methods:
    //    - Build List<String> to map names of MM annotated methods to ints
    //      validate: such methods must have exactly 1 MM annotation that
    //          is in annoNamesForClass.
    //    - Build Set<String> of all InfoMethod annotated methods.
    //      validate: such methods must be private, return void, and have
    //          an empty body.  May NOT have MM annotation.
    private void scanMethods() {
        final List<MethodNode> methods = currentClass.methods;
        Map<String, String> mmnToDescriptions = new HashMap<String, String>();

        Map<String, TimingPointType> mmnToTPT = new HashMap<String, TimingPointType>();

        Map<String, String> mmnToTPN = new HashMap<String, String>();

        Map<String, String> mmnToAnnotationName = new HashMap<String, String>();

        for (MethodNode mn : methods) {
            final String mname = mn.name;
            final String mdesc = util.getFullMethodDescriptor(mn);

            String monitoredMethodMMAnno = null;
            String shortClassName = className;
            int index = shortClassName.lastIndexOf('/');
            if (index >= 0) {
                shortClassName = className.substring(index + 1);
            }

            String description = "Timer for method " + mname + " in class " + shortClassName; // default
            TimingPointType tpt = TimingPointType.BOTH; // default for non-InfoMethod
            String tpName = mname; // default for non-InfoMethod

            boolean hasMethodInfoAnno = false;

            final List<AnnotationNode> annotations = mn.visibleAnnotations;
            if (annotations != null) {
                for (AnnotationNode an : annotations) {
                    final String aname = Type.getType(an.desc).getInternalName();

                    if (aname.equals(DESCRIPTION_NAME)) {
                        Object value = getAttribute(an, "value");
                        if (value != null && value instanceof String) {
                            description = (String) value;
                        }
                    } else if (aname.equals(INFO_METHOD_NAME)) {
                        // set the correct default for InfoMethod!
                        tpt = TimingPointType.NONE;

                        // Check for private method!
                        if (!util.hasAccess(mn.access, Opcodes.ACC_PRIVATE)) {

                            util.error("Method " + mdesc + " for Class " + currentClass.name
                                    + " is a non-private @InfoMethod," + " which is not allowed");
                        }

                        hasMethodInfoAnno = true;

                        Object value = getAttribute(an, "tpType");
                        if (value != null && value instanceof String[]) {
                            String[] enumData = (String[]) value;
                            if (enumData.length == 2) {
                                // [0] is desc, [1] is value
                                tpt = TimingPointType.valueOf(enumData[1]);
                            }
                        }

                        Object value2 = getAttribute(an, "tpName");
                        String tpn = "";
                        if ((value2 != null) && value2 instanceof String) {
                            tpn = (String) value2;
                        }

                        if (tpt != TimingPointType.NONE) {
                            if (tpn.length() == 0) {
                                util.error("Method " + mdesc + " for Class " + currentClass.name
                                        + " is an info method with timing point type " + tpt
                                        + " but no tpName was specified");
                            } else {
                                tpName = tpn;
                            }
                        }
                    } else if (annoNamesForClass.contains(aname)) {
                        if (monitoredMethodMMAnno == null) {
                            monitoredMethodMMAnno = aname;
                        } else {
                            util.error("Method " + mdesc + " for Class " + currentClass.name
                                    + "has multiple MM annotations");
                        }
                    } else if (annotationNames.contains(aname)) {
                        util.error("Method " + mdesc + " for Class " + currentClass.name
                                + " has an MM annotation which " + "is not on its class");
                    }
                }

                if (hasMethodInfoAnno && monitoredMethodMMAnno != null) {
                    util.error("Method " + mdesc + " for Class " + currentClass.name
                            + " has both @InfoMethod annotation and" + " a MM annotation");
                }

                // This check is not really essential, but it simplifies
                // passing information to later phases for code generation
                // if we can assume that all @InfoMethod annotated methods
                // are non-static. (Simply because we only need to look for
                // INVOKESPECIAL).
                final boolean isStatic = util.hasAccess(mn.access, Opcodes.ACC_STATIC);
                if (hasMethodInfoAnno && isStatic) {
                    util.error("Method " + mdesc + " for Class " + currentClass.name
                            + " is a static method, but must not be");
                }

                // TF Annotations are not permitted on constructors
                if (mname.equals("<init>")) {
                    if (hasMethodInfoAnno) {
                        util.error("Constructors must not have an " + "@InfoMethod annotations");
                    } else if (monitoredMethodMMAnno != null) {
                        util.error("Constructors must not have an " + "MM annotation");
                    }
                }

                // This will be a null value for InfoMethods, which is what
                // we want.
                mmnToAnnotationName.put(mname, monitoredMethodMMAnno);

                // We could have a method at this point that is annotated with
                // something OTHER than tracing annotations.  Do not add
                // such methods to the ECD.
                if (hasMethodInfoAnno || (monitoredMethodMMAnno != null)) {
                    // Both infoMethods and MM annotated methods go into 
                    // methodNames
                    methodNames.add(mname);

                    mmnToDescriptions.put(mname, description);
                    mmnToTPT.put(mname, tpt);
                    mmnToTPN.put(mname, tpName);

                    if (hasMethodInfoAnno) {
                        infoMethodDescs.add(mdesc);
                    } else {
                        mmMethodDescs.add(mdesc);
                        methodToAnno.put(mdesc, monitoredMethodMMAnno);
                    }
                }
            }
        }

        Collections.sort(methodNames);

        for (String str : methodNames) {
            methodDescriptions.add(mmnToDescriptions.get(str));
            methodTPTs.add(mmnToTPT.get(str));
            methodAnnoList.add(mmnToAnnotationName.get(str));
            methodTPNames.add(mmnToTPN.get(str));
        }

        if (util.getDebug()) {
            util.msg("\tinfoMethodDescs = " + infoMethodDescs);
            util.msg("\tmmMethodDescs = " + mmMethodDescs);
            util.msg("\tmethodNames = " + methodNames);
            util.msg("\tmethodToAnno = " + methodToAnno);
            util.msg("\tmethodDescriptions = " + methodDescriptions);
            util.msg("\tmethodTPTs = " + methodTPTs);
            util.msg("\tmethodTPNs = " + methodTPNames);
        }
    }

    public EnhancedClassDataASMImpl(Util util, Set<String> mmAnnotations, ClassNode cn) {

        super(util, mmAnnotations);

        currentClass = cn;

        // Compute data here: only look at data available to
        // java reflection, so that a runtime version of 
        // EnhancedClassData using reflection can be created.
        className = cn.name;
        processClassAnnotations();
        scanMethods();
    }
}