1.1 --- a/core.netigso/manifest.mf Tue Nov 12 21:57:59 2013 +0000
1.2 +++ b/core.netigso/manifest.mf Wed Nov 13 15:46:25 2013 +0100
1.3 @@ -2,7 +2,7 @@
1.4 OpenIDE-Module: org.netbeans.core.netigso
1.5 OpenIDE-Module-Localizing-Bundle: org/netbeans/core/netigso/Bundle.properties
1.6 OpenIDE-Module-Provides: org.netbeans.NetigsoFramework
1.7 -OpenIDE-Module-Specification-Version: 1.24
1.8 +OpenIDE-Module-Specification-Version: 1.25
1.9 OpenIDE-Module-Needs: org.osgi.framework.launch.FrameworkFactory
1.10 AutoUpdate-Essential-Module: true
1.11
2.1 --- a/core.netigso/src/org/netbeans/core/netigso/Netigso.java Tue Nov 12 21:57:59 2013 +0000
2.2 +++ b/core.netigso/src/org/netbeans/core/netigso/Netigso.java Wed Nov 13 15:46:25 2013 +0100
2.3 @@ -49,6 +49,7 @@
2.4 import java.io.IOException;
2.5 import java.io.InputStream;
2.6 import java.net.URL;
2.7 +import java.security.ProtectionDomain;
2.8 import java.util.Arrays;
2.9 import java.util.Collection;
2.10 import java.util.Collections;
2.11 @@ -785,4 +786,8 @@
2.12 }
2.13 return pkgs;
2.14 }
2.15 +
2.16 + public final byte[] patchBC(ClassLoader l, String className, ProtectionDomain pd, byte[] arr) {
2.17 + return patchByteCode(l, className, pd, arr);
2.18 + }
2.19 }
3.1 --- a/core.netigso/src/org/netbeans/core/netigso/spi/NetigsoArchive.java Tue Nov 12 21:57:59 2013 +0000
3.2 +++ b/core.netigso/src/org/netbeans/core/netigso/spi/NetigsoArchive.java Wed Nov 13 15:46:25 2013 +0100
3.3 @@ -43,6 +43,9 @@
3.4 package org.netbeans.core.netigso.spi;
3.5
3.6 import java.io.IOException;
3.7 +import java.lang.instrument.IllegalClassFormatException;
3.8 +import java.lang.instrument.Instrumentation;
3.9 +import java.security.ProtectionDomain;
3.10 import org.netbeans.ArchiveResources;
3.11 import org.netbeans.core.netigso.Netigso;
3.12 import org.netbeans.core.netigso.NetigsoArchiveFactory;
3.13 @@ -112,6 +115,25 @@
3.14 public boolean isActive() {
3.15 return netigso.isArchiveActive();
3.16 }
3.17 +
3.18 + /** Gives OSGi containers that support bytecode patching a chance to
3.19 + * call into NetBeans internal patching system based on
3.20 + * {@link Instrumentation}.
3.21 + *
3.22 + * @param l class loader loading the class
3.23 + * @param className the name of the class to define
3.24 + * @param pd its protection domain
3.25 + * @param arr bytecode (must not be modified)
3.26 + * @return the same or alternative bytecode to use for the defined class
3.27 + * @since 1.25
3.28 + */
3.29 + public final byte[] patchByteCode(ClassLoader l,
3.30 + String className,
3.31 + ProtectionDomain pd,
3.32 + byte[] arr
3.33 + ) {
3.34 + return netigso.patchBC(l, className, pd, arr);
3.35 + }
3.36
3.37 static {
3.38 NetigsoArchiveFactory f = new NetigsoArchiveFactory() {
4.1 --- a/netbinox/src/org/netbeans/modules/netbinox/NetbinoxHooks.java Tue Nov 12 21:57:59 2013 +0000
4.2 +++ b/netbinox/src/org/netbeans/modules/netbinox/NetbinoxHooks.java Wed Nov 13 15:46:25 2013 +0100
4.3 @@ -75,6 +75,7 @@
4.4 import org.osgi.framework.BundleException;
4.5 import org.osgi.framework.FrameworkEvent;
4.6 import org.osgi.framework.FrameworkListener;
4.7 +import org.osgi.framework.wiring.BundleWiring;
4.8
4.9 /**
4.10 *
4.11 @@ -96,8 +97,10 @@
4.12
4.13
4.14 @Override
4.15 - public byte[] processClass(String string, byte[] bytes, ClasspathEntry ce, BundleEntry be, ClasspathManager cm) {
4.16 - return bytes;
4.17 + public byte[] processClass(String className, byte[] bytes, ClasspathEntry ce, BundleEntry be, ClasspathManager cm) {
4.18 + BundleWiring w = ce.getBaseData().getBundle().adapt(org.osgi.framework.wiring.BundleWiring.class);
4.19 + ClassLoader loader = w.getClassLoader();
4.20 + return archive.patchByteCode(loader, className, ce.getDomain(), bytes);
4.21 }
4.22
4.23 @Override
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/netbinox/test/unit/data/jars/agent.mf Wed Nov 13 15:46:25 2013 +0100
5.3 @@ -0,0 +1,3 @@
5.4 +Manifest-Version: 1.0
5.5 +Bundle-SymbolicName: org.agent
5.6 +Agent-Class: org.agent.HelloWorldAgent
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/netbinox/test/unit/data/jars/agent/org/agent/HelloWorld.java Wed Nov 13 15:46:25 2013 +0100
6.3 @@ -0,0 +1,56 @@
6.4 +/*
6.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
6.6 + *
6.7 + * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
6.8 + *
6.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
6.10 + * Other names may be trademarks of their respective owners.
6.11 + *
6.12 + * The contents of this file are subject to the terms of either the GNU
6.13 + * General Public License Version 2 only ("GPL") or the Common
6.14 + * Development and Distribution License("CDDL") (collectively, the
6.15 + * "License"). You may not use this file except in compliance with the
6.16 + * License. You can obtain a copy of the License at
6.17 + * http://www.netbeans.org/cddl-gplv2.html
6.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
6.19 + * specific language governing permissions and limitations under the
6.20 + * License. When distributing the software, include this License Header
6.21 + * Notice in each file and include the License file at
6.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
6.23 + * particular file as subject to the "Classpath" exception as provided
6.24 + * by Oracle in the GPL Version 2 section of the License file that
6.25 + * accompanied this code. If applicable, add the following below the
6.26 + * License Header, with the fields enclosed by brackets [] replaced by
6.27 + * your own identifying information:
6.28 + * "Portions Copyrighted [year] [name of copyright owner]"
6.29 + *
6.30 + * If you wish your version of this file to be governed by only the CDDL
6.31 + * or only the GPL Version 2, indicate your decision by adding
6.32 + * "[Contributor] elects to include this software in this distribution
6.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
6.34 + * single choice of license, a recipient has the option to distribute
6.35 + * your version of this file under either the CDDL, the GPL Version 2 or
6.36 + * to extend the choice of license to its licensees as provided above.
6.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
6.38 + * Version 2 license, then the option applies only if the new code is
6.39 + * made subject to such option by the copyright holder.
6.40 + *
6.41 + * Contributor(s):
6.42 + *
6.43 + * Portions Copyrighted 2013 Sun Microsystems, Inc.
6.44 + */
6.45 +
6.46 +package org.agent;
6.47 +
6.48 +import java.util.concurrent.Callable;
6.49 +
6.50 +/** Sample application.
6.51 + *
6.52 + * @author Jaroslav Tulach <jtulach@netbeans.org>
6.53 + */
6.54 +public class HelloWorld implements Callable<String> {
6.55 + @Override
6.56 + public String call() {
6.57 + return "Helo World!";
6.58 + }
6.59 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/netbinox/test/unit/data/jars/agent/org/agent/HelloWorldAgent.java Wed Nov 13 15:46:25 2013 +0100
7.3 @@ -0,0 +1,32 @@
7.4 +package org.agent;
7.5 +
7.6 +import java.lang.instrument.ClassFileTransformer;
7.7 +import java.lang.instrument.IllegalClassFormatException;
7.8 +import java.lang.instrument.Instrumentation;
7.9 +import java.security.ProtectionDomain;
7.10 +
7.11 +public class HelloWorldAgent implements ClassFileTransformer {
7.12 + public static void agentmain(String args, Instrumentation inst) {
7.13 + inst.addTransformer(new HelloWorldAgent());
7.14 + }
7.15 +
7.16 + @Override
7.17 + public byte[] transform(
7.18 + ClassLoader loader, String className, Class<?> classBeingRedefined,
7.19 + ProtectionDomain protectionDomain, byte[] arr
7.20 + ) throws IllegalClassFormatException {
7.21 + byte[] ret = arr;
7.22 + for (int i = 0; i < arr.length - 4; i++) {
7.23 + if (arr[i] == 'H' && arr[i + 1] == 'e' && arr[i + 2] == 'l' &&
7.24 + arr[i + 3] == 'o'
7.25 + ) {
7.26 + ret = ret.clone();
7.27 + ret[i] = 'A';
7.28 + ret[i + 1] = 'h';
7.29 + ret[i + 2] = 'o';
7.30 + ret[i + 3] = 'j';
7.31 + }
7.32 + }
7.33 + return ret;
7.34 + }
7.35 +}
7.36 \ No newline at end of file
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/netbinox/test/unit/src/org/netbeans/modules/netbinox/AgentTest.java Wed Nov 13 15:46:25 2013 +0100
8.3 @@ -0,0 +1,91 @@
8.4 +/*
8.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
8.6 + *
8.7 + * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
8.8 + *
8.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
8.10 + * Other names may be trademarks of their respective owners.
8.11 + *
8.12 + * The contents of this file are subject to the terms of either the GNU
8.13 + * General Public License Version 2 only ("GPL") or the Common
8.14 + * Development and Distribution License("CDDL") (collectively, the
8.15 + * "License"). You may not use this file except in compliance with the
8.16 + * License. You can obtain a copy of the License at
8.17 + * http://www.netbeans.org/cddl-gplv2.html
8.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
8.19 + * specific language governing permissions and limitations under the
8.20 + * License. When distributing the software, include this License Header
8.21 + * Notice in each file and include the License file at
8.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
8.23 + * particular file as subject to the "Classpath" exception as provided
8.24 + * by Oracle in the GPL Version 2 section of the License file that
8.25 + * accompanied this code. If applicable, add the following below the
8.26 + * License Header, with the fields enclosed by brackets [] replaced by
8.27 + * your own identifying information:
8.28 + * "Portions Copyrighted [year] [name of copyright owner]"
8.29 + *
8.30 + * If you wish your version of this file to be governed by only the CDDL
8.31 + * or only the GPL Version 2, indicate your decision by adding
8.32 + * "[Contributor] elects to include this software in this distribution
8.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
8.34 + * single choice of license, a recipient has the option to distribute
8.35 + * your version of this file under either the CDDL, the GPL Version 2 or
8.36 + * to extend the choice of license to its licensees as provided above.
8.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
8.38 + * Version 2 license, then the option applies only if the new code is
8.39 + * made subject to such option by the copyright holder.
8.40 + *
8.41 + * Contributor(s):
8.42 + *
8.43 + * Portions Copyrighted 2013 Sun Microsystems, Inc.
8.44 + */
8.45 +
8.46 +package org.netbeans.modules.netbinox;
8.47 +
8.48 +import java.io.File;
8.49 +import java.util.Locale;
8.50 +import java.util.concurrent.Callable;
8.51 +import org.netbeans.Module;
8.52 +import org.netbeans.ModuleManager;
8.53 +import org.netbeans.core.startup.Main;
8.54 +import org.netbeans.core.startup.ModuleSystem;
8.55 +
8.56 +/**
8.57 + *
8.58 + * @author Jaroslav Tulach <jtulach@netbeans.org>
8.59 + */
8.60 +public class AgentTest extends NetigsoHid {
8.61 + private File agent;
8.62 + private ModuleManager mgr;
8.63 + public AgentTest(String name) {
8.64 + super(name);
8.65 + }
8.66 +
8.67 + protected @Override void setUp() throws Exception {
8.68 + Locale.setDefault(Locale.US);
8.69 + clearWorkDir();
8.70 + File ud = new File(getWorkDir(), "ud");
8.71 + ud.mkdirs();
8.72 + System.setProperty("netbeans.user", ud.getPath());
8.73 +
8.74 + data = new File(getDataDir(), "jars");
8.75 + jars = new File(getWorkDir(), "space in path");
8.76 + jars.mkdirs();
8.77 + agent = createTestJAR("agent", null);
8.78 + }
8.79 +
8.80 + public void testAgentClassRedefinesHello() throws Exception {
8.81 + ModuleSystem ms = Main.getModuleSystem();
8.82 + mgr = ms.getManager();
8.83 + mgr.mutexPrivileged().enterWriteAccess();
8.84 + mgr.mutexPrivileged().enterWriteAccess();
8.85 + Module m = mgr.create(agent, null, false, false, false);
8.86 + try {
8.87 + mgr.enable(m);
8.88 + Callable<?> c = (Callable<?>) m.getClassLoader().loadClass("org.agent.HelloWorld").newInstance();
8.89 + assertEquals("Bytecode has been patched", "Ahoj World!", c.call());
8.90 + } finally {
8.91 + mgr.disable(m);
8.92 + }
8.93 + }
8.94 +}
9.1 --- a/o.n.bootstrap/apichanges.xml Tue Nov 12 21:57:59 2013 +0000
9.2 +++ b/o.n.bootstrap/apichanges.xml Wed Nov 13 15:46:25 2013 +0100
9.3 @@ -53,6 +53,22 @@
9.4 </apidefs>
9.5
9.6 <changes>
9.7 + <change id="patch.classes">
9.8 + <api name="launcher"/>
9.9 + <summary>Ability to patch classes</summary>
9.10 + <version major="2" minor="66"/>
9.11 + <date day="14" month="11" year="2013"/>
9.12 + <author login="jtulach"/>
9.13 + <compatibility addition="yes" binary="compatible" semantic="compatible" />
9.14 + <description>
9.15 + <p>
9.16 + Define your <a href="@TOP@architecture-summary.html#usecase-patch">
9.17 + Agent-Class
9.18 + </a> to participate in patching other classes.
9.19 + </p>
9.20 + </description>
9.21 + <issue number="237919"/>
9.22 + </change>
9.23 <change id="disable.cli.server">
9.24 <api name="launcher"/>
9.25 <summary>Property to disable CLI server</summary>
10.1 --- a/o.n.bootstrap/arch.xml Tue Nov 12 21:57:59 2013 +0000
10.2 +++ b/o.n.bootstrap/arch.xml Wed Nov 13 15:46:25 2013 +0100
10.3 @@ -1492,6 +1492,47 @@
10.4 product installation structure.
10.5 </p>
10.6 </usecase>
10.7 + <usecase name="Patch classes" id="patch">
10.8 + <a name="usecase-patch"></a>
10.9 + <p>
10.10 + In case one wants to modify other classes before they get loaded into
10.11 + the VM, one can register its own
10.12 + <code>Agent-Class</code> and then be called whenever another
10.13 + class is defined. The behavior matches as closely as possible the one
10.14 + provided by <a href="@JDK@/java/lang/instrument/package-summary.html">
10.15 + JDK instrument package</a>. To patch bytecode of other classes
10.16 + register your class in manifest and in its static <code>agentmain</code> method
10.17 + add your own <a href="@JDK@/java/lang/instrument/ClassFileTransformer.html">ClassFileTransformer</a>
10.18 + which will then be consulted whenever new classes are loaded.
10.19 + </p>
10.20 + <pre>
10.21 +# following line should be in manifest of your module
10.22 +Agent-Class: your.pkg.Transformer
10.23 +
10.24 +// this should be your class
10.25 +package your.pkg;
10.26 +public class Transformer implements ClassFileTransformer {
10.27 + public static void agentmain(String args, Instrumentation i) {
10.28 + i.addTransformer(new Transformer());
10.29 + }
10.30 +
10.31 + @Override public byte[] transform(
10.32 + ClassLoader loader, String className, Class classBeingRedefined,
10.33 + ProtectionDomain protectionDomain, byte[] classfileBuffer
10.34 + ) {
10.35 + // do your transformation
10.36 + }
10.37 +}
10.38 +</pre>
10.39 + <p>
10.40 + The implementation tries to be as complient as possible with the
10.41 + original JDK's specification, but
10.42 + some differences are inevitable. For example the <code>classBeingRedefined</code>
10.43 + parameter in the previous example is always <code>null</code>
10.44 + as NetBeans runtime container does not
10.45 + have access to the class instance that is being defined yet.
10.46 + </p>
10.47 + </usecase>
10.48 </answer>
10.49
10.50
11.1 --- a/o.n.bootstrap/manifest.mf Tue Nov 12 21:57:59 2013 +0000
11.2 +++ b/o.n.bootstrap/manifest.mf Wed Nov 13 15:46:25 2013 +0100
11.3 @@ -1,6 +1,6 @@
11.4 Manifest-Version: 1.0
11.5 OpenIDE-Module: org.netbeans.bootstrap/1
11.6 -OpenIDE-Module-Specification-Version: 2.64
11.7 +OpenIDE-Module-Specification-Version: 2.66
11.8 OpenIDE-Module-Localizing-Bundle: org/netbeans/Bundle.properties
11.9 OpenIDE-Module-Recommends: org.netbeans.NetigsoFramework
11.10
12.1 --- a/o.n.bootstrap/src/org/netbeans/JarClassLoader.java Tue Nov 12 21:57:59 2013 +0000
12.2 +++ b/o.n.bootstrap/src/org/netbeans/JarClassLoader.java Wed Nov 13 15:46:25 2013 +0100
12.3 @@ -52,6 +52,7 @@
12.4 import java.io.IOException;
12.5 import java.io.InputStream;
12.6 import java.io.OutputStream;
12.7 +import java.lang.instrument.IllegalClassFormatException;
12.8 import java.lang.ref.Reference;
12.9 import java.lang.ref.SoftReference;
12.10 import java.lang.reflect.Method;
12.11 @@ -280,7 +281,11 @@
12.12 LOGGER.log(Level.FINE, null, x);
12.13 }
12.14 }
12.15 -
12.16 + try {
12.17 + data = NbInstrumentation.patchByteCode(this, name, src.getProtectionDomain(), data);
12.18 + } catch (IllegalClassFormatException ex) {
12.19 + LOGGER.log(Level.WARNING, "Problems patching" + name, ex);
12.20 + }
12.21 return defineClass (name, data, 0, data.length, src.getProtectionDomain());
12.22 }
12.23 return null;
13.1 --- a/o.n.bootstrap/src/org/netbeans/Module.java Tue Nov 12 21:57:59 2013 +0000
13.2 +++ b/o.n.bootstrap/src/org/netbeans/Module.java Wed Nov 13 15:46:25 2013 +0100
13.3 @@ -103,6 +103,7 @@
13.4 protected ClassLoader classloader;
13.5
13.6 private ModuleData data;
13.7 + private NbInstrumentation instr;
13.8
13.9 private static Method findResources;
13.10 private static final Object DATA_LOCK = new Object();
13.11 @@ -611,6 +612,14 @@
13.12 return false;
13.13 }
13.14
13.15 + final void assignInstrumentation(NbInstrumentation agent) {
13.16 + instr = agent;
13.17 + }
13.18 +
13.19 + void unregisterInstrumentation() {
13.20 + NbInstrumentation.unregisterAgent(instr);
13.21 + }
13.22 +
13.23 /** Struct representing a package exported from a module.
13.24 * @since org.netbeans.core/1 > 1.4
13.25 * @see Module#getPublicPackages
14.1 --- a/o.n.bootstrap/src/org/netbeans/ModuleData.java Tue Nov 12 21:57:59 2013 +0000
14.2 +++ b/o.n.bootstrap/src/org/netbeans/ModuleData.java Wed Nov 13 15:46:25 2013 +0100
14.3 @@ -84,6 +84,7 @@
14.4 private final String[] provides;
14.5 private final Dependency[] dependencies;
14.6 private final Set<String> coveredPackages;
14.7 + private final String agentClass;
14.8
14.9
14.10 ModuleData(Manifest mf, Module forModule) throws InvalidException {
14.11 @@ -214,6 +215,7 @@
14.12 throw (InvalidException) new InvalidException("While parsing " + codeName + " a dependency attribute: " + iae.toString()).initCause(iae); // NOI18N
14.13 }
14.14 this.coveredPackages = new HashSet<String>();
14.15 + this.agentClass = attr.getValue("Agent-Class");
14.16 }
14.17
14.18 ModuleData(Manifest mf, NetigsoModule m) throws InvalidException {
14.19 @@ -246,6 +248,7 @@
14.20 this.provides = computeProvides(m, mf.getMainAttributes(), false, true);
14.21 this.dependencies = computeImported(mf.getMainAttributes());
14.22 this.coveredPackages = new HashSet<String>();
14.23 + this.agentClass = getMainAttribute(mf, "Agent-Class"); // NOI18N
14.24 }
14.25
14.26 ModuleData(ObjectInput dis) throws IOException {
14.27 @@ -261,6 +264,7 @@
14.28 this.friendNames = readStrings(dis, new HashSet<String>(), false);
14.29 this.specVers = new SpecificationVersion(dis.readUTF());
14.30 this.publicPackages = Module.PackageExport.read(dis);
14.31 + this.agentClass = dis.readUTF();
14.32 } catch (ClassNotFoundException cnfe) {
14.33 throw new IOException(cnfe);
14.34 }
14.35 @@ -278,6 +282,7 @@
14.36 writeStrings(dos, friendNames);
14.37 dos.writeUTF(specVers != null ? specVers.toString() : "0");
14.38 Module.PackageExport.write(dos, publicPackages);
14.39 + dos.writeUTF(agentClass == null ? "" : agentClass);
14.40 }
14.41
14.42 private Dependency[] computeImported(Attributes attr) {
14.43 @@ -528,4 +533,7 @@
14.44 return new SpecificationVersion(v.substring(0, pos));
14.45 }
14.46
14.47 + final String getAgentClass() {
14.48 + return agentClass == null || agentClass.isEmpty() ? null : agentClass;
14.49 + }
14.50 }
15.1 --- a/o.n.bootstrap/src/org/netbeans/ModuleManager.java Tue Nov 12 21:57:59 2013 +0000
15.2 +++ b/o.n.bootstrap/src/org/netbeans/ModuleManager.java Wed Nov 13 15:46:25 2013 +0100
15.3 @@ -1195,6 +1195,13 @@
15.4 netigso.startFramework();
15.5 break;
15.6 }
15.7 + // register bytecode manipulation agents
15.8 + for (Module m : toEnable) {
15.9 + final String agentClass = m.data().getAgentClass();
15.10 + if (agentClass != null) {
15.11 + m.assignInstrumentation(NbInstrumentation.registerAgent(m.getClassLoader(), agentClass));
15.12 + }
15.13 + }
15.14 {
15.15 // Take care of notifying various changes.
15.16 Util.err.fine("enable: firing changes");
15.17 @@ -1243,6 +1250,7 @@
15.18 for (Module m : toDisable) {
15.19 installer.dispose(m);
15.20 m.setEnabled(false);
15.21 + m.unregisterInstrumentation();
15.22 m.classLoaderDown();
15.23 }
15.24 System.gc(); // hope OneModuleClassLoader.finalize() is called...
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
16.2 +++ b/o.n.bootstrap/src/org/netbeans/NbInstrumentation.java Wed Nov 13 15:46:25 2013 +0100
16.3 @@ -0,0 +1,210 @@
16.4 +/*
16.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
16.6 + *
16.7 + * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
16.8 + *
16.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
16.10 + * Other names may be trademarks of their respective owners.
16.11 + *
16.12 + * The contents of this file are subject to the terms of either the GNU
16.13 + * General Public License Version 2 only ("GPL") or the Common
16.14 + * Development and Distribution License("CDDL") (collectively, the
16.15 + * "License"). You may not use this file except in compliance with the
16.16 + * License. You can obtain a copy of the License at
16.17 + * http://www.netbeans.org/cddl-gplv2.html
16.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16.19 + * specific language governing permissions and limitations under the
16.20 + * License. When distributing the software, include this License Header
16.21 + * Notice in each file and include the License file at
16.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
16.23 + * particular file as subject to the "Classpath" exception as provided
16.24 + * by Oracle in the GPL Version 2 section of the License file that
16.25 + * accompanied this code. If applicable, add the following below the
16.26 + * License Header, with the fields enclosed by brackets [] replaced by
16.27 + * your own identifying information:
16.28 + * "Portions Copyrighted [year] [name of copyright owner]"
16.29 + *
16.30 + * If you wish your version of this file to be governed by only the CDDL
16.31 + * or only the GPL Version 2, indicate your decision by adding
16.32 + * "[Contributor] elects to include this software in this distribution
16.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
16.34 + * single choice of license, a recipient has the option to distribute
16.35 + * your version of this file under either the CDDL, the GPL Version 2 or
16.36 + * to extend the choice of license to its licensees as provided above.
16.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
16.38 + * Version 2 license, then the option applies only if the new code is
16.39 + * made subject to such option by the copyright holder.
16.40 + *
16.41 + * Contributor(s):
16.42 + *
16.43 + * Portions Copyrighted 2013 Sun Microsystems, Inc.
16.44 + */
16.45 +
16.46 +package org.netbeans;
16.47 +
16.48 +import java.lang.instrument.ClassDefinition;
16.49 +import java.lang.instrument.ClassFileTransformer;
16.50 +import java.lang.instrument.IllegalClassFormatException;
16.51 +import java.lang.instrument.Instrumentation;
16.52 +import java.lang.instrument.UnmodifiableClassException;
16.53 +import java.lang.reflect.InvocationTargetException;
16.54 +import java.lang.reflect.Method;
16.55 +import java.security.ProtectionDomain;
16.56 +import java.util.Collection;
16.57 +import java.util.Collections;
16.58 +import java.util.List;
16.59 +import java.util.concurrent.CopyOnWriteArrayList;
16.60 +import java.util.jar.JarFile;
16.61 +import java.util.logging.Level;
16.62 +import java.util.logging.Logger;
16.63 +import org.openide.util.WeakSet;
16.64 +
16.65 +/**
16.66 + *
16.67 + * @author Jaroslav Tulach <jtulach@netbeans.org>
16.68 + */
16.69 +final class NbInstrumentation implements Instrumentation {
16.70 + private static final Logger LOG = Logger.getLogger(NbInstrumentation.class.getName());
16.71 + private static final Object LOCK = new Object();
16.72 + private static volatile Collection<NbInstrumentation> ACTIVE;
16.73 +
16.74 + private final List<ClassFileTransformer> transformers = new CopyOnWriteArrayList<ClassFileTransformer>();
16.75 + private static final ThreadLocal<Boolean> IN = new ThreadLocal<Boolean>();
16.76 +
16.77 + static NbInstrumentation registerAgent(ClassLoader l, String agentClassName) {
16.78 + try {
16.79 + return registerImpl(agentClassName, l);
16.80 + } catch (Throwable ex) {
16.81 + LOG.log(Level.WARNING, "Cannot register " + agentClassName, ex);
16.82 + return null;
16.83 + }
16.84 + }
16.85 + static void unregisterAgent(NbInstrumentation instr) {
16.86 + synchronized (LOCK) {
16.87 + if (ACTIVE != null) {
16.88 + Collection<NbInstrumentation> clone = new WeakSet<NbInstrumentation>(ACTIVE);
16.89 + clone.remove(instr);
16.90 + ACTIVE = clone;
16.91 + }
16.92 + }
16.93 + }
16.94 + private static NbInstrumentation registerImpl(String agentClassName, ClassLoader l) throws ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException {
16.95 + final NbInstrumentation inst = new NbInstrumentation();
16.96 + synchronized (LOCK) {
16.97 + if (ACTIVE == null) {
16.98 + ACTIVE = new WeakSet<NbInstrumentation>();
16.99 + } else {
16.100 + ACTIVE = new WeakSet<NbInstrumentation>(ACTIVE);
16.101 + }
16.102 + ACTIVE.add(inst);
16.103 + }
16.104 + Class<?> agentClass = Class.forName(agentClassName, true, l);
16.105 + try {
16.106 + Method m = agentClass.getMethod("agentmain", String.class, Instrumentation.class); // NOI18N
16.107 + m.invoke(null, "", inst);
16.108 + } catch (NoSuchMethodException ex) {
16.109 + Method m = agentClass.getMethod("agentmain", String.class); // NOI18N
16.110 + m.invoke(null, "");
16.111 + }
16.112 + return inst;
16.113 + }
16.114 +
16.115 + public static byte[] patchByteCode(ClassLoader l, String className, ProtectionDomain pd, byte[] arr) throws IllegalClassFormatException {
16.116 + if (ACTIVE == null) {
16.117 + return arr;
16.118 + }
16.119 + if (Boolean.TRUE.equals(IN.get())) {
16.120 + return arr;
16.121 + }
16.122 + try {
16.123 + IN.set(Boolean.TRUE);
16.124 + for (NbInstrumentation inst : ACTIVE) {
16.125 + for (ClassFileTransformer t : inst.transformers) {
16.126 + arr = t.transform(l, className, null, pd, arr);
16.127 + }
16.128 + }
16.129 + } finally {
16.130 + IN.set(null);
16.131 + }
16.132 + return arr;
16.133 + }
16.134 +
16.135 + //
16.136 + // Instrumentation methods
16.137 + //
16.138 +
16.139 + @Override
16.140 + public void addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
16.141 + transformers.add(transformer);
16.142 + }
16.143 +
16.144 + @Override
16.145 + public void addTransformer(ClassFileTransformer transformer) {
16.146 + transformers.add(transformer);
16.147 + }
16.148 +
16.149 + @Override
16.150 + public boolean removeTransformer(ClassFileTransformer transformer) {
16.151 + return transformers.remove(transformer);
16.152 + }
16.153 +
16.154 + @Override
16.155 + public boolean isRetransformClassesSupported() {
16.156 + return false;
16.157 + }
16.158 +
16.159 + @Override
16.160 + public void retransformClasses(Class<?>... classes) throws UnmodifiableClassException {
16.161 + throw new UnmodifiableClassException();
16.162 + }
16.163 +
16.164 + @Override
16.165 + public boolean isRedefineClassesSupported() {
16.166 + return false;
16.167 + }
16.168 +
16.169 + @Override
16.170 + public void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException {
16.171 + throw new UnmodifiableClassException();
16.172 + }
16.173 +
16.174 + @Override
16.175 + public boolean isModifiableClass(Class<?> theClass) {
16.176 + return false;
16.177 + }
16.178 +
16.179 + @Override
16.180 + public Class[] getAllLoadedClasses() {
16.181 + return new Class[0];
16.182 + }
16.183 +
16.184 + @Override
16.185 + public Class[] getInitiatedClasses(ClassLoader loader) {
16.186 + return new Class[0];
16.187 + }
16.188 +
16.189 + @Override
16.190 + public long getObjectSize(Object objectToSize) {
16.191 + return 42;
16.192 + }
16.193 +
16.194 + @Override
16.195 + public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {
16.196 + }
16.197 +
16.198 + @Override
16.199 + public void appendToSystemClassLoaderSearch(JarFile jarfile) {
16.200 + throw new UnsupportedOperationException();
16.201 + }
16.202 +
16.203 + @Override
16.204 + public boolean isNativeMethodPrefixSupported() {
16.205 + return false;
16.206 + }
16.207 +
16.208 + @Override
16.209 + public void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
16.210 + throw new UnsupportedOperationException();
16.211 + }
16.212 +
16.213 +}
17.1 --- a/o.n.bootstrap/src/org/netbeans/NetigsoFramework.java Tue Nov 12 21:57:59 2013 +0000
17.2 +++ b/o.n.bootstrap/src/org/netbeans/NetigsoFramework.java Wed Nov 13 15:46:25 2013 +0100
17.3 @@ -44,11 +44,15 @@
17.4
17.5 import java.io.File;
17.6 import java.io.IOException;
17.7 +import java.lang.instrument.IllegalClassFormatException;
17.8 +import java.lang.instrument.Instrumentation;
17.9 import java.net.URL;
17.10 +import java.security.ProtectionDomain;
17.11 import java.util.*;
17.12 import java.util.logging.Level;
17.13 import org.openide.modules.ModuleInfo;
17.14 import org.openide.util.Enumerations;
17.15 +import org.openide.util.Exceptions;
17.16 import org.openide.util.Lookup;
17.17
17.18 /** This class contains abstracted calls to OSGi provided by core.netigso
17.19 @@ -186,4 +190,26 @@
17.20 protected final Module findModule(String cnb) {
17.21 return mgr.get(cnb);
17.22 }
17.23 +
17.24 + /** Gives OSGi support access to NetBeans bytecode manipulation libraries.
17.25 + * They are built over {@link Instrumentation} specification
17.26 + * read more at <a href="@TOP@architecture-overview.html#usecase-bytecode.patching">
17.27 + * architecture document</a>.
17.28 + *
17.29 + * @param l the classloader that is loading the class
17.30 + * @param className the class name
17.31 + * @param pd protection domain to use
17.32 + * @param arr bytecode (must not be modified)
17.33 + * @return same or alternative bytecode for the class
17.34 + * @throws IllegalStateException if the byte code is not valid
17.35 + * @since 2.65
17.36 + */
17.37 + protected final byte[] patchByteCode(ClassLoader l, String className, ProtectionDomain pd, byte[] arr) {
17.38 + try {
17.39 + return NbInstrumentation.patchByteCode(l, className, null, arr);
17.40 + } catch (IllegalClassFormatException ex) {
17.41 + throw new IllegalStateException(ex);
17.42 + }
17.43 + }
17.44 +
17.45 }
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
18.2 +++ b/o.n.bootstrap/test/unit/data/jars/agent.mf Wed Nov 13 15:46:25 2013 +0100
18.3 @@ -0,0 +1,4 @@
18.4 +Manifest-Version: 1.0
18.5 +OpenIDE-Module: org.agent/1
18.6 +OpenIDE-Module-Name: Hello World Agent
18.7 +Agent-Class: org.agent.HelloWorldAgent
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
19.2 +++ b/o.n.bootstrap/test/unit/data/jars/agent/org/agent/HelloWorld.java Wed Nov 13 15:46:25 2013 +0100
19.3 @@ -0,0 +1,56 @@
19.4 +/*
19.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
19.6 + *
19.7 + * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
19.8 + *
19.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
19.10 + * Other names may be trademarks of their respective owners.
19.11 + *
19.12 + * The contents of this file are subject to the terms of either the GNU
19.13 + * General Public License Version 2 only ("GPL") or the Common
19.14 + * Development and Distribution License("CDDL") (collectively, the
19.15 + * "License"). You may not use this file except in compliance with the
19.16 + * License. You can obtain a copy of the License at
19.17 + * http://www.netbeans.org/cddl-gplv2.html
19.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
19.19 + * specific language governing permissions and limitations under the
19.20 + * License. When distributing the software, include this License Header
19.21 + * Notice in each file and include the License file at
19.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
19.23 + * particular file as subject to the "Classpath" exception as provided
19.24 + * by Oracle in the GPL Version 2 section of the License file that
19.25 + * accompanied this code. If applicable, add the following below the
19.26 + * License Header, with the fields enclosed by brackets [] replaced by
19.27 + * your own identifying information:
19.28 + * "Portions Copyrighted [year] [name of copyright owner]"
19.29 + *
19.30 + * If you wish your version of this file to be governed by only the CDDL
19.31 + * or only the GPL Version 2, indicate your decision by adding
19.32 + * "[Contributor] elects to include this software in this distribution
19.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
19.34 + * single choice of license, a recipient has the option to distribute
19.35 + * your version of this file under either the CDDL, the GPL Version 2 or
19.36 + * to extend the choice of license to its licensees as provided above.
19.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
19.38 + * Version 2 license, then the option applies only if the new code is
19.39 + * made subject to such option by the copyright holder.
19.40 + *
19.41 + * Contributor(s):
19.42 + *
19.43 + * Portions Copyrighted 2013 Sun Microsystems, Inc.
19.44 + */
19.45 +
19.46 +package org.agent;
19.47 +
19.48 +import java.util.concurrent.Callable;
19.49 +
19.50 +/** Sample application.
19.51 + *
19.52 + * @author Jaroslav Tulach <jtulach@netbeans.org>
19.53 + */
19.54 +public class HelloWorld implements Callable<String> {
19.55 + @Override
19.56 + public String call() {
19.57 + return "Helo World!";
19.58 + }
19.59 +}
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
20.2 +++ b/o.n.bootstrap/test/unit/data/jars/agent/org/agent/HelloWorldAgent.java Wed Nov 13 15:46:25 2013 +0100
20.3 @@ -0,0 +1,33 @@
20.4 +package org.agent;
20.5 +
20.6 +import java.lang.instrument.ClassFileTransformer;
20.7 +import java.lang.instrument.IllegalClassFormatException;
20.8 +import java.lang.instrument.Instrumentation;
20.9 +import java.security.ProtectionDomain;
20.10 +
20.11 +public class HelloWorldAgent implements ClassFileTransformer {
20.12 + public static void agentmain(String args, Instrumentation inst) {
20.13 + inst.getClass(); // null check
20.14 + inst.addTransformer(new HelloWorldAgent());
20.15 + }
20.16 +
20.17 + @Override
20.18 + public byte[] transform(
20.19 + ClassLoader loader, String className, Class<?> classBeingRedefined,
20.20 + ProtectionDomain protectionDomain, byte[] arr
20.21 + ) throws IllegalClassFormatException {
20.22 + byte[] ret = arr;
20.23 + for (int i = 0; i < arr.length - 4; i++) {
20.24 + if (arr[i] == 'H' && arr[i + 1] == 'e' && arr[i + 2] == 'l' &&
20.25 + arr[i + 3] == 'o'
20.26 + ) {
20.27 + ret = ret.clone();
20.28 + ret[i] = 'A';
20.29 + ret[i + 1] = 'h';
20.30 + ret[i + 2] = 'o';
20.31 + ret[i + 3] = 'j';
20.32 + }
20.33 + }
20.34 + return ret;
20.35 + }
20.36 +}
20.37 \ No newline at end of file
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
21.2 +++ b/o.n.bootstrap/test/unit/src/org/netbeans/AgentTest.java Wed Nov 13 15:46:25 2013 +0100
21.3 @@ -0,0 +1,72 @@
21.4 +/*
21.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
21.6 + *
21.7 + * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
21.8 + *
21.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
21.10 + * Other names may be trademarks of their respective owners.
21.11 + *
21.12 + * The contents of this file are subject to the terms of either the GNU
21.13 + * General Public License Version 2 only ("GPL") or the Common
21.14 + * Development and Distribution License("CDDL") (collectively, the
21.15 + * "License"). You may not use this file except in compliance with the
21.16 + * License. You can obtain a copy of the License at
21.17 + * http://www.netbeans.org/cddl-gplv2.html
21.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
21.19 + * specific language governing permissions and limitations under the
21.20 + * License. When distributing the software, include this License Header
21.21 + * Notice in each file and include the License file at
21.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
21.23 + * particular file as subject to the "Classpath" exception as provided
21.24 + * by Oracle in the GPL Version 2 section of the License file that
21.25 + * accompanied this code. If applicable, add the following below the
21.26 + * License Header, with the fields enclosed by brackets [] replaced by
21.27 + * your own identifying information:
21.28 + * "Portions Copyrighted [year] [name of copyright owner]"
21.29 + *
21.30 + * If you wish your version of this file to be governed by only the CDDL
21.31 + * or only the GPL Version 2, indicate your decision by adding
21.32 + * "[Contributor] elects to include this software in this distribution
21.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
21.34 + * single choice of license, a recipient has the option to distribute
21.35 + * your version of this file under either the CDDL, the GPL Version 2 or
21.36 + * to extend the choice of license to its licensees as provided above.
21.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
21.38 + * Version 2 license, then the option applies only if the new code is
21.39 + * made subject to such option by the copyright holder.
21.40 + *
21.41 + * Contributor(s):
21.42 + *
21.43 + * Portions Copyrighted 2013 Sun Microsystems, Inc.
21.44 + */
21.45 +
21.46 +package org.netbeans;
21.47 +
21.48 +import java.io.File;
21.49 +import java.util.concurrent.Callable;
21.50 +
21.51 +/**
21.52 + *
21.53 + * @author Jaroslav Tulach <jtulach@netbeans.org>
21.54 + */
21.55 +public class AgentTest extends SetupHid {
21.56 + public AgentTest(String name) {
21.57 + super(name);
21.58 + }
21.59 +
21.60 + public void testAgentClassRedefinesHello() throws Exception {
21.61 + File jar = new File(jars, "agent.jar");
21.62 + MockModuleInstaller installer = new MockModuleInstaller();
21.63 + MockEvents ev = new MockEvents();
21.64 + ModuleManager mgr = new ModuleManager(installer, ev);
21.65 + mgr.mutexPrivileged().enterWriteAccess();
21.66 + Module m = mgr.create(jar, null, false, false, false);
21.67 + try {
21.68 + mgr.enable(m);
21.69 + Callable<?> c = (Callable<?>) m.getClassLoader().loadClass("org.agent.HelloWorld").newInstance();
21.70 + assertEquals("Bytecode has been patched", "Ahoj World!", c.call());
21.71 + } finally {
21.72 + mgr.disable(m);
21.73 + }
21.74 + }
21.75 +}
22.1 --- a/o.n.bootstrap/test/unit/src/org/netbeans/SetupHid.java Tue Nov 12 21:57:59 2013 +0000
22.2 +++ b/o.n.bootstrap/test/unit/src/org/netbeans/SetupHid.java Wed Nov 13 15:46:25 2013 +0100
22.3 @@ -237,6 +237,7 @@
22.4 createTestJAR("needs-foo", null);
22.5 createTestJAR("recommends-foo", null);
22.6 createTestJAR("prov-foo-depends-needs_foo", "prov-foo");
22.7 + createTestJAR("agent", "agent");
22.8 createTestJAR("api-mod-export-all", "exposes-api");
22.9 createTestJAR("api-mod-export-none", "exposes-api");
22.10 File exposesAPI = createTestJAR("api-mod-export-api", "exposes-api");