├── .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 | [  ](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 | 
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 |
--------------------------------------------------------------------------------