├── .gitignore ├── JAgent_based_agent_scheme.png ├── LICENSE.txt ├── README.md ├── jagent-api ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── devexperts │ └── jagent │ ├── InnerJarClassLoader.java │ └── JAgentRunner.java ├── jagent-impl ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── devexperts │ └── jagent │ ├── CachingClassFileTransformer.java │ ├── ClassInfo.java │ ├── ClassInfoCache.java │ ├── ClassInfoMap.java │ ├── ClassInfoVisitor.java │ ├── FastByteBuffer.java │ ├── FastFmtUtil.java │ ├── FastOutputStreamWriter.java │ ├── FrameClassWriter.java │ ├── JAgent.java │ ├── JAgentUtil.java │ └── Log.java ├── pom.xml └── sample ├── agent └── pom.xml ├── core ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── devexperts │ │ └── jagent │ │ └── sample │ │ └── SampleAgentRunner.java │ └── resources │ └── META-INF │ └── MANIFEST.MF ├── pom.xml ├── test ├── pom.xml └── src │ └── test │ └── java │ └── com │ └── devexperts │ └── jagent │ └── sample │ └── test │ └── SampleAgentTest.java └── transformer ├── pom.xml └── src └── main ├── java └── com │ └── devexperts │ └── jagent │ └── sample │ ├── Configuration.java │ ├── MethodDeleterTransformer.java │ └── SampleAgent.java └── resources └── sample.properties /.gitignore: -------------------------------------------------------------------------------- 1 | #idea files 2 | .idea/ 3 | *.iml 4 | *.ipr 5 | *.iws 6 | atlassian-ide-plugin.xml 7 | /out/ 8 | 9 | # java 10 | *.class 11 | *.jar 12 | *.war 13 | *.ear 14 | 15 | # maven specific 16 | target/ 17 | pom.xml.tag 18 | pom.xml.releaseBackup 19 | pom.xml.versionsBackup 20 | pom.xml.next 21 | release.properties 22 | dependency-reduced-pom.xml 23 | buildNumber.properties 24 | .mvn/timing.properties 25 | 26 | # common 27 | *~ 28 | *.bak 29 | .DS_Store 30 | .DS_Store? 31 | ._* -------------------------------------------------------------------------------- /JAgent_based_agent_scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devexperts/jagent/d6f6871badee80a0e076d4af83f861d458747144/JAgent_based_agent_scheme.png -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JAgent 2 | ====== 3 | 4 | [ ![Download](https://api.bintray.com/packages/devexperts/Maven/jagent/images/download.svg) ](https://bintray.com/devexperts/Maven/jagent/_latestVersion) 5 | 6 | **JAgent** framework helps you to implement java agents. 7 | It has some extra functionality instead of standard Java's API. 8 | 9 | Class redefining 10 | ---------------- 11 | By default, java agents don't redefine already loaded classes. 12 | Standard API has method 13 | `java.lang.instrument.Instrumentation#redefineClasses(ClassDefinition... definitions)` 14 | to redefine them. 15 | But it's impossible to redefine some classes (for example, `java.lang.String`) using this method. 16 | 17 | **JAgent** has its own implementation of redefinition logic and allows to redefine all classes. 18 | But make careful with transforming classes from `java.*` package because of invalid transformation 19 | can cause strange errors into JVM code (segmentation fault, for example). 20 | 21 | Class caching 22 | ------------- 23 | This feature is under development. See `com.devexperts.jagent.CachingClassFileTransformer`. 24 | 25 | 26 | FrameClassWriter 27 | ---------------- 28 | If you use ASM to code transformation then you can face a problem with frame counting. `ClassWriter` has a `getCommonSuperClass(String type1, String type2)` method to merge two types to compute stack frame attributes. If one of the types is the one being processed by this `ClassWriter` then you will catch `LinkageError` or `ClassCircularityError`. The cause of this problem is inside this method: `ClassWriter` uses `ClassLoader#forName(String type)` method to load class of specified type. **JAgent** has its own implementation of `getCommonSuperClass` method based on caching class metadata during transforming. So if you should find superclass of two types and one of them is the one being processed then you have information about it already and you don't need to load it again. 29 | 30 | ### Example: ### 31 | 32 | ```java 33 | private final ClassInfoCache ciCache; 34 | 35 | @Override 36 | public byte[] transform(ClassLoader loader, String className, Class clazz, ProtectionDomain pd, byte[] classfileBuffer) throws IllegalClassFormatException { 37 | ClassReader cr = new ClassReader(classfileBuffer); 38 | // this visitor exctracts information about class 39 | ClassInfoVisitor ciVisitor = new ClassInfoVisitor(); 40 | cr.accept(ciVisitor, 0); 41 | // update cache with information about classes 42 | ciCache.getOrInitClassInfoMap(loader).put(className, ciVisitor.getClassInfo()); 43 | // create FrameClassWriter using cache 44 | ClassWriter cw = new FrameClassWriter(loader, ciCache); 45 | ... 46 | return cw.toByteArray(); 47 | } 48 | ``` 49 | 50 | Own classloader 51 | --------------- 52 | **JAgent** has `InnerJarClassLoader` which is used to load transformation code and libraries via custom class loader. 53 | What is it for? 54 | 55 | If you have 2 agents that use library XXX with different versions then you have a version conflict. 56 | But it is all right if you load them under different class loaders. 57 | Use `InnerJarClassLoader` to load your transformation code and external libraries for it. 58 | 59 | See scheme below to understand how to write java agents with this feature: 60 | ![JAgent based agent scheme](JAgent_based_agent_scheme.png) 61 | 62 | Fast logging 63 | ------------ 64 | Every `JAgent` instance has a `Log` object which is used to write logs efficiently. 65 | You can use `JAgent#getLog()` method to get the `Log` object for your purposes. 66 | 67 | Logs are written according to following format: 68 | ``` 69 | 2016-02-10T02:48:19.902+0300 : 70 | ``` 71 | 72 | Sample 73 | ====== 74 | You can find `sample` module in source code to see how to use **JAgent** for implementing your own java agent. 75 | 76 | Maven 77 | ===== 78 | Here is a guide how to create java agents via **JAgent** with *Maven*. 79 | 80 | If you use `InnerJarClassLoader` then you should use 4 modules in your agent code: 81 | * **core** - contains code that runs transformation and new code for runtime 82 | * **transformer** - contains transformation logic 83 | * **agent** - builds agent's jar, no code here 84 | * **test** - tests for your agent 85 | 86 | transformer module 87 | ------------------- 88 | Contains code that is executable in transformation phase. 89 | Should have `com.devexperts.jagent:jagent-impl` as dependency. 90 | This code with dependencies is loaded under `InnerJarClassLoader`. 91 | 92 | #### com.agent.package.YourJAgentImpl.java 93 | Implementation of `JAgent` abstract class. This implementation is used in `com.agent.package.AgentRunner` code. 94 | 95 | ```java 96 | public class YourJAgentImpl extends JAgent { 97 | 98 | private YourJAgentImpl(Instrumentation inst, String args, String agentName, String agentVersion, Log log) { 99 | super(inst, agentName, agentVersion, log); 100 | } 101 | 102 | public static YourJAgentImpl create(Instrumentation inst, String agentArgs) { 103 | String agentName = JAgentUtil.getImplTitle(YourJAgentImpl.class); 104 | String agentVersion = JAgentUtil.getImplVersion(YourJAgentImpl.class); 105 | Log log = new Log(agentName, Log.Level.DEBUG, null); 106 | YourJAgentImpl agent = new YourJAgentImpl(inst, agentArgs, agentName, agentVersion, log); 107 | agent.addTransformer(...); 108 | ... 109 | return agent; 110 | } 111 | } 112 | ``` 113 | 114 | #### *pom.xml* example: 115 | Just contains needful dependencies for transformation. 116 | 117 | ```xml 118 | 119 | 120 | com.devexperts.jagent 121 | jagent-impl 122 | 0.2 123 | 124 | 125 | org.ow2.asm 126 | asm-all 127 | 5.0.4 128 | 129 | 130 | ``` 131 | 132 | core module 133 | ---- 134 | Contains code that runs transformation and new code for runtime. 135 | All classes from this module will be available via system class loader. 136 | 137 | #### MANIFEST.MF 138 | Configures agent information and permissions. 139 | 140 | ``` 141 | Premain-Class: com.agent.package.AgentRunner 142 | Can-Redefine-Class: true 143 | Boot-Class-Path: ${agent.artifact.name}.jar 144 | 145 | Name: com/agent/package 146 | Implementation-Title: ${agent.artifact.name} 147 | Implementation-Version: ${project.version} 148 | Implementation-Vendor: 149 | ``` 150 | 151 | #### com.agent.package.AgentRunner.java 152 | Runs transformation code via `JAgentRunner`. 153 | 154 | ```java 155 | public class AgentRunner { 156 | public static void premain(String agentArgs, Instrumentation inst) throws Exception { 157 | JAgentRunner.runAgent("com.agent.package.YourJAgentImpl", inst, agentArgs, 158 | // All jars are located into your_agent.jar 159 | InnerJarClassLoader.createForJars("jagent-impl.jar", "transformer.jar", "asm-all.jar")); 160 | } 161 | } 162 | ``` 163 | 164 | #### *pom.xml* example: 165 | Builds jar with specified *MANIFEST.MF* and 166 | uses *maven-shade-plugin* to get rid of problems with different *jagent-api*'s versions in several java agents. 167 | 168 | ```xml 169 | ... 170 | 171 | 172 | 173 | 174 | src/main/resources 175 | true 176 | 177 | 178 | 179 | 180 | 181 | maven-jar-plugin 182 | 183 | 184 | false 185 | 186 | true 187 | 188 | 189 | 192 | 193 | maven-shade-plugin 194 | 195 | 196 | package 197 | 198 | shade 199 | 200 | 201 | 202 | 203 | com.devexperts.jagent:jagent-api 204 | 205 | 206 | 207 | 208 | com.devexperts.jagent.api 209 | com.agent.package.shaded.com.devexperts.jagent.api 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | com.devexperts.jagent 222 | jagent-api 223 | 0.2 224 | 225 | 226 | ... 227 | ``` 228 | 229 | agent module 230 | ----- 231 | This module contains only `pom.xml` to build `your_agent.jar`. 232 | 233 | #### *pom.xml* example: 234 | Unpacks `core` classes and adds *transformer*, *jagent-impl* and external libraries JARs to `your_agent.jar`. 235 | These JARs are loaded via `InnerJarClassLoader`. 236 | 237 | ```xml 238 | 239 | 240 | ${agent.artifact.name} 241 | 242 | 246 | 247 | maven-dependency-plugin 248 | 249 | 250 | copy-dependencies 251 | prepare-package 252 | 253 | copy-dependencies 254 | 255 | 256 | core,jagent-api 257 | ${project.build.outputDirectory} 258 | true 259 | 260 | 261 | 262 | unpack-dependencies 263 | prepare-package 264 | 265 | unpack-dependencies 266 | 267 | 268 | core 269 | ${project.build.outputDirectory} 270 | 271 | 272 | 273 | 274 | 276 | 277 | maven-jar-plugin 278 | 279 | 280 | false 281 | 282 | true 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | com.agent.package 291 | core 292 | ${project.version} 293 | 294 | 295 | com.agent.package 296 | transformer 297 | ${project.version} 298 | 299 | 300 | ``` 301 | 302 | test module 303 | ---- 304 | Contains tests for your agent. Here is an example for unit testing. 305 | 306 | #### *pom.xml* example: 307 | Configures *maven-surefire-plugin* to run tests under yout agent. 308 | 309 | ```xml 310 | ... 311 | 312 | 313 | 314 | maven-dependency-plugin 315 | 316 | 317 | copy-sample-agent 318 | process-test-classes 319 | 320 | copy 321 | 322 | 323 | 324 | 325 | com.agent.package 326 | your_agent 327 | ${project.version} 328 | ${project.build.directory} 329 | ${agent.artifact.name}.jar 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | maven-surefire-plugin 339 | 340 | -javaagent:${agent.directory}/${agent.artifact.name}.jar 341 | 342 | 343 | 344 | ... 345 | ``` 346 | 347 | 348 | Getting support 349 | =============== 350 | If you need help, you have a question, or you need further details on how to use **JAgent**, you can refer to the following resources: 351 | 352 | * [dxlab](https://code.devexperts.com/display/DXLAB/dxLab+research) research group at Devexperts 353 | * GitHub issues: [https://github.com/Devexperts/jagent/issues]() -------------------------------------------------------------------------------- /jagent-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jagent 5 | com.devexperts.jagent 6 | 1.4 7 | 8 | 4.0.0 9 | 10 | jagent-api 11 | JAgent API 12 | 13 | -------------------------------------------------------------------------------- /jagent-api/src/main/java/com/devexperts/jagent/InnerJarClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent API 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import java.io.*; 26 | import java.net.URL; 27 | import java.net.URLClassLoader; 28 | import java.security.*; 29 | import java.security.cert.Certificate; 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | import java.util.jar.JarEntry; 33 | import java.util.jar.JarInputStream; 34 | import java.util.jar.Manifest; 35 | 36 | /** 37 | * This ClassLoader allows to load classes from URLs located inside JAR or ZIP files. 38 | */ 39 | public class InnerJarClassLoader extends URLClassLoader { 40 | private static final String CLASS_FILE_SUFFIX = ".class"; 41 | private static final int BUFFER_SIZE = 4096; 42 | 43 | /** 44 | * The context to be used for loading classes and resources. 45 | */ 46 | private final AccessControlContext acc; 47 | /** 48 | * All cached classes. 49 | */ 50 | private final Map classes = new HashMap<>(); 51 | /** 52 | * All cached classes and other resources. 53 | */ 54 | private final Map resources = new HashMap<>(); 55 | 56 | /** 57 | * Creates class loader for specified JAR files. 58 | */ 59 | public InnerJarClassLoader(URL... jars) throws IOException { 60 | super(new URL[0]); 61 | acc = AccessController.getContext(); 62 | for (URL jar : jars) 63 | cacheJar(jar); 64 | } 65 | 66 | /** 67 | * Creates {@link InnerJarClassLoader} for specified jar file urls. 68 | */ 69 | public static InnerJarClassLoader createForUrls(URL... jars) throws IOException { 70 | return new InnerJarClassLoader(jars); 71 | } 72 | 73 | /** 74 | * Creates {@link InnerJarClassLoader} for specified jar files. 75 | */ 76 | public static InnerJarClassLoader createForJars(String... jars) throws IOException { 77 | URL[] urls = new URL[jars.length]; 78 | for (int i = 0; i < jars.length; i++) 79 | urls[i] = Thread.currentThread().getContextClassLoader().getResource(jars[i]); 80 | return createForUrls(urls); 81 | } 82 | 83 | @Override 84 | protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { 85 | // First, check if the class has already been loaded 86 | Class clazz = findLoadedClass(name); 87 | if (clazz == null) { 88 | CachedClass cc = classes.get(name); 89 | if (cc != null) 90 | clazz = cc.define(); 91 | if (clazz == null) 92 | clazz = getParent().loadClass(name); 93 | } 94 | if (resolve) 95 | resolveClass(clazz); 96 | return clazz; 97 | } 98 | 99 | @Override 100 | protected Class findClass(String name) throws ClassNotFoundException { 101 | CachedClass cc = classes.get(name); 102 | if (cc != null) 103 | return cc.define(); 104 | throw new ClassNotFoundException(name); 105 | } 106 | 107 | @Override 108 | public InputStream getResourceAsStream(String name) { 109 | CachedResource resource = resources.get(name); 110 | if (resource != null) 111 | return resource.openStream(); 112 | return super.getResourceAsStream(name); 113 | } 114 | 115 | private void cacheJar(URL url) throws IOException { 116 | try (JarInputStream jarInputStream = new JarInputStream(url.openStream())) { 117 | Manifest manifest = jarInputStream.getManifest(); 118 | while (true) { 119 | JarEntry entry = jarInputStream.getNextJarEntry(); 120 | if (entry == null) { 121 | break; 122 | } 123 | if (entry.isDirectory()) { 124 | continue; 125 | } 126 | String name = entry.getName(); 127 | int size = (int) entry.getSize(); 128 | byte[] bytes; 129 | if (size > 0) { 130 | bytes = new byte[size]; 131 | for (int read = 0; read < size; ) { 132 | int rd = jarInputStream.read(bytes, read, size - read); 133 | if (rd < 0) { 134 | throw new EOFException(url.getFile() + ", " + name + ": read " + read + " of " + size); 135 | } 136 | read += rd; 137 | } 138 | } else { 139 | // Size is unknown 140 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 141 | byte[] buf = new byte[BUFFER_SIZE]; 142 | int n; 143 | while ((n = jarInputStream.read(buf)) > 0) { 144 | baos.write(buf, 0, n); 145 | } 146 | bytes = baos.toByteArray(); 147 | } 148 | if (name.endsWith(CLASS_FILE_SUFFIX)) { 149 | cacheClass(url, manifest, name, bytes, entry.getCertificates()); 150 | } else { 151 | cacheResource(url, manifest, name, bytes, entry.getCertificates()); 152 | } 153 | } 154 | } 155 | } 156 | 157 | private void cacheClass(URL url, Manifest manifest, String name, byte[] bytes, Certificate[] certificates) { 158 | CachedClass cachedClass = new CachedClass(url, manifest, name, bytes, certificates); 159 | classes.put(cachedClass.className, cachedClass); 160 | resources.put(name, cachedClass); 161 | } 162 | 163 | private void cacheResource(URL url, Manifest manifest, String name, byte[] bytes, Certificate[] certificates) { 164 | resources.put(name, new CachedResource(url, manifest, name, bytes, certificates)); 165 | } 166 | 167 | /** 168 | * Cached resource. 169 | */ 170 | private static class CachedResource { 171 | protected final URL url; 172 | protected final Manifest manifest; 173 | protected final String resourceName; 174 | protected final byte[] bytes; 175 | protected final Certificate[] certificates; 176 | 177 | private CachedResource(URL url, Manifest manifest, String resourceName, byte[] bytes, Certificate[] certificates) { 178 | this.url = url; 179 | this.manifest = manifest; 180 | this.resourceName = resourceName; 181 | this.bytes = bytes; 182 | this.certificates = certificates; 183 | } 184 | 185 | /** 186 | * Opens this resource as stream. 187 | */ 188 | public InputStream openStream() { 189 | return new ByteArrayInputStream(bytes); 190 | } 191 | } 192 | 193 | /** 194 | * Cached class. Should be defined with {@link #define()}. 195 | */ 196 | private class CachedClass extends CachedResource { 197 | protected final String className; 198 | 199 | private CachedClass(URL url, Manifest manifest, String resourceName, byte[] bytes, Certificate[] certificates) { 200 | super(url, manifest, resourceName, bytes, certificates); 201 | assert resourceName.endsWith(CLASS_FILE_SUFFIX); 202 | className = resourceName.replace('/', '.').substring(0, resourceName.length() - CLASS_FILE_SUFFIX.length()); 203 | } 204 | 205 | public Class define() { 206 | try { 207 | return AccessController.doPrivileged(new PrivilegedExceptionAction>() { 208 | public Class run() { 209 | int i = className.lastIndexOf('.'); 210 | if (i != -1) { 211 | String packageName = className.substring(0, i); 212 | Package pkg = getPackage(packageName); 213 | if (pkg == null) { 214 | if (manifest != null) { 215 | definePackage(packageName, manifest, url); 216 | } else { 217 | definePackage(packageName, null, null, null, null, null, null, null); 218 | } 219 | } 220 | } 221 | CodeSource cs = new CodeSource(url, certificates); 222 | return defineClass(className, bytes, 0, bytes.length, cs); 223 | } 224 | }, acc); 225 | } catch (PrivilegedActionException pae) { 226 | pae.printStackTrace(); 227 | throw new IllegalStateException(pae.getException()); 228 | } 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /jagent-api/src/main/java/com/devexperts/jagent/JAgentRunner.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent API 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import java.lang.instrument.Instrumentation; 26 | import java.lang.reflect.InvocationTargetException; 27 | 28 | /** 29 | * Utility class that helps you to run {@code JAgent} instance from {@code premain} method. 30 | */ 31 | public class JAgentRunner { 32 | 33 | // Utility class, no instance 34 | private JAgentRunner() { 35 | } 36 | 37 | /** 38 | * Runs agent under specified {@link ClassLoader class loader}. 39 | * 40 | * @param jagentClass agent class to be run. Should inherits {@code JAgent} class 41 | * and have {@code (Instrumentation inst, String agentArgs} constructor. 42 | * @param inst instrumentation from {@code premain} method. 43 | * @param agentArgs agent arguments from {@code premain} method. 44 | * @param classLoader {@link ClassLoader class loader} to be used for running agent. 45 | * @throws ClassNotFoundException if specified agent class isn't found. 46 | * @throws IllegalArgumentException if specified agent class has invalid format. 47 | */ 48 | public static void runAgent(String jagentClass, Instrumentation inst, String agentArgs, ClassLoader classLoader) throws ClassNotFoundException { 49 | Class agentClass = classLoader.loadClass(jagentClass); 50 | Object agent; 51 | try { 52 | try { 53 | agent = agentClass.getConstructor(Instrumentation.class, String.class).newInstance(inst, agentArgs); 54 | } catch (NoSuchMethodException | SecurityException ignored) { 55 | agent = agentClass.getMethod("create", Instrumentation.class, String.class).invoke(null, inst, agentArgs); 56 | } 57 | agentClass.getMethod("go").invoke(agent); 58 | } catch (Exception e) { 59 | throw new IllegalArgumentException("Cannot start agent " + jagentClass, e); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /jagent-impl/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jagent 5 | com.devexperts.jagent 6 | 1.4 7 | 8 | 4.0.0 9 | 10 | jagent-impl 11 | JAgent Impl 12 | 13 | 14 | 15 | org.ow2.asm 16 | asm-commons 17 | 5.0.2 18 | provided 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/CachingClassFileTransformer.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import java.io.IOException; 26 | import java.lang.instrument.ClassFileTransformer; 27 | import java.lang.instrument.IllegalClassFormatException; 28 | import java.nio.file.Files; 29 | import java.nio.file.Path; 30 | import java.nio.file.Paths; 31 | import java.security.ProtectionDomain; 32 | import java.util.zip.CRC32; 33 | import java.util.zip.Checksum; 34 | 35 | public abstract class CachingClassFileTransformer implements ClassFileTransformer { 36 | 37 | protected final Log log; 38 | private final String agentVersion; 39 | private volatile String cacheDir; 40 | private volatile String dumpDir; 41 | 42 | private final ThreadLocal checksum = new ThreadLocal() { 43 | @Override 44 | protected Checksum initialValue() { 45 | return new CRC32(); 46 | } 47 | }; 48 | 49 | protected CachingClassFileTransformer(Log log, String agentVersion) { 50 | this.log = log; 51 | this.agentVersion = agentVersion; 52 | } 53 | 54 | @Override 55 | public final byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, 56 | ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException 57 | { 58 | if (!processClass(className, loader)) 59 | return null; 60 | log.debug("Transforming ", className, " loaded by ", loader); 61 | byte[] res = getCachedOrTransform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); 62 | dumpClassIfNeeded(className, loader, res); 63 | log.debug("Transformed ", className, " loaded by ", loader); 64 | return res; 65 | } 66 | 67 | private byte[] getCachedOrTransform(ClassLoader loader, String className, Class classBeingRedefined, 68 | ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException 69 | { 70 | try { 71 | if (cacheDir == null) 72 | return transformImpl(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); 73 | try { 74 | Checksum checksum = this.checksum.get(); 75 | checksum.update(classfileBuffer, 0, classfileBuffer.length); 76 | Path cachedFilePath = Paths.get(cacheDir, agentVersion, className + "#crc32=" + checksum.getValue() + ".class"); 77 | if (Files.exists(cachedFilePath)) { 78 | log.debug("Load class ", className, " loaded by ", loader, " from cached file ", cachedFilePath); 79 | return Files.readAllBytes(cachedFilePath); 80 | } 81 | Files.createDirectories(cachedFilePath.getParent()); 82 | byte[] res = transformImpl(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); 83 | if (res == null) 84 | res = classfileBuffer; 85 | Files.write(cachedFilePath, res); 86 | log.debug("Cache class ", className, " loaded by ", loader, " to ", cachedFilePath); 87 | return res; 88 | } catch (IOException e) { 89 | log.warn("Exception on cache reading/writing ", className, " loaded by ", loader, e); 90 | return transformImpl(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); 91 | } 92 | } catch (Exception e) { 93 | log.error("Unable to transform class ", className, " loaded by ", loader, e); 94 | return null; 95 | } 96 | } 97 | 98 | private void dumpClassIfNeeded(String className, ClassLoader loader, byte[] classfileBuffer) { 99 | if (dumpDir == null) 100 | return; 101 | try { 102 | Path classPath = Paths.get(dumpDir, className + "#loaderHashCode=" 103 | + (loader != null ? loader.hashCode() : null) + ".class"); 104 | if (classPath.getParent() != null) 105 | Files.createDirectories(classPath.getParent()); 106 | Files.write(classPath, classfileBuffer); 107 | log.debug("Dump class ", className, " loaded by ", loader, " to ", classPath); 108 | } catch (Exception e) { 109 | log.warn("Unable to dump class ", className, " loaded by ", loader, e); 110 | } 111 | } 112 | 113 | protected abstract boolean processClass(String className, ClassLoader loader); 114 | 115 | protected abstract byte[] transformImpl(ClassLoader loader, String className, Class classBeingRedefined, 116 | ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException; 117 | 118 | public void setCacheDir(String cacheDir) { 119 | this.cacheDir = cacheDir; 120 | } 121 | 122 | public void setDumpDir(String dumpDir) { 123 | this.dumpDir = dumpDir; 124 | } 125 | 126 | public static CachingClassFileTransformer createFromClassFileTransformer(final ClassFileTransformer transformer, 127 | Log log, String agentVersion) 128 | { 129 | return new CachingClassFileTransformer(log, agentVersion) { 130 | 131 | @Override 132 | protected boolean processClass(String className, ClassLoader loader) { 133 | return true; 134 | } 135 | 136 | @Override 137 | protected byte[] transformImpl(ClassLoader loader, String className, Class classBeingRedefined, 138 | ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 139 | return transformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); 140 | } 141 | }; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/ClassInfo.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import org.objectweb.asm.Opcodes; 26 | 27 | public class ClassInfo { 28 | private static final ClassInfo[] EMPTY_INFOS = new ClassInfo[0]; 29 | 30 | private final int access; 31 | private final int version; 32 | private final String sourceFile; 33 | private final String internalName; 34 | private final String internalSuperName; 35 | private final String[] internalInterfaceNames; 36 | 37 | // created on first need 38 | private String className; 39 | private ClassInfo superClassInfo; 40 | private ClassInfo[] interfaceInfos; 41 | 42 | private ClassInfo(int access, int version, String sourceFile, String internalName, String internalSuperName, String[] internalInterfaceNames) { 43 | this.access = access; 44 | this.version = version; 45 | this.sourceFile = sourceFile; 46 | this.internalName = internalName; 47 | this.internalSuperName = internalSuperName; 48 | this.internalInterfaceNames = internalInterfaceNames; 49 | } 50 | 51 | public int getVersion() { 52 | return version; 53 | } 54 | 55 | public String getInternalName() { 56 | return internalName; 57 | } 58 | 59 | public String getClassName() { 60 | if (className == null) 61 | className = internalName.replace('/', '.'); 62 | return className; 63 | } 64 | 65 | public String getSourceFile() { 66 | return sourceFile; 67 | } 68 | 69 | public boolean isInterface() { 70 | return (access & Opcodes.ACC_INTERFACE) != 0; 71 | } 72 | 73 | public String getInternalSuperName() { 74 | return internalSuperName; 75 | } 76 | 77 | public String[] getInternalInterfaceNames() { 78 | return internalInterfaceNames; 79 | } 80 | 81 | // Returns null if not found or failed to load 82 | public ClassInfo getSuperclassInfo(ClassInfoCache ciCache, ClassLoader loader) { 83 | if (internalSuperName == null) 84 | return null; 85 | if (superClassInfo == null) 86 | superClassInfo = ciCache.getOrBuildClassInfo(internalSuperName, loader); 87 | return superClassInfo; 88 | } 89 | 90 | // throws RuntimeException if not found or failed to load 91 | public ClassInfo getRequiredSuperclassInfo(ClassInfoCache ciCache, ClassLoader loader) { 92 | if (internalSuperName == null) 93 | return null; 94 | if (superClassInfo == null) 95 | superClassInfo = ciCache.getOrBuildRequiredClassInfo(internalSuperName, loader); 96 | return superClassInfo; 97 | } 98 | 99 | // Returns null infos inside if not found or failed to load 100 | public ClassInfo[] getInterfaceInfos(ClassInfoCache ciCache, ClassLoader loader) { 101 | if (interfaceInfos == null) { 102 | if (internalInterfaceNames == null || internalInterfaceNames.length == 0) 103 | interfaceInfos = EMPTY_INFOS; 104 | else { 105 | int n = internalInterfaceNames.length; 106 | interfaceInfos = new ClassInfo[n]; 107 | for (int i = 0; i < n; i++) 108 | interfaceInfos[i] = ciCache.getOrBuildClassInfo(internalInterfaceNames[i], loader); 109 | } 110 | } 111 | return interfaceInfos; 112 | } 113 | 114 | // throws RuntimeException if not found or failed to load 115 | public ClassInfo[] getRequiredInterfaceInfos(ClassInfoCache ciCache, ClassLoader loader) { 116 | ClassInfo[] ii = getInterfaceInfos(ciCache, loader); 117 | for (int i = 0; i < ii.length; i++) 118 | if (ii[i] == null) 119 | ii[i] = ciCache.getOrBuildRequiredClassInfo(internalInterfaceNames[i], loader); 120 | return ii; 121 | } 122 | 123 | private boolean implementsInterface(ClassInfo that, ClassInfoCache ciCache, ClassLoader loader) { 124 | for (ClassInfo c = this; c != null; c = c.getRequiredSuperclassInfo(ciCache, loader)) { 125 | for (ClassInfo ti : c.getRequiredInterfaceInfos(ciCache, loader)) { 126 | if (ti.getInternalName().equals(that.getInternalName()) 127 | || ti.implementsInterface(that, ciCache, loader)) 128 | { 129 | return true; 130 | } 131 | } 132 | } 133 | return false; 134 | } 135 | 136 | private boolean isSubclassOf(ClassInfo that, ClassInfoCache ciCache, ClassLoader loader) { 137 | for (ClassInfo c = this; c != null; c = c.getRequiredSuperclassInfo(ciCache, loader)) { 138 | if (c.getRequiredSuperclassInfo(ciCache, loader) != null 139 | && c.getRequiredSuperclassInfo(ciCache, loader).getInternalName().equals(that.getInternalName())) 140 | { 141 | return true; 142 | } 143 | } 144 | return false; 145 | } 146 | 147 | boolean isAssignableFrom(ClassInfo that, ClassInfoCache ciCache, ClassLoader loader) { 148 | return this == that 149 | || that.isSubclassOf(this, ciCache, loader) 150 | || that.implementsInterface(this, ciCache, loader); 151 | } 152 | 153 | static class Builder { 154 | private int access; 155 | private int version; 156 | private String internalName; 157 | private String internalSuperName; 158 | private String[] internalInterfaceNames; 159 | private String sourceFile; 160 | 161 | Builder access(int access) { 162 | this.access = access; 163 | return this; 164 | } 165 | 166 | Builder version(int version) { 167 | this.version = version; 168 | return this; 169 | } 170 | 171 | Builder internalName(String internalName) { 172 | this.internalName = internalName; 173 | return this; 174 | } 175 | 176 | Builder internalSuperName(String internalSuperName) { 177 | this.internalSuperName = internalSuperName; 178 | return this; 179 | } 180 | 181 | Builder internalInterfaceNames(String[] internalInterfaceNames) { 182 | this.internalInterfaceNames = internalInterfaceNames; 183 | return this; 184 | } 185 | 186 | Builder sourceFile(String sourceFile) { 187 | this.sourceFile = sourceFile; 188 | return this; 189 | } 190 | 191 | ClassInfo build() { 192 | return new ClassInfo(access, version, sourceFile, internalName, internalSuperName, internalInterfaceNames); 193 | } 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/ClassInfoCache.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import org.objectweb.asm.ClassReader; 26 | 27 | import java.io.InputStream; 28 | import java.util.*; 29 | 30 | public class ClassInfoCache { 31 | 32 | private final Log log; 33 | 34 | // ClassLoader -> internalClassName -> ClassInfo 35 | private WeakHashMap classInfoCache = new WeakHashMap<>(); 36 | 37 | public ClassInfoCache(Log log) { 38 | this.log = log; 39 | } 40 | 41 | public synchronized ClassInfo getClassInfo(String internalClassName, ClassLoader loader) { 42 | while (true) { 43 | ClassInfo classInfo = getOrInitClassInfoMap(loader).get(internalClassName); 44 | if (classInfo != null) 45 | return classInfo; 46 | if (loader == null) 47 | return null; 48 | loader = loader.getParent(); 49 | } 50 | } 51 | 52 | // Returns null if not found or failed to load 53 | public synchronized ClassInfo getOrBuildClassInfo(String internalClassName, ClassLoader loader) { 54 | ClassInfo classInfo = getOrInitClassInfoMap(loader).get(internalClassName); 55 | if (classInfo != null) 56 | return classInfo; 57 | ClassInfoMap classInfoMap = classInfoCache.get(loader); 58 | classInfo = buildClassInfo(internalClassName, loader); 59 | if (classInfo != null) 60 | classInfoMap.put(internalClassName, classInfo); 61 | return classInfo; 62 | } 63 | 64 | // throws RuntimeException if not found or failed to load 65 | synchronized ClassInfo getOrBuildRequiredClassInfo(String internalClassName, ClassLoader loader) { 66 | ClassInfo classInfo = getOrBuildClassInfo(internalClassName, loader); 67 | if (classInfo == null) 68 | throw new RuntimeException("Cannot load class information for " + internalClassName.replace('/', '.')); 69 | return classInfo; 70 | } 71 | 72 | public synchronized ClassInfoMap getOrInitClassInfoMap(ClassLoader loader) { 73 | ClassInfoMap classInfoMap = classInfoCache.get(loader); 74 | if (classInfoMap == null) { 75 | // make sure we have parent loader's map first 76 | if (loader != null) 77 | getOrInitClassInfoMap(loader.getParent()); 78 | // at first time when class loader is discovered, tracked classes in this class loader are cached 79 | classInfoCache.put(loader, classInfoMap = new ClassInfoMap()); 80 | } 81 | return classInfoMap; 82 | } 83 | 84 | private ClassInfo buildClassInfo(String internalClassName, ClassLoader loader) { 85 | // check if parent class loader has this class info 86 | if (loader != null) { 87 | ClassInfo classInfo = getOrBuildClassInfo(internalClassName, loader.getParent()); 88 | if (classInfo != null) 89 | return classInfo; 90 | } 91 | // actually build it 92 | try { 93 | String classFileName = internalClassName + ".class"; 94 | InputStream in; 95 | if (loader == null) 96 | in = getClass().getResourceAsStream("/" + classFileName); 97 | else 98 | in = loader.getResourceAsStream(classFileName); 99 | if (in == null) 100 | return null; 101 | ClassInfoVisitor visitor = new ClassInfoVisitor(); 102 | try { 103 | ClassReader cr = new ClassReader(in); 104 | cr.accept(visitor, ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES + ClassReader.SKIP_CODE); 105 | } finally { 106 | in.close(); 107 | } 108 | return visitor.buildClassInfo(); 109 | } catch (Throwable t) { 110 | log.error("Failed to build class info for ", internalClassName, " loaded by ", loader, t); 111 | return null; 112 | } 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/ClassInfoMap.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | public class ClassInfoMap { 29 | private final Map map = new HashMap<>(); 30 | 31 | public ClassInfo get(String internalClassName) { 32 | return map.get(internalClassName); 33 | } 34 | 35 | public void put(String internalClassName, ClassInfo classInfo) { 36 | map.put(internalClassName, classInfo); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/ClassInfoVisitor.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import org.objectweb.asm.ClassVisitor; 26 | import org.objectweb.asm.Opcodes; 27 | 28 | public class ClassInfoVisitor extends ClassVisitor { 29 | private ClassInfo.Builder classInfoBuilder = new ClassInfo.Builder(); 30 | 31 | public ClassInfoVisitor() { 32 | super(Opcodes.ASM5); 33 | } 34 | 35 | @Override 36 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 37 | classInfoBuilder.version(version).access(access).internalName(name).internalSuperName(superName).internalInterfaceNames(interfaces); 38 | } 39 | 40 | @Override 41 | public void visitSource(String source, String debug) { 42 | classInfoBuilder.sourceFile(source); 43 | } 44 | 45 | public ClassInfo buildClassInfo() { 46 | return classInfoBuilder.build(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/FastByteBuffer.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | 28 | class FastByteBuffer { 29 | private byte[] buf = new byte[65536]; 30 | private int size; 31 | 32 | public boolean isEmpty() { 33 | return size == 0; 34 | } 35 | 36 | public void clear() { 37 | size = 0; 38 | } 39 | 40 | private void ensureCapacity(int capacity) { 41 | if (buf.length >= capacity) 42 | return; 43 | byte[] bytes = new byte[Math.max(buf.length + buf.length / 2, capacity)]; 44 | System.arraycopy(buf, 0, bytes, 0, size); 45 | buf = bytes; 46 | } 47 | 48 | public byte[] getBytes() { 49 | byte[] bytes = new byte[size]; 50 | System.arraycopy(buf, 0, bytes, 0, size); 51 | return bytes; 52 | } 53 | 54 | public void readFrom(InputStream is) throws IOException { 55 | size = 0; 56 | int n; 57 | while ((n = is.available()) > 0) { 58 | ensureCapacity(size + n); 59 | n = is.read(buf, size, buf.length - size); 60 | if (n <= 0) 61 | return; 62 | size += n; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/FastFmtUtil.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import java.io.*; 26 | import java.text.SimpleDateFormat; 27 | import java.util.Date; 28 | import java.util.Locale; 29 | 30 | class FastFmtUtil { 31 | private FastFmtUtil() {} // do not create 32 | 33 | private static final int TEAR_LINE_LENGTH = 120; 34 | 35 | private static final ThreadLocal TD_FMT = new ThreadLocal<>(); 36 | 37 | public static void printlnTearLine(PrintWriter out, char c) { 38 | for (int i = 0; i < TEAR_LINE_LENGTH; i++) 39 | out.print(c); 40 | out.println(); 41 | } 42 | 43 | public static void printNum(PrintWriter out, long value) { 44 | if (value < 0) { 45 | out.print('-'); 46 | value = -value; 47 | } 48 | boolean fill = false; 49 | for (long x = 1000000000000000000L; x >= 1; x /= 1000) { 50 | if (value >= x || fill) { 51 | if (fill) 52 | out.print(","); 53 | print3(out, (int)(value / x), fill); 54 | value = value % x; 55 | fill = true; 56 | } 57 | } 58 | if (!fill) 59 | out.print("0"); 60 | } 61 | 62 | public static void print3(PrintWriter out, int value, boolean fill) { 63 | if (fill || value >= 100) 64 | out.print((char)(value / 100 + '0')); 65 | print2(out, value, fill); 66 | } 67 | 68 | public static void print2(PrintWriter out, int value, boolean fill) { 69 | if (fill || value >= 10) 70 | out.print((char)(value / 10 % 10 + '0')); 71 | out.print((char)(value % 10 + '0')); 72 | } 73 | 74 | public static void printAvg(PrintWriter out, long size, long count) { 75 | out.print("(avg size "); 76 | printNum(out, Math.round((double)size / count)); 77 | out.print(" bytes)"); 78 | } 79 | 80 | public static void printNumPercent(PrintWriter out, long count, long total) { 81 | printNum(out, count); 82 | if (count > 0 && total > 0) { 83 | out.print(" ("); 84 | long pp = count * 10000 / total; 85 | printNum(out, pp / 100); 86 | out.print("."); 87 | print2(out, (int)(pp % 100), true); 88 | out.print("%)"); 89 | } 90 | } 91 | 92 | public static void printTimePeriod(PrintWriter out, long millis) { 93 | long hour = millis / (60 * 60000); 94 | int min = (int)(millis / 60000 % 60); 95 | int sec = (int)(millis / 1000 % 60); 96 | printNum(out, hour); 97 | out.print("h"); 98 | print2(out, min, true); 99 | out.print("m"); 100 | print2(out, sec, true); 101 | out.print("s"); 102 | } 103 | 104 | public static void printTimeAndDate(PrintWriter out, long millis) { 105 | TimeAndDateFormatter fmt = TD_FMT.get(); 106 | if (fmt == null) 107 | TD_FMT.set(fmt = new TimeAndDateFormatter()); 108 | fmt.print(out, millis); 109 | } 110 | 111 | private static class TimeAndDateFormatter { 112 | private static final long SECOND = 1000; 113 | private static final long MINUTE = 60 * SECOND; 114 | private static final long HOUR = 60 * MINUTE; 115 | 116 | SimpleDateFormat dayFmt = new SimpleDateFormat("yyyy-MM-dd", Locale.US); 117 | SimpleDateFormat hourFmt = new SimpleDateFormat("HH", Locale.US); 118 | SimpleDateFormat zoneFmt = new SimpleDateFormat("Z", Locale.US); 119 | long lastHourStart; 120 | String lastDay; 121 | String lastHour; 122 | String lastZone; 123 | 124 | public void print(PrintWriter out, long millis) { 125 | if (lastHourStart == 0 || millis < lastHourStart || millis >= lastHourStart + HOUR) { 126 | lastHourStart = millis / HOUR * HOUR; 127 | Date date = new Date(lastHourStart); 128 | lastDay = dayFmt.format(date); 129 | lastHour = hourFmt.format(date); 130 | lastZone = zoneFmt.format(date); 131 | } 132 | out.print(lastDay); 133 | out.print('T'); 134 | out.print(lastHour); 135 | out.print(':'); 136 | print2(out, (int)((millis / MINUTE) % 60), true); 137 | out.print(':'); 138 | print2(out, (int)((millis / SECOND) % 60), true); 139 | out.print('.'); 140 | print3(out, (int)(millis % SECOND), true); 141 | out.print(lastZone); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/FastOutputStreamWriter.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import java.io.*; 26 | 27 | class FastOutputStreamWriter extends Writer { 28 | private static final char[] HEX = "0123456789abcdef".toCharArray(); 29 | 30 | private final BufferedOutputStream out; 31 | 32 | public FastOutputStreamWriter(OutputStream out) { 33 | this.out = new BufferedOutputStream(out); 34 | } 35 | 36 | public void flush() throws IOException { 37 | out.flush(); 38 | } 39 | 40 | public void close() throws IOException { 41 | out.close(); 42 | } 43 | 44 | private void write(char c) throws IOException { 45 | if (c <= 0x7f) 46 | out.write(c); 47 | else { 48 | out.write('\\'); 49 | out.write('u'); 50 | out.write(HEX[(c >> 12) & 0xf]); 51 | out.write(HEX[(c >> 8) & 0xf]); 52 | out.write(HEX[(c >> 4) & 0xf]); 53 | out.write(HEX[c & 0xf]); 54 | } 55 | } 56 | 57 | public void write(int c) throws IOException { 58 | write((char)c); 59 | } 60 | 61 | public void write(char cbuf[], int off, int len) throws IOException { 62 | for (int i = off; i < off + len; i++) 63 | write(cbuf[i]); 64 | } 65 | 66 | public void write(String str, int off, int len) throws IOException { 67 | for (int i = off; i < off + len; i++) 68 | write(str.charAt(i)); 69 | } 70 | 71 | public Writer append(CharSequence csq) throws IOException { 72 | if (csq == null) 73 | write("null"); 74 | else 75 | append(csq, 0, csq.length()); 76 | return this; 77 | } 78 | 79 | public Writer append(CharSequence csq, int start, int end) throws IOException { 80 | if (csq == null) 81 | write("null"); 82 | else 83 | for (int i = start; i < end; i++) 84 | write(csq.charAt(i)); 85 | return this; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/FrameClassWriter.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import org.objectweb.asm.ClassWriter; 26 | 27 | import static org.objectweb.asm.Opcodes.V1_6; 28 | 29 | public class FrameClassWriter extends ClassWriter { 30 | private static final String OBJECT = "java/lang/Object"; 31 | 32 | private final ClassLoader loader; // internalClassName -> ClassInfo 33 | private final ClassInfoCache ciCache; 34 | 35 | public FrameClassWriter(ClassLoader loader, ClassInfoCache ciCache, int classVersion) { 36 | super(classVersion > V1_6 ? COMPUTE_FRAMES : COMPUTE_MAXS); 37 | this.loader = loader; 38 | this.ciCache = ciCache; 39 | } 40 | 41 | /** 42 | * The reason of overriding is to avoid ClassCircularityError (or LinkageError with "duplicate class loading" reason) 43 | * which occurs during processing of classes related to java.util.TimeZone and use cache of ClassInfo. 44 | */ 45 | @Override 46 | protected String getCommonSuperClass(String type1, String type2) { 47 | ClassInfo c = ciCache.getOrBuildRequiredClassInfo(type1, loader); 48 | ClassInfo d = ciCache.getOrBuildRequiredClassInfo(type2, loader); 49 | 50 | if (c.isAssignableFrom(d, ciCache, loader)) 51 | return type1; 52 | if (d.isAssignableFrom(c, ciCache, loader)) 53 | return type2; 54 | 55 | if (c.isInterface() || d.isInterface()) { 56 | return OBJECT; 57 | } else { 58 | do { 59 | c = c.getSuperclassInfo(ciCache, loader); 60 | } while (c != null && !c.isAssignableFrom(d, ciCache, loader)); 61 | 62 | return c == null ? OBJECT : c.getInternalName(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/JAgent.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.lang.instrument.*; 28 | import java.util.*; 29 | 30 | /** 31 | * Java agent abstraction that contains a lot of boilerplate code. 32 | * 33 | * Implementation of current class should have {@code (Instrumentation inst, String agentArgs)} 34 | * to run it via {@code JAgentRunner}. 35 | * Method {@link #go()} is executed to run agent's code. 36 | * 37 | * See {@code Sample} 38 | */ 39 | public abstract class JAgent { 40 | // Required parameters 41 | private final Instrumentation inst; 42 | private final List transformers = new ArrayList<>(); 43 | private final Log log; 44 | private final String agentName; 45 | private final String version; 46 | // Instance variables 47 | private boolean started = false; 48 | // Optional parameters 49 | private boolean redefine = false; 50 | private boolean isVerboseRedefinition = false; 51 | 52 | public JAgent(Instrumentation inst, String agentName, String version, Log log) { 53 | this.inst = inst; 54 | this.log = log; 55 | this.agentName = agentName; 56 | this.version = version; 57 | } 58 | 59 | /** 60 | * Add transformer to be used for redefining. 61 | * 62 | * @param transformer transformer to be used. 63 | */ 64 | public void addTransformer(ClassFileTransformer transformer) { 65 | checkNotStarted(); 66 | transformers.add(transformer); 67 | } 68 | 69 | /** 70 | * Add transformers to be used for redefining. 71 | * 72 | * @param transformers collection of transformers to be used. 73 | */ 74 | public void addTransformers(Collection transformers) { 75 | checkNotStarted(); 76 | this.transformers.addAll(transformers); 77 | } 78 | 79 | /** 80 | * Set to {@code true} to redefine classes one at a time instead of all together. 81 | */ 82 | public void setIsVerboseRedefinition(boolean isVerboseRedefinition) { 83 | checkNotStarted(); 84 | this.isVerboseRedefinition = isVerboseRedefinition; 85 | } 86 | 87 | /** 88 | * Set to {@code true} to enable classes redefinition. 89 | */ 90 | public void setRedefineClasses(boolean redefine) { 91 | this.redefine = redefine; 92 | } 93 | 94 | /** 95 | * Method that add transformers to instrumentation and redefines already loaded classes. 96 | * Use {@code JAgentRunner} to invoke it. 97 | */ 98 | public void go() throws Exception { 99 | started = true; 100 | log.info("Loading ", agentName, " ", version, "..."); 101 | if (redefine) { 102 | log.info("Start redefining with ", agentName); 103 | // redefine all classes loader so far 104 | for (ClassFileTransformer transformer : transformers) 105 | redefine(transformer); 106 | log.info("Done redefining with ", agentName, "."); 107 | } 108 | for (ClassFileTransformer transformer : transformers) { 109 | inst.addTransformer(transformer); 110 | } 111 | } 112 | 113 | private void redefine(ClassFileTransformer transformer) 114 | throws IllegalClassFormatException, ClassNotFoundException, UnmodifiableClassException { 115 | ArrayList classes = new ArrayList<>(); 116 | HashSet done = new HashSet<>(); 117 | FastByteBuffer buf = new FastByteBuffer(); 118 | CachingClassFileTransformer ourTransformer = transformer instanceof CachingClassFileTransformer ? 119 | (CachingClassFileTransformer) transformer : null; 120 | for (int pass = 1; ; pass++) { 121 | classes.addAll(Arrays.asList(inst.getAllLoadedClasses())); 122 | List cdl = new ArrayList<>(classes.size()); 123 | log.debug("Redefining classes pass #", pass, "..."); 124 | for (Class clazz : classes) { 125 | if (clazz.isArray()) 126 | continue; 127 | if (!done.add(clazz)) 128 | continue; 129 | String name = clazz.getName().replace('.', '/'); 130 | if (ourTransformer != null && !ourTransformer.processClass(name, clazz.getClassLoader())) 131 | continue; 132 | InputStream is = clazz.getResourceAsStream("/" + name + ".class"); 133 | buf.clear(); 134 | if (is != null) 135 | try { 136 | try { 137 | buf.readFrom(is); 138 | } finally { 139 | is.close(); 140 | } 141 | } catch (IOException e) { 142 | log.warn("Failed to read class resource: ", name, e); 143 | } 144 | if (buf.isEmpty()) { 145 | log.warn("Cannot read class resource: ", name); 146 | continue; 147 | } 148 | byte[] result = transformer.transform( 149 | clazz.getClassLoader(), name, clazz, clazz.getProtectionDomain(), buf.getBytes()); 150 | if (result != null) 151 | cdl.add(new ClassDefinition(clazz, result)); 152 | } 153 | classes.clear(); 154 | if (cdl.isEmpty()) 155 | break; // all classes were redefined 156 | log.debug("Redefining classes pass #", pass, "..."); 157 | 158 | if (isVerboseRedefinition) { 159 | for (ClassDefinition cd : cdl) { 160 | String name = cd.getDefinitionClass().getName(); 161 | log.debug("Redefining class ", name); 162 | try { 163 | inst.redefineClasses(cd); 164 | } catch (Exception e) { 165 | log.error("Failed to redefine class ", name, e); 166 | } 167 | } 168 | } else { 169 | inst.redefineClasses(cdl.toArray(new ClassDefinition[cdl.size()])); 170 | } 171 | } 172 | } 173 | 174 | private void checkNotStarted() { 175 | if (started) 176 | throw new IllegalStateException("The agent was started already"); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/JAgentUtil.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | /** 26 | * Utility class that helps to implement {@link JAgent}. 27 | */ 28 | public class JAgentUtil { 29 | 30 | /** 31 | * Returns implementation title of package for specified class. 32 | */ 33 | public static String getImplTitle(Class clazz) { 34 | return clazz.getPackage().getImplementationTitle(); 35 | } 36 | 37 | /** 38 | * Returns implementation version of package for specified class. 39 | */ 40 | public static String getImplVersion(Class clazz) { 41 | return clazz.getPackage().getImplementationVersion(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /jagent-impl/src/main/java/com/devexperts/jagent/Log.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent; 2 | 3 | /* 4 | * #%L 5 | * JAgent Impl 6 | * %% 7 | * Copyright (C) 2015 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import java.io.FileNotFoundException; 26 | import java.io.FileOutputStream; 27 | import java.io.OutputStream; 28 | import java.io.PrintWriter; 29 | 30 | /** 31 | * Lightweight logger implementation. 32 | */ 33 | public class Log { 34 | 35 | private final Level logLevel; 36 | private final String agentName; 37 | private final LogPrintWriter out; 38 | 39 | /** 40 | * Creates logger. 41 | * 42 | * @param agentName agent name to be used in header 43 | * @param level logging level 44 | * @param logFile file to which log will be recorded, pass {@code null} to use standard output stream. 45 | */ 46 | public Log(String agentName, Level level, String logFile) { 47 | this.logLevel = level; 48 | this.agentName = agentName; 49 | LogPrintWriter tempOut = new LogPrintWriter(System.out); 50 | if (logFile != null && !logFile.isEmpty()) { 51 | try { 52 | tempOut = new LogPrintWriter(new FileOutputStream(logFile)); 53 | } catch (FileNotFoundException | SecurityException e) { 54 | tempOut.println("Failed to log to file: " + e); 55 | e.printStackTrace(tempOut); 56 | } 57 | } 58 | out = tempOut; 59 | } 60 | 61 | public Level getLogLevel() { 62 | return logLevel; 63 | } 64 | 65 | public void log(Level level, Object msg) { 66 | if (level.priority >= logLevel.priority) 67 | out.println(msg); 68 | } 69 | 70 | public void log(Level level, Object msg1, Object msg2) { 71 | if (level.priority >= logLevel.priority) 72 | out.println("" + msg1 + msg2); 73 | } 74 | 75 | public void log(Level level, Object msg1, Object msg2, Object msg3) { 76 | if (level.priority >= logLevel.priority) 77 | out.println("" + msg1 + msg2 + msg3); 78 | } 79 | 80 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4) { 81 | if (level.priority >= logLevel.priority) 82 | out.println("" + msg1 + msg2 + msg3 + msg4); 83 | } 84 | 85 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) { 86 | if (level.priority >= logLevel.priority) 87 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5); 88 | } 89 | 90 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) { 91 | if (level.priority >= logLevel.priority) 92 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5 + msg6); 93 | } 94 | 95 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) { 96 | if (level.priority >= logLevel.priority) 97 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5 + msg6 + msg7); 98 | } 99 | 100 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) { 101 | if (level.priority >= logLevel.priority) 102 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5 + msg6 + msg7 + msg8); 103 | } 104 | 105 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Object msg9) { 106 | if (level.priority >= logLevel.priority) 107 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5 + msg6 + msg7 + msg8 + msg9); 108 | } 109 | 110 | public void log(Level level, Object... msgs) { 111 | if (level.priority >= logLevel.priority) { 112 | StringBuilder builder = new StringBuilder(); 113 | for (int i = 0; i < msgs.length; i++) { 114 | if (i == msgs.length - 1 && msgs[i] instanceof Throwable) { 115 | Throwable t = (Throwable) msgs[i]; 116 | t.printStackTrace(); 117 | break; 118 | } 119 | builder.append(msgs[i]); 120 | } 121 | debug(builder); 122 | } 123 | } 124 | 125 | public void log(Level level, Object msg, Throwable t) { 126 | if (level.priority >= logLevel.priority) { 127 | out.println(msg); 128 | t.printStackTrace(out); 129 | } 130 | } 131 | 132 | public void log(Level level, Object msg1, Object msg2, Throwable t) { 133 | if (level.priority >= logLevel.priority) { 134 | out.println("" + msg1 + msg2); 135 | t.printStackTrace(out); 136 | } 137 | } 138 | 139 | public void log(Level level, Object msg1, Object msg2, Object msg3, Throwable t) { 140 | if (level.priority >= logLevel.priority) { 141 | out.println("" + msg1 + msg2 + msg3); 142 | t.printStackTrace(out); 143 | } 144 | } 145 | 146 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Throwable t) { 147 | if (level.priority >= logLevel.priority) { 148 | out.println("" + msg1 + msg2 + msg3 + msg4); 149 | t.printStackTrace(out); 150 | } 151 | } 152 | 153 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Throwable t) { 154 | if (level.priority >= logLevel.priority) { 155 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5); 156 | t.printStackTrace(out); 157 | } 158 | } 159 | 160 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Throwable t) { 161 | if (level.priority >= logLevel.priority) { 162 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5 + msg6); 163 | t.printStackTrace(out); 164 | } 165 | } 166 | 167 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Throwable t) { 168 | if (level.priority >= logLevel.priority) { 169 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5 + msg6 + msg7); 170 | t.printStackTrace(out); 171 | } 172 | } 173 | 174 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Throwable t) { 175 | if (level.priority >= logLevel.priority) { 176 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5 + msg6 + msg7 + msg8); 177 | t.printStackTrace(out); 178 | } 179 | } 180 | 181 | public void log(Level level, Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Object msg9, Throwable t) { 182 | if (level.priority >= logLevel.priority) { 183 | out.println("" + msg1 + msg2 + msg3 + msg4 + msg5 + msg6 + msg7 + msg8 + msg9); 184 | t.printStackTrace(out); 185 | } 186 | } 187 | 188 | public void debug(Object msg) { 189 | log(Level.DEBUG, msg); 190 | } 191 | 192 | public void debug(Object... msgs) { 193 | log(Level.DEBUG, msgs); 194 | } 195 | 196 | public void debug(Object msg1, Object msg2) { 197 | log(Level.DEBUG, msg1, msg2); 198 | } 199 | 200 | public void debug(Object msg1, Object msg2, Object msg3) { 201 | log(Level.DEBUG, msg1, msg2, msg3); 202 | } 203 | 204 | public void debug(Object msg1, Object msg2, Object msg3, Object msg4) { 205 | log(Level.DEBUG, msg1, msg2, msg3, msg4); 206 | } 207 | 208 | public void debug(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) { 209 | log(Level.DEBUG, msg1, msg2, msg3, msg4, msg5); 210 | } 211 | 212 | public void debug(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) { 213 | log(Level.DEBUG, msg1, msg2, msg3, msg4, msg5, msg6); 214 | } 215 | 216 | public void debug(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) { 217 | log(Level.DEBUG, msg1, msg2, msg3, msg4, msg5, msg6, msg7); 218 | } 219 | 220 | public void debug(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) { 221 | log(Level.DEBUG, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8); 222 | } 223 | 224 | public void debug(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Object msg9) { 225 | log(Level.DEBUG, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, msg9); 226 | } 227 | 228 | public void info(Object msg) { 229 | log(Level.INFO, msg); 230 | } 231 | 232 | public void info(Object... msgs) { 233 | log(Level.INFO, msgs); 234 | } 235 | 236 | public void info(Object msg1, Object msg2) { 237 | log(Level.INFO, msg1, msg2); 238 | } 239 | 240 | public void info(Object msg1, Object msg2, Object msg3) { 241 | log(Level.INFO, msg1, msg2, msg3); 242 | } 243 | 244 | public void info(Object msg1, Object msg2, Object msg3, Object msg4) { 245 | log(Level.INFO, msg1, msg2, msg3, msg4); 246 | } 247 | 248 | public void info(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) { 249 | log(Level.INFO, msg1, msg2, msg3, msg4, msg5); 250 | } 251 | 252 | public void info(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) { 253 | log(Level.INFO, msg1, msg2, msg3, msg4, msg5, msg6); 254 | } 255 | 256 | public void info(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) { 257 | log(Level.INFO, msg1, msg2, msg3, msg4, msg5, msg6, msg7); 258 | } 259 | 260 | public void info(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) { 261 | log(Level.INFO, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8); 262 | } 263 | 264 | public void info(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Object msg9) { 265 | log(Level.INFO, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, msg9); 266 | } 267 | 268 | public void warn(Object msg) { 269 | log(Level.WARN, msg); 270 | } 271 | 272 | public void warn(Object... msgs) { 273 | log(Level.WARN, msgs); 274 | } 275 | 276 | public void warn(Object msg1, Object msg2) { 277 | log(Level.WARN, msg1, msg2); 278 | } 279 | 280 | public void warn(Object msg1, Object msg2, Object msg3) { 281 | log(Level.WARN, msg1, msg2, msg3); 282 | } 283 | 284 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4) { 285 | log(Level.WARN, msg1, msg2, msg3, msg4); 286 | } 287 | 288 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) { 289 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5); 290 | } 291 | 292 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) { 293 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5, msg6); 294 | } 295 | 296 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) { 297 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5, msg6, msg7); 298 | } 299 | 300 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) { 301 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8); 302 | } 303 | 304 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Object msg9) { 305 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, msg9); 306 | } 307 | 308 | public void warn(Object msg, Throwable t) { 309 | log(Level.WARN, msg, t); 310 | } 311 | 312 | public void warn(Object msg1, Object msg2, Throwable t) { 313 | log(Level.WARN, msg1, msg2, t); 314 | } 315 | 316 | public void warn(Object msg1, Object msg2, Object msg3, Throwable t) { 317 | log(Level.WARN, msg1, msg2, msg3, t); 318 | } 319 | 320 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Throwable t) { 321 | log(Level.WARN, msg1, msg2, msg3, msg4, t); 322 | } 323 | 324 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Throwable t) { 325 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5, t); 326 | } 327 | 328 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Throwable t) { 329 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5, msg6, t); 330 | } 331 | 332 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Throwable t) { 333 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5, msg6, msg7, t); 334 | } 335 | 336 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Throwable t) { 337 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, t); 338 | } 339 | 340 | public void warn(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Object msg9, Throwable t) { 341 | log(Level.WARN, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, msg9, t); 342 | } 343 | 344 | public void error(Object msg) { 345 | log(Level.ERROR, msg); 346 | } 347 | 348 | public void error(Object... msgs) { 349 | log(Level.ERROR, msgs); 350 | } 351 | 352 | public void error(Object msg1, Object msg2) { 353 | log(Level.ERROR, msg1, msg2); 354 | } 355 | 356 | public void error(Object msg1, Object msg2, Object msg3) { 357 | log(Level.ERROR, msg1, msg2, msg3); 358 | } 359 | 360 | public void error(Object msg1, Object msg2, Object msg3, Object msg4) { 361 | log(Level.ERROR, msg1, msg2, msg3, msg4); 362 | } 363 | 364 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) { 365 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5); 366 | } 367 | 368 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) { 369 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5, msg6); 370 | } 371 | 372 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) { 373 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5, msg6, msg7); 374 | } 375 | 376 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) { 377 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8); 378 | } 379 | 380 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Object msg9) { 381 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, msg9); 382 | } 383 | 384 | public void error(Object msg, Throwable t) { 385 | log(Level.ERROR, msg, t); 386 | } 387 | 388 | public void error(Object msg1, Object msg2, Throwable t) { 389 | log(Level.ERROR, msg1, msg2, t); 390 | } 391 | 392 | public void error(Object msg1, Object msg2, Object msg3, Throwable t) { 393 | log(Level.ERROR, msg1, msg2, msg3, t); 394 | } 395 | 396 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Throwable t) { 397 | log(Level.ERROR, msg1, msg2, msg3, msg4, t); 398 | } 399 | 400 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Throwable t) { 401 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5, t); 402 | } 403 | 404 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Throwable t) { 405 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5, msg6, t); 406 | } 407 | 408 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Throwable t) { 409 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5, msg6, msg7, t); 410 | } 411 | 412 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Throwable t) { 413 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, t); 414 | } 415 | 416 | public void error(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8, Object msg9, Throwable t) { 417 | log(Level.ERROR, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, msg9, t); 418 | } 419 | 420 | public enum Level { 421 | DEBUG(1), INFO(2), WARN(3), ERROR(4); 422 | 423 | private int priority; 424 | 425 | Level(int priority) { 426 | this.priority = priority; 427 | } 428 | } 429 | 430 | private class LogPrintWriter extends PrintWriter { 431 | 432 | private final OutputStream out; 433 | 434 | public LogPrintWriter(OutputStream out) { 435 | super(new FastOutputStreamWriter(out), true); 436 | this.out = out; 437 | } 438 | 439 | @Override 440 | public void println(Object x) { 441 | if (x instanceof CharSequence) 442 | println((CharSequence) x); 443 | else 444 | println(String.valueOf(x)); 445 | } 446 | 447 | @Override 448 | public void println(String x) { 449 | synchronized (out) { 450 | printHeader(); 451 | super.println(x); 452 | } 453 | } 454 | 455 | private void println(CharSequence cs) { 456 | synchronized (out) { 457 | printHeader(); 458 | for (int i = 0; i < cs.length(); i++) 459 | super.print(cs.charAt(i)); 460 | super.println(); 461 | } 462 | } 463 | 464 | private void printHeader() { 465 | FastFmtUtil.printTimeAndDate(this, System.currentTimeMillis()); 466 | print(' '); 467 | print(agentName); 468 | print(':'); 469 | print(' '); 470 | } 471 | } 472 | } 473 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.devexperts.jagent 6 | jagent 7 | 1.4 8 | pom 9 | 10 | JAgent 11 | 2015 12 | 13 | Devexperts, LLC 14 | http://www.devexperts.com/ 15 | 16 | 17 | 18 | 1.7 19 | 1.7 20 | UTF-8 21 | lgpl_v3 22 | 2016 23 | 24 | 25 | 26 | jagent-api 27 | jagent-impl 28 | sample 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-jar-plugin 38 | 2.6 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-surefire-plugin 43 | 2.18 44 | 45 | 46 | org.codehaus.mojo 47 | license-maven-plugin 48 | 1.8 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-release-plugin 53 | 2.5.2 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-javadoc-plugin 58 | 2.10.3 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-shade-plugin 63 | 2.4.3 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-deploy-plugin 68 | 2.8.2 69 | 70 | 71 | com.devexperts.bintray 72 | bintray-maven-plugin 73 | 1.3 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | maven-jar-plugin 82 | 83 | 84 | 85 | 86 | Copyright (C) ${project.inceptionYear} - ${lastCopyrightYear} Devexperts, LLC 87 | 88 | 89 | 90 | 91 | 92 | 93 | org.codehaus.mojo 94 | license-maven-plugin 95 | 96 | 97 | check-headers 98 | 99 | update-file-header 100 | 101 | process-sources 102 | 103 | 104 | 105 | 106 | maven-release-plugin 107 | 108 | release 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | release 117 | 118 | 119 | 120 | org.codehaus.mojo 121 | license-maven-plugin 122 | 123 | 124 | check-headers 125 | 126 | check-file-header 127 | 128 | package 129 | 130 | 131 | 132 | 133 | maven-source-plugin 134 | 2.4 135 | 136 | 137 | attach-sources 138 | 139 | jar 140 | 141 | 142 | 143 | 144 | 145 | maven-javadoc-plugin 146 | 147 | 148 | attach-javadoc 149 | 150 | jar 151 | 152 | 153 | 154 | 155 | 156 | com.devexperts.bintray 157 | bintray-maven-plugin 158 | 159 | bintray-devexperts 160 | https://api.bintray.com/maven/devexperts/Maven/jagent 161 | 162 | 163 | 164 | bintray-deploy 165 | 166 | deploy 167 | publish 168 | 169 | 170 | 171 | 172 | 173 | 174 | com.devexperts.jgitflow 175 | jgitflow-maven-plugin 176 | 1.0-m5.1-devexperts 177 | 178 | true 179 | 180 | jagent- 181 | 182 | true 183 | [release] 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | false 195 | 196 | bintray-devexperts 197 | bintray-plugins 198 | http://dl.bintray.com/devexperts/Maven 199 | 200 | 201 | 202 | 203 | scm:git:https://stash.in.devexperts.com/scm/dxlab/jagent.git 204 | scm:git:ssh://git@stash.in.devexperts.com:7999/dxlab/jagent.git 205 | HEAD 206 | 207 | 208 | 209 | 210 | jrc 211 | https://maven.in.devexperts.com/content/repositories/jrc 212 | 213 | 214 | jrc-snapshot 215 | https://maven.in.devexperts.com/content/repositories/jrc-snapshot 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /sample/agent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sample 5 | com.devexperts.jagent.sample 6 | 1.4 7 | 8 | 4.0.0 9 | 10 | agent 11 | Sample Agent 12 | 13 | 14 | 15 | ${agent.artifact.name} 16 | 17 | 21 | 22 | maven-dependency-plugin 23 | 24 | 25 | copy-dependencies 26 | prepare-package 27 | 28 | copy-dependencies 29 | 30 | 31 | core,jagent-api 32 | ${project.build.outputDirectory} 33 | true 34 | 35 | 36 | 37 | unpack-dependencies 38 | prepare-package 39 | 40 | unpack-dependencies 41 | 42 | 43 | core 44 | ${project.build.outputDirectory} 45 | 46 | 47 | 48 | 49 | 51 | 52 | maven-jar-plugin 53 | 54 | 55 | false 56 | 57 | true 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | com.devexperts.jagent.sample 66 | core 67 | ${project.version} 68 | 69 | 70 | com.devexperts.jagent.sample 71 | transformer 72 | ${project.version} 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /sample/core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sample 5 | com.devexperts.jagent.sample 6 | 1.4 7 | 8 | 4.0.0 9 | 10 | core 11 | Sample Core 12 | Core of Sample agent. All classes from this module will be available 13 | via system class loader at runtime. 14 | 15 | 16 | 17 | 18 | 19 | src/main/resources 20 | true 21 | 22 | 23 | 24 | 25 | 26 | maven-jar-plugin 27 | 28 | 29 | false 30 | 31 | true 32 | 33 | 34 | 37 | 38 | maven-shade-plugin 39 | 40 | 41 | package 42 | 43 | shade 44 | 45 | 46 | 47 | 48 | com.devexperts.jagent:jagent-api 49 | 50 | 51 | 52 | 53 | com.devexperts.jagent.api 54 | com.devexperts.jagent.sample.shaded.com.devexperts.jagent.api 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | com.devexperts.jagent 68 | jagent-api 69 | ${project.version} 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /sample/core/src/main/java/com/devexperts/jagent/sample/SampleAgentRunner.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent.sample; 2 | 3 | /* 4 | * #%L 5 | * Sample Core 6 | * %% 7 | * Copyright (C) 2015 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import com.devexperts.jagent.InnerJarClassLoader; 26 | import com.devexperts.jagent.JAgentRunner; 27 | 28 | import java.lang.instrument.Instrumentation; 29 | 30 | public class SampleAgentRunner { 31 | 32 | public static void premain(String agentArgs, Instrumentation inst) throws Exception { 33 | // Run "SampleAgent" using "JAgentRunner". "SampleAgent" is loaded via created "InnerJarClassLoader". 34 | JAgentRunner.runAgent("com.devexperts.jagent.sample.SampleAgent", inst, agentArgs, 35 | InnerJarClassLoader.createForJars("asm-all.jar", "jagent-impl.jar", "transformer.jar")); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sample/core/src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Premain-Class: com.devexperts.jagent.sample.SampleAgentRunner 2 | Can-Redefine-Class: true 3 | Boot-Class-Path: ${agent.artifact.name}.jar 4 | 5 | Name: com/devexperts/jagent/sample/ 6 | Implementation-Title: ${agent.artifact.name} 7 | Implementation-Version: ${project.version} 8 | Implementation-Vendor: Devexperts LLC 9 | -------------------------------------------------------------------------------- /sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jagent 5 | com.devexperts.jagent 6 | 1.4 7 | 8 | 4.0.0 9 | 10 | com.devexperts.jagent.sample 11 | sample 12 | pom 13 | 14 | Sample 15 | Sample agent which is written with JAgent framework 16 | 17 | 18 | sample 19 | true 20 | 21 | 22 | 23 | transformer 24 | core 25 | agent 26 | test 27 | 28 | 29 | -------------------------------------------------------------------------------- /sample/test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sample 5 | com.devexperts.jagent.sample 6 | 1.4 7 | 8 | 4.0.0 9 | 10 | test 11 | Sample Tests 12 | 13 | 14 | ${project.build.directory}/agents 15 | 16 | 17 | 18 | 19 | 20 | 21 | maven-dependency-plugin 22 | 23 | 24 | copy-sample-agent 25 | process-test-classes 26 | 27 | copy 28 | 29 | 30 | 31 | 32 | com.devexperts.jagent.sample 33 | agent 34 | ${project.version} 35 | ${agent.directory} 36 | ${agent.artifact.name}.jar 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | maven-surefire-plugin 46 | 47 | -javaagent:${agent.directory}/${agent.artifact.name}.jar -Dsample.delete.method=deleteMe 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | com.devexperts.jagent.sample 56 | agent 57 | ${project.version} 58 | test 59 | 60 | 61 | junit 62 | junit 63 | 4.12 64 | test 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /sample/test/src/test/java/com/devexperts/jagent/sample/test/SampleAgentTest.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent.sample.test;/* 2 | * #%L 3 | * Sample Tests 4 | * %% 5 | * Copyright (C) 2015 Devexperts, LLC 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Lesser Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Lesser Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | 23 | import org.junit.Test; 24 | 25 | // Tests that SampleAgent works alright, 26 | public class SampleAgentTest { 27 | 28 | @Test(expected = NoSuchMethodError.class) 29 | public void testDeleteMe() { 30 | deleteMe(); 31 | } 32 | 33 | @Test 34 | public void testDoNotDeleteMe() { 35 | doNotDeleteMe(); 36 | } 37 | 38 | private void deleteMe() { 39 | } 40 | 41 | private void doNotDeleteMe() { 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sample/transformer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sample 5 | com.devexperts.jagent.sample 6 | 1.4 7 | 8 | 4.0.0 9 | 10 | transformer 11 | Sample Transformer 12 | Contains code that is executable in transformation phase 13 | 14 | 15 | 16 | com.devexperts.jagent 17 | jagent-impl 18 | ${project.version} 19 | 20 | 21 | org.ow2.asm 22 | asm-all 23 | 5.0.4 24 | 25 | 26 | 27 | org.aeonbits.owner 28 | owner-java8 29 | 1.0.9 30 | 31 | 32 | -------------------------------------------------------------------------------- /sample/transformer/src/main/java/com/devexperts/jagent/sample/Configuration.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent.sample; 2 | 3 | /* 4 | * #%L 5 | * Sample Transformer 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import org.aeonbits.owner.Config; 26 | 27 | /** 28 | * Configuration via {@code OWNER} framework. 29 | */ 30 | @Config.Sources("classpath:sample.properties") 31 | public interface Configuration extends Config { 32 | 33 | @Key("sample.delete.method") 34 | String method(); 35 | 36 | @Key("sample.delete.class") 37 | String clazz(); 38 | 39 | @Key("sample.log.level") 40 | @DefaultValue("INFO") 41 | String logLevel(); 42 | 43 | @Key("sample.log.file") 44 | String logFile(); 45 | 46 | @Key("sample.redifinition.verbose") 47 | @DefaultValue("false") 48 | boolean verboseRedifinition(); 49 | 50 | @Key("sample.redefinition.enabled") 51 | @DefaultValue("false") 52 | boolean redefine(); 53 | 54 | @Key("sample.cache.dir") 55 | String cacheDir(); 56 | 57 | @Key("sample.dump.dir") 58 | String dumpDir(); 59 | } 60 | -------------------------------------------------------------------------------- /sample/transformer/src/main/java/com/devexperts/jagent/sample/MethodDeleterTransformer.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent.sample; 2 | 3 | /* 4 | * #%L 5 | * Sample Transformer 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import com.devexperts.jagent.*; 26 | import org.objectweb.asm.*; 27 | 28 | import java.lang.instrument.ClassFileTransformer; 29 | import java.lang.instrument.IllegalClassFormatException; 30 | import java.security.ProtectionDomain; 31 | 32 | class MethodDeleterTransformer implements ClassFileTransformer { 33 | 34 | private static final int ASM_API = Opcodes.ASM5; 35 | 36 | private final String classNameToProcess; 37 | private final String methodToDelete; 38 | private final ClassInfoCache ciCache; 39 | 40 | MethodDeleterTransformer(String clazz, String method, Log log) { 41 | this.classNameToProcess = clazz.replace('.', '/'); 42 | this.methodToDelete = method; 43 | this.ciCache = new ClassInfoCache(log); 44 | } 45 | 46 | @Override 47 | public byte[] transform(ClassLoader loader, String className, Class clazz, ProtectionDomain protectionDomain, 48 | byte[] classfileBuffer) throws IllegalClassFormatException { 49 | if (!classNameToProcess.equals(className)) 50 | return null; 51 | ClassReader cr = new ClassReader(classfileBuffer); 52 | ClassInfoVisitor ciVisitor = new ClassInfoVisitor(); 53 | cr.accept(ciVisitor, 0); 54 | ClassInfo cInfo = ciVisitor.buildClassInfo(); 55 | ciCache.getOrInitClassInfoMap(loader).put(className, cInfo); 56 | ClassWriter cw = new FrameClassWriter(loader, ciCache, cInfo.getVersion()); 57 | ClassVisitor transformer = new ClassVisitor(ASM_API, cw) { 58 | @Override 59 | public MethodVisitor visitMethod(int access, final String mname, final String desc, String signature, String[] exceptions) { 60 | if (methodToDelete.equals(mname)) 61 | return null; 62 | return super.visitMethod(access, mname, desc, signature, exceptions); 63 | } 64 | }; 65 | cr.accept(transformer, 0); 66 | return cw.toByteArray(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sample/transformer/src/main/java/com/devexperts/jagent/sample/SampleAgent.java: -------------------------------------------------------------------------------- 1 | package com.devexperts.jagent.sample; 2 | 3 | /* 4 | * #%L 5 | * Sample Transformer 6 | * %% 7 | * Copyright (C) 2015 - 2016 Devexperts, LLC 8 | * %% 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Lesser Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Lesser Public 20 | * License along with this program. If not, see 21 | * . 22 | * #L% 23 | */ 24 | 25 | import com.devexperts.jagent.JAgent; 26 | import com.devexperts.jagent.JAgentUtil; 27 | import com.devexperts.jagent.Log; 28 | import org.aeonbits.owner.ConfigFactory; 29 | 30 | import java.lang.instrument.Instrumentation; 31 | 32 | @SuppressWarnings("unused") 33 | public class SampleAgent extends JAgent { 34 | 35 | private SampleAgent(Instrumentation inst, String agentArgs, String agentName, String agentVersion, Log log) { 36 | super(inst, agentName, agentVersion, log); 37 | } 38 | 39 | public static SampleAgent create(Instrumentation inst, String agentArgs) { 40 | String agentName = JAgentUtil.getImplTitle(SampleAgent.class); 41 | String agentVersion = JAgentUtil.getImplVersion(SampleAgent.class); 42 | Configuration cfg = ConfigFactory.create(Configuration.class, System.getProperties()); 43 | Log.Level logLevel; 44 | try { 45 | logLevel = Log.Level.valueOf(cfg.logLevel()); 46 | } catch (IllegalArgumentException e) { 47 | System.err.println("Invalid log level: " + cfg.logLevel() + ", INFO used by default"); 48 | logLevel = Log.Level.INFO; 49 | } 50 | Log log = new Log(agentName, logLevel, cfg.logFile()); 51 | SampleAgent agent = new SampleAgent(inst, agentArgs, agentName, agentVersion, log); 52 | agent.setRedefineClasses(cfg.redefine()); 53 | agent.setIsVerboseRedefinition(cfg.verboseRedifinition()); 54 | agent.addTransformer(new MethodDeleterTransformer(cfg.clazz(), cfg.method(), log)); 55 | return agent; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sample/transformer/src/main/resources/sample.properties: -------------------------------------------------------------------------------- 1 | ### 2 | # #%L 3 | # Sample Transformer 4 | # %% 5 | # Copyright (C) 2015 - 2016 Devexperts, LLC 6 | # %% 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Lesser General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Lesser Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Lesser Public 18 | # License along with this program. If not, see 19 | # . 20 | # #L% 21 | ### 22 | sample.delete.class = com.devexperts.jagent.sample.test.SampleAgentTest 23 | # Tests that system property has higher priority 24 | sample.delete.method = XXX 25 | --------------------------------------------------------------------------------