methodRef,
134 | Object target,
135 | Object[] params,
136 | Throwable throwExp) {
137 | return new Advice(
138 | loader,
139 | clazzRef,
140 | methodRef,
141 | target,
142 | params,
143 | null, //returnObj
144 | throwExp,
145 | ACCESS_AFTER_THROWING
146 | );
147 | }
148 |
149 | /**
150 | * 获取Java类
151 | *
152 | * @return Java Class
153 | */
154 | public Class> getClazz() {
155 | return clazzRef.get();
156 | }
157 |
158 | /**
159 | * 获取Java方法
160 | *
161 | * @return Java Method
162 | */
163 | public GaMethod getMethod() {
164 | return methodRef.get();
165 | }
166 |
167 |
168 | /**
169 | * 本次调用是否支持阿里巴巴中间件鹰眼系统
170 | *
171 | * @return true:支持;false:不支持;
172 | */
173 | public boolean isAliEagleEyeSupport() {
174 | return AliEagleEyeUtils.isEagleEyeSupport(aliEagleEyeTraceIdRef.get());
175 | }
176 |
177 | /**
178 | * 获取本次调用阿里巴巴中间件鹰眼跟踪号
179 | *
180 | * @return 本次调用阿里巴巴中间件鹰眼跟踪号
181 | */
182 | public String getAliEagleEyeTraceId() {
183 | return aliEagleEyeTraceIdRef.get();
184 | }
185 |
186 | /**
187 | * 本次调用是否支持中间件跟踪
188 | * 在很多大公司中,会有比较多的中间件调用链路渲染技术用来记录和支撑分布式调用场景下的系统串联
189 | * 用于串联各个系统调用的一般是一个全局唯一的跟踪号,如果当前调用支持被跟踪,则返回true;
190 | *
191 | * 在阿里中,进行跟踪的调用号被称为EagleEye
192 | *
193 | * @return true:支持被跟踪;false:不支持
194 | */
195 | public boolean isTraceSupport() {
196 | return GlobalOptions.isEnableTraceId
197 | && isAliEagleEyeSupport();
198 | }
199 |
200 | /**
201 | * {{@link #getAliEagleEyeTraceId()}} 的别名,方便命令行使用
202 | *
203 | * @return 本次调用的跟踪号
204 | */
205 | public String getTraceId() {
206 | return getAliEagleEyeTraceId();
207 | }
208 |
209 | }
210 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/ClassDataSource.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core;
2 |
3 | import java.util.Collection;
4 |
5 | /**
6 | * 类据源
7 | * Created by oldmanpushcart@gmail.com on 15/12/13.
8 | */
9 | public interface ClassDataSource {
10 |
11 | /**
12 | * 获取所有可被感知的Class
13 | *
14 | * @return Class集合
15 | */
16 | Collection> allLoadedClasses();
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/Configure.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core;
2 |
3 | import com.github.ompc.greys.core.util.FeatureCodec;
4 |
5 | import java.lang.reflect.Field;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | import static com.github.ompc.greys.core.util.GaReflectUtils.*;
10 | import static com.github.ompc.greys.core.util.GaStringUtils.newString;
11 | import static java.lang.reflect.Modifier.isStatic;
12 |
13 | /**
14 | * 配置类
15 | *
16 | * @author oldmanpushcart@gmail.com
17 | */
18 | public class Configure {
19 |
20 | private String targetIp; // 目标主机IP
21 | private int targetPort; // 目标进程号
22 | private int javaPid; // 对方java进程号
23 | private int connectTimeout = 6000; // 连接超时时间(ms)
24 | private String greysCore;
25 | private String greysAgent;
26 |
27 | public String getTargetIp() {
28 | return targetIp;
29 | }
30 |
31 | public void setTargetIp(String targetIp) {
32 | this.targetIp = targetIp;
33 | }
34 |
35 | public int getTargetPort() {
36 | return targetPort;
37 | }
38 |
39 | public void setTargetPort(int targetPort) {
40 | this.targetPort = targetPort;
41 | }
42 |
43 | public int getJavaPid() {
44 | return javaPid;
45 | }
46 |
47 | public void setJavaPid(int javaPid) {
48 | this.javaPid = javaPid;
49 | }
50 |
51 | public int getConnectTimeout() {
52 | return connectTimeout;
53 | }
54 |
55 | public void setConnectTimeout(int connectTimeout) {
56 | this.connectTimeout = connectTimeout;
57 | }
58 |
59 | public String getGreysAgent() {
60 | return greysAgent;
61 | }
62 |
63 | public void setGreysAgent(String greysAgent) {
64 | this.greysAgent = greysAgent;
65 | }
66 |
67 | public String getGreysCore() {
68 | return greysCore;
69 | }
70 |
71 | public void setGreysCore(String greysCore) {
72 | this.greysCore = greysCore;
73 | }
74 |
75 | // 对象的编码解码器
76 | private final static FeatureCodec codec = new FeatureCodec(';', '=');
77 |
78 | /**
79 | * 序列化成字符串
80 | *
81 | * @return 序列化字符串
82 | */
83 | @Override
84 | public String toString() {
85 |
86 | final Map map = new HashMap();
87 | for (Field field : getFields(Configure.class)) {
88 |
89 | // 过滤掉静态类
90 | if (isStatic(field.getModifiers())) {
91 | continue;
92 | }
93 |
94 | // 非静态的才需要纳入非序列化过程
95 | try {
96 | map.put(field.getName(), newString(getValue(this, field)));
97 | } catch (Throwable t) {
98 | //
99 | }
100 |
101 | }
102 |
103 | return codec.toString(map);
104 | }
105 |
106 | /**
107 | * 反序列化字符串成对象
108 | *
109 | * @param toString 序列化字符串
110 | * @return 反序列化的对象
111 | */
112 | public static Configure toConfigure(String toString) {
113 | final Configure configure = new Configure();
114 | final Map map = codec.toMap(toString);
115 |
116 | for (Map.Entry entry : map.entrySet()) {
117 | try {
118 | final Field field = getField(Configure.class, entry.getKey());
119 | if (null != field
120 | && !isStatic(field.getModifiers())) {
121 | setValue(field, valueOf(field.getType(), entry.getValue()), configure);
122 | }
123 | } catch (Throwable t) {
124 | //
125 | }
126 | }
127 | return configure;
128 | }
129 |
130 | }
131 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/GlobalOptions.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * 全局开关
10 | * Created by oldmanpushcart@gmail.com on 15/6/4.
11 | */
12 | public class GlobalOptions {
13 |
14 | /**
15 | * 是否支持系统类
16 | * 这个开关打开之后将能代理到来自JVM的部分类,由于有非常强的安全风险可能会引起系统崩溃
17 | * 所以这个开关默认是关闭的,除非你非常了解你要做什么,否则请不要打开
18 | */
19 | @Option(level = 0,
20 | name = "unsafe",
21 | summary = "Option to support system-level class",
22 | description =
23 | "This option enables to proxy functionality of JVM classes. "
24 | + "Due to serious security risk a JVM crash is possibly be introduced. "
25 | + "Do not activate it unless you are able to manage."
26 | )
27 | public static volatile boolean isUnsafe = false;
28 |
29 | /**
30 | * 是否支持dump被增强的类
31 | * 这个开关打开这后,每次增强类的时候都将会将增强的类dump到文件中,以便于进行反编译分析
32 | */
33 | @Option(level = 1,
34 | name = "dump",
35 | summary = "Option to dump the enhanced classes",
36 | description =
37 | "This option enables the enhanced classes to be dumped to external file for further de-compilation and analysis."
38 | )
39 | public static volatile boolean isDump = false;
40 |
41 | /**
42 | * 是否支持批量增强
43 | * 这个开关打开后,每次均是批量增强类
44 | */
45 | @Option(level = 1,
46 | name = "batch-re-transform",
47 | summary = "Option to support batch reTransform Class",
48 | description = "This options enables to reTransform classes with batch mode."
49 | )
50 | public static volatile boolean isBatchReTransform = true;
51 |
52 | /**
53 | * 是否支持json格式化输出
54 | * 这个开关打开后,使用json格式输出目标对象,配合-x参数使用
55 | */
56 | @Option(level = 2,
57 | name = "json-format",
58 | summary = "Option to support JSON format of object output",
59 | description = "This option enables to format object output with JSON when -x option selected."
60 | )
61 | public static volatile boolean isUsingJson = false;
62 |
63 | /**
64 | * 是否在asm中输出
65 | */
66 | @Option(level = 1,
67 | name = "debug-for-asm",
68 | summary = "Option to print DEBUG message if ASM is involved",
69 | description = "This option enables to print DEBUG message of ASM for each method invocation."
70 | )
71 | public static volatile boolean isDebugForAsm = false;
72 |
73 | @Option(
74 | level = 1,
75 | name = "ptrace-class-matcher-lru-capacity",
76 | summary = "...",
77 | description = "..."
78 | )
79 | public static volatile int ptraceClassMatcherLruCapacity = 1024;
80 |
81 | @Option(
82 | level = 1,
83 | name = "ptrace-method-matcher-lru-capacity",
84 | summary = "...",
85 | description = "..."
86 | )
87 | public static volatile int ptraceMethodMatcherLruCapacity = 2048;
88 |
89 |
90 | @Option(
91 | level = 1,
92 | name = "session-write-queue-capacity",
93 | summary = "...",
94 | description = "..."
95 | )
96 | public static volatile int sessionWriteQueueCapacity = 2048;
97 |
98 | @Option(
99 | level = 3,
100 | name = "is-display-object-size",
101 | summary = "...",
102 | description = "..."
103 | )
104 | public static volatile boolean isDisplayObjectSize = false;
105 |
106 | @Option(
107 | level = 1,
108 | name = "is-disable-sub-class",
109 | summary = "Option to control include sub class when class matching",
110 | description = "This option disable to include sub class when matching class."
111 | )
112 | public static volatile boolean isDisableSubClass = false;
113 |
114 | // @Option(
115 | // level = 0,
116 | // name = "is-enable-fast-enhance",
117 | // summary = "Option to control enable fast enhance class",
118 | // description = "This option enable fast enhance class, maybe some class will enhance failed."
119 | // )
120 | // public static volatile boolean isEnableFastEnhance = false;
121 |
122 | // @Option(
123 | // level = 2,
124 | // name = "is-tracing-sub-class",
125 | // summary = "try to fix #94",
126 | // description = "try to fix #94"
127 | // )
128 | // public static volatile boolean isTracingSubClass = false;
129 |
130 | @Option(
131 | level = 1,
132 | name = "is-enable-trace-id",
133 | summary = "Option to control enable traceId display.",
134 | description = "This option enable to control traceId display."
135 | )
136 | public static volatile boolean isEnableTraceId = true;
137 |
138 | /**
139 | * 选项
140 | */
141 | @Retention(RetentionPolicy.RUNTIME)
142 | @Target(ElementType.FIELD)
143 | public @interface Option {
144 |
145 | /*
146 | * 选项级别,数字越小级别越高
147 | */
148 | int level();
149 |
150 | /*
151 | * 选项名称
152 | */
153 | String name();
154 |
155 | /*
156 | * 选项摘要说明
157 | */
158 | String summary();
159 |
160 | /*
161 | * 命令描述
162 | */
163 | String description();
164 |
165 | }
166 |
167 | }
168 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/GreysLauncher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core;
2 |
3 | import joptsimple.OptionParser;
4 | import joptsimple.OptionSet;
5 |
6 | import java.util.List;
7 |
8 | import static com.github.ompc.greys.core.util.GaStringUtils.getCauseMessage;
9 | import static java.io.File.separator;
10 | import static java.lang.System.getProperty;
11 |
12 | /**
13 | * Greys启动器
14 | */
15 | public class GreysLauncher {
16 |
17 | /**
18 | * greys' core jarfile
19 | */
20 | public static final String CORE_JARFILE =
21 | getProperty("user.dir") + separator + "greys-core.jar";
22 |
23 | /**
24 | * greys' agent jarfile
25 | */
26 | public static final String AGENT_JARFILE =
27 | getProperty("user.dir") + separator + "greys-agent.jar";
28 |
29 |
30 | public GreysLauncher(String[] args) throws Exception {
31 |
32 | // 解析配置文件
33 | Configure configure = analyzeConfigure(args);
34 |
35 | // 加载agent
36 | attachAgent(configure);
37 |
38 | }
39 |
40 | /*
41 | * 解析Configure
42 | */
43 | private Configure analyzeConfigure(String[] args) {
44 | final OptionParser parser = new OptionParser();
45 | parser.accepts("pid").withRequiredArg().ofType(int.class).required();
46 | parser.accepts("target").withOptionalArg().ofType(String.class);
47 | parser.accepts("multi").withOptionalArg().ofType(int.class);
48 | parser.accepts("core").withOptionalArg().ofType(String.class);
49 | parser.accepts("agent").withOptionalArg().ofType(String.class);
50 |
51 | final OptionSet os = parser.parse(args);
52 | final Configure configure = new Configure();
53 |
54 | if (os.has("target")) {
55 | final String[] strSplit = ((String) os.valueOf("target")).split(":");
56 | configure.setTargetIp(strSplit[0]);
57 | configure.setTargetPort(Integer.valueOf(strSplit[1]));
58 | }
59 |
60 | configure.setJavaPid((Integer) os.valueOf("pid"));
61 | configure.setGreysAgent((String) os.valueOf("agent"));
62 | configure.setGreysCore((String) os.valueOf("core"));
63 |
64 | return configure;
65 | }
66 |
67 | /*
68 | * 加载Agent
69 | */
70 | private void attachAgent(Configure configure) throws Exception {
71 |
72 | final ClassLoader loader = Thread.currentThread().getContextClassLoader();
73 | final Class> vmdClass = loader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
74 | final Class> vmClass = loader.loadClass("com.sun.tools.attach.VirtualMachine");
75 |
76 | Object attachVmdObj = null;
77 | for (Object obj : (List>) vmClass.getMethod("list", (Class>[]) null).invoke(null, (Object[]) null)) {
78 | if ((vmdClass.getMethod("id", (Class>[]) null).invoke(obj, (Object[]) null))
79 | .equals(Integer.toString(configure.getJavaPid()))) {
80 | attachVmdObj = obj;
81 | }
82 | }
83 |
84 | // if (null == attachVmdObj) {
85 | // // throw new IllegalArgumentException("pid:" + configure.getJavaPid() + " not existed.");
86 | // }
87 |
88 | Object vmObj = null;
89 | try {
90 | if (null == attachVmdObj) { // 使用 attach(String pid) 这种方式
91 | vmObj = vmClass.getMethod("attach", String.class).invoke(null, "" + configure.getJavaPid());
92 | } else {
93 | vmObj = vmClass.getMethod("attach", vmdClass).invoke(null, attachVmdObj);
94 | }
95 | vmClass.getMethod("loadAgent", String.class, String.class).invoke(vmObj, configure.getGreysAgent(), configure.getGreysCore() + ";" + configure.toString());
96 | } finally {
97 | if (null != vmObj) {
98 | vmClass.getMethod("detach", (Class>[]) null).invoke(vmObj, (Object[]) null);
99 | }
100 | }
101 |
102 | }
103 |
104 |
105 | public static void main(String[] args) {
106 | try {
107 | new GreysLauncher(args);
108 | } catch (Throwable t) {
109 | System.err.println("start greys failed, because : " + getCauseMessage(t));
110 | System.exit(-1);
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/TimeFragment.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * 时间片段
7 | * Created by oldmanpushcart@gmail.com on 15/10/4.
8 | */
9 | public final class TimeFragment {
10 |
11 | // 时间片段ID
12 | public final int id;
13 |
14 | // 过程ID
15 | public final int processId;
16 |
17 | // 通知数据
18 | public final Advice advice;
19 |
20 | // 记录时间戳
21 | public final Date gmtCreate;
22 |
23 | // 片段耗时
24 | public final long cost;
25 |
26 | // 片段堆栈
27 | public final String stack;
28 |
29 | /**
30 | * 时间片段构建器
31 | *
32 | * @param id 时间片段唯一ID
33 | * @param processId 时间片段执行过程ID
34 | * @param advice 时间片段所包含得通知上下文
35 | * @param gmtCreate 时间片段创建时间
36 | * @param cost 时间片段执行耗时
37 | * @param stack 时间片段触发堆栈
38 | */
39 | public TimeFragment(int id, int processId, Advice advice, Date gmtCreate, long cost, String stack) {
40 | this.id = id;
41 | this.processId = processId;
42 | this.advice = advice;
43 | this.gmtCreate = gmtCreate;
44 | this.cost = cost;
45 | this.stack = stack;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/advisor/AdviceListener.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.advisor;
2 |
3 | /**
4 | * 通知监听器
5 | * Created by oldmanpushcart@gmail.com on 15/5/17.
6 | */
7 | public interface AdviceListener {
8 |
9 | /**
10 | * 监听器创建
11 | * 监听器被注册时触发
12 | */
13 | void create();
14 |
15 | /**
16 | * 监听器销毁
17 | * 监听器被销毁时触发
18 | */
19 | void destroy();
20 |
21 | /**
22 | * 前置通知
23 | *
24 | * @param loader 类加载器
25 | * @param className 类名
26 | * @param methodName 方法名
27 | * @param methodDesc 方法描述
28 | * @param target 目标类实例
29 | * 若目标为静态方法,则为null
30 | * @param args 参数列表
31 | * @throws Throwable 通知过程出错
32 | */
33 | void before(
34 | ClassLoader loader, String className, String methodName, String methodDesc,
35 | Object target, Object[] args) throws Throwable;
36 |
37 | /**
38 | * 返回通知
39 | *
40 | * @param loader 类加载器
41 | * @param className 类名
42 | * @param methodName 方法名
43 | * @param methodDesc 方法描述
44 | * @param target 目标类实例
45 | * 若目标为静态方法,则为null
46 | * @param args 参数列表
47 | * @param returnObject 返回结果
48 | * 若为无返回值方法(void),则为null
49 | * @throws Throwable 通知过程出错
50 | */
51 | void afterReturning(
52 | ClassLoader loader, String className, String methodName, String methodDesc,
53 | Object target, Object[] args,
54 | Object returnObject) throws Throwable;
55 |
56 | /**
57 | * 异常通知
58 | *
59 | * @param loader 类加载器
60 | * @param className 类名
61 | * @param methodName 方法名
62 | * @param methodDesc 方法描述
63 | * @param target 目标类实例
64 | * 若目标为静态方法,则为null
65 | * @param args 参数列表
66 | * @param throwable 目标异常
67 | * @throws Throwable 通知过程出错
68 | */
69 | void afterThrowing(
70 | ClassLoader loader, String className, String methodName, String methodDesc,
71 | Object target, Object[] args,
72 | Throwable throwable) throws Throwable;
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/advisor/AdviceListenerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.advisor;
2 |
3 | /**
4 | * 监听适配器
5 | * Created by vlinux on 16/6/1.
6 | */
7 | public class AdviceListenerAdapter implements AdviceListener {
8 |
9 | @Override
10 | public void create() {
11 |
12 | }
13 |
14 | @Override
15 | public void destroy() {
16 |
17 | }
18 |
19 | @Override
20 | public void before(ClassLoader loader, String className, String methodName, String methodDesc, Object target, Object[] args) throws Throwable {
21 |
22 | }
23 |
24 | @Override
25 | public void afterReturning(ClassLoader loader, String className, String methodName, String methodDesc, Object target, Object[] args, Object returnObject) throws Throwable {
26 |
27 | }
28 |
29 | @Override
30 | public void afterThrowing(ClassLoader loader, String className, String methodName, String methodDesc, Object target, Object[] args, Throwable throwable) throws Throwable {
31 |
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/advisor/AdviceTracingListener.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.advisor;
2 |
3 | /**
4 | * 方法通知调用监听器
5 | * Created by oldmanpushcart@gmail.com on 15/5/27.
6 | */
7 | public interface AdviceTracingListener extends AdviceListener, InvokeTraceable {
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/advisor/InitCallback.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.advisor;
2 |
3 | /**
4 | * 初始化回调
5 | * Created by oldmanpushcart@gmail.com on 15/10/5.
6 | */
7 | public interface InitCallback {
8 |
9 | /**
10 | * 初始化
11 | *
12 | * @return 初始化值
13 | */
14 | T init();
15 |
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/advisor/InvokeTraceable.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.advisor;
2 |
3 | /**
4 | * 方法调用跟踪
5 | * 当一个方法内部调用另外一个方法时,会触发此跟踪方法
6 | * Created by oldmanpushcart@gmail.com on 15/5/27.
7 | */
8 | public interface InvokeTraceable {
9 |
10 | /**
11 | * 调用之前跟踪
12 | *
13 | * @param tracingLineNumber 跟踪行号
14 | * @param tracingClassName 调用类名
15 | * @param tracingMethodName 调用方法名
16 | * @param tracingMethodDesc 调用方法描述
17 | * @throws Throwable 通知过程出错
18 | */
19 | void invokeBeforeTracing(
20 | Integer tracingLineNumber,
21 | String tracingClassName,
22 | String tracingMethodName,
23 | String tracingMethodDesc) throws Throwable;
24 |
25 | /**
26 | * 抛异常后跟踪
27 | *
28 | * @param tracingLineNumber 跟踪行号
29 | * @param tracingClassName 调用类名
30 | * @param tracingMethodName 调用方法名
31 | * @param tracingMethodDesc 调用方法描述
32 | * @param throwException 抛出异常信息
33 | * @throws Throwable 通知过程出错
34 | */
35 | void invokeThrowTracing(
36 | Integer tracingLineNumber,
37 | String tracingClassName,
38 | String tracingMethodName,
39 | String tracingMethodDesc,
40 | String throwException) throws Throwable;
41 |
42 | /**
43 | * 调用之后跟踪
44 | *
45 | * @param tracingLineNumber 跟踪行号
46 | * @param tracingClassName 调用类名
47 | * @param tracingMethodName 调用方法名
48 | * @param tracingMethodDesc 调用方法描述
49 | * @throws Throwable 通知过程出错
50 | */
51 | void invokeAfterTracing(
52 | Integer tracingLineNumber,
53 | String tracingClassName,
54 | String tracingMethodName,
55 | String tracingMethodDesc) throws Throwable;
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/advisor/ReflectAdviceTracingListenerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.advisor;
2 |
3 | import com.github.ompc.greys.core.util.collection.GaStack;
4 | import com.github.ompc.greys.core.util.collection.ThreadUnsafeGaStack;
5 |
6 | /**
7 | * 反射版的方法通知调用通知适配器
8 | * Created by oldmanpushcart@gmail.com on 15/7/24.
9 | */
10 | public abstract class ReflectAdviceTracingListenerAdapter
11 | extends ReflectAdviceListenerAdapter implements AdviceTracingListener {
12 |
13 | // 修复问题 #78
14 | // 在当前类的调用之前JVM会先调用super., 这些步骤只能被暂时跳过
15 | // 所以这里需要记录下被掉过的信息
16 | private final ThreadLocal> skipSuperInitStackRef = new ThreadLocal>() {
17 | @Override
18 | protected GaStack initialValue() {
19 | return new ThreadUnsafeGaStack();
20 | }
21 | };
22 |
23 | // before()是否已经被调用,用于修正 #78 问题
24 | private final ThreadLocal isBeforeCalledRef = new ThreadLocal() {
25 | @Override
26 | protected Boolean initialValue() {
27 | return false;
28 | }
29 | };
30 |
31 | @Override
32 | void beforeHook() {
33 | super.beforeHook();
34 | isBeforeCalledRef.set(true);
35 | }
36 |
37 | @Override
38 | void finishHook() {
39 | super.finishHook();
40 | isBeforeCalledRef.remove();
41 | }
42 |
43 | @Override
44 | final public void invokeBeforeTracing(Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) throws Throwable {
45 |
46 | // 如果before()方法尚未被调用
47 | // 为 #78 问题所触发的情况
48 | if (!isBeforeCalledRef.get()) {
49 | skipSuperInitStackRef.get().push(new Tracing(tracingLineNumber, tracingClassName, tracingMethodName, tracingMethodDesc));
50 | return;
51 | }
52 |
53 | tracingInvokeBefore(tracingLineNumber, tracingClassName, tracingMethodName, tracingMethodDesc);
54 | }
55 |
56 |
57 | // 校验之前有多少步骤需要被跳过
58 | private void popSuperInit() throws Throwable {
59 |
60 | final GaStack stack = skipSuperInitStackRef.get();
61 | if (!stack.isEmpty()) {
62 | final Tracing tracing = stack.pop();
63 | tracingInvokeBefore(
64 | tracing.tracingLineNumber,
65 | tracing.tracingClassName,
66 | tracing.tracingMethodName,
67 | tracing.tracingMethodDesc
68 | );
69 | }
70 |
71 | }
72 |
73 | @Override
74 | final public void invokeThrowTracing(Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc, String throwException) throws Throwable {
75 | popSuperInit();
76 | tracingInvokeThrowing(tracingLineNumber, tracingClassName, tracingMethodName, tracingMethodDesc, throwException);
77 | }
78 |
79 | @Override
80 | final public void invokeAfterTracing(Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) throws Throwable {
81 | popSuperInit();
82 | tracingInvokeAfter(tracingLineNumber, tracingClassName, tracingMethodName, tracingMethodDesc);
83 | }
84 |
85 | public void tracingInvokeBefore(
86 | Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) throws Throwable {
87 |
88 | }
89 |
90 | public void tracingInvokeAfter(
91 | Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) throws Throwable {
92 |
93 | }
94 |
95 | public void tracingInvokeThrowing(
96 | Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc, String throwException) throws Throwable {
97 |
98 | }
99 |
100 |
101 | static class Tracing {
102 |
103 | private final Integer tracingLineNumber;
104 | private final String tracingClassName;
105 | private final String tracingMethodName;
106 | private final String tracingMethodDesc;
107 |
108 | Tracing(Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) {
109 | this.tracingLineNumber = tracingLineNumber;
110 | this.tracingClassName = tracingClassName;
111 | this.tracingMethodName = tracingMethodName;
112 | this.tracingMethodDesc = tracingMethodDesc;
113 | }
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/Command.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 | import com.github.ompc.greys.core.advisor.AdviceListener;
4 | import com.github.ompc.greys.core.server.Session;
5 | import com.github.ompc.greys.core.util.PointCut;
6 | import com.github.ompc.greys.core.util.affect.RowAffect;
7 |
8 | import java.lang.instrument.Instrumentation;
9 |
10 | /**
11 | * 命令
12 | * Created by oldmanpushcart@gmail.com on 15/5/18.
13 | */
14 | public interface Command {
15 |
16 | /**
17 | * 信息发送者
18 | *
19 | * @author oldmanpushcart@gmail.com
20 | */
21 | interface Printer {
22 |
23 | /**
24 | * 发送信息
25 | *
26 | * @param isF 是否结束打印
27 | * @param message 发送信息内容
28 | */
29 | Printer print(boolean isF, String message);
30 |
31 | /**
32 | * 发送信息
33 | *
34 | * @param message 发送信息内容
35 | */
36 | Printer print(String message);
37 |
38 | /**
39 | * 换行发送信息
40 | *
41 | * @param isF 是否结束打印
42 | * @param message 发送信息内容
43 | */
44 | Printer println(boolean isF, String message);
45 |
46 | /**
47 | * 换行发送信息
48 | *
49 | * @param message 发送信息内容
50 | */
51 | Printer println(String message);
52 |
53 | /**
54 | * 结束打印
55 | */
56 | void finish();
57 |
58 | }
59 |
60 | /**
61 | * 类增强
62 | */
63 | interface GetEnhancer {
64 |
65 | /**
66 | * 获取增强功能点
67 | * @return
68 | */
69 | PointCut getPointCut();
70 |
71 | /**
72 | * 获取监听器
73 | *
74 | * @return 返回监听器
75 | */
76 | AdviceListener getAdviceListener();
77 |
78 | }
79 |
80 |
81 | /**
82 | * 命令动作
83 | */
84 | interface Action {
85 |
86 |
87 | }
88 |
89 |
90 | /**
91 | * 类增强动作
92 | */
93 | interface GetEnhancerAction extends Action {
94 |
95 | /**
96 | * 执行动作
97 | *
98 | * @param session 会话
99 | * @param inst inst
100 | * @param printer 信息发送者
101 | * @return 类增强
102 | * @throws Throwable 动作执行出错
103 | */
104 | GetEnhancer action(Session session, Instrumentation inst, Printer printer) throws Throwable;
105 |
106 | }
107 |
108 | /**
109 | * 安静命令动作
110 | */
111 | interface SilentAction extends Action {
112 |
113 | /**
114 | * 安静的执行动作
115 | *
116 | * @param session 会话
117 | * @param inst inst
118 | * @param printer 信息发送者
119 | * @throws Throwable 动作执行出错
120 | */
121 | void action(Session session, Instrumentation inst, Printer printer) throws Throwable;
122 |
123 | }
124 |
125 |
126 | /**
127 | * 影响动作
128 | */
129 | interface RowAction extends Action {
130 |
131 | /**
132 | * 安静的执行动作
133 | *
134 | * @param session 会话
135 | * @param inst inst
136 | * @param printer 信息发送者
137 | * @return 影响范围
138 | * @throws Throwable 动作执行出错
139 | */
140 | RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable;
141 |
142 | }
143 |
144 | /**
145 | * 获取命令动作
146 | *
147 | * @return 返回命令所对应的命令动作
148 | */
149 | Action getAction();
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/QuitCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 | import com.github.ompc.greys.core.command.annotation.Cmd;
4 | import com.github.ompc.greys.core.server.Session;
5 |
6 | import java.lang.instrument.Instrumentation;
7 |
8 | /**
9 | * 退出命令
10 | * Created by oldmanpushcart@gmail.com on 15/5/18.
11 | */
12 | @Cmd(name = "quit", sort = 8, summary = "Quit Greys console",
13 | eg = {
14 | "quit"
15 | })
16 | public class QuitCommand implements Command {
17 |
18 | @Override
19 | public Action getAction() {
20 | return new SilentAction() {
21 |
22 | @Override
23 | public void action(Session session, Instrumentation inst, Printer printer) throws Throwable {
24 | printer.println("Bye!").finish();
25 | }
26 |
27 | };
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/ResetCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 | import com.github.ompc.greys.core.advisor.Enhancer;
4 | import com.github.ompc.greys.core.command.annotation.Cmd;
5 | import com.github.ompc.greys.core.server.Session;
6 | import com.github.ompc.greys.core.util.affect.EnhancerAffect;
7 | import com.github.ompc.greys.core.util.affect.RowAffect;
8 |
9 | import java.lang.instrument.Instrumentation;
10 |
11 | import static org.apache.commons.lang3.StringUtils.EMPTY;
12 |
13 | /**
14 | * 恢复所有增强类
15 | * Created by oldmanpushcart@gmail.com on 15/5/29.
16 | */
17 | @Cmd(name = "reset", sort = 11, summary = "Reset all the enhanced classes",
18 | eg = {
19 | "reset",
20 | "reset *List",
21 | "reset -E .*List"
22 | })
23 | public class ResetCommand implements Command {
24 |
25 | @Override
26 | public Action getAction() {
27 |
28 | return new RowAction() {
29 |
30 | @Override
31 | public RowAffect action(
32 | Session session,
33 | Instrumentation inst,
34 | Printer printer) throws Throwable {
35 |
36 | final EnhancerAffect enhancerAffect = Enhancer.reset(inst);
37 | printer.print(EMPTY).finish();
38 | return new RowAffect(enhancerAffect.cCnt());
39 | }
40 |
41 |
42 | };
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/ResourceCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 | import com.github.ompc.greys.core.command.annotation.IndexArg;
4 | import com.github.ompc.greys.core.server.Session;
5 | import com.github.ompc.greys.core.util.affect.RowAffect;
6 |
7 | import java.lang.instrument.Instrumentation;
8 | import java.net.URL;
9 | import java.util.HashMap;
10 | import java.util.HashSet;
11 | import java.util.Map;
12 | import java.util.Set;
13 |
14 | /**
15 | * 资源查询命令
16 | * Created by vlinux on 16/9/24.
17 | */
18 | //@Cmd(name = "resource", sort = 8, summary = "Finds the resource with the given name",
19 | // eg = {
20 | // "resource *spring-manager.xml"
21 | // })
22 | // TODO : 尚未想好如何输出格式内容,有待后续完善
23 | public class ResourceCommand implements Command {
24 |
25 | @IndexArg(index = 0, name = "resource-name", summary = "Path and resource of Pattern Matching")
26 | private String resourceName;
27 |
28 | @Override
29 | public Command.Action getAction() {
30 | return new Command.RowAction() {
31 |
32 | // 列出所有已经被加载的ClassLoader
33 | private Set listLoadedClassLoader(Instrumentation inst) {
34 | final Set classLoaderSet = new HashSet();
35 | for (Class> clazz : inst.getAllLoadedClasses()) {
36 | if (null == clazz) {
37 | continue;
38 | }
39 | final ClassLoader loader = clazz.getClassLoader();
40 | if (null != loader) {
41 | classLoaderSet.add(loader);
42 | }
43 | }
44 | return classLoaderSet;
45 | }
46 |
47 | private Map searchResourceMapByName(final Set classLoaderSet,
48 | final String resourceName) {
49 | final Map classLoaderResourceMapping = new HashMap();
50 | for (final ClassLoader loader : classLoaderSet) {
51 | final URL resourceURL = loader.getResource(resourceName);
52 | if (null != resourceURL
53 | && !classLoaderResourceMapping.containsKey(loader)) {
54 | classLoaderResourceMapping.put(loader, resourceURL);
55 | }
56 | }
57 | return classLoaderResourceMapping;
58 | }
59 |
60 | @Override
61 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable {
62 | final Map resourceMap = searchResourceMapByName(listLoadedClassLoader(inst), resourceName);
63 | for (final Map.Entry entry : resourceMap.entrySet()) {
64 |
65 | final String title = String.format("// RESOURCE INFORMATION FOR \"%s\" @ClassLoader:%s",
66 | entry.getValue().toString(),
67 | entry.getKey()
68 | );
69 |
70 |
71 | }
72 | return new RowAffect(resourceMap.size());
73 | }
74 | };
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/ScriptSupportCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 | /**
4 | * 脚本支持命令
5 | * Created by oldmanpushcart@gmail.com on 15/6/1.
6 | */
7 | public interface ScriptSupportCommand {
8 |
9 | /**
10 | * 输出器
11 | */
12 | interface Output {
13 |
14 | /**
15 | * 输出字符串(不换行)
16 | *
17 | * @param string 待输出字符串
18 | * @return this
19 | */
20 | Output print(String string);
21 |
22 | /**
23 | * 输出字符串(换行)
24 | *
25 | * @param string 待输出字符串
26 | * @return this
27 | */
28 | Output println(String string);
29 |
30 | /**
31 | * 结束当前脚本
32 | *
33 | * @return this
34 | */
35 | Output finish();
36 |
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/SearchClassCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 | import com.github.ompc.greys.core.command.annotation.Cmd;
4 | import com.github.ompc.greys.core.command.annotation.IndexArg;
5 | import com.github.ompc.greys.core.command.annotation.NamedArg;
6 | import com.github.ompc.greys.core.manager.ReflectManager;
7 | import com.github.ompc.greys.core.server.Session;
8 | import com.github.ompc.greys.core.textui.ext.TClassInfo;
9 | import com.github.ompc.greys.core.util.affect.RowAffect;
10 | import com.github.ompc.greys.core.util.matcher.ClassMatcher;
11 | import com.github.ompc.greys.core.util.matcher.PatternMatcher;
12 |
13 | import java.lang.instrument.Instrumentation;
14 | import java.util.Collection;
15 |
16 | import static org.apache.commons.lang3.StringUtils.EMPTY;
17 |
18 | /**
19 | * 展示类信息
20 | *
21 | * @author oldmanpushcart@gmail.com
22 | */
23 | @Cmd(name = "sc", sort = 0, summary = "Search all the classes loaded by JVM",
24 | eg = {
25 | "sc -E org\\.apache\\.commons\\.lang\\.StringUtils",
26 | "sc -d org.apache.commons.lang.StringUtils",
27 | })
28 | public class SearchClassCommand implements Command {
29 |
30 | @IndexArg(index = 0, name = "class-pattern", summary = "Path and classname of Pattern Matching")
31 | private String classPattern;
32 |
33 | @NamedArg(name = "d", summary = "Display the details of class")
34 | private boolean isDetail = false;
35 |
36 | @NamedArg(name = "f", summary = "Display all the member variables")
37 | private boolean isField = false;
38 |
39 | @NamedArg(name = "E", summary = "Enable regular expression to match (wildcard matching by default)")
40 | private boolean isRegEx = false;
41 |
42 | private final ReflectManager reflectManager = ReflectManager.Factory.getInstance();
43 |
44 | @Override
45 | public Action getAction() {
46 | return new RowAction() {
47 |
48 | @Override
49 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable {
50 |
51 | final ClassMatcher classMatcher = new ClassMatcher(new PatternMatcher(isRegEx, classPattern));
52 | final Collection> matchedClassSet = reflectManager.searchClassWithSubClass(classMatcher);
53 |
54 | // 展示类详情
55 | if (isDetail) {
56 |
57 | for (Class> clazz : matchedClassSet) {
58 | printer.println(new TClassInfo(clazz, isField).rendering());
59 | }
60 |
61 | }
62 |
63 | // 展示类该要列表
64 | else {
65 |
66 | for (Class> clazz : matchedClassSet) {
67 | printer.println(clazz.getName());
68 | }
69 |
70 | }
71 |
72 | printer.print(EMPTY).finish();
73 | return new RowAffect(matchedClassSet.size());
74 | }
75 |
76 | };
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/SearchMethodCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 | import com.github.ompc.greys.core.command.annotation.Cmd;
4 | import com.github.ompc.greys.core.command.annotation.IndexArg;
5 | import com.github.ompc.greys.core.command.annotation.NamedArg;
6 | import com.github.ompc.greys.core.manager.ReflectManager;
7 | import com.github.ompc.greys.core.server.Session;
8 | import com.github.ompc.greys.core.textui.TLadder;
9 | import com.github.ompc.greys.core.textui.TTable;
10 | import com.github.ompc.greys.core.textui.ext.TGaMethodInfo;
11 | import com.github.ompc.greys.core.util.GaMethod;
12 | import com.github.ompc.greys.core.util.affect.RowAffect;
13 | import com.github.ompc.greys.core.util.matcher.ClassMatcher;
14 | import com.github.ompc.greys.core.util.matcher.GaMethodMatcher;
15 | import com.github.ompc.greys.core.util.matcher.PatternMatcher;
16 | import com.github.ompc.greys.core.util.matcher.TrueMatcher;
17 | import org.apache.commons.lang3.StringUtils;
18 |
19 | import java.lang.instrument.Instrumentation;
20 | import java.util.Collection;
21 | import java.util.HashSet;
22 | import java.util.Set;
23 |
24 | /**
25 | * 展示方法信息
26 | *
27 | * @author oldmanpushcart@gmail.com
28 | */
29 | @Cmd(name = "sm", sort = 1, summary = "Search the method of classes loaded by JVM",
30 | eg = {
31 | "sm -Ed org\\.apache\\.commons\\.lang\\.StringUtils .*",
32 | "sm org.apache.commons.????.StringUtils *",
33 | "sm -d org.apache.commons.lang.StringUtils",
34 | "sm *String????s *"
35 | })
36 | public class SearchMethodCommand implements Command {
37 |
38 | @IndexArg(index = 0, name = "class-pattern", summary = "Path and classname of Pattern Matching")
39 | private String classPattern;
40 |
41 | @IndexArg(index = 1, isRequired = false, name = "method-pattern", summary = "Method of Pattern Matching")
42 | private String methodPattern;
43 |
44 | @NamedArg(name = "d", summary = "Display the details of method")
45 | private boolean isDetail = false;
46 |
47 | @NamedArg(name = "E", summary = "Enable regular expression to match (wildcard matching by default)")
48 | private boolean isRegEx = false;
49 |
50 | private final ReflectManager reflectManager = ReflectManager.Factory.getInstance();
51 |
52 | @Override
53 | public Action getAction() {
54 |
55 | return new RowAction() {
56 |
57 | @Override
58 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable {
59 |
60 | final RowAffect affect = new RowAffect();
61 |
62 | final ClassMatcher classMatcher = new ClassMatcher(new PatternMatcher(isRegEx, classPattern));
63 | final TTable tTable = new TTable(new TTable.ColumnDefine[]{
64 | new TTable.ColumnDefine(30, TTable.Align.LEFT),
65 | new TTable.ColumnDefine(100, TTable.Align.LEFT),
66 | })
67 | .addRow("DECLARED-CLASS", "VISIBLE-METHOD")
68 | .padding(1);
69 | for (Class> clazz : reflectManager.searchClassWithSubClass(classMatcher)) {
70 | renderingMethodSummary(tTable, clazz, affect);
71 | }
72 |
73 | printer.print(tTable.rendering()).finish();
74 | return affect;
75 | }
76 |
77 | };
78 | }
79 |
80 |
81 | /*
82 | * 构造方法名匹配
83 | * 这里修复一个网友的咨询,如果methodPattern不填,是否可以默认为匹配为所有方法
84 | * 这个是我的一个疏忽,在老的版本中不填methodPattern确实greys会自动默认进行全方法匹配
85 | * 在某一个版本的优化中我随意去掉了这个功能,导致用户行为习惯受阻,非常抱歉
86 | */
87 | private GaMethodMatcher toGaMethodMatcher() {
88 | return new GaMethodMatcher(
89 | StringUtils.isBlank(methodPattern)
90 | ? new TrueMatcher()
91 | : new PatternMatcher(isRegEx, methodPattern)
92 | );
93 | }
94 |
95 |
96 | /*
97 | * 渲染类方法摘要信息
98 | */
99 | private void renderingMethodSummary(final TTable view, final Class> clazz, final RowAffect affect) {
100 |
101 | final TLadder classLadderView = new TLadder();
102 | final GaMethodMatcher gaMethodMatcher = toGaMethodMatcher();
103 | final Collection gaMethods = reflectManager.searchClassGaMethods(clazz, gaMethodMatcher);
104 | final Set> uniqueSet = new HashSet>();
105 |
106 | classLadderView.addItem(clazz.getName());
107 | uniqueSet.add(clazz);
108 |
109 | for (GaMethod gaMethod : gaMethods) {
110 |
111 | final Class> declaringClass = gaMethod.getDeclaringClass();
112 | if (!uniqueSet.contains(declaringClass)) {
113 | classLadderView.addItem(declaringClass.getName());
114 | uniqueSet.add(declaringClass);
115 | }
116 |
117 | if (isDetail) {
118 | view.addRow(classLadderView.rendering(), new TGaMethodInfo(gaMethod).rendering());
119 | } else {
120 | view.addRow(classLadderView.rendering(), gaMethod.getName());
121 | }
122 | affect.rCnt(1);
123 |
124 | }
125 |
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/SessionCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 | import com.github.ompc.greys.core.command.annotation.Cmd;
4 | import com.github.ompc.greys.core.command.annotation.NamedArg;
5 | import com.github.ompc.greys.core.server.Session;
6 | import com.github.ompc.greys.core.textui.TTable;
7 | import com.github.ompc.greys.core.util.affect.RowAffect;
8 |
9 | import java.lang.instrument.Instrumentation;
10 | import java.nio.charset.Charset;
11 | import java.nio.charset.UnsupportedCharsetException;
12 |
13 | import static java.lang.String.format;
14 | import static org.apache.commons.lang3.StringUtils.isNotBlank;
15 |
16 | /**
17 | * 查看会话状态命令
18 | * Created by oldmanpushcart@gmail.com on 15/5/3.
19 | */
20 | @Cmd(name = "session", sort = 8, summary = "Display current session information",
21 | eg = {
22 | "session",
23 | "session -c GBK",
24 | "session -c UTF-8",
25 | "session -s true"
26 | })
27 | public class SessionCommand implements Command {
28 |
29 | @NamedArg(name = "c", hasValue = true, summary = "Modify the character of session")
30 | private String charsetString;
31 |
32 | @NamedArg(name = "s", hasValue = true, summary = "Modify the silent of session")
33 | private Boolean silent;
34 |
35 | @Override
36 | public Action getAction() {
37 | return new RowAction() {
38 |
39 | @Override
40 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable {
41 |
42 | // 设置字符集
43 | if (isNotBlank(charsetString)) {
44 |
45 | try {
46 | final Charset newCharset = Charset.forName(charsetString);
47 | final Charset beforeCharset = session.getCharset();
48 | session.setCharset(newCharset);
49 |
50 | printer.println(format("Character setValue is modified. [%s] -> [%s]",
51 | beforeCharset,
52 | newCharset))
53 | .finish();
54 |
55 | } catch (UnsupportedCharsetException e) {
56 | printer.println(format("Desupported character setValue : \"%s\"", charsetString)).finish();
57 | }
58 |
59 | } else {
60 | printer.print(sessionToString(session)).finish();
61 | }
62 |
63 | // 设置会话静默
64 | if (null != silent) {
65 | final boolean beforeSilent = session.isSilent();
66 | session.setSilent(silent);
67 | printer.println(format("Silent setValue is modified. [%s] -> [%s]",
68 | beforeSilent,
69 | session.isSilent()))
70 | .finish();
71 | }
72 |
73 | return new RowAffect(1);
74 | }
75 |
76 | };
77 | }
78 |
79 | /*
80 | * 会话详情
81 | */
82 | private String sessionToString(Session session) {
83 |
84 | return new TTable(new TTable.ColumnDefine[]{
85 | new TTable.ColumnDefine(TTable.Align.RIGHT),
86 | new TTable.ColumnDefine(TTable.Align.LEFT)
87 | })
88 | .addRow("JAVA_PID", session.getJavaPid())
89 | .addRow("SESSION_ID", session.getSessionId())
90 | .addRow("DURATION", session.getSessionDuration())
91 | .addRow("SILENT", session.isSilent())
92 | .addRow("CHARSET", session.getCharset())
93 | .addRow("PROMPT", session.getPrompt())
94 | .addRow("FROM", session.getSocketChannel().socket().getRemoteSocketAddress())
95 | .addRow("TO", session.getSocketChannel().socket().getLocalSocketAddress())
96 | .padding(1)
97 | .rendering();
98 |
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/ShutdownCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 | import com.github.ompc.greys.core.advisor.Enhancer;
4 | import com.github.ompc.greys.core.command.annotation.Cmd;
5 | import com.github.ompc.greys.core.manager.ReflectManager;
6 | import com.github.ompc.greys.core.server.Session;
7 | import com.github.ompc.greys.core.util.LogUtil;
8 | import com.github.ompc.greys.core.util.affect.EnhancerAffect;
9 | import com.github.ompc.greys.core.util.affect.RowAffect;
10 | import com.github.ompc.greys.core.util.matcher.ClassMatcher;
11 | import com.github.ompc.greys.core.util.matcher.PatternMatcher;
12 | import org.slf4j.Logger;
13 |
14 | import java.lang.instrument.Instrumentation;
15 | import java.lang.reflect.InvocationTargetException;
16 | import java.lang.reflect.Method;
17 |
18 | import static com.github.ompc.greys.core.util.matcher.PatternMatcher.Strategy.WILDCARD;
19 | import static org.apache.commons.lang3.reflect.FieldUtils.getField;
20 |
21 | /**
22 | * 关闭命令
23 | * Created by oldmanpushcart@gmail.com on 14/10/23.
24 | */
25 | @Cmd(name = "shutdown", sort = 11, summary = "Shut down Greys server and exit the console",
26 | eg = {
27 | "shutdown"
28 | })
29 | public class ShutdownCommand implements Command {
30 |
31 | private final Logger logger = LogUtil.getLogger();
32 | private final ReflectManager reflectManager = ReflectManager.Factory.getInstance();
33 |
34 | /*
35 | * 从GreysClassLoader中加载Spy
36 | */
37 | private Class> loadSpyClassFromGreysClassLoader(final ClassLoader greysClassLoader, final String spyClassName) {
38 | try {
39 | return greysClassLoader.loadClass(spyClassName);
40 | } catch (ClassNotFoundException e) {
41 | logger.warn("Spy load failed from GreysClassLoader, that is impossible!", e);
42 | return null;
43 | }
44 | }
45 |
46 | /*
47 | * 重置agent的greys
48 | * 让下载重新加载greys的时候能重新初始化ClassLoader
49 | */
50 | private void reset() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
51 | // 从GreysClassLoader中加载Spy
52 | final Class> spyClassFromGreysClassLoader = loadSpyClassFromGreysClassLoader(
53 | ShutdownCommand.class.getClassLoader(),
54 | "com.github.ompc.greys.agent.Spy"
55 | );
56 | if (null != spyClassFromGreysClassLoader) {
57 |
58 | // 重置整个greys
59 | final Method agentResetMethod = (Method) getField(spyClassFromGreysClassLoader, "AGENT_RESET_METHOD").get(null);
60 | agentResetMethod.invoke(null);
61 |
62 | }
63 | }
64 |
65 | /*
66 | * 重置所有已经加载到JVM的Spy
67 | */
68 | private void cleanSpy() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
69 | for (final Class> spyClass : reflectManager.searchClass(new ClassMatcher(new PatternMatcher(WILDCARD, "com.github.ompc.greys.agent.Spy")))) {
70 | final Method cleanMethod = spyClass.getMethod("clean");
71 | cleanMethod.invoke(null);
72 | }
73 | }
74 |
75 | @Override
76 | public Action getAction() {
77 | return new RowAction() {
78 | @Override
79 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable {
80 |
81 | // 退出之前需要重置所有的增强类
82 | // 重置之前增强的类
83 | final EnhancerAffect enhancerAffect = Enhancer.reset(inst);
84 |
85 | // reset for agent ClassLoader
86 | reset();
87 |
88 | // cleanSpy the spy
89 | cleanSpy();
90 |
91 | printer.println("Greys Server is shut down.").finish();
92 | return new RowAffect(enhancerAffect.cCnt());
93 | }
94 |
95 | };
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/VersionCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command;
2 |
3 |
4 | import com.github.ompc.greys.core.command.annotation.Cmd;
5 | import com.github.ompc.greys.core.util.affect.RowAffect;
6 | import com.github.ompc.greys.core.server.Session;
7 |
8 | import java.lang.instrument.Instrumentation;
9 |
10 | import static com.github.ompc.greys.core.util.GaStringUtils.getLogo;
11 |
12 | /**
13 | * 输出版本
14 | *
15 | * @author oldmanpushcart@gmail.com
16 | */
17 | @Cmd(name = "version", sort = 9, summary = "Display Greys version",
18 | eg = {
19 | "version"
20 | })
21 | public class VersionCommand implements Command {
22 |
23 | @Override
24 | public Action getAction() {
25 | return new RowAction() {
26 |
27 | @Override
28 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable {
29 | printer.print(getLogo()).finish();
30 | return new RowAffect(1);
31 | }
32 |
33 | };
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/annotation/Cmd.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * 指令集
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(ElementType.TYPE)
13 | public @interface Cmd {
14 |
15 | /**
16 | * 指定命令的名称
17 | *
18 | * @return 返回命令的名称
19 | */
20 | String name();
21 |
22 | /**
23 | * 指定命令的解释
24 | *
25 | * @return 返回命令的解释
26 | */
27 | String summary();
28 |
29 | /**
30 | * 例子
31 | *
32 | * @return 返回命令的例子
33 | */
34 | String[] eg() default {};
35 |
36 | /**
37 | * 排序,在help命令中
38 | *
39 | * @return 返回命令在目录中的排序
40 | */
41 | int sort() default 0;
42 |
43 | /**
44 | * 是否hacking命令
45 | * hacking命令是给开发人员进行命令调试的隐藏命令
46 | * 由于不需要让普通用户感知,所以不需要在help命令中展示
47 | * 也不会对这个命令是否在下个版本进行兼容
48 | *
49 | * @return true/false
50 | */
51 | boolean isHacking() default false;
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/annotation/IndexArg.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * 指令位置参数
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(ElementType.FIELD)
13 | public @interface IndexArg {
14 |
15 | /**
16 | * 参数在命令中的位置
17 | *
18 | * @return 参数在命令中的位置
19 | */
20 | int index();
21 |
22 | /**
23 | * 参数名称
24 | *
25 | * @return 参数名称
26 | */
27 | String name();
28 |
29 | /**
30 | * 参数摘要
31 | *
32 | * @return 参数摘要
33 | */
34 | String summary() default "";
35 |
36 | /**
37 | * 更详细的参数注释
38 | *
39 | * @return 更详细的参数注释
40 | */
41 | String description() default "";
42 |
43 | /**
44 | * 是否必填
45 | *
46 | * @return 是否必填
47 | */
48 | boolean isRequired() default true;
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/annotation/NamedArg.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * 指令命名参数
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(ElementType.FIELD)
13 | public @interface NamedArg {
14 |
15 | /**
16 | * 参数在命令中的命名
17 | *
18 | * @return 参数在命令中的命名
19 | */
20 | String name();
21 |
22 | /**
23 | * 参数摘要
24 | *
25 | * @return 参数摘要
26 | */
27 | String summary() default "";
28 |
29 | /**
30 | * 更详细的参数注释
31 | *
32 | * @return 更详细的参数注释
33 | */
34 | String description() default "";
35 |
36 | /**
37 | * 是否有值
38 | *
39 | * @return 是否有值
40 | */
41 | boolean hasValue() default false;
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/command/hacking/ThanksCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.command.hacking;
2 |
3 | import com.github.ompc.greys.core.command.Command;
4 | import com.github.ompc.greys.core.command.annotation.Cmd;
5 | import com.github.ompc.greys.core.server.Session;
6 | import org.apache.commons.io.IOUtils;
7 |
8 | import java.lang.instrument.Instrumentation;
9 |
10 | /**
11 | * 工具介绍
12 | * 感谢
13 | * Created by oldmanpushcart@gmail.com on 15/9/1.
14 | */
15 | @Cmd(isHacking = true, name = "thanks", summary = "Thanks",
16 | eg = {
17 | "thanks"
18 | }
19 | )
20 | public class ThanksCommand implements Command {
21 |
22 | @Override
23 | public Action getAction() {
24 | return new SilentAction() {
25 |
26 | @Override
27 | public void action(Session session, Instrumentation inst, Printer printer) throws Throwable {
28 |
29 | printer.println(IOUtils.toString(getClass().getResourceAsStream("/com/github/ompc/greys/core/res/thanks.txt"))).finish();
30 |
31 | }
32 | };
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/exception/CommandException.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.exception;
2 |
3 | /**
4 | * 命令执行错误
5 | * Created by oldmanpushcart@gmail.com on 15/5/2.
6 | */
7 | public class CommandException extends Exception {
8 |
9 | private final String command;
10 |
11 |
12 | public CommandException(String command) {
13 | this.command = command;
14 | }
15 |
16 | public CommandException(String command, Throwable cause) {
17 | super(cause);
18 | this.command = command;
19 | }
20 |
21 | /**
22 | * 获取出错命令
23 | *
24 | * @return 命令名称
25 | */
26 | public String getCommand() {
27 | return command;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/exception/CommandInitializationException.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.exception;
2 |
3 | /**
4 | * 命令初始化出错
5 | */
6 | public class CommandInitializationException extends CommandException {
7 |
8 | public CommandInitializationException(String command, Throwable cause) {
9 | super(command, cause);
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/exception/CommandNotFoundException.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.exception;
2 |
3 | /**
4 | * 命令不存在
5 | */
6 | public class CommandNotFoundException extends CommandException {
7 |
8 | public CommandNotFoundException(String command) {
9 | super(command);
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/exception/ExpressException.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.exception;
2 |
3 | /**
4 | * 表达式异常
5 | * Created by oldmanpushcart@gmail.com on 15/5/20.
6 | */
7 | public class ExpressException extends Exception {
8 |
9 | private final String express;
10 |
11 | /**
12 | * 表达式异常
13 | *
14 | * @param express 原始表达式
15 | * @param cause 异常原因
16 | */
17 | public ExpressException(String express, Throwable cause) {
18 | super(cause);
19 | this.express = express;
20 | }
21 |
22 | /**
23 | * 获取表达式
24 | *
25 | * @return 返回出问题的表达式
26 | */
27 | public String getExpress() {
28 | return express;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/exception/GaExecuteException.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.exception;
2 |
3 | /**
4 | * Greys 执行异常
5 | * Created by oldmanpushcart@gmail.com on 15/5/3.
6 | */
7 | public class GaExecuteException extends Exception {
8 |
9 | public GaExecuteException(String message, Throwable cause) {
10 | super(message, cause);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/exception/UnCaughtException.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.exception;
2 |
3 | /**
4 | * 未捕获异常
5 | * 用来封装不希望抛出的异常
6 | * Created by vlinux on 16/5/21.
7 | */
8 | public class UnCaughtException extends RuntimeException {
9 |
10 | public UnCaughtException(Throwable cause) {
11 | super(cause);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/manager/ReflectManager.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.manager;
2 |
3 | import com.github.ompc.greys.core.ClassDataSource;
4 | import com.github.ompc.greys.core.manager.impl.DefaultReflectManager;
5 | import com.github.ompc.greys.core.util.GaMethod;
6 | import com.github.ompc.greys.core.util.matcher.Matcher;
7 |
8 | import java.util.Collection;
9 |
10 | /**
11 | * 反射操作管理类
12 | * Created by oldmanpushcart@gmail.com on 15/11/1.
13 | */
14 | public interface ReflectManager {
15 |
16 | /**
17 | * 搜索类
18 | *
19 | * @param classMatcher 类匹配
20 | * @return 返回匹配的类集合
21 | */
22 | Collection> searchClass(Matcher> classMatcher);
23 |
24 | /**
25 | * 搜索目标类的所有子类
26 | *
27 | * @param targetClass 目标类
28 | * @return 返回匹配的类集合
29 | */
30 | Collection> searchSubClass(Class> targetClass);
31 |
32 | /**
33 | * 搜索类极其子类
34 | *
35 | * @param classMatcher 类匹配
36 | * @return 返回匹配的类集合
37 | */
38 | Collection> searchClassWithSubClass(Matcher> classMatcher);
39 |
40 | /**
41 | * 搜索目标类的所有可见匹配方法
42 | *
43 | * @param targetClass 目标类
44 | * @param gaMethodMatcher 方法匹配
45 | * @return 返回匹配的方法集合
46 | */
47 | Collection searchClassGaMethods(Class> targetClass, Matcher gaMethodMatcher);
48 |
49 | class Factory {
50 |
51 | private volatile static ReflectManager instance = null;
52 |
53 | public static ReflectManager initInstance(final ClassDataSource classDataSource) {
54 | if (null == instance) {
55 | synchronized (ReflectManager.class) {
56 | if (null == instance) {
57 | instance = new DefaultReflectManager(classDataSource);
58 | }
59 | }
60 | }
61 | return instance;
62 | }
63 |
64 | public static ReflectManager getInstance() {
65 | if (null == instance) {
66 | synchronized (ReflectManager.class) {
67 | if (null == instance) {
68 | throw new IllegalStateException("not init yet.");
69 | }
70 | }
71 | }
72 | return instance;
73 | }
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/manager/TimeFragmentManager.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.manager;
2 |
3 | import com.github.ompc.greys.core.Advice;
4 | import com.github.ompc.greys.core.TimeFragment;
5 | import com.github.ompc.greys.core.manager.impl.DefaultTimeFragmentManager;
6 |
7 | import java.util.ArrayList;
8 | import java.util.Date;
9 |
10 | /**
11 | * 时间片段管理
12 | * Created by oldmanpushcart@gmail.com on 15/10/3.
13 | */
14 | public interface TimeFragmentManager {
15 |
16 |
17 | /**
18 | * 生成全局过程ID
19 | *
20 | * @return 过程ID
21 | */
22 | int generateProcessId();
23 |
24 | /**
25 | * 追加时间片段
26 | *
27 | * @param processId 过程ID
28 | * @param advice 通知数据
29 | * @param gmtCreate 记录时间戳
30 | * @param cost 片段耗时
31 | * @param stack 片段堆栈
32 | * @return 时间片段
33 | */
34 | TimeFragment append(int processId, Advice advice, Date gmtCreate, long cost, String stack);
35 |
36 | /**
37 | * 列出所有时间碎片
38 | *
39 | * @return 时间碎片列表
40 | */
41 | ArrayList list();
42 |
43 | /**
44 | * 搜索碎片内容
45 | *
46 | * @param express 搜索表达式
47 | * @return 搜索时间碎片集合
48 | */
49 | ArrayList search(String express);
50 |
51 | /**
52 | * 根据ID获取时间碎片
53 | *
54 | * @param id 时间碎片ID
55 | * @return 时间碎片
56 | */
57 | TimeFragment get(int id);
58 |
59 | /**
60 | * 根据ID删除时间碎片
61 | *
62 | * @param id 时间碎片ID
63 | * @return 被的时间碎片;若时间碎片不存在返回null
64 | */
65 | TimeFragment delete(int id);
66 |
67 | /**
68 | * 清除所有的时间碎片
69 | *
70 | * @return 清除的时间碎片数量
71 | */
72 | int clean();
73 |
74 | /**
75 | * 工厂
76 | */
77 | class Factory {
78 |
79 | private static volatile TimeFragmentManager instance = null;
80 |
81 | public static TimeFragmentManager getInstance() {
82 | if (null == instance) {
83 | synchronized (TimeFragmentManager.class) {
84 | if (instance == null) {
85 | instance = new DefaultTimeFragmentManager();
86 | }
87 | }
88 | }
89 | return instance;
90 | }
91 |
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/manager/impl/DefaultReflectManager.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.manager.impl;
2 |
3 | import com.github.ompc.greys.core.ClassDataSource;
4 | import com.github.ompc.greys.core.GlobalOptions;
5 | import com.github.ompc.greys.core.manager.ReflectManager;
6 | import com.github.ompc.greys.core.util.GaCheckUtils;
7 | import com.github.ompc.greys.core.util.GaMethod;
8 | import com.github.ompc.greys.core.util.matcher.Matcher;
9 |
10 | import java.lang.reflect.Constructor;
11 | import java.lang.reflect.Method;
12 | import java.lang.reflect.Modifier;
13 | import java.util.Collection;
14 | import java.util.Iterator;
15 | import java.util.LinkedHashSet;
16 | import java.util.Set;
17 |
18 | import static com.github.ompc.greys.core.util.GaReflectUtils.recGetSuperClass;
19 |
20 | /**
21 | * 默认反射操作管理类实现
22 | * Created by vlinux on 15/11/1.
23 | */
24 | public class DefaultReflectManager implements ReflectManager {
25 |
26 | private final ClassDataSource classDataSource;
27 |
28 | public DefaultReflectManager(ClassDataSource classDataSource) {
29 | this.classDataSource = classDataSource;
30 | }
31 |
32 | @Override
33 | public Collection> searchClass(final Matcher> classMatcher) {
34 | final Set> classSet = new LinkedHashSet>();
35 | for (Class> clazz : classDataSource.allLoadedClasses()) {
36 | if (classMatcher.matching(clazz)) {
37 | classSet.add(clazz);
38 | }
39 | }
40 | return classSet;
41 | }
42 |
43 | @Override
44 | public Collection> searchSubClass(final Class> targetClass) {
45 | final Set> classSet = new LinkedHashSet>();
46 | for (Class> clazz : classDataSource.allLoadedClasses()) {
47 | if (!clazz.equals(targetClass)
48 | && targetClass.isAssignableFrom(clazz)) {
49 | classSet.add(clazz);
50 | }
51 | }
52 | return classSet;
53 | }
54 |
55 | @Override
56 | public Collection> searchClassWithSubClass(Matcher> classMatcher) {
57 | final Set> matchedClassSet = new LinkedHashSet>();
58 |
59 | // 搜索所有匹配器需求
60 | // 搜索当前匹配器所匹配的类
61 | for (Class> matchedClass : searchClass(classMatcher)) {
62 |
63 | // 首先添加自己
64 | matchedClassSet.add(matchedClass);
65 |
66 | if (!GlobalOptions.isDisableSubClass) {
67 | // 继续搜索子类
68 | matchedClassSet.addAll(searchSubClass(matchedClass));
69 | }
70 |
71 | }
72 | return matchedClassSet;
73 | }
74 |
75 |
76 | /**
77 | * 返回类中的所有可见方法
78 | * 所谓可见方法的定义是开发在类中可以直接通过Java语法继承关系感知到的方法
79 | *
80 | * @param clazz 目标类
81 | * @return 类的所有可见方法
82 | */
83 | private Set listVisualMethod(final Class> clazz) {
84 | final Set methodSet = new LinkedHashSet();
85 |
86 | // 首先查出当前类所声明的所有方法
87 | final Method[] classDeclaredMethodArray = clazz.getDeclaredMethods();
88 | if (null != classDeclaredMethodArray) {
89 | for (Method declaredMethod : classDeclaredMethodArray) {
90 | methodSet.add(declaredMethod);
91 | }
92 | }
93 |
94 | // 查出当前类所有的父类
95 | final Collection> superClassSet = recGetSuperClass(clazz);
96 |
97 | // 查出所有父类的可见方法
98 | for (Class> superClass : superClassSet) {
99 | final Method[] superClassDeclaredMethodArray = superClass.getDeclaredMethods();
100 | if (null != superClassDeclaredMethodArray) {
101 | for (Method superClassDeclaredMethod : superClassDeclaredMethodArray) {
102 |
103 | final int modifier = superClassDeclaredMethod.getModifiers();
104 |
105 | // 私有方法可以过滤掉
106 | if (Modifier.isPrivate(modifier)) {
107 | continue;
108 | }
109 |
110 | // public & protected 这两种情况是可以通过继承可见
111 | // 所以放行
112 | else if (Modifier.isPublic(modifier)
113 | || Modifier.isProtected(modifier)) {
114 | methodSet.add(superClassDeclaredMethod);
115 | }
116 |
117 | // 剩下的情况只剩下默认, 默认的范围需要同包才能生效
118 | else if (null != clazz
119 | && null != superClassDeclaredMethod
120 | && null != superClassDeclaredMethod.getDeclaringClass()
121 | && GaCheckUtils.isEquals(clazz.getPackage(), superClassDeclaredMethod.getDeclaringClass().getPackage())) {
122 | methodSet.add(superClassDeclaredMethod);
123 | }
124 |
125 | }
126 | }
127 | }
128 |
129 | return methodSet;
130 | }
131 |
132 | /*
133 | * 移除来自{@link java.lang.Object}的方法
134 | */
135 | private Collection removeObjectMethods(final Collection gaMethods) {
136 | final Iterator gaMethodIt = gaMethods.iterator();
137 | while (gaMethodIt.hasNext()) {
138 |
139 | final GaMethod gaMethod = gaMethodIt.next();
140 | if (GaCheckUtils.isEquals(gaMethod.getDeclaringClass(), Object.class)) {
141 | gaMethodIt.remove();
142 | }
143 |
144 | }
145 |
146 | return gaMethods;
147 | }
148 |
149 | @Override
150 | public Collection searchClassGaMethods(Class> targetClass, Matcher gaMethodMatcher) {
151 |
152 | final Set gaMethodSet = new LinkedHashSet();
153 |
154 | for (final Method method : listVisualMethod(targetClass)) {
155 | final GaMethod gaMethod = new GaMethod.MethodImpl(method);
156 | if (gaMethodMatcher.matching(gaMethod)) {
157 | gaMethodSet.add(gaMethod);
158 | }
159 | }
160 |
161 | // 因为构造函数不能继承,所以这里就不用像方法这么复杂的做可视化处理了
162 | for (final Constructor> constructor : targetClass.getDeclaredConstructors()) {
163 | final GaMethod gaMethod = new GaMethod.ConstructorImpl(constructor);
164 | if (gaMethodMatcher.matching(gaMethod)) {
165 | gaMethodSet.add(gaMethod);
166 | }
167 | }
168 |
169 | return removeObjectMethods(gaMethodSet);
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/manager/impl/DefaultTimeFragmentManager.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.manager.impl;
2 |
3 | import com.github.ompc.greys.core.Advice;
4 | import com.github.ompc.greys.core.TimeFragment;
5 | import com.github.ompc.greys.core.exception.ExpressException;
6 | import com.github.ompc.greys.core.manager.TimeFragmentManager;
7 | import com.github.ompc.greys.core.util.Express;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Date;
11 | import java.util.LinkedHashMap;
12 | import java.util.Map;
13 | import java.util.concurrent.atomic.AtomicInteger;
14 |
15 | /**
16 | * 默认时间碎片实现
17 | * Created by oldmanpushcart@gmail.com on 15/10/3.
18 | */
19 | public class DefaultTimeFragmentManager implements TimeFragmentManager {
20 |
21 | // 时间碎片序列生成器
22 | private final AtomicInteger TIME_FRAGMENT_SEQUENCER
23 | = new AtomicInteger(1000);
24 |
25 | private final AtomicInteger PROCESS_SEQUENCER
26 | = new AtomicInteger(1000);
27 |
28 | // 时间碎片存储
29 | private final Map timeFragmentStore
30 | = new LinkedHashMap();
31 |
32 | /*
33 | * 生成下一条序列
34 | */
35 | private int nextSequence() {
36 | return TIME_FRAGMENT_SEQUENCER.incrementAndGet();
37 | }
38 |
39 | @Override
40 | public int generateProcessId() {
41 | return PROCESS_SEQUENCER.incrementAndGet();
42 | }
43 |
44 | @Override
45 | public TimeFragment append(int processId, Advice advice, Date gmtCreate, long cost, String stack) {
46 | final int id = nextSequence();
47 | final TimeFragment timeFragment = new TimeFragment(
48 | id,
49 | processId,
50 | advice,
51 | gmtCreate,
52 | cost,
53 | stack
54 | );
55 | timeFragmentStore.put(id, timeFragment);
56 | return timeFragment;
57 | }
58 |
59 | @Override
60 | public ArrayList list() {
61 | return new ArrayList(timeFragmentStore.values());
62 | }
63 |
64 | /*
65 | * 搜索匹配
66 | */
67 | private boolean is(final TimeFragment timeFragment, final String express) {
68 | try {
69 | return Express.ExpressFactory
70 | .newExpress(timeFragment.advice)
71 | .bind("processId", timeFragment.processId)
72 | .bind("index", timeFragment.id)
73 | .bind("cost", timeFragment.cost)
74 | .is(express);
75 | } catch (ExpressException e) {
76 | return false;
77 | }
78 | }
79 |
80 | @Override
81 | public ArrayList search(final String express) {
82 | final ArrayList timeFragments = new ArrayList();
83 | for (TimeFragment timeFragment : timeFragmentStore.values()) {
84 | if (is(timeFragment, express)) {
85 | timeFragments.add(timeFragment);
86 | }
87 | }
88 | return timeFragments;
89 | }
90 |
91 | @Override
92 | public TimeFragment get(int id) {
93 | return timeFragmentStore.get(id);
94 | }
95 |
96 | @Override
97 | public TimeFragment delete(int id) {
98 | return timeFragmentStore.remove(id);
99 | }
100 |
101 | @Override
102 | public int clean() {
103 | final int size = timeFragmentStore.size();
104 | timeFragmentStore.clear();
105 | return size;
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/server/CommandHandler.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.server;
2 |
3 | import java.io.IOException;
4 |
5 | /**
6 | * 命令处理器
7 | * Created by oldmanpushcart@gmail.com on 15/5/3.
8 | */
9 | public interface CommandHandler {
10 |
11 | /**
12 | * 解析输入行并执行命令
13 | *
14 | * @param line 输入的命令行
15 | * @param session 会话
16 | * @throws IOException IO错误
17 | */
18 | void executeCommand(final String line, final Session session) throws IOException;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/server/DefaultSessionManager.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.server;
2 |
3 | import com.github.ompc.greys.core.util.LogUtil;
4 | import org.slf4j.Logger;
5 |
6 | import java.io.IOException;
7 | import java.nio.ByteBuffer;
8 | import java.nio.channels.SocketChannel;
9 | import java.nio.charset.Charset;
10 | import java.util.Map;
11 | import java.util.concurrent.ConcurrentHashMap;
12 | import java.util.concurrent.atomic.AtomicBoolean;
13 | import java.util.concurrent.atomic.AtomicInteger;
14 |
15 | /**
16 | * 默认会话管理器实现
17 | * Created by oldmanpushcart@gmail.com on 15/5/2.
18 | */
19 | public class DefaultSessionManager implements SessionManager {
20 |
21 | private final Logger logger = LogUtil.getLogger();
22 |
23 | // 5分钟
24 | private static final long DURATION_5_MINUTE = 5L * 60 * 1000;
25 |
26 | // 会话超时时间
27 | private static final long DEFAULT_SESSION_DURATION = DURATION_5_MINUTE;
28 |
29 | // 会话管理Map
30 | private final ConcurrentHashMap sessionMap = new ConcurrentHashMap();
31 |
32 | // 会话ID序列生成器
33 | private final AtomicInteger sessionIndexSequence = new AtomicInteger(0);
34 |
35 | private final AtomicBoolean isDestroyRef = new AtomicBoolean(false);
36 |
37 | public DefaultSessionManager() {
38 | activeSessionExpireDaemon();
39 | }
40 |
41 | @Override
42 | public Session getSession(int sessionId) {
43 | return sessionMap.get(sessionId);
44 | }
45 |
46 | @Override
47 | public Session newSession(int javaPid, SocketChannel socketChannel, Charset charset) {
48 | final int sessionId = sessionIndexSequence.getAndIncrement();
49 | final Session session = new Session(javaPid, sessionId, DEFAULT_SESSION_DURATION, socketChannel, charset) {
50 | @Override
51 | public void destroy() {
52 | super.destroy();
53 | sessionMap.remove(sessionId);
54 | }
55 | };
56 |
57 | final Session sessionInMap = sessionMap.putIfAbsent(sessionId, session);
58 | if (null != sessionInMap) {
59 | // 之前竟然存在,返回之前的
60 | return sessionInMap;
61 | }
62 |
63 | return session;
64 | }
65 |
66 | /**
67 | * 激活会话过期管理守护线程
68 | */
69 | private void activeSessionExpireDaemon() {
70 | final Thread sessionExpireDaemon = new Thread("ga-session-expire-daemon") {
71 |
72 | @Override
73 | public void run() {
74 | while (!isDestroyRef.get()
75 | && !isInterrupted()) {
76 |
77 | // 间隔200ms检测一次
78 | try {
79 | Thread.sleep(200);
80 | } catch (InterruptedException e) {
81 | interrupt();
82 | }
83 |
84 | for (final Map.Entry entry : sessionMap.entrySet()) {
85 |
86 | final int sessionId = entry.getKey();
87 | final Session session = entry.getValue();
88 | if (null == session
89 | || session.isExpired()) {
90 |
91 | logger.info("session[{}] was expired", sessionId);
92 |
93 | if (null != session) {
94 |
95 | try {
96 | // 会话超时,关闭之前输出超时信息
97 | session.getSocketChannel().write(ByteBuffer.wrap("Session timed out.\n".getBytes(session.getCharset())));
98 | } catch (IOException e) {
99 | logger.debug("write expired message to session[{}] failed.", sessionId, e);
100 | }
101 |
102 | session.destroy();
103 | }
104 |
105 | sessionMap.remove(sessionId);
106 |
107 | }
108 |
109 | }
110 |
111 | }
112 | }
113 | };
114 | sessionExpireDaemon.setDaemon(true);
115 | sessionExpireDaemon.start();
116 | }
117 |
118 |
119 | @Override
120 | public void clean() {
121 | // shutdown all the session
122 | for (Session session : sessionMap.values()) {
123 | session.destroy();
124 | }
125 |
126 | sessionMap.clear();
127 |
128 | logger.info("session manager clean completed.");
129 |
130 | }
131 |
132 | @Override
133 | public boolean isDestroy() {
134 | return isDestroyRef.get();
135 | }
136 |
137 | @Override
138 | public void destroy() {
139 |
140 | if (!isDestroyRef.compareAndSet(false, true)) {
141 | throw new IllegalStateException("already destroy");
142 | }
143 |
144 | clean();
145 |
146 | logger.info("session manager destroy completed.");
147 |
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/server/Session.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.server;
2 |
3 | import com.github.ompc.greys.core.GlobalOptions;
4 | import com.github.ompc.greys.core.advisor.AdviceWeaver;
5 | import com.github.ompc.greys.core.util.LogUtil;
6 | import org.slf4j.Logger;
7 |
8 | import java.nio.channels.SocketChannel;
9 | import java.nio.charset.Charset;
10 | import java.util.concurrent.BlockingQueue;
11 | import java.util.concurrent.LinkedBlockingQueue;
12 | import java.util.concurrent.atomic.AtomicInteger;
13 |
14 | import static com.github.ompc.greys.core.util.GaStringUtils.DEFAULT_PROMPT;
15 | import static java.lang.System.currentTimeMillis;
16 | import static org.apache.commons.io.IOUtils.closeQuietly;
17 | import static org.apache.commons.lang3.StringUtils.EMPTY;
18 | import static org.apache.commons.lang3.StringUtils.isNotBlank;
19 |
20 | /**
21 | * 服务端会话
22 | * Created by oldmanpushcart@gmail.com on 15/5/2.
23 | */
24 | public class Session {
25 |
26 | private final Logger logger = LogUtil.getLogger();
27 |
28 | // 会话锁ID序列
29 | private final static AtomicInteger lockTxSeq = new AtomicInteger();
30 |
31 | // 空锁
32 | private final static int LOCK_TX_EMPTY = -1;
33 |
34 |
35 | private final int javaPid;
36 | private final int sessionId;
37 | private final long sessionDuration;
38 | private final SocketChannel socketChannel;
39 | private Charset charset;
40 |
41 | // 是否会话静默,静默的会话不输出提示符,LOGO,同时也会影响一些命令的输出
42 | private boolean silent = true;
43 |
44 | // 提示符
45 | private String prompt = DEFAULT_PROMPT;
46 |
47 | // 会话最后一次交互时间(触摸时间)
48 | private volatile long gmtLastTouch;
49 |
50 | // 是否被销毁
51 | private volatile boolean isDestroy = false;
52 |
53 | // 会话锁ID
54 | private final AtomicInteger lockTx = new AtomicInteger(LOCK_TX_EMPTY);
55 |
56 | // 会话输出阻塞队列
57 | private final BlockingQueue writeQueue = new LinkedBlockingQueue(GlobalOptions.sessionWriteQueueCapacity);
58 |
59 | /**
60 | * 构建Session
61 | *
62 | * @param javaPid Java进程ID
63 | * @param sessionId 会话ID
64 | * @param sessionDuration 会话持续时间(单位毫秒)
65 | * @param socketChannel socket
66 | * @param charset 会话字符集
67 | */
68 | Session(int javaPid, int sessionId, long sessionDuration, SocketChannel socketChannel, Charset charset) {
69 | this.javaPid = javaPid;
70 | this.sessionId = sessionId;
71 | this.sessionDuration = sessionDuration;
72 | this.socketChannel = socketChannel;
73 | this.charset = charset;
74 | this.gmtLastTouch = currentTimeMillis();
75 | }
76 |
77 | /**
78 | * 销毁会话
79 | */
80 | public void destroy() {
81 |
82 | isDestroy = true;
83 | closeQuietly(socketChannel);
84 |
85 | logger.info("session[{}] destroyed.", sessionId);
86 |
87 | }
88 |
89 | /**
90 | * 触摸会话
91 | * 超时触摸的会话有可能会被超时
92 | */
93 | public void touch() {
94 | gmtLastTouch = currentTimeMillis();
95 | }
96 |
97 | /**
98 | * 会话是否到期
99 | *
100 | * @return true:会话到期 / false:会话尚未到期
101 | */
102 | public boolean isExpired() {
103 | return sessionDuration <= currentTimeMillis() - gmtLastTouch;
104 | }
105 |
106 | /**
107 | * 锁定会话
108 | *
109 | * @return true : 锁定成功 / false : 锁定失败
110 | */
111 | public boolean tryLock() {
112 | return lockTx.compareAndSet(LOCK_TX_EMPTY, lockTxSeq.getAndIncrement());
113 | }
114 |
115 | /**
116 | * 解锁会话
117 | */
118 | public void unLock() {
119 |
120 | final int currentLockTx = lockTx.get();
121 |
122 | // 如果当前锁已经是LOCK_TX_EMPTY,则没有必要继续执行
123 | if (LOCK_TX_EMPTY == currentLockTx) {
124 | return;
125 | }
126 | if (!lockTx.compareAndSet(currentLockTx, LOCK_TX_EMPTY)) {
127 | // 能到这一步说明是lock()/unLock()编写出错,需要开发排查
128 | throw new IllegalStateException();
129 | }
130 |
131 | // 解锁的时候需要清理输出队列
132 | writeQueue.clear();
133 |
134 | // 取消监听注册
135 | AdviceWeaver.unReg(currentLockTx);
136 | }
137 |
138 | /**
139 | * 当前会话是否已经被锁定
140 | *
141 | * @return true : 锁定 / false : 未锁定
142 | */
143 | public boolean isLocked() {
144 | return lockTx.get() != LOCK_TX_EMPTY;
145 | }
146 |
147 | /**
148 | * 获取锁
149 | *
150 | * @return 返回锁ID
151 | */
152 | public int getLock() {
153 | return lockTx.get();
154 | }
155 |
156 | /**
157 | * 当前会话是否已经被销毁
158 | *
159 | * @return true : 销毁 / false : 违背销毁
160 | */
161 | public boolean isDestroy() {
162 | return isDestroy;
163 | }
164 |
165 | /**
166 | * 获取提示符
167 | * 这里主要是要求增加上\r
168 | *
169 | * @return 可以正常绘制的提示符
170 | */
171 | public String prompt() {
172 | return isNotBlank(getPrompt())
173 | ? "\r" + getPrompt()
174 | : EMPTY;
175 | }
176 |
177 | /**
178 | * 获取会话提示符
179 | *
180 | * @return 会话提示符
181 | */
182 | public String getPrompt() {
183 | return prompt;
184 | }
185 |
186 | /**
187 | * 设置会话提示符
188 | *
189 | * @param prompt 会话提示符
190 | */
191 | public void setPrompt(String prompt) {
192 | this.prompt = prompt;
193 | }
194 |
195 | /**
196 | * 获取会话写入队列
197 | *
198 | * @return 写入队列
199 | */
200 | public BlockingQueue getWriteQueue() {
201 | return writeQueue;
202 | }
203 |
204 | public int getSessionId() {
205 | return sessionId;
206 | }
207 |
208 | public Charset getCharset() {
209 | return charset;
210 | }
211 |
212 | public SocketChannel getSocketChannel() {
213 | return socketChannel;
214 | }
215 |
216 | public void setCharset(Charset charset) {
217 | this.charset = charset;
218 | }
219 |
220 | public long getSessionDuration() {
221 | return sessionDuration;
222 | }
223 |
224 | public int getJavaPid() {
225 | return javaPid;
226 | }
227 |
228 | public boolean isSilent() {
229 | return silent;
230 | }
231 |
232 | public void setSilent(boolean silent) {
233 | this.silent = silent;
234 | }
235 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/server/SessionManager.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.server;
2 |
3 | import java.nio.channels.SocketChannel;
4 | import java.nio.charset.Charset;
5 |
6 | /**
7 | * 会话管理
8 | * Created by oldmanpushcart@gmail.com on 15/5/2.
9 | */
10 | public interface SessionManager {
11 |
12 | /**
13 | * 创建一个会话
14 | *
15 | * @param javaPid Java进程号
16 | * @param socketChannel 会话所对应的SocketChannel
17 | * @param charset 会话所用字符集
18 | * @return 创建的会话
19 | */
20 | Session newSession(int javaPid, SocketChannel socketChannel, Charset charset);
21 |
22 | /**
23 | * 获取一个会话
24 | *
25 | * @param sessionId 会话ID
26 | * @return 返回会话
27 | */
28 | Session getSession(int sessionId);
29 |
30 | /**
31 | * 关闭所有会话
32 | */
33 | void clean();
34 |
35 | /**
36 | * 是否已经被销毁
37 | *
38 | * @return true/false
39 | */
40 | boolean isDestroy();
41 |
42 | /**
43 | * 销毁会话管理器所管理的所有会话
44 | */
45 | void destroy();
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/textui/TComponent.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.textui;
2 |
3 | /**
4 | * 命令行控件
5 | * Created by oldmanpushcart@gmail.com on 15/5/7.
6 | */
7 | public interface TComponent {
8 |
9 | /**
10 | * 渲染组件内容
11 | */
12 | String rendering();
13 |
14 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/textui/TKv.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.textui;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 |
5 | import java.util.Scanner;
6 |
7 | /**
8 | * KV排版控件
9 | * Created by oldmanpushcart@gmail.com on 15/5/9.
10 | */
11 | public class TKv implements TComponent {
12 |
13 | private final TTable tTable;
14 |
15 | public TKv() {
16 | this.tTable = new TTable(new TTable.ColumnDefine[]{
17 | new TTable.ColumnDefine(TTable.Align.RIGHT),
18 | new TTable.ColumnDefine(TTable.Align.RIGHT),
19 | new TTable.ColumnDefine(TTable.Align.LEFT)
20 | })
21 | .padding(0);
22 | this.tTable.getBorder().set(TTable.Border.BORDER_NON);
23 | }
24 |
25 | public TKv(TTable.ColumnDefine keyColumnDefine, TTable.ColumnDefine valueColumnDefine) {
26 | this.tTable = new TTable(new TTable.ColumnDefine[]{
27 | keyColumnDefine,
28 | new TTable.ColumnDefine(TTable.Align.RIGHT),
29 | valueColumnDefine
30 | })
31 | .padding(0);
32 | this.tTable.getBorder().set(TTable.Border.BORDER_NON);
33 | }
34 |
35 | public TKv add(final Object key, final Object value) {
36 | tTable.addRow(key, " : ", value);
37 | return this;
38 | }
39 |
40 | @Override
41 | public String rendering() {
42 | return filterEmptyLine(tTable.rendering());
43 | }
44 |
45 |
46 | /*
47 | * 出现多余的空行的原因是,KVview在输出时,会补全空格到最长的长度。所以在"yyyyy”后面会多出来很多的空格。
48 | * 再经过TableView的固定列处理,多余的空格就会在一行里放不下,输出成两行(第二行前面是空格)
49 | *
50 | * @see https://github.com/oldmanpushcart/greys-anatomy/issues/82
51 | */
52 | private String filterEmptyLine(String content) {
53 | final StringBuilder sb = new StringBuilder();
54 | Scanner scanner = null;
55 | try {
56 | scanner = new Scanner(content);
57 | while (scanner.hasNextLine()) {
58 | String line = scanner.nextLine();
59 | if (line != null) {
60 | //清理一行后面多余的空格
61 | line = StringUtils.stripEnd(line, " ");
62 | if (line.isEmpty()) {
63 | line = " ";
64 | }
65 | }
66 | sb.append(line).append('\n');
67 | }
68 | } finally {
69 | if (null != scanner) {
70 | scanner.close();
71 | }
72 | }
73 |
74 | return sb.toString();
75 | // return content;
76 | }
77 |
78 | public static void main(String... args) {
79 |
80 | final TKv tKv = new TKv(new TTable.ColumnDefine(TTable.Align.RIGHT),new TTable.ColumnDefine(10,false, TTable.Align.LEFT));
81 | tKv.add("KEY-1","ABCDEFGHIJKLMNOPQRSTUVWXYZ");
82 | tKv.add("KEY-2","1234567890");
83 | tKv.add("KEY-3","1234567890");
84 |
85 | final TTable tTable = new TTable(new TTable.ColumnDefine[]{
86 | new TTable.ColumnDefine(),
87 | new TTable.ColumnDefine(20,false, TTable.Align.LEFT)
88 | });
89 |
90 | tTable.addRow("OPTIONS", tKv.rendering());
91 |
92 | System.out.println(tTable.rendering());
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/textui/TLadder.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.textui;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import static org.apache.commons.lang3.StringUtils.repeat;
7 |
8 | /**
9 | * 阶梯缩进控件
10 | * Created by oldmanpushcart@gmail.com on 15/5/8.
11 | */
12 | public class TLadder implements TComponent {
13 |
14 | // 分隔符
15 | private static final String LADDER_CHAR = "`-";
16 |
17 | // 缩进符
18 | private static final String STEP_CHAR = " ";
19 |
20 | // 缩进长度
21 | private static final int INDENT_STEP = 2;
22 |
23 | private final List items = new ArrayList();
24 |
25 |
26 | @Override
27 | public String rendering() {
28 | final StringBuilder ladderSB = new StringBuilder();
29 | int deep = 0;
30 | for (String item : items) {
31 |
32 | // 第一个条目不需要分隔符
33 | if (deep == 0) {
34 | ladderSB
35 | .append(item)
36 | .append("\n");
37 | }
38 |
39 | // 其他的需要添加分隔符
40 | else {
41 | ladderSB
42 | .append(repeat(STEP_CHAR, deep * INDENT_STEP))
43 | .append(LADDER_CHAR)
44 | .append(item)
45 | .append("\n");
46 | }
47 |
48 | deep++;
49 |
50 | }
51 | return ladderSB.toString();
52 | }
53 |
54 | /**
55 | * 添加一个项目
56 | *
57 | * @param item 项目
58 | * @return this
59 | */
60 | public TLadder addItem(String item) {
61 | items.add(item);
62 | return this;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/textui/ext/TGaMethodInfo.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.textui.ext;
2 |
3 | import com.github.ompc.greys.core.textui.TComponent;
4 | import com.github.ompc.greys.core.textui.TKv;
5 | import com.github.ompc.greys.core.util.GaMethod;
6 |
7 | import java.lang.annotation.Annotation;
8 |
9 | import static com.github.ompc.greys.core.util.GaStringUtils.tranClassName;
10 | import static com.github.ompc.greys.core.util.GaStringUtils.tranModifier;
11 | import static org.apache.commons.lang3.StringUtils.EMPTY;
12 |
13 | /**
14 | * Java方法信息控件
15 | * Created by oldmanpushcart@gmail.com on 15/5/9.
16 | */
17 | public class TGaMethodInfo implements TComponent {
18 |
19 | private final GaMethod gaMethod;
20 |
21 | public TGaMethodInfo(GaMethod gaMethod) {
22 | this.gaMethod = gaMethod;
23 | }
24 |
25 | @Override
26 | public String rendering() {
27 | return new TKv()
28 | .add("declaring-class", gaMethod.getDeclaringClass())
29 | .add("gaMethod-name", gaMethod.getName())
30 | .add("modifier", tranModifier(gaMethod.getModifiers()))
31 | .add("annotation", drawAnnotation())
32 | .add("parameters", drawParameters())
33 | .add("return", drawReturn())
34 | .add("exceptions", drawExceptions())
35 | // .padding(1)
36 | // .hasBorder(true)
37 | .rendering();
38 | }
39 |
40 | private String drawAnnotation() {
41 |
42 | final StringBuilder annotationSB = new StringBuilder();
43 | final Annotation[] annotationArray = gaMethod.getDeclaredAnnotations();
44 |
45 | if (null != annotationArray && annotationArray.length > 0) {
46 | for (Annotation annotation : annotationArray) {
47 | annotationSB.append(tranClassName(annotation.annotationType())).append(",");
48 | }
49 | if (annotationSB.length() > 0) {
50 | annotationSB.deleteCharAt(annotationSB.length() - 1);
51 | }
52 | } else {
53 | annotationSB.append(EMPTY);
54 | }
55 |
56 | return annotationSB.toString();
57 | }
58 |
59 | private String drawParameters() {
60 | final StringBuilder paramsSB = new StringBuilder();
61 | final Class>[] paramTypes = gaMethod.getParameterTypes();
62 | if (null != paramTypes && paramTypes.length > 0) {
63 | for (Class> clazz : paramTypes) {
64 | paramsSB.append(tranClassName(clazz)).append("\n");
65 | }
66 | }
67 | return paramsSB.toString();
68 | }
69 |
70 | private String drawReturn() {
71 | final StringBuilder returnSB = new StringBuilder();
72 | final Class> returnTypeClass = gaMethod.getReturnType();
73 | returnSB.append(tranClassName(returnTypeClass)).append("\n");
74 | return returnSB.toString();
75 | }
76 |
77 | private String drawExceptions() {
78 | final StringBuilder exceptionSB = new StringBuilder();
79 | final Class>[] exceptionTypes = gaMethod.getExceptionTypes();
80 | if (null != exceptionTypes && exceptionTypes.length > 0) {
81 | for (Class> clazz : exceptionTypes) {
82 | exceptionSB.append(tranClassName(clazz)).append("\n");
83 | }
84 | }
85 | return exceptionSB.toString();
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/textui/ext/TTimeFragmentDetail.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.textui.ext;
2 |
3 | import com.github.ompc.greys.core.Advice;
4 | import com.github.ompc.greys.core.GlobalOptions;
5 | import com.github.ompc.greys.core.TimeFragment;
6 | import com.github.ompc.greys.core.textui.TComponent;
7 | import com.github.ompc.greys.core.textui.TTable;
8 | import com.github.ompc.greys.core.textui.TTable.ColumnDefine;
9 | import com.github.ompc.greys.core.util.GaStringUtils;
10 | import com.github.ompc.greys.core.util.SimpleDateFormatHolder;
11 | import com.github.ompc.greys.core.util.SizeOf;
12 |
13 | import java.io.PrintWriter;
14 | import java.io.StringWriter;
15 | import java.lang.instrument.Instrumentation;
16 |
17 | import static com.github.ompc.greys.core.textui.TTable.Align.LEFT;
18 | import static com.github.ompc.greys.core.textui.TTable.Align.RIGHT;
19 |
20 | /**
21 | * 时间碎片详情展示
22 | * Created by oldmanpushcart@gmail.com on 15/10/3.
23 | */
24 | public class TTimeFragmentDetail implements TComponent {
25 |
26 | private final Instrumentation inst;
27 | private final TimeFragment timeFragment;
28 | private final Integer expend;
29 |
30 | public TTimeFragmentDetail(final Instrumentation inst, final TimeFragment timeFragment, final Integer expend) {
31 | this.inst = inst;
32 | this.timeFragment = timeFragment;
33 | this.expend = expend;
34 | }
35 |
36 | /**
37 | * 是否需要展开输出对象
38 | */
39 | private boolean isNeedExpend() {
40 | return null != expend
41 | && expend > 0;
42 | }
43 |
44 | @Override
45 | public String rendering() {
46 |
47 | final Advice advice = timeFragment.advice;
48 | final String className = advice.getClazz().getName();
49 | final String methodName = advice.getMethod().getName();
50 |
51 | final TTable tTable = new TTable(
52 | new ColumnDefine[]{
53 | new ColumnDefine(15, false, RIGHT),
54 | new ColumnDefine(150, false, LEFT)
55 | })
56 | .padding(1)
57 | .addRow("INDEX", timeFragment.id)
58 | .addRow("PROCESS-ID", timeFragment.processId)
59 | .addRow("GMT-CREATE", SimpleDateFormatHolder.getInstance().format(timeFragment.gmtCreate))
60 | .addRow("COST(ms)", timeFragment.cost)
61 | .addRow("OBJECT", GaStringUtils.hashCodeToHexString(advice.target))
62 | .addRow("CLASS", className)
63 | .addRow("METHOD", methodName)
64 | .addRow("IS-RETURN", advice.isReturn)
65 | .addRow("IS-EXCEPTION", advice.isThrow);
66 |
67 | if (advice.isTraceSupport()) {
68 | tTable.addRow("TRACE-ID", advice.getTraceId());
69 | }
70 |
71 |
72 | // fill the parameters
73 | if (null != advice.params) {
74 |
75 | int paramIndex = 0;
76 | for (Object param : advice.params) {
77 |
78 | final StringBuilder titleSB = new StringBuilder("PARAMETERS[" + paramIndex++ + "]");
79 | if (GlobalOptions.isDisplayObjectSize) {
80 | titleSB.append("\n").append(new SizeOf(inst, param, SizeOf.HeapType.SHALLOW));
81 | titleSB.append("\n").append(new SizeOf(inst, param, SizeOf.HeapType.RETAINED));
82 | }
83 | tTable.addRow(titleSB.toString(), new TObject(param, expend).rendering());
84 | }
85 |
86 | }
87 |
88 | // fill the returnObj
89 | if (!advice.isThrow) {
90 |
91 | final StringBuilder titleSB = new StringBuilder("RETURN-OBJ");
92 | if (GlobalOptions.isDisplayObjectSize) {
93 | titleSB.append("\n").append(new SizeOf(inst, advice.returnObj, SizeOf.HeapType.SHALLOW));
94 | titleSB.append("\n").append(new SizeOf(inst, advice.returnObj, SizeOf.HeapType.RETAINED));
95 | }
96 | tTable.addRow(
97 | titleSB.toString(),
98 | new TObject(advice.returnObj, expend).rendering()
99 | );
100 |
101 | }
102 |
103 | // fill the throw exception
104 | if (advice.isThrow) {
105 |
106 | //noinspection ThrowableResultOfMethodCallIgnored
107 | final Throwable throwable = advice.throwExp;
108 |
109 | final StringBuilder titleSB = new StringBuilder("THROW-EXCEPTION");
110 | if (GlobalOptions.isDisplayObjectSize) {
111 | titleSB.append("\n").append(new SizeOf(inst, advice.throwExp, SizeOf.HeapType.SHALLOW));
112 | titleSB.append("\n").append(new SizeOf(inst, advice.throwExp, SizeOf.HeapType.RETAINED));
113 | }
114 |
115 | if (isNeedExpend()) {
116 | tTable.addRow(titleSB.toString(), new TObject(advice.throwExp, expend).rendering());
117 | } else {
118 | final StringWriter stringWriter = new StringWriter();
119 | final PrintWriter printWriter = new PrintWriter(stringWriter);
120 | try {
121 | throwable.printStackTrace(printWriter);
122 | tTable.addRow(titleSB.toString(), stringWriter.toString());
123 | } finally {
124 | printWriter.close();
125 | }
126 |
127 | }
128 |
129 | }
130 |
131 | // fill the stack
132 | tTable.addRow("STACK", timeFragment.stack);
133 |
134 | return tTable.rendering();
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/textui/ext/TTimeFragmentTable.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.textui.ext;
2 |
3 | import com.github.ompc.greys.core.Advice;
4 | import com.github.ompc.greys.core.TimeFragment;
5 | import com.github.ompc.greys.core.textui.TComponent;
6 | import com.github.ompc.greys.core.textui.TTable;
7 | import com.github.ompc.greys.core.util.SimpleDateFormatHolder;
8 |
9 | import static com.github.ompc.greys.core.util.GaStringUtils.hashCodeToHexString;
10 | import static org.apache.commons.lang3.StringUtils.substringAfterLast;
11 |
12 | /**
13 | * 时间片段表格
14 | * Created by oldmanpushcart@gmail.com on 15/10/3.
15 | */
16 | public class TTimeFragmentTable implements TComponent {
17 |
18 | /*
19 | * 各列宽度
20 | */
21 | private static final int[] TABLE_COL_WIDTH = new int[]{
22 | 8, // index
23 | 10, // processId
24 | 20, // timestamp
25 | 10, // cost(ms)
26 | 8, // isRet
27 | 8, // isExp
28 | 15, // object address
29 | 30, // class
30 | 30, // method
31 | };
32 |
33 | /*
34 | * 各列名称
35 | */
36 | private static final String[] TABLE_COL_TITLE = new String[]{
37 | "INDEX",
38 | "PROCESS-ID",
39 | "TIMESTAMP",
40 | "COST(ms)",
41 | "IS-RET",
42 | "IS-EXP",
43 | "OBJECT",
44 | "CLASS",
45 | "METHOD"
46 |
47 | };
48 |
49 | private final TTable tTable;
50 |
51 | public TTimeFragmentTable(boolean isPrintTitle) {
52 | this.tTable = new TTable(
53 | new TTable.ColumnDefine[]{
54 | new TTable.ColumnDefine(TABLE_COL_WIDTH[0], false, TTable.Align.RIGHT),
55 | new TTable.ColumnDefine(TABLE_COL_WIDTH[1], false, TTable.Align.RIGHT),
56 | new TTable.ColumnDefine(TABLE_COL_WIDTH[2], false, TTable.Align.RIGHT),
57 | new TTable.ColumnDefine(TABLE_COL_WIDTH[3], false, TTable.Align.RIGHT),
58 | new TTable.ColumnDefine(TABLE_COL_WIDTH[4], false, TTable.Align.RIGHT),
59 | new TTable.ColumnDefine(TABLE_COL_WIDTH[5], false, TTable.Align.RIGHT),
60 | new TTable.ColumnDefine(TABLE_COL_WIDTH[6], false, TTable.Align.RIGHT),
61 | new TTable.ColumnDefine(TABLE_COL_WIDTH[7], false, TTable.Align.RIGHT),
62 | new TTable.ColumnDefine(TABLE_COL_WIDTH[8], false, TTable.Align.RIGHT)
63 | }
64 | ).padding(1);
65 |
66 | if (isPrintTitle) {
67 | fillTableTitle();
68 | }
69 |
70 | }
71 |
72 | /**
73 | * 添加标题
74 | */
75 | private void fillTableTitle() {
76 | this.tTable.addRow(
77 | TABLE_COL_TITLE[0],
78 | TABLE_COL_TITLE[1],
79 | TABLE_COL_TITLE[2],
80 | TABLE_COL_TITLE[3],
81 | TABLE_COL_TITLE[4],
82 | TABLE_COL_TITLE[5],
83 | TABLE_COL_TITLE[6],
84 | TABLE_COL_TITLE[7],
85 | TABLE_COL_TITLE[8]
86 | );
87 | }
88 |
89 | /*
90 | * 填充表格行
91 | */
92 | public TTimeFragmentTable add(TimeFragment timeFragment) {
93 | final Advice advice = timeFragment.advice;
94 | tTable.addRow(
95 | timeFragment.id,
96 | timeFragment.processId,
97 | SimpleDateFormatHolder.getInstance().format(timeFragment.gmtCreate),
98 | timeFragment.cost,
99 | advice.isReturn,
100 | advice.isThrow,
101 | hashCodeToHexString(advice.target),
102 | substringAfterLast("." + advice.getClazz().getName(), "."),
103 | advice.getMethod().getName()
104 | );
105 | return this;
106 | }
107 |
108 | /**
109 | * 关闭下边框
110 | */
111 | public TTimeFragmentTable turnOffBottom() {
112 | tTable.getBorder().remove(TTable.Border.BORDER_OUTER_BOTTOM);
113 | return this;
114 | }
115 |
116 | /**
117 | * 打开下边框
118 | */
119 | public TTimeFragmentTable turnOnBottom() {
120 | tTable.getBorder().add(TTable.Border.BORDER_OUTER_BOTTOM);
121 | return this;
122 | }
123 |
124 | @Override
125 | public String rendering() {
126 | return tTable.rendering();
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/AliEagleEyeUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | import com.github.ompc.greys.core.GlobalOptions;
4 | import org.apache.commons.lang3.StringUtils;
5 |
6 | import java.lang.reflect.Method;
7 |
8 | /**
9 | * 阿里巴巴 EagleEye 中间件工具类
10 | * Created by vlinux on 16/9/24.
11 | */
12 | public class AliEagleEyeUtils {
13 |
14 | private static final String ILLEGAL_EAGLE_EYE_TRACE_ID = "-1";
15 | private static final String EAGLE_EYE_CLASS_NAME = "com.taobao.eagleeye.EagleEye";
16 | private static final String GET_TRACE_ID_NAME = "getTraceId";
17 |
18 | /**
19 | * 获取EagleEyeId
20 | *
21 | * @param loader 目标ClassLoader
22 | * @return EagleEyeId
23 | */
24 | public static String getTraceId(final ClassLoader loader) {
25 | if (!GlobalOptions.isEnableTraceId) {
26 | return ILLEGAL_EAGLE_EYE_TRACE_ID;
27 | }
28 | final Thread currentThread = Thread.currentThread();
29 | final ClassLoader contextClassLoader = currentThread.getContextClassLoader();
30 | currentThread.setContextClassLoader(loader);
31 | try {
32 | final Class> classOfEagleEye = loader.loadClass(EAGLE_EYE_CLASS_NAME);
33 | final Method methodOfGetTraceId = classOfEagleEye.getMethod(GET_TRACE_ID_NAME);
34 | final Object returnOfGetTraceId = methodOfGetTraceId.invoke(null);
35 | if (null != returnOfGetTraceId
36 | && returnOfGetTraceId instanceof String
37 | && StringUtils.isNoneBlank((String) returnOfGetTraceId)) {
38 | return (String) returnOfGetTraceId;
39 | } else {
40 | return ILLEGAL_EAGLE_EYE_TRACE_ID;
41 | }
42 | } catch (Throwable t) {
43 | return ILLEGAL_EAGLE_EYE_TRACE_ID;
44 | } finally {
45 | currentThread.setContextClassLoader(contextClassLoader);
46 | }
47 | }
48 |
49 | /**
50 | * 判断是否支持EagleEye
51 | *
52 | * @param eagleEyeTraceId 目标EagleEyeId
53 | * @return true:支持EagleEye;false:不支持;
54 | */
55 | public static boolean isEagleEyeSupport(final String eagleEyeTraceId) {
56 | return !StringUtils.equals(ILLEGAL_EAGLE_EYE_TRACE_ID, eagleEyeTraceId);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/AsmCodeLock.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | import org.objectweb.asm.Opcodes;
4 | import org.objectweb.asm.commons.AdviceAdapter;
5 |
6 | /**
7 | * ASM代码锁
8 | * Created by oldmanpushcart@gmail.com on 15/5/28.
9 | */
10 | public class AsmCodeLock implements CodeLock, Opcodes {
11 |
12 | private final AdviceAdapter aa;
13 |
14 | // 锁标记
15 | private boolean isLook;
16 |
17 | // 代码块开始特征数组
18 | private final int[] beginCodeArray;
19 |
20 | // 代码块结束特征数组
21 | private final int[] endCodeArray;
22 |
23 | // 代码匹配索引
24 | private int index = 0;
25 |
26 |
27 | /**
28 | * 用ASM构建代码锁
29 | *
30 | * @param aa ASM
31 | * @param beginCodeArray 代码块开始特征数组
32 | * 字节码流要求不能破坏执行堆栈
33 | * @param endCodeArray 代码块结束特征数组
34 | * 字节码流要求不能破坏执行堆栈
35 | */
36 | public AsmCodeLock(AdviceAdapter aa, int[] beginCodeArray, int[] endCodeArray) {
37 | if (null == beginCodeArray
38 | || null == endCodeArray
39 | || beginCodeArray.length != endCodeArray.length) {
40 | throw new IllegalArgumentException();
41 | }
42 |
43 | this.aa = aa;
44 | this.beginCodeArray = beginCodeArray;
45 | this.endCodeArray = endCodeArray;
46 |
47 | }
48 |
49 | @Override
50 | public void code(int code) {
51 |
52 | final int[] codes = isLock() ? endCodeArray : beginCodeArray;
53 |
54 | if (index >= codes.length) {
55 | reset();
56 | return;
57 | }
58 |
59 | if (codes[index] != code) {
60 | reset();
61 | return;
62 | }
63 |
64 | if (++index == codes.length) {
65 | // 翻转锁状态
66 | isLook = !isLook;
67 | reset();
68 | }
69 |
70 | }
71 |
72 | /*
73 | * 重置索引
74 | * 一般在代码序列判断失败时,则会对索引进行重置,冲头开始匹配特征序列
75 | */
76 | private void reset() {
77 | index = 0;
78 | }
79 |
80 |
81 | private void asm(int opcode) {
82 | aa.visitInsn(opcode);
83 | }
84 |
85 | /**
86 | * 锁定序列
87 | */
88 | private void lock() {
89 | for (int op : beginCodeArray) {
90 | asm(op);
91 | }
92 | }
93 |
94 | /*
95 | * 解锁序列
96 | */
97 | private void unLock() {
98 | for (int op : endCodeArray) {
99 | asm(op);
100 | }
101 | }
102 |
103 | @Override
104 | public boolean isLock() {
105 | return isLook;
106 | }
107 |
108 | @Override
109 | public void lock(Block block) {
110 | lock();
111 | try {
112 | block.code();
113 | } finally {
114 | unLock();
115 | }
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/CodeLock.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | /**
4 | * 代码锁
5 | * 什么叫代码锁?代码锁的出现是由于在字节码中,我们无法用简单的if语句来判定这段代码是生成的还是原有的。
6 | * 这会导致一些监控逻辑的混乱,比如trace命令如果不使用代码锁保护,将能看到Greys所植入的代码并进行跟踪
7 | * Created by oldmanpushcart@gmail.com on 15/5/28.
8 | */
9 | public interface CodeLock {
10 |
11 | /**
12 | * 根据字节码流锁或解锁代码
13 | * 通过对字节码流的判断,决定当前代码是锁定和解锁
14 | *
15 | * @param opcode 字节码
16 | */
17 | void code(int opcode);
18 |
19 | /**
20 | * 判断当前代码是否还在锁定中
21 | *
22 | * @return true/false
23 | */
24 | boolean isLock();
25 |
26 | /**
27 | * 将一个代码块纳入代码锁保护范围
28 | *
29 | * @param block 代码块
30 | */
31 | void lock(Block block);
32 |
33 | /**
34 | * 代码块
35 | */
36 | interface Block {
37 | /**
38 | * 代码
39 | */
40 | void code();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/Express.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | import com.github.ompc.greys.core.exception.ExpressException;
4 | import ognl.DefaultMemberAccess;
5 | import ognl.Ognl;
6 | import ognl.OgnlContext;
7 |
8 | /**
9 | * 表达式
10 | * Created by oldmanpushcart@gmail.com on 15/5/20.
11 | */
12 | public interface Express {
13 |
14 | /**
15 | * 根据表达式获取值
16 | *
17 | * @param express 表达式
18 | * @return 表达式运算后的值
19 | * @throws ExpressException 表达式运算出错
20 | */
21 | Object get(String express) throws ExpressException;
22 |
23 | /**
24 | * 根据表达式判断是与否
25 | *
26 | * @param express 表达式
27 | * @return 表达式运算后的布尔值
28 | * @throws ExpressException 表达式运算出错
29 | */
30 | boolean is(String express) throws ExpressException;
31 |
32 | /**
33 | * 绑定对象
34 | *
35 | * @param object 待绑定对象
36 | * @return this
37 | */
38 | Express bind(Object object);
39 |
40 | /**
41 | * 绑定变量
42 | *
43 | * @param name 变量名
44 | * @param value 变量值
45 | * @return this
46 | */
47 | Express bind(String name, Object value);
48 |
49 | /**
50 | * 重置整个表达式
51 | *
52 | * @return this
53 | */
54 | Express reset();
55 |
56 |
57 | /**
58 | * 表达式工厂类
59 | */
60 | class ExpressFactory {
61 |
62 | private static final ThreadLocal expressRef = new ThreadLocal() {
63 | @Override
64 | protected Express initialValue() {
65 | return new OgnlExpress();
66 | }
67 | };
68 |
69 | /**
70 | * 构造表达式执行类
71 | *
72 | * @param object 执行对象
73 | * @return 返回表达式实现
74 | */
75 | public static Express newExpress(Object object) {
76 | return expressRef.get().reset().bind(object);
77 | // return new OgnlExpress().bind(object);
78 | }
79 |
80 | }
81 |
82 | class OgnlExpress implements Express {
83 |
84 | private Object bindObject;
85 | private final OgnlContext context = new OgnlContext();
86 |
87 | @Override
88 | public Object get(String express) throws ExpressException {
89 | try {
90 | context.setMemberAccess(new DefaultMemberAccess(true));
91 | return Ognl.getValue(express, context, bindObject);
92 | } catch (Exception e) {
93 | throw new ExpressException(express, e);
94 | }
95 | }
96 |
97 | @Override
98 | public boolean is(String express) throws ExpressException {
99 | try {
100 | final Object ret = get(express);
101 | return null != ret
102 | && ret instanceof Boolean
103 | && (Boolean) ret;
104 | } catch (Throwable t) {
105 | return false;
106 | }
107 | }
108 |
109 | @Override
110 | public Express bind(Object object) {
111 | this.bindObject = object;
112 | return this;
113 | }
114 |
115 | @Override
116 | public Express bind(String name, Object value) {
117 | context.put(name, value);
118 | return this;
119 | }
120 |
121 | @Override
122 | public Express reset() {
123 | context.clear();
124 | return this;
125 | }
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/GaNetCat.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | import org.apache.commons.io.IOUtils;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.OutputStream;
8 | import java.net.InetSocketAddress;
9 | import java.net.Socket;
10 |
11 | /**
12 | * GaNetCat封装
13 | * 这个代码不要看,不要看,不要看...不是我写的...
14 | * Created by vlinux on 16/2/4.
15 | */
16 | public class GaNetCat {
17 |
18 | public static void main(String... args) throws IOException {
19 |
20 | final InputStream is = System.in;
21 | final OutputStream os = System.out;
22 | final Socket socket = new Socket();
23 | try {
24 |
25 | socket.connect(new InetSocketAddress(args[0], Integer.valueOf(args[1])));
26 | final InputStream nis = socket.getInputStream();
27 | final OutputStream nos = socket.getOutputStream();
28 |
29 | final byte[] dataArray = new byte[1024];
30 | int length;
31 | // do write
32 | do {
33 | length = is.read(dataArray);
34 | if (length <= 0) {
35 | break;
36 | }
37 | nos.write(dataArray, 0, length);
38 | nos.flush();
39 | } while (length > 0);
40 |
41 | // do read
42 | do {
43 | length = nis.read(dataArray);
44 | if (length == 1
45 | && dataArray[0] == 0x04) {
46 | os.flush();
47 | break;
48 | }
49 | if (length <= 0) {
50 | break;
51 | }
52 | os.write(dataArray, 0, length);
53 | } while (length > 0);
54 |
55 | } finally {
56 | IOUtils.closeQuietly(is);
57 | IOUtils.closeQuietly(os);
58 |
59 | try {
60 | socket.close();
61 | } catch (IOException e) {
62 | // ignore
63 | }
64 | }
65 |
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/InvokeCost.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | /**
4 | * 调用耗时
5 | * Created by vlinux on 16/6/1.
6 | */
7 | public class InvokeCost {
8 |
9 | private final ThreadLocal timestampRef = new ThreadLocal();
10 |
11 | public long begin() {
12 | final long timestamp = System.currentTimeMillis();
13 | timestampRef.set(timestamp);
14 | return timestamp;
15 | }
16 |
17 | public long cost() {
18 | return System.currentTimeMillis() - timestampRef.get();
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/LazyGet.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | import com.github.ompc.greys.core.exception.UnCaughtException;
4 |
5 | /**
6 | * 懒加载
7 | * Created by vlinux on 16/6/1.
8 | */
9 | public abstract class LazyGet {
10 |
11 | private volatile boolean isInit = false;
12 | private volatile T object;
13 |
14 | abstract protected T initialValue() throws Throwable;
15 |
16 | public T get() {
17 |
18 | if (isInit) {
19 | return object;
20 | }
21 |
22 | // lazy get
23 | try {
24 | object = initialValue();
25 | isInit = true;
26 | return object;
27 | } catch (Throwable throwable) {
28 | throw new UnCaughtException(throwable);
29 | }
30 |
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/LogUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | import ch.qos.logback.classic.LoggerContext;
4 | import ch.qos.logback.classic.joran.JoranConfigurator;
5 | import ch.qos.logback.core.joran.spi.JoranException;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | /**
10 | * Greys日志
11 | * Created by oldmanpushcart@gmail.com on 15/3/8.
12 | */
13 | public class LogUtil {
14 |
15 | private static final Logger logger;
16 |
17 | static {
18 |
19 | final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
20 | final JoranConfigurator configurator = new JoranConfigurator();
21 | configurator.setContext(loggerContext);
22 | loggerContext.reset();
23 | try {
24 |
25 | configurator.doConfigure(LogUtil.class.getResourceAsStream("/com/github/ompc/greys/core/res/greys-logback.xml"));
26 | } catch (JoranException e) {
27 | throw new RuntimeException("load logback config failed, you need restart greys", e);
28 | } finally {
29 | logger = LoggerFactory.getLogger("greys-anatomy");
30 | }
31 |
32 | }
33 |
34 | public static Logger getLogger() {
35 | return logger;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/PlayIndexHolder.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | /**
4 | * 回放时间片段ID线程上下文传递
5 | * 因为需要排查Perm区泄漏问题,所以暂时先废弃掉相关代码
6 | * Created by oldmanpushcart@gmail.com on 15/10/5.
7 | */
8 | public class PlayIndexHolder extends ThreadLocal {
9 |
10 | private static final PlayIndexHolder instance = new PlayIndexHolder();
11 |
12 | private PlayIndexHolder() {
13 | //
14 | }
15 |
16 | public static PlayIndexHolder getInstance() {
17 | return instance;
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/PointCut.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | import com.github.ompc.greys.core.util.matcher.Matcher;
4 |
5 | /**
6 | * 切入点
7 | * Created by oldmanpushcart@gmail.com on 15/10/24.
8 | */
9 | public class PointCut {
10 |
11 | // 类匹配
12 | private final Matcher> classMatcher;
13 |
14 | // 方法匹配
15 | private final Matcher gaMethodMatcher;
16 |
17 | // 匹配是否包含子类
18 | private final boolean isIncludeSubClass;
19 |
20 | /**
21 | * 构造切入点
22 | *
23 | * @param classMatcher 类匹配
24 | * @param gaMethodMatcher 方法匹配
25 | */
26 | public PointCut(Matcher> classMatcher, Matcher gaMethodMatcher) {
27 | this(classMatcher, gaMethodMatcher, true);
28 | }
29 |
30 | /**
31 | * 构造切入点
32 | *
33 | * @param classMatcher 类匹配
34 | * @param gaMethodMatcher 方法匹配
35 | * @param isIncludeSubClass 类匹配是否包含子类
36 | */
37 | public PointCut(Matcher> classMatcher, Matcher gaMethodMatcher, boolean isIncludeSubClass) {
38 | this.classMatcher = classMatcher;
39 | this.gaMethodMatcher = gaMethodMatcher;
40 | this.isIncludeSubClass = isIncludeSubClass;
41 | }
42 |
43 | public Matcher> getClassMatcher() {
44 | return classMatcher;
45 | }
46 |
47 | public Matcher getGaMethodMatcher() {
48 | return gaMethodMatcher;
49 | }
50 |
51 | public boolean isIncludeSubClass() {
52 | return isIncludeSubClass;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/SimpleDateFormatHolder.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Date;
5 |
6 | /**
7 | * SimpleDateFormat Holder
8 | * Created by oldmanpushcart@gmail.com on 15/10/6.
9 | */
10 | public class SimpleDateFormatHolder extends ThreadLocal {
11 |
12 | @Override
13 | protected SimpleDateFormat initialValue() {
14 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
15 | }
16 |
17 | private static final SimpleDateFormatHolder instance = new SimpleDateFormatHolder();
18 |
19 | private SimpleDateFormatHolder() {
20 | //
21 | }
22 |
23 | public static SimpleDateFormatHolder getInstance() {
24 | return instance;
25 | }
26 |
27 | /**
28 | * 格式化日期
29 | *
30 | * @param date 日期
31 | * @return 格式化后字符串
32 | */
33 | public String format(Date date) {
34 | return getInstance().get().format(date);
35 | }
36 |
37 |
38 | /**
39 | * 格式化日期
40 | *
41 | * @param gmt gmt
42 | * @return 格式化后字符串
43 | */
44 | public String format(long gmt) {
45 | return getInstance().get().format(new Date(gmt));
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/affect/Affect.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.affect;
2 |
3 | import static java.lang.System.currentTimeMillis;
4 |
5 | /**
6 | * 影响反馈
7 | * Created by oldmanpushcart@gmail.com on 15/5/21.
8 | */
9 | public class Affect {
10 |
11 | private final long start = currentTimeMillis();
12 |
13 | /**
14 | * 影响耗时(ms)
15 | *
16 | * @return 获取耗时(ms)
17 | */
18 | public long cost() {
19 | return currentTimeMillis() - start;
20 | }
21 |
22 | @Override
23 | public String toString() {
24 | return String.format("Affect cost in %s ms.", cost());
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/affect/AsmAffect.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.affect;
2 |
3 | import java.security.ProtectionDomain;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | /**
8 | * Asm命令特殊返回
9 | * Created by vlinux on 16/1/7.
10 | */
11 | public final class AsmAffect extends RowAffect {
12 |
13 | private final List classInfos = new ArrayList();
14 |
15 |
16 | /**
17 | * 获取类信息集合
18 | *
19 | * @return 类信息集合
20 | */
21 | public List getClassInfos() {
22 | return classInfos;
23 | }
24 |
25 | /**
26 | * 信息
27 | */
28 | public static class ClassInfo {
29 |
30 | private final Class> clazz;
31 | private final ClassLoader loader;
32 | private final byte[] byteArray;
33 | private final ProtectionDomain protectionDomain;
34 |
35 | public ClassInfo(Class> clazz, ClassLoader loader, byte[] byteArray, ProtectionDomain protectionDomain) {
36 | this.clazz = clazz;
37 | this.loader = loader;
38 | this.byteArray = byteArray;
39 | this.protectionDomain = protectionDomain;
40 | }
41 |
42 | public Class> getClazz() {
43 | return clazz;
44 | }
45 |
46 | public ClassLoader getLoader() {
47 | return loader;
48 | }
49 |
50 | public byte[] getByteArray() {
51 | return byteArray;
52 | }
53 |
54 | public ProtectionDomain getProtectionDomain() {
55 | return protectionDomain;
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/affect/EnhancerAffect.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.affect;
2 |
3 | import com.github.ompc.greys.core.GlobalOptions;
4 |
5 | import java.io.File;
6 | import java.util.*;
7 | import java.util.concurrent.atomic.AtomicInteger;
8 |
9 | import static java.lang.String.format;
10 |
11 | /**
12 | * 增强影响范围
13 | * 统计影响类/方法/耗时
14 | * Created by oldmanpushcart@gmail.com on 15/5/19.
15 | */
16 | public final class EnhancerAffect extends Affect {
17 |
18 | private final AtomicInteger cCnt = new AtomicInteger();
19 | private final AtomicInteger mCnt = new AtomicInteger();
20 |
21 | /*
22 | * dumpClass的文件存放集合
23 | */
24 | private final Collection classDumpFiles = new ArrayList();
25 |
26 | public EnhancerAffect() {
27 |
28 | }
29 |
30 | public EnhancerAffect(int cCnt, int mCnt) {
31 | this.cCnt(cCnt);
32 | this.mCnt(mCnt);
33 | }
34 |
35 | /**
36 | * 影响类统计
37 | *
38 | * @param cc 类影响计数
39 | * @return 当前影响类个数
40 | */
41 | public int cCnt(int cc) {
42 | return cCnt.addAndGet(cc);
43 | }
44 |
45 | /**
46 | * 影响方法统计
47 | *
48 | * @param mc 方法影响计数
49 | * @return 当前影响方法个数
50 | */
51 | public int mCnt(int mc) {
52 | return mCnt.addAndGet(mc);
53 | }
54 |
55 | /**
56 | * 获取影响类个数
57 | *
58 | * @return 影响类个数
59 | */
60 | public int cCnt() {
61 | return cCnt.get();
62 | }
63 |
64 | /**
65 | * 获取影响方法个数
66 | *
67 | * @return 影响方法个数
68 | */
69 | public int mCnt() {
70 | return mCnt.get();
71 | }
72 |
73 | /**
74 | * 获取dump的Class文件集合
75 | *
76 | * @return classDumpList
77 | */
78 | public Collection getClassDumpFiles() {
79 | return classDumpFiles;
80 | }
81 |
82 | @Override
83 | public String toString() {
84 | final StringBuilder infoSB = new StringBuilder();
85 | if (GlobalOptions.isDump
86 | && !classDumpFiles.isEmpty()) {
87 |
88 | for (File classDumpFile : classDumpFiles) {
89 | infoSB.append("[dump: ").append(classDumpFile.getAbsoluteFile()).append("]\n");
90 | }
91 | }
92 | infoSB.append(format("Affect(class-cnt:%d , method-cnt:%d) cost in %s ms.",
93 | cCnt(),
94 | mCnt(),
95 | cost()));
96 | return infoSB.toString();
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/affect/RowAffect.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.affect;
2 |
3 | import java.util.concurrent.atomic.AtomicInteger;
4 |
5 | /**
6 | * 行记录影响反馈
7 | * Created by oldmanpushcart@gmail.com on 15/5/21.
8 | */
9 | public class RowAffect extends Affect {
10 |
11 | private final AtomicInteger rCnt = new AtomicInteger();
12 |
13 | public RowAffect() {
14 | }
15 |
16 | public RowAffect(int rCnt) {
17 | this.rCnt(rCnt);
18 | }
19 |
20 | /**
21 | * 影响行数统计
22 | *
23 | * @param mc 行影响计数
24 | * @return 当前影响行个数
25 | */
26 | public int rCnt(int mc) {
27 | return rCnt.addAndGet(mc);
28 | }
29 |
30 | /**
31 | * 获取影响行个数
32 | *
33 | * @return 影响行个数
34 | */
35 | public int rCnt() {
36 | return rCnt.get();
37 | }
38 |
39 | @Override
40 | public String toString() {
41 | return String.format("Affect(row-cnt:%d) cost in %s ms.",
42 | rCnt(),
43 | cost());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/collection/GaStack.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.collection;
2 |
3 | /**
4 | * 堆栈
5 | * Created by oldmanpushcart@gmail.com on 15/6/21.
6 | * @param
7 | */
8 | public interface GaStack {
9 |
10 | E pop();
11 |
12 | void push(E e);
13 |
14 | E peek();
15 |
16 | boolean isEmpty();
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/collection/ThreadUnsafeFixGaStack.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.collection;
2 |
3 | import java.util.NoSuchElementException;
4 |
5 | /**
6 | * 线程不安全固定栈深的堆栈实现
7 | * 固定堆栈深度的实现能比JDK自带的堆栈实现提高10倍的性能.
8 | * Created by oldmanpushcart@gmail.com on 15/6/21.
9 | * @param
10 | */
11 | public class ThreadUnsafeFixGaStack implements GaStack {
12 |
13 | private final static int EMPTY_INDEX = -1;
14 | private final Object[] elementArray;
15 | private final int max;
16 | private int current = EMPTY_INDEX;
17 |
18 | public ThreadUnsafeFixGaStack(int max) {
19 | this.max = max;
20 | this.elementArray = new Object[max];
21 | }
22 |
23 | private void checkForPush() {
24 | // stack is full
25 | if (current == max) {
26 | throw new ArrayIndexOutOfBoundsException();
27 | }
28 | }
29 |
30 | private void checkForPopOrPeek() {
31 | // stack is empty
32 | if (isEmpty()) {
33 | throw new NoSuchElementException();
34 | }
35 | }
36 |
37 | @Override
38 | public E pop() {
39 | checkForPopOrPeek();
40 | //noinspection unchecked
41 | return (E) elementArray[current--];
42 | }
43 |
44 | @Override
45 | public void push(E e) {
46 | checkForPush();
47 | elementArray[++current] = e;
48 | }
49 |
50 | @Override
51 | public E peek() {
52 | checkForPopOrPeek();
53 | //noinspection unchecked
54 | return (E) elementArray[current];
55 | }
56 |
57 | @Override
58 | public boolean isEmpty() {
59 | return current == EMPTY_INDEX;
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/collection/ThreadUnsafeGaStack.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.collection;
2 |
3 | import java.util.NoSuchElementException;
4 |
5 | import static java.lang.System.arraycopy;
6 |
7 | /**
8 | * 线程不安全不固定栈深的堆栈实现
9 | * 比默认的实现带来3倍的性能提升
10 | * Created by oldmanpushcart@gmail.com on 15/6/21.
11 | * @param
12 | */
13 | public class ThreadUnsafeGaStack implements GaStack {
14 |
15 | private final static int EMPTY_INDEX = -1;
16 | private final static int DEFAULT_STACK_DEEP = 12;
17 |
18 | private Object[] elementArray;
19 | private int current = EMPTY_INDEX;
20 |
21 | public ThreadUnsafeGaStack() {
22 | this(DEFAULT_STACK_DEEP);
23 | }
24 |
25 | public ThreadUnsafeGaStack(int stackSize) {
26 | this.elementArray = new Object[stackSize];
27 | }
28 |
29 |
30 | /**
31 | * 自动扩容
32 | * 当前堆栈最大深度不满足期望时会自动扩容(2倍扩容)
33 | *
34 | * @param expectDeep 期望堆栈深度
35 | */
36 | private void ensureCapacityInternal(int expectDeep) {
37 | final int currentStackSize = elementArray.length;
38 | if (elementArray.length <= expectDeep) {
39 | final Object[] newElementArray = new Object[currentStackSize * 2];
40 | arraycopy(elementArray, 0, newElementArray, 0, currentStackSize);
41 | this.elementArray = newElementArray;
42 | }
43 | }
44 |
45 | private void checkForPopOrPeek() {
46 | // stack is empty
47 | if (isEmpty()) {
48 | throw new NoSuchElementException();
49 | }
50 | }
51 |
52 | @Override
53 | public E pop() {
54 | checkForPopOrPeek();
55 | //noinspection unchecked
56 | return (E) elementArray[current--];
57 | }
58 |
59 | @Override
60 | public void push(E e) {
61 | ensureCapacityInternal(current + 1);
62 | elementArray[++current] = e;
63 | }
64 |
65 | @Override
66 | public E peek() {
67 | checkForPopOrPeek();
68 | //noinspection unchecked
69 | return (E) elementArray[current];
70 | }
71 |
72 | @Override
73 | public boolean isEmpty() {
74 | return current == EMPTY_INDEX;
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/collection/ThreadUnsafeLRUHashMap.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.collection;
2 |
3 | import java.util.LinkedHashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * 非线程安全的带LRU功能的HashMap
8 | * Created by oldmanpushcart@gmail.com on 15/10/2.
9 | */
10 | public class ThreadUnsafeLRUHashMap extends LinkedHashMap {
11 |
12 | // LRU缓存空间
13 | private final int capacity;
14 |
15 | // 加载因子
16 | private static final float DEFAULT_LOAD_FACTOR = 0.75f;
17 |
18 | // 访问顺序优先
19 | private static final boolean DEFAULT_ACCESS_ORDER = true;
20 |
21 | public ThreadUnsafeLRUHashMap(int capacity) {
22 | this(capacity, DEFAULT_LOAD_FACTOR, DEFAULT_ACCESS_ORDER);
23 | }
24 |
25 | public ThreadUnsafeLRUHashMap(int capacity, float loadFactor, boolean accessOrder) {
26 | super(capacity, loadFactor, accessOrder);
27 | this.capacity = capacity;
28 | }
29 |
30 | @Override
31 | protected boolean removeEldestEntry(Map.Entry eldest) {
32 | return size() > capacity;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/matcher/CachedMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.matcher;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * 带缓存的匹配
7 | * Created by oldmanpushcart@gmail.com on 15/12/12.
8 | */
9 | public class CachedMatcher implements Matcher {
10 |
11 | private final Matcher matcher;
12 | private final Map cachedMap;
13 |
14 | public CachedMatcher(Matcher matcher, Map cachedMap) {
15 | this.matcher = matcher;
16 | this.cachedMap = cachedMap;
17 | }
18 |
19 | @Override
20 | public boolean matching(T target) {
21 |
22 | final Boolean valueInCache = cachedMap.get(target);
23 | if (null == valueInCache) {
24 | final boolean value = matcher.matching(target);
25 | cachedMap.put(target, value);
26 | return value;
27 | } else {
28 | return valueInCache;
29 | }
30 |
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/matcher/ClassMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.matcher;
2 |
3 | import com.github.ompc.greys.core.util.GaReflectUtils;
4 |
5 | import java.lang.annotation.Annotation;
6 | import java.util.Collection;
7 |
8 | import static com.github.ompc.greys.core.util.GaReflectUtils.*;
9 |
10 | /**
11 | * 类匹配
12 | * Created by vlinux on 15/10/31.
13 | */
14 | public class ClassMatcher extends ReflectMatcher> {
15 |
16 | // 类型
17 | private final int type;
18 |
19 | /**
20 | * 类匹配通用构造函数
21 | *
22 | * @param modifier 访问修饰符枚举,参考 {@link GaReflectUtils}
23 | * @param type 类型枚举,参考 {@link GaReflectUtils}
24 | * @param classNameMatcher 类名匹配
25 | * @param annotations 直接修饰类的Annotation匹配器
26 | */
27 | public ClassMatcher(
28 | final int modifier,
29 | final int type,
30 | final Matcher classNameMatcher,
31 | final Collection>> annotations) {
32 | super(modifier, classNameMatcher, annotations);
33 | this.type = type;
34 | }
35 |
36 |
37 | /**
38 | * 类类匹配构造函数
39 | * 主要用于Command的场景
40 | *
41 | * @param classNameMatcher 类名匹配
42 | */
43 | public ClassMatcher(final Matcher classNameMatcher) {
44 | this(DEFAULT_MOD, DEFAULT_TYPE, classNameMatcher, null);
45 | }
46 |
47 | @Override
48 | public boolean reflectMatching(Class> target) {
49 |
50 | // 匹配type
51 | return matchingType(target);
52 |
53 | }
54 |
55 | @Override
56 | int getTargetModifiers(Class> target) {
57 | return computeModifier(target);
58 | }
59 |
60 | @Override
61 | String getTargetName(Class> target) {
62 | return target.getName();
63 | }
64 |
65 | @Override
66 | Annotation[] getTargetAnnotationArray(Class> target) {
67 | return target.getAnnotations();
68 | }
69 |
70 | private boolean matchingType(Class> targetClass) {
71 | // 如果默认就是全类型,就不用比了
72 | if (type == DEFAULT_TYPE) {
73 | return true;
74 | }
75 | return (type & computeClassType(targetClass)) != 0;
76 | }
77 |
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/matcher/EqualsMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.matcher;
2 |
3 | import com.github.ompc.greys.core.util.GaCheckUtils;
4 |
5 | /**
6 | * 相等比对
7 | * Created by oldmanpushcart@gmail.com on 15/12/12.
8 | */
9 | public class EqualsMatcher implements Matcher {
10 |
11 | private final T source;
12 |
13 | public EqualsMatcher(T source) {
14 | this.source = source;
15 | }
16 |
17 | @Override
18 | public boolean matching(T target) {
19 | return GaCheckUtils.isEquals(source, target);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/matcher/GaMethodMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.matcher;
2 |
3 | import com.github.ompc.greys.core.util.GaMethod;
4 |
5 | import java.lang.annotation.Annotation;
6 | import java.util.Collection;
7 | import java.util.List;
8 |
9 | import static com.github.ompc.greys.core.util.GaReflectUtils.DEFAULT_MOD;
10 |
11 | /**
12 | * 方法匹配
13 | * Created by vlinux on 15/11/1.
14 | */
15 | public class GaMethodMatcher extends ReflectMatcher {
16 |
17 | // 方法参数匹配(顺序相关)
18 | private final List>> parameters;
19 |
20 | public GaMethodMatcher(
21 | final int modifier,
22 | final Matcher name,
23 | final List>> parameters,
24 | final Collection>> annotations) {
25 | super(modifier, name, annotations);
26 | this.parameters = parameters;
27 | }
28 |
29 | public GaMethodMatcher(final Matcher methodNameMatcher) {
30 | this(DEFAULT_MOD, methodNameMatcher, null, null);
31 | }
32 |
33 | @Override
34 | boolean reflectMatching(GaMethod targetMethod) {
35 | return matchingParameters(targetMethod);
36 | }
37 |
38 | private boolean matchingParameters(final GaMethod targetMethod) {
39 |
40 | final Class>[] targetParameterClassArray = targetMethod.getParameterTypes();
41 |
42 | // 推空保护
43 | if (null == parameters
44 | || null == targetParameterClassArray) {
45 | return true;
46 | }
47 |
48 | // 参数集合长度和参数匹配集合不匹配,说明参数列表都对不上了
49 | // 直接返回false
50 | if (targetParameterClassArray.length != parameters.size()) {
51 | return false;
52 | }
53 |
54 | final int length = targetParameterClassArray.length;
55 | for (int index = 0; index < length; index++) {
56 | final Matcher> classMatcher = parameters.get(index);
57 | if (!classMatcher.matching(targetParameterClassArray[index])) {
58 | return false;
59 | }
60 | }
61 | return true;
62 | }
63 |
64 | @Override
65 | int getTargetModifiers(GaMethod target) {
66 | return target.getModifiers();
67 | }
68 |
69 | @Override
70 | String getTargetName(GaMethod target) {
71 | return target.getName();
72 | }
73 |
74 | @Override
75 | Annotation[] getTargetAnnotationArray(GaMethod target) {
76 | return target.getAnnotations();
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/matcher/GroupMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.matcher;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.Collection;
6 |
7 | /**
8 | * 组匹配
9 | * Created by vlinux on 15/11/1.
10 | */
11 | public interface GroupMatcher extends Matcher {
12 |
13 | /**
14 | * 追加匹配器
15 | *
16 | * @param matcher 匹配器
17 | */
18 | void add(Matcher matcher);
19 |
20 | /**
21 | * 与关系组匹配
22 | *
23 | * @param 匹配类型
24 | */
25 | class And implements GroupMatcher {
26 |
27 | private final Collection> matchers;
28 |
29 | /**
30 | * 与关系组匹配构造
31 | * 当且仅当目标符合匹配组的所有条件时才判定匹配成功
32 | *
33 | * @param matchers 待进行与关系组匹配的匹配集合
34 | */
35 | public And(Matcher... matchers) {
36 | this.matchers = Arrays.asList(matchers);
37 | }
38 |
39 | @Override
40 | public boolean matching(T target) {
41 | for (Matcher matcher : matchers) {
42 | if (!matcher.matching(target)) {
43 | return false;
44 | }
45 | }
46 | return true;
47 | }
48 |
49 | @Override
50 | public void add(Matcher matcher) {
51 | matchers.add(matcher);
52 | }
53 | }
54 |
55 | /**
56 | * 或关系组匹配
57 | *
58 | * @param 匹配类型
59 | */
60 | class Or implements GroupMatcher {
61 |
62 | private final Collection> matchers;
63 |
64 | public Or() {
65 | this.matchers = new ArrayList>();
66 | }
67 |
68 | /**
69 | * 或关系组匹配构造
70 | * 当且仅当目标符合匹配组的任一条件时就判定匹配成功
71 | *
72 | * @param matchers 待进行或关系组匹配的匹配集合
73 | */
74 | public Or(Matcher... matchers) {
75 | this.matchers = Arrays.asList(matchers);
76 | }
77 |
78 | public Or(Collection> matchers) {
79 | this.matchers = matchers;
80 | }
81 |
82 | @Override
83 | public boolean matching(T target) {
84 | for (Matcher matcher : matchers) {
85 | if (matcher.matching(target)) {
86 | return true;
87 | }
88 | }
89 | return false;
90 | }
91 |
92 | @Override
93 | public void add(Matcher matcher) {
94 | matchers.add(matcher);
95 | }
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/matcher/Matcher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.matcher;
2 |
3 | /**
4 | * 匹配器
5 | * Created by oldmanpushcart@gmail.com on 15/5/17.
6 | */
7 | public interface Matcher {
8 |
9 | /**
10 | * 是否匹配
11 | *
12 | * @param target 目标对象
13 | * @return 目标是否匹配
14 | */
15 | boolean matching(T target);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/matcher/PatternMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.matcher;
2 |
3 | /**
4 | * 模版匹配
5 | * Created by oldmanpushcart@gmail.com on 15/10/31.
6 | */
7 | public class PatternMatcher implements Matcher {
8 |
9 | /**
10 | * 匹配策略
11 | */
12 | public enum Strategy {
13 |
14 | /**
15 | * 通配符匹配
16 | */
17 | WILDCARD,
18 |
19 | /**
20 | * 正则表达式匹配
21 | */
22 | REGEX,
23 |
24 | /**
25 | * 字符串全匹配
26 | */
27 | EQUALS
28 |
29 | }
30 |
31 | // 匹配器
32 | private final Matcher matcher;
33 |
34 | /**
35 | * 正则/通配符匹配切换构造函数
36 | * 这个构造函数的出现主要是迎合Command中的使用需要
37 | *
38 | * @param isRegex 是否正则表达式匹配
39 | * @param pattern 匹配模版
40 | */
41 | public PatternMatcher(final boolean isRegex, final String pattern) {
42 | this(isRegex ? Strategy.REGEX : Strategy.WILDCARD, pattern);
43 | }
44 |
45 | /**
46 | * 通用构造函数
47 | *
48 | * @param strategy 匹配策略
49 | * @param pattern 匹配模版
50 | */
51 | public PatternMatcher(Strategy strategy, String pattern) {
52 | switch (strategy) {
53 | case WILDCARD: {
54 | this.matcher = new WildcardMatcher(pattern);
55 | break;
56 | }
57 | case REGEX: {
58 | this.matcher = new RegexMatcher(pattern);
59 | break;
60 | }
61 | case EQUALS: {
62 | this.matcher = new EqualsMatcher(pattern);
63 | break;
64 | }
65 | default: {
66 | throw new IllegalArgumentException("unKnow strategy:" + strategy);
67 | }
68 | }
69 | }
70 |
71 | @Override
72 | public boolean matching(String target) {
73 | return matcher.matching(target);
74 | }
75 |
76 | /**
77 | * 正则表达式匹配
78 | */
79 | class RegexMatcher implements Matcher {
80 |
81 | private final String pattern;
82 |
83 | public RegexMatcher(String pattern) {
84 | this.pattern = pattern;
85 | }
86 |
87 | @Override
88 | public boolean matching(String target) {
89 | return null != target
90 | && null != pattern
91 | && target.matches(pattern);
92 | }
93 | }
94 |
95 |
96 | /**
97 | * 通配符表达式匹配
98 | */
99 | class WildcardMatcher implements Matcher {
100 |
101 | private final String pattern;
102 |
103 | public WildcardMatcher(String pattern) {
104 | this.pattern = pattern;
105 | }
106 |
107 |
108 | @Override
109 | public boolean matching(String target) {
110 | return match(target, pattern, 0, 0);
111 | }
112 |
113 | /**
114 | * Internal matching recursive function.
115 | */
116 | private boolean match(String string, String pattern, int stringStartNdx, int patternStartNdx) {
117 | int pNdx = patternStartNdx;
118 | int sNdx = stringStartNdx;
119 | int pLen = pattern.length();
120 | if (pLen == 1) {
121 | if (pattern.charAt(0) == '*') { // speed-up
122 | return true;
123 | }
124 | }
125 | int sLen = string.length();
126 | boolean nextIsNotWildcard = false;
127 |
128 | while (true) {
129 |
130 | // check if end of string and/or pattern occurred
131 | if ((sNdx >= sLen)) { // end of string still may have pending '*' callback pattern
132 | while ((pNdx < pLen) && (pattern.charAt(pNdx) == '*')) {
133 | pNdx++;
134 | }
135 | return pNdx >= pLen;
136 | }
137 | if (pNdx >= pLen) { // end of pattern, but not end of the string
138 | return false;
139 | }
140 | char p = pattern.charAt(pNdx); // pattern char
141 |
142 | // perform logic
143 | if (!nextIsNotWildcard) {
144 |
145 | if (p == '\\') {
146 | pNdx++;
147 | nextIsNotWildcard = true;
148 | continue;
149 | }
150 | if (p == '?') {
151 | sNdx++;
152 | pNdx++;
153 | continue;
154 | }
155 | if (p == '*') {
156 | char pnext = 0; // next pattern char
157 | if (pNdx + 1 < pLen) {
158 | pnext = pattern.charAt(pNdx + 1);
159 | }
160 | if (pnext == '*') { // double '*' have the same effect as one '*'
161 | pNdx++;
162 | continue;
163 | }
164 | int i;
165 | pNdx++;
166 |
167 | // find recursively if there is any substring from the end of the
168 | // line that matches the rest of the pattern !!!
169 | for (i = string.length(); i >= sNdx; i--) {
170 | if (match(string, pattern, i, pNdx)) {
171 | return true;
172 | }
173 | }
174 | return false;
175 | }
176 | } else {
177 | nextIsNotWildcard = false;
178 | }
179 |
180 | // check if pattern char and string char are equals
181 | if (p != string.charAt(sNdx)) {
182 | return false;
183 | }
184 |
185 | // everything matches for now, continue
186 | sNdx++;
187 | pNdx++;
188 | }
189 | }
190 |
191 | }
192 |
193 | }
194 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/matcher/ReflectMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.matcher;
2 |
3 | import java.lang.annotation.Annotation;
4 | import java.util.Collection;
5 |
6 | import static com.github.ompc.greys.core.util.GaReflectUtils.DEFAULT_MOD;
7 |
8 | /**
9 | * 反射相关匹配器
10 | * Created by vlinux on 15/11/1.
11 | */
12 | public abstract class ReflectMatcher implements Matcher {
13 |
14 | // 访问修饰符
15 | private final int modifier;
16 |
17 | // 名称匹配
18 | private final Matcher name;
19 |
20 | // 声明Annotation匹配
21 | private final Collection>> annotations;
22 |
23 | /**
24 | * 构造反射操作相关匹配器
25 | *
26 | * @param modifier 访问修饰符 具体枚举类型,参考 {@link ReflectMatcher}
27 | * @param name 匹配名称
28 | * @param annotations 声名的Annotation匹配器
29 | */
30 | public ReflectMatcher(
31 | final int modifier,
32 | final Matcher name,
33 | final Collection>> annotations) {
34 | this.modifier = modifier;
35 | this.name = name;
36 | this.annotations = annotations;
37 | }
38 |
39 | @Override
40 | final public boolean matching(T target) {
41 | return (null != target && // 推空保护
42 | matchingModifier(getTargetModifiers(target)) && // 匹配mod
43 | matchingName(getTargetName(target)) && // 匹配名称
44 | matchingAnnotation(getTargetAnnotationArray(target)) && // 匹配Annotation
45 | reflectMatching(target)); // 执行目标实现类的比对
46 | }
47 |
48 | /**
49 | * @param target 匹配目标
50 | * @return 匹配结果
51 | */
52 | abstract boolean reflectMatching(T target);
53 |
54 | /**
55 | * 根据实现类的不同,获取目标的访问修饰符
56 | *
57 | * @param target 匹配目标
58 | * @return 匹配目标访问修饰符
59 | */
60 | abstract int getTargetModifiers(T target);
61 |
62 | /**
63 | * 根据实现类的不同,获取匹配目标的名称
64 | *
65 | * @param target 匹配目标
66 | * @return 匹配目标的名称
67 | */
68 | abstract String getTargetName(T target);
69 |
70 | /**
71 | * 根据实现类的不同,获取匹配目标所声明的Annotation数组
72 | *
73 | * @param target 匹配目标
74 | * @return 匹配目标所声明的Annotation数组
75 | */
76 | abstract Annotation[] getTargetAnnotationArray(T target);
77 |
78 | private boolean matchingModifier(int targetModifier) {
79 | // 如果默认就是全匹配,就不用比了
80 | if (modifier == DEFAULT_MOD) {
81 | return true;
82 | }
83 | return (modifier & targetModifier) != 0;
84 | }
85 |
86 | private boolean matchingName(String className) {
87 | return name.matching(className);
88 | }
89 |
90 | private boolean matchingAnnotation(Annotation[] targetAnnotationArray) {
91 |
92 | // 推空保护,如果匹配器为空则认为放弃对Annotation的匹配需求
93 | if (null == annotations
94 | || annotations.isEmpty()) {
95 | return true;
96 | }
97 |
98 | // 推空保护,如果目标类没有声明Annotation
99 | // 而匹配器要求对Annotation进行匹配要求,则直接认为不符合匹配需求
100 | if (null == targetAnnotationArray) {
101 | return false;
102 | }
103 |
104 | // 对传入的Annotation匹配需求进行严格的逐个匹配
105 | // 只要有一个不匹配则认为匹配失败
106 | MATCHING_LOOP:
107 | for (Matcher> matcher : annotations) {
108 |
109 | for (Annotation targetAnnotation : targetAnnotationArray) {
110 | if (matcher.matching(targetAnnotation.getClass())) {
111 | // 只要匹配上一个,则跳过匹配循环
112 | continue MATCHING_LOOP;
113 | }
114 | }
115 |
116 | // 能走到这一步,说明当前一个都没有匹配上
117 | return false;
118 | }
119 |
120 | // 经过上边恶毒的循环之后,没有被拦下,说明符合对Annotation的匹配要求
121 | return true;
122 |
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/ompc/greys/core/util/matcher/TrueMatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.util.matcher;
2 |
3 | /**
4 | * 永远为真匹配器
5 | * Created by oldmanpushcart@gmail.com on 15/12/12.
6 | */
7 | public final class TrueMatcher implements Matcher {
8 |
9 | @Override
10 | public boolean matching(T target) {
11 | return true;
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/resources/com/github/ompc/greys/core/res/greys-logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/core/src/main/resources/com/github/ompc/greys/core/res/javascript/greys-module.js:
--------------------------------------------------------------------------------
1 | /**
2 | * GREYS模块
3 | * function watching(listener)
4 | */
5 | define('greys', function () {
6 |
7 | // 监听器集合
8 | var _listeners = [];
9 |
10 | // 带匹配需求的监听器集合
11 | var _regex_listeners = [];
12 |
13 | /**
14 | * 数组forEach遍历
15 | * @param arr 数组
16 | * @param func 回调函数
17 | */
18 | function arrayForEach(arr, func) {
19 | for (var index = 0; index < arr.length; index++) {
20 | func(index, arr[index]);
21 | }
22 | }
23 |
24 | function watching() {
25 | // watching(listener)
26 | if (arguments.length == 1
27 | && arguments[0] instanceof Object) {
28 | _listeners.push(arguments[0]);
29 | }
30 |
31 | // watching(/class_name_regex/, listener)
32 | else if (arguments.length == 2
33 | && arguments[0] instanceof RegExp
34 | && arguments[1] instanceof Object) {
35 | watching(arguments[0], /.*/, arguments[1]);
36 | }
37 |
38 | // watching(/class_name_regex/, /method_name_regex/, listener)
39 | else if (arguments.length == 3
40 | && arguments[0] instanceof RegExp
41 | && arguments[1] instanceof RegExp
42 | && arguments[2] instanceof Object) {
43 | _regex_listeners.push({
44 | testClass: arguments[0],
45 | testMethod: arguments[1],
46 | listener: arguments[2]
47 | });
48 | }
49 | }
50 |
51 | return {
52 |
53 | /**
54 | * 添加监听器,凡是被js命令所拦截到的方法都会流入到注册的监听器中
55 | * watching(listener)
56 | * watching(/class_name_regex/,/method_name_regex/,listener)
57 | * watching(/class_name_regex/,listener)
58 | */
59 | watching: watching,
60 |
61 | testJavaClassName: function (javaClassName) {
62 | for (var index in _regex_listeners) {
63 | var _regex_listener = _regex_listeners[index];
64 | if (_regex_listener
65 | && _regex_listener.testClass.test(javaClassName)) {
66 | return true;
67 | }
68 | }//for
69 | return false;
70 | },
71 |
72 | testJavaMethodName: function (javaMethodName) {
73 | for (var index in _regex_listeners) {
74 | var _regex_listener = _regex_listeners[index];
75 | if (_regex_listener
76 | && _regex_listener.testMethod.test(javaMethodName)) {
77 | return true;
78 | }
79 | }//for
80 | return false;
81 | },
82 |
83 | create: function (output) {
84 | arrayForEach(_listeners, function (index, listener) {
85 | if (listener.create) {
86 | listener.create(output);
87 | }
88 | });
89 | arrayForEach(_regex_listeners, function (index, _regex_listener) {
90 | if (_regex_listener.listener.create) {
91 | _regex_listener.listener.create(output);
92 | }
93 | });
94 | },
95 |
96 | destroy: function (output) {
97 | arrayForEach(_listeners, function (index, listener) {
98 | if (listener.destroy) {
99 | listener.destroy(output);
100 | }
101 | });
102 | arrayForEach(_regex_listeners, function (index, _regex_listener) {
103 | if (_regex_listener.listener.destroy) {
104 | _regex_listener.listener.destroy(output);
105 | }
106 | });
107 | },
108 |
109 | before: function (output, advice, context) {
110 | arrayForEach(_listeners, function (index, listener) {
111 | if (listener.before) {
112 | listener.before(output, advice, context);
113 | }
114 | });
115 | arrayForEach(_regex_listeners, function (index, _regex_listener) {
116 | if (_regex_listener.listener.before) {
117 | _regex_listener.listener.before(output, advice, context);
118 | }
119 | });
120 | },
121 |
122 | returning: function (output, advice, context) {
123 | arrayForEach(_listeners, function (index, listener) {
124 | if (listener.returning) {
125 | listener.returning(output, advice, context);
126 | }
127 | });
128 | arrayForEach(_regex_listeners, function (index, _regex_listener) {
129 | if (_regex_listener.listener.returning) {
130 | _regex_listener.listener.returning(output, advice, context);
131 | }
132 | });
133 | },
134 |
135 | throwing: function (output, advice, context) {
136 | arrayForEach(_listeners, function (index, listener) {
137 | if (listener.throwing) {
138 | listener.throwing(output, advice, context);
139 | }
140 | });
141 | arrayForEach(_regex_listeners, function (index, _regex_listener) {
142 | if (_regex_listener.listener.throwing) {
143 | _regex_listener.listener.throwing(output, advice, context);
144 | }
145 | });
146 | },
147 |
148 | }
149 |
150 | })
151 |
152 | // 向全局对象注入GREYS回调函数
153 | require(['global', 'greys'], function (global, greys) {
154 | global.__greys_module_test_java_class_name = function (javaClassName) {
155 | return greys.testJavaClassName(javaClassName);
156 | }
157 | global.__greys_module_test_java_method_name = function (javaMethodName) {
158 | return greys.testJavaMethodName(javaMethodName);
159 | }
160 | global.__greys_module_create = function (output) {
161 | greys.create(output);
162 | }
163 | global.__greys_module_destroy = function (output) {
164 | greys.destroy(output);
165 | }
166 | global.__greys_module_before = function (output, advice, context) {
167 | greys.before(output, advice, context);
168 | }
169 | global.__greys_module_returning = function (output, advice, context) {
170 | greys.returning(output, advice, context);
171 | }
172 | global.__greys_module_throwing = function (output, advice, context) {
173 | greys.throwing(output, advice, context);
174 | }
175 | })
--------------------------------------------------------------------------------
/core/src/main/resources/com/github/ompc/greys/core/res/logo.txt:
--------------------------------------------------------------------------------
1 | _
2 | ____ ____ _____ _ _ ___ _____ _____ ____ _____ _| |_ ___ ____ _ _
3 | / _ |/ ___) ___ | | | |/___|_____|____ | _ \(____ (_ _) _ \| \| | | |
4 | ( (_| | | | ____| |_| |___ | / ___ | | | / ___ | | || |_| | | | | |_| |
5 | \___ |_| |_____)\__ (___/ \_____|_| |_\_____| \__)___/|_|_|_|\__ |
6 | (_____| (____/ (____/
7 |
--------------------------------------------------------------------------------
/core/src/main/resources/com/github/ompc/greys/core/res/thanks.txt:
--------------------------------------------------------------------------------
1 |
2 | SUMMARY
3 |
4 | Greys anatomy could not exist without the continued generous support from the community.
5 | We would like to take this opportunity to thank our Developers.
6 | If you are interested in developer the Greys, please contact us.
7 |
8 |
9 | PLATINUM DEVELOPERS
10 |
11 | vlinux
12 | email : oldmanpushcart@gmail.com
13 | weibo : http://weibo.com/vlinux
14 |
15 | chengtd
16 | email : chengtongda@163.com
17 | weibo : http://weibo.com/chengtd
18 |
19 | JieChenCN
20 | weibo : http://weibo.com/471760204
21 | blog : http://xapp.in
22 |
23 | JamesPan
24 | email : panjiabang@gmail.com
25 | blog : http://blog.jamespan.me
26 |
27 | jotcmd
28 | github : https://github.com/jotcmd
29 |
30 | hengyunabc
31 | github : https://github.com/hengyunabc
32 |
33 | huxing.zhang
34 | email : huxing.zhang@gmail.com
35 |
36 | diecui1202
37 | email : diecui1202@gmail.com
38 |
39 |
40 | ABOUT
41 |
42 | Thank you very much.
43 |
44 |
--------------------------------------------------------------------------------
/core/src/main/resources/com/github/ompc/greys/core/res/version:
--------------------------------------------------------------------------------
1 | 1.7.6.6
2 |
--------------------------------------------------------------------------------
/core/src/test/java/com/github/ompc/greys/core/js/JavaScriptTestCase.java:
--------------------------------------------------------------------------------
1 | package com.github.ompc.greys.core.js;
2 |
3 | import com.github.ompc.greys.core.util.GaStringUtils;
4 | import org.apache.commons.io.IOUtils;
5 | import org.junit.After;
6 | import org.junit.Before;
7 | import org.junit.Ignore;
8 | import org.junit.Test;
9 |
10 | import javax.script.*;
11 | import java.io.IOException;
12 | import java.nio.charset.Charset;
13 |
14 | /**
15 | * JavaScript测试用例
16 | * Created by vlinux on 16/2/16.
17 | */
18 | @Ignore
19 | public class JavaScriptTestCase {
20 |
21 | protected ScriptEngine scriptEngine;
22 | protected Compilable compilable;
23 | protected Invocable invocable;
24 |
25 | @Before
26 | public void before() throws IOException, ScriptException {
27 | final ScriptEngineManager mgr = new ScriptEngineManager();
28 | this.scriptEngine = mgr.getEngineByMimeType("application/javascript");
29 | this.compilable = (Compilable) scriptEngine;
30 | this.invocable = (Invocable) scriptEngine;
31 | loadJavaScriptSupport();
32 | }
33 |
34 | /*
35 | * 加载JavaScriptSupport
36 | */
37 | private void loadJavaScriptSupport() throws IOException, ScriptException {
38 | compilable.compile(IOUtils.toString(
39 | GaStringUtils.class.getResourceAsStream("/com/github/ompc/greys/core/res/javascript/gblocking.js"),
40 | Charset.forName("UTF-8")
41 | )).eval();
42 |
43 | // compilable.compile(IOUtils.toString(
44 | // GaStringUtils.class.getResourceAsStream("/com/github/ompc/greys/core/res/javascript/greys-module.js"),
45 | // Charset.forName("UTF-8")
46 | // )).eval();
47 |
48 | compilable.compile(IOUtils.toString(
49 | GaStringUtils.class.getResourceAsStream("/src/test/resources/com.github.ompc.greys.core.res/javascript/test2.js"),
50 | Charset.forName("UTF-8")
51 | )).eval();
52 | }
53 |
54 | @After
55 | public void after() {
56 | this.scriptEngine = null;
57 | this.compilable = null;
58 | this.invocable = null;
59 | }
60 |
61 |
62 | class Father {
63 |
64 | protected String name = "Father";
65 |
66 | }
67 |
68 | class Son extends Father {
69 | protected String name = "Son";
70 | }
71 |
72 | @Test
73 | public void test_compile_javascript_support_success() throws IOException, ScriptException, NoSuchMethodException, InterruptedException, NoSuchFieldException, IllegalAccessException {
74 |
75 |
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/core/src/test/resources/com.github.ompc.greys.core.res/javascript/test2.js:
--------------------------------------------------------------------------------
1 | require({
2 | paths: {
3 | 'lang': 'https://raw.githubusercontent.com/oldmanpushcart/greys-javascript-lib/master/script/lib/common-lang-module.js',
4 | }
5 | })
6 |
7 | /**
8 | * 定义了一个console模块
9 | * 简单实现,不要吐槽
10 | */
11 | define('console', function () {
12 |
13 | function print(string) {
14 | java.lang.System.out.print("" + string);
15 | }
16 |
17 | function println(string) {
18 | print(string + '\n');
19 | }
20 |
21 | return {
22 | log: function (msg) {
23 | println(msg);
24 | }
25 | }
26 | })
27 |
28 |
29 | require(['lang', 'console'], function (lang, console) {
30 |
31 | var words = lang.string.format('{0} is dead, but {1} is alive! {0} {2}', 'ASP', 'ASP.NET');
32 | console.log(words);
33 |
34 | })
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.github.ompc.greys
7 | greys
8 | 1.0.0-SNAPSHOT
9 | pom
10 |
11 | greys
12 | https://github.com/oldmanpushcart/greys-anatomy
13 |
14 |
15 | UTF-8
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | junit
62 | junit
63 | 4.9
64 | test
65 |
66 |
67 | net.sf.jopt-simple
68 | jopt-simple
69 | 4.3
70 |
71 |
72 | org.ow2.asm
73 | asm
74 | 6.0
75 |
76 |
77 | org.ow2.asm
78 | asm-commons
79 | 6.0
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | org.ow2.asm
89 | asm-util
90 | 6.0
91 |
92 |
93 | org.apache.commons
94 | commons-lang3
95 | 3.4
96 |
97 |
98 | commons-io
99 | commons-io
100 | 2.4
101 |
102 |
103 | jline
104 | jline
105 | 2.12
106 |
107 |
108 | ognl
109 | ognl
110 | 3.0.8
111 |
112 |
113 | org.slf4j
114 | slf4j-api
115 | 1.7.5
116 |
117 |
118 | ch.qos.logback
119 | logback-classic
120 | 1.0.13
121 |
122 |
123 | ch.qos.logback
124 | logback-core
125 | 1.0.13
126 |
127 |
128 | com.google.code.gson
129 | gson
130 | 2.3.1
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | core
158 | agent
159 |
160 |
--------------------------------------------------------------------------------
/scripts/logger.js:
--------------------------------------------------------------------------------
1 | /*
2 | * JavaScriptLogger
3 | * 这是一个JavaScript脚本支撑的样板工程,方法的执行日志
4 | * @author : oldmanpushcart@gmail.com
5 | */
6 |
7 | __greys_require(['greys'], function (greys) {
8 |
9 | if (!Date.hasOwnProperty.format) {
10 | /**
11 | * 对Date的扩展,将 Date 转化为指定格式的String
12 | * 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
13 | * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
14 | * 例子:
15 | * (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
16 | * (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18
17 | * @param fmt 日期格式
18 | * @returns 格式化后的日期字符串
19 | * @author: meizz
20 | */
21 | Date.prototype.format = function (fmt) {
22 | var o = {
23 | "M+": this.getMonth() + 1, //月份
24 | "d+": this.getDate(), //日
25 | "h+": this.getHours(), //小时
26 | "m+": this.getMinutes(), //分
27 | "s+": this.getSeconds(), //秒
28 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度
29 | "S": this.getMilliseconds() //毫秒
30 | };
31 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
32 | for (var k in o)
33 | if (new RegExp("(" + k + ")").test(fmt)) fmt
34 | = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
35 | return fmt;
36 | }
37 | }
38 |
39 | if (!String.format) {
40 | /**
41 | * 字符串格式化函数
42 | * var template1="我是{0},今年{1}了";
43 | * var template2="我是{name},今年{age}了";
44 | * var result1=template1.format("loogn",22);
45 | * var result2=template1.format({name:"loogn",age:22});
46 | * @param args 参数列表
47 | * @returns 格式化后的字符串
48 | */
49 | String.prototype.format = function (args) {
50 | if (arguments.length > 0) {
51 | var result = this;
52 | if (arguments.length == 1 && typeof (args) == "object") {
53 | for (var key in args) {
54 | var reg = new RegExp("({" + key + "})", "g");
55 | result = result.replace(reg, args[key]);
56 | }
57 | }
58 | else {
59 | for (var i = 0; i < arguments.length; i++) {
60 | if (arguments[i] == undefined) {
61 | return "";
62 | }
63 | else {
64 | var reg = new RegExp("({[" + i + "]})", "g");
65 | result = result.replace(reg, arguments[i]);
66 | }
67 | }
68 | }
69 | return result;
70 | }
71 | else {
72 | return this;
73 | }
74 | }
75 | }
76 |
77 | /**
78 | * 获取当前系统时间戳
79 | * @returns 时间戳字符串
80 | */
81 | function timestamp() {
82 | return new Date().format("yyyy-MM-dd hh:mm:ss.S");
83 | }
84 |
85 | /**
86 | * 日志前缀
87 | * @param advice Advice
88 | * @return 日志前缀内容
89 | */
90 | function prefix(advice) {
91 | return "{timestamp} {classname} {methodname}".format({
92 | 'timestamp': timestamp(),
93 | 'classname': advice.clazz.name,
94 | 'methodname': advice.method.name
95 | });
96 | }
97 |
98 | /**
99 | * 输出Java的Throwable异常信息
100 | * @param throwing Java异常信息
101 | * @return 异常信息字符串堆栈
102 | */
103 | function printingJavaThrowable(throwing) {
104 | var throwingString = null;
105 | var sw = new java.io.StringWriter();
106 | var pw = new java.io.PrintWriter(sw);
107 | try {
108 | throwing.printStackTrace(pw);
109 | throwingString = sw.toString();
110 | } finally {
111 | pw.close();
112 | sw.close();
113 | }
114 | return throwingString;
115 | }
116 |
117 | function finish(output, advice, context) {
118 | var content = "{0} : cost={1}ms;".format(prefix(advice), context.cost);
119 |
120 | // 拼装参数列表
121 | if (advice.params.length > 0) {
122 | content += "params[{0}];".format(function () {
123 | var paramString = "";
124 | for (var index in advice.params) {
125 | paramString += advice.params[index];
126 | if (index < advice.params.length - 1) {
127 | paramString += ",";
128 | }
129 | }
130 | return paramString;
131 | });
132 | }
133 |
134 | // 拼装返回值
135 | if (advice.isReturning) {
136 | content += "return[{0}];".format(advice.returnObj);
137 | }
138 |
139 | // 拼装异常信息
140 | if (advice.isThrowing) {
141 | content += "throwing[{throwing}];".format({'throwing': advice.throwExp});
142 | content += "\n" + printingJavaThrowable(advice.throwExp);
143 | }
144 |
145 | output.println(content);
146 | }
147 |
148 |
149 | greys.watching({
150 | returning: function (output, advice, context) {
151 | finish(output, advice, context);
152 | },
153 | throwing: function (output, advice, context) {
154 | finish(output, advice, context);
155 | }
156 | })
157 |
158 | })
--------------------------------------------------------------------------------
/scripts/scheduler-module.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 调度器模块,可以模拟javascript的setTimeout/setInterval
3 | */
4 |
5 | module.exports = function () {
6 |
7 | var executor = java.util.concurrent.Executors.newSingleThreadScheduledExecutor();
8 | var counter = 1;
9 | var ids = {};
10 |
11 | function clearTimeout(id) {
12 | var future = ids[id];
13 | if (future) {
14 | ids[id].cancel(false);
15 | executor.purge();
16 | delete ids[id];
17 | }
18 | }
19 |
20 | return {
21 |
22 | setTimeout: function (fn, delay) {
23 | var id = counter++;
24 | ids[id] = executor.schedule(new java.lang.Runnable({run: fn}), delay, java.util.concurrent.TimeUnit.MILLISECONDS);
25 | return id;
26 | },
27 |
28 | clearTimeout: clearTimeout,
29 |
30 | setInterval: function (fn, delay) {
31 | var id = counter++;
32 | ids[id] = executor.scheduleAtFixedRate(new java.lang.Runnable({run: fn}), delay, delay, java.util.concurrent.TimeUnit.MILLISECONDS);
33 | return id;
34 | },
35 |
36 | clearInterval: clearTimeout,
37 |
38 | /**
39 | * 关闭调度器
40 | */
41 | shutdown: function () {
42 | executor.shutdown();
43 | }
44 |
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/scripts/template.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 模版
3 | */
4 | require(['greys'], function (greys) {
5 | greys.watching({
6 |
7 | /**
8 | * 脚本创建函数
9 | * 在脚本第一次运行时候执行,可以在这个函数中进行脚本初始化工作
10 | * @param output 输出器
11 | */
12 | create: function (output) {
13 |
14 | },
15 |
16 | /**
17 | * 脚本销毁函数
18 | * 在脚本运行完成时候执行,可以在这个函数中进行脚本销毁工作
19 | * @param output 输出器
20 | */
21 | destroy: function (output) {
22 |
23 | },
24 |
25 | /**
26 | * 方法执行前回调函数
27 | * 在Java方法执行之前执行该函数
28 | * @param output 输出器
29 | * @param advice 通知点
30 | * @param context 方法执行上下文(线程安全)
31 | */
32 | before: function (output, advice, context) {
33 |
34 | },
35 |
36 | /**
37 | * 方法返回回调函数
38 | * 在Java方法执行成功之后,Java方法返回之前执行该函数
39 | * @param output 输出器
40 | * @param advice 通知点
41 | * @param context 方法执行上下文(线程安全)
42 | */
43 | returning: function (output, advice, context) {
44 |
45 | },
46 |
47 | /**
48 | * 方法抛异常回调函数
49 | * 在Java方法内部执行抛异常之后,Java方法对外抛异常之前执行该函数
50 | * @param output 输出器
51 | * @param advice 通知点
52 | * @param context 方法执行上下文(线程安全)
53 | */
54 | throwing: function (output, advice, context) {
55 |
56 | },
57 |
58 | });
59 | })
--------------------------------------------------------------------------------