equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/NbInstrumentation.java
author Jaroslav Tulach <jaroslav.tulach@netbeans.org>
Fri, 07 Feb 2014 07:44:34 +0100
changeset 551 7ca2253fa86d
parent 393 8b025bcde7bb
child 790 30f20d9c0986
permissions -rw-r--r--
Updating copyright headers to mention current year
jaroslav@393
     1
/**
jaroslav@393
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jaroslav@393
     3
 *
jaroslav@551
     4
 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
jaroslav@393
     5
 *
jaroslav@393
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
jaroslav@393
     7
 * Other names may be trademarks of their respective owners.
jaroslav@393
     8
 *
jaroslav@393
     9
 * The contents of this file are subject to the terms of either the GNU
jaroslav@393
    10
 * General Public License Version 2 only ("GPL") or the Common
jaroslav@393
    11
 * Development and Distribution License("CDDL") (collectively, the
jaroslav@393
    12
 * "License"). You may not use this file except in compliance with the
jaroslav@393
    13
 * License. You can obtain a copy of the License at
jaroslav@393
    14
 * http://www.netbeans.org/cddl-gplv2.html
jaroslav@393
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
jaroslav@393
    16
 * specific language governing permissions and limitations under the
jaroslav@393
    17
 * License.  When distributing the software, include this License Header
jaroslav@393
    18
 * Notice in each file and include the License file at
jaroslav@393
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
jaroslav@393
    20
 * particular file as subject to the "Classpath" exception as provided
jaroslav@393
    21
 * by Oracle in the GPL Version 2 section of the License file that
jaroslav@393
    22
 * accompanied this code. If applicable, add the following below the
jaroslav@393
    23
 * License Header, with the fields enclosed by brackets [] replaced by
jaroslav@393
    24
 * your own identifying information:
jaroslav@393
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
jaroslav@393
    26
 *
jaroslav@393
    27
 * Contributor(s):
jaroslav@393
    28
 *
jaroslav@393
    29
 * The Original Software is NetBeans. The Initial Developer of the Original
jaroslav@551
    30
 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
jaroslav@393
    31
 *
jaroslav@393
    32
 * If you wish your version of this file to be governed by only the CDDL
jaroslav@393
    33
 * or only the GPL Version 2, indicate your decision by adding
jaroslav@393
    34
 * "[Contributor] elects to include this software in this distribution
jaroslav@393
    35
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
jaroslav@393
    36
 * single choice of license, a recipient has the option to distribute
jaroslav@393
    37
 * your version of this file under either the CDDL, the GPL Version 2 or
jaroslav@393
    38
 * to extend the choice of license to its licensees as provided above.
jaroslav@393
    39
 * However, if you add GPL Version 2 code and therefore, elected the GPL
jaroslav@393
    40
 * Version 2 license, then the option applies only if the new code is
jaroslav@393
    41
 * made subject to such option by the copyright holder.
jaroslav@393
    42
 */
jaroslav@393
    43
package org.netbeans.html.equinox.agentclass;
jaroslav@393
    44
jaroslav@393
    45
import java.lang.instrument.ClassDefinition;
jaroslav@393
    46
import java.lang.instrument.ClassFileTransformer;
jaroslav@393
    47
import java.lang.instrument.IllegalClassFormatException;
jaroslav@393
    48
import java.lang.instrument.Instrumentation;
jaroslav@393
    49
import java.lang.instrument.UnmodifiableClassException;
jaroslav@393
    50
import java.lang.reflect.InvocationTargetException;
jaroslav@393
    51
import java.lang.reflect.Method;
jaroslav@393
    52
import java.security.ProtectionDomain;
jaroslav@393
    53
import java.util.Collection;
jaroslav@393
    54
import java.util.HashSet;
jaroslav@393
    55
import java.util.List;
jaroslav@393
    56
import java.util.Set;
jaroslav@393
    57
import java.util.concurrent.CopyOnWriteArrayList;
jaroslav@393
    58
import java.util.jar.JarFile;
jaroslav@393
    59
import java.util.logging.Level;
jaroslav@393
    60
import java.util.logging.Logger;
jaroslav@393
    61
jaroslav@393
    62
/**
jaroslav@393
    63
 *
jaroslav@393
    64
 * @author Jaroslav Tulach <jtulach@netbeans.org>
jaroslav@393
    65
 */
jaroslav@393
    66
final class NbInstrumentation implements Instrumentation {
jaroslav@393
    67
    private static final Logger LOG = Logger.getLogger(NbInstrumentation.class.getName());
jaroslav@393
    68
    private static final Object LOCK = new Object();
jaroslav@393
    69
    private static volatile Collection<NbInstrumentation> ACTIVE;
jaroslav@393
    70
jaroslav@393
    71
    private final List<ClassFileTransformer> transformers = new CopyOnWriteArrayList<ClassFileTransformer>();
jaroslav@393
    72
    private static final ThreadLocal<Boolean> IN = new ThreadLocal<Boolean>();
jaroslav@393
    73
    
jaroslav@393
    74
    static NbInstrumentation registerAgent(ClassLoader l, String agentClassName) {
jaroslav@393
    75
        try {
jaroslav@393
    76
            return registerImpl(agentClassName, l);
jaroslav@393
    77
        } catch (Throwable ex) {
jaroslav@393
    78
            LOG.log(Level.WARNING, "Cannot register " + agentClassName, ex);
jaroslav@393
    79
            return null;
jaroslav@393
    80
        }
jaroslav@393
    81
    }
jaroslav@393
    82
    static void unregisterAgent(NbInstrumentation instr) {
jaroslav@393
    83
        synchronized (LOCK) {
jaroslav@393
    84
            if (ACTIVE != null) {
jaroslav@393
    85
                Collection<NbInstrumentation> clone = new HashSet<NbInstrumentation>();
jaroslav@393
    86
                clone.addAll(ACTIVE);
jaroslav@393
    87
                clone.remove(instr);
jaroslav@393
    88
                ACTIVE = clone;
jaroslav@393
    89
            }
jaroslav@393
    90
        }
jaroslav@393
    91
    }
jaroslav@393
    92
    private static NbInstrumentation registerImpl(String agentClassName, ClassLoader l) throws ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException {
jaroslav@393
    93
        final NbInstrumentation inst = new NbInstrumentation();
jaroslav@393
    94
        synchronized (LOCK) {
jaroslav@393
    95
            if (ACTIVE == null) {
jaroslav@393
    96
                ACTIVE = new HashSet<NbInstrumentation>();
jaroslav@393
    97
            } else {
jaroslav@393
    98
                Set<NbInstrumentation> s = new HashSet<NbInstrumentation>();
jaroslav@393
    99
                s.addAll(ACTIVE);
jaroslav@393
   100
                ACTIVE = s;
jaroslav@393
   101
            }
jaroslav@393
   102
            ACTIVE.add(inst);
jaroslav@393
   103
        }
jaroslav@393
   104
        Class<?> agentClass = Class.forName(agentClassName, true, l);
jaroslav@393
   105
        try {
jaroslav@393
   106
            Method m = agentClass.getMethod("agentmain", String.class, Instrumentation.class); // NOI18N
jaroslav@393
   107
            m.invoke(null, "", inst);
jaroslav@393
   108
        } catch (NoSuchMethodException ex) {
jaroslav@393
   109
            Method m = agentClass.getMethod("agentmain", String.class); // NOI18N
jaroslav@393
   110
            m.invoke(null, "");
jaroslav@393
   111
        }
jaroslav@393
   112
        return inst;
jaroslav@393
   113
    }
jaroslav@393
   114
    
jaroslav@393
   115
    public static byte[] patchByteCode(ClassLoader l, String className, ProtectionDomain pd, byte[] arr) throws IllegalClassFormatException {
jaroslav@393
   116
        if (ACTIVE == null) {
jaroslav@393
   117
            return arr;
jaroslav@393
   118
        }
jaroslav@393
   119
        if (Boolean.TRUE.equals(IN.get())) {
jaroslav@393
   120
            return arr;
jaroslav@393
   121
        }
jaroslav@393
   122
        try {
jaroslav@393
   123
            IN.set(Boolean.TRUE);
jaroslav@393
   124
            for (NbInstrumentation inst : ACTIVE) {
jaroslav@393
   125
                for (ClassFileTransformer t : inst.transformers) {
jaroslav@393
   126
                    arr = t.transform(l, className, null, pd, arr);
jaroslav@393
   127
                }
jaroslav@393
   128
            }
jaroslav@393
   129
        } finally {
jaroslav@393
   130
            IN.set(null);
jaroslav@393
   131
        }
jaroslav@393
   132
        return arr;
jaroslav@393
   133
    }
jaroslav@393
   134
    
jaroslav@393
   135
    //
jaroslav@393
   136
    // Instrumentation methods
jaroslav@393
   137
    //
jaroslav@393
   138
jaroslav@393
   139
    @Override
jaroslav@393
   140
    public void addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
jaroslav@393
   141
        transformers.add(transformer);
jaroslav@393
   142
    }
jaroslav@393
   143
jaroslav@393
   144
    @Override
jaroslav@393
   145
    public void addTransformer(ClassFileTransformer transformer) {
jaroslav@393
   146
        transformers.add(transformer);
jaroslav@393
   147
    }
jaroslav@393
   148
jaroslav@393
   149
    @Override
jaroslav@393
   150
    public boolean removeTransformer(ClassFileTransformer transformer) {
jaroslav@393
   151
        return transformers.remove(transformer);
jaroslav@393
   152
    }
jaroslav@393
   153
jaroslav@393
   154
    @Override
jaroslav@393
   155
    public boolean isRetransformClassesSupported() {
jaroslav@393
   156
        return false;
jaroslav@393
   157
    }
jaroslav@393
   158
jaroslav@393
   159
    @Override
jaroslav@393
   160
    public void retransformClasses(Class<?>... classes) throws UnmodifiableClassException {
jaroslav@393
   161
        throw new UnmodifiableClassException();
jaroslav@393
   162
    }
jaroslav@393
   163
jaroslav@393
   164
    @Override
jaroslav@393
   165
    public boolean isRedefineClassesSupported() {
jaroslav@393
   166
        return false;
jaroslav@393
   167
    }
jaroslav@393
   168
jaroslav@393
   169
    @Override
jaroslav@393
   170
    public void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException {
jaroslav@393
   171
        throw new UnmodifiableClassException();
jaroslav@393
   172
    }
jaroslav@393
   173
jaroslav@393
   174
    @Override
jaroslav@393
   175
    public boolean isModifiableClass(Class<?> theClass) {
jaroslav@393
   176
        return false;
jaroslav@393
   177
    }
jaroslav@393
   178
jaroslav@393
   179
    @Override
jaroslav@393
   180
    public Class[] getAllLoadedClasses() {
jaroslav@393
   181
        return new Class[0];
jaroslav@393
   182
    }
jaroslav@393
   183
jaroslav@393
   184
    @Override
jaroslav@393
   185
    public Class[] getInitiatedClasses(ClassLoader loader) {
jaroslav@393
   186
        return new Class[0];
jaroslav@393
   187
    }
jaroslav@393
   188
jaroslav@393
   189
    @Override
jaroslav@393
   190
    public long getObjectSize(Object objectToSize) {
jaroslav@393
   191
        return 42;
jaroslav@393
   192
    }
jaroslav@393
   193
jaroslav@393
   194
    @Override
jaroslav@393
   195
    public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {
jaroslav@393
   196
    }
jaroslav@393
   197
jaroslav@393
   198
    @Override
jaroslav@393
   199
    public void appendToSystemClassLoaderSearch(JarFile jarfile) {
jaroslav@393
   200
        throw new UnsupportedOperationException();
jaroslav@393
   201
    }
jaroslav@393
   202
jaroslav@393
   203
    @Override
jaroslav@393
   204
    public boolean isNativeMethodPrefixSupported() {
jaroslav@393
   205
        return false;
jaroslav@393
   206
    }
jaroslav@393
   207
jaroslav@393
   208
    @Override
jaroslav@393
   209
    public void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
jaroslav@393
   210
        throw new UnsupportedOperationException();
jaroslav@393
   211
    }
jaroslav@393
   212
    
jaroslav@393
   213
}