Skip to content

Commit

Permalink
feature: support after event processing of method
Browse files Browse the repository at this point in the history
  • Loading branch information
xcaspar authored and RinaisSuper committed Oct 8, 2019
1 parent df0c8a2 commit 49a4366
Show file tree
Hide file tree
Showing 20 changed files with 494 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.chaosblade.exec.bootstrap.jvmsandbox;

import java.lang.reflect.Method;
import java.util.Arrays;

import com.alibaba.chaosblade.exec.common.aop.Plugin;
import com.alibaba.chaosblade.exec.common.exception.InterruptProcessException;
import com.alibaba.chaosblade.exec.common.exception.InterruptProcessException.State;
import com.alibaba.chaosblade.exec.common.util.ReflectUtil;
import com.alibaba.jvm.sandbox.api.ProcessControlException;
import com.alibaba.jvm.sandbox.api.event.BeforeEvent;
import com.alibaba.jvm.sandbox.api.event.Event;
import com.alibaba.jvm.sandbox.api.event.ReturnEvent;
import com.alibaba.jvm.sandbox.api.listener.EventListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Changjun Xiao
*/
public class AfterEventListener implements EventListener {
private static final Logger LOGGER = LoggerFactory.getLogger(AfterEventListener.class);

private Plugin plugin;
private ThreadLocal<BeforeEventBean> beforeEventCache;

public AfterEventListener(Plugin plugin) {
this.plugin = plugin;
this.beforeEventCache = new ThreadLocal<BeforeEventBean>();
}

@Override
public void onEvent(Event event) throws Throwable {
if (event instanceof BeforeEvent) {
BeforeEvent beforeEvent = (BeforeEvent)event;
BeforeEventBean beforeEventBean = new BeforeEventBean(
beforeEvent.javaClassLoader,
beforeEvent.target,
beforeEvent.javaClassName,
beforeEvent.javaMethodName,
beforeEvent.javaMethodDesc,
beforeEvent.argumentArray);
beforeEventCache.set(beforeEventBean);
return;
}
if (!(event instanceof ReturnEvent)) {
beforeEventCache.remove();
return;
}
BeforeEventBean beforeEventBean = beforeEventCache.get();
if (beforeEventBean == null) {
return;
}
ReturnEvent returnEvent = (ReturnEvent)event;
// get the method class
Class clazz;
if (beforeEventBean.getObject() == null) {
clazz = beforeEventBean.getClassLoader().loadClass(beforeEventBean.getClassName());
} else {
clazz = beforeEventBean.getObject().getClass();
}
Method method;
try {
method = ReflectUtil.getMethod(clazz, beforeEventBean.getMethodDesc(), beforeEventBean.getMethodName());
} catch (NoSuchMethodException e) {
LOGGER.warn("get method by reflection exception for after event. "
+ "class: {}, method: {}, arguments: {}, desc: {}",
clazz, beforeEventBean.getMethodName(), Arrays.toString(beforeEventBean.getArgumentArray()),
beforeEventBean.getMethodDesc(), e);
beforeEventCache.remove();
return;
}

try {
// do enhancer
plugin.getEnhancer().afterAdvice(plugin.getModelSpec().getTarget(), beforeEventBean.getClassLoader(),
beforeEventBean.getClassName(), beforeEventBean.getObject(), method, beforeEventBean.getArgumentArray(),
returnEvent.object);
} catch (Exception e) {
// handle return or throw exception
if (e instanceof InterruptProcessException) {
InterruptProcessException exception = (InterruptProcessException)e;
if (exception.getState() == State.RETURN_IMMEDIATELY) {
ProcessControlException.throwReturnImmediately(exception.getResponse());
} else if (exception.getState() == State.THROWS_IMMEDIATELY) {
ProcessControlException.throwThrowsImmediately((Throwable)exception.getResponse());
}
} else {
throw e;
}
} finally {
beforeEventCache.remove();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.chaosblade.exec.bootstrap.jvmsandbox;

/**
* @author Changjun Xiao
*/
public class BeforeEventBean {

private ClassLoader classLoader;
private Object object;
private String className;
private String methodName;
private String methodDesc;
private Object[] argumentArray;

public BeforeEventBean(ClassLoader classLoader, Object object, String className, String methodName,
String methodDesc, Object[] argumentArray) {
this.classLoader = classLoader;
this.object = object;
this.className = className;
this.methodName = methodName;
this.methodDesc = methodDesc;
this.argumentArray = argumentArray;
}

public ClassLoader getClassLoader() {
return classLoader;
}

public Object getObject() {
return object;
}

public String getClassName() {
return className;
}

public String getMethodName() {
return methodName;
}

public String getMethodDesc() {
return methodDesc;
}

public Object[] getArgumentArray() {
return argumentArray;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,14 @@ public boolean doMethodFilter(int access, String javaMethodName,
public static EventListener createBeforeEventListener(Plugin plugin) {
return new BeforeEventListener(plugin);
}

/**
* Create the after event listener for handing the method result
*
* @param plugin
* @return
*/
public static EventListener createAfterEventListener(Plugin plugin) {
return new AfterEventListener(plugin);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import com.alibaba.jvm.sandbox.api.Module;
import com.alibaba.jvm.sandbox.api.ModuleLifecycle;
import com.alibaba.jvm.sandbox.api.event.Event;
import com.alibaba.jvm.sandbox.api.event.Event.Type;
import com.alibaba.jvm.sandbox.api.filter.Filter;
import com.alibaba.jvm.sandbox.api.http.Http;
import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher;

Expand Down Expand Up @@ -165,18 +167,32 @@ public void add(PluginBean plugin) {
return;
}
String enhancerName = plugin.getEnhancer().getClass().getSimpleName();
int watcherId = moduleEventWatcher.watch(
SandboxEnhancerFactory.createFilter(enhancerName, pointCut),
SandboxEnhancerFactory.createBeforeEventListener(plugin), Event.Type.BEFORE);
watchIds.put(PluginUtil.getIdentifier(plugin), watcherId);
Filter filter = SandboxEnhancerFactory.createFilter(enhancerName, pointCut);
// add after event listener. For the after event, the reason for adding the before event is to cache the
// necessary parameters.
if (plugin.isAfterEvent()) {
int watcherId = moduleEventWatcher.watch(filter, SandboxEnhancerFactory.createAfterEventListener(plugin),
Type.BEFORE, Type.RETURN);
watchIds.put(PluginUtil.getIdentifierForAfterEvent(plugin), watcherId);
} else {
int watcherId = moduleEventWatcher.watch(
filter, SandboxEnhancerFactory.createBeforeEventListener(plugin), Event.Type.BEFORE);
watchIds.put(PluginUtil.getIdentifier(plugin), watcherId);
}
}

@Override
public void delete(PluginBean plugin) {
if (plugin.getPointCut() == null) {
return;
}
String identifier = PluginUtil.getIdentifier(plugin);
String identifier;
// remove the after event plugin
if (plugin.isAfterEvent()) {
identifier = PluginUtil.getIdentifierForAfterEvent(plugin);
} else {
identifier = PluginUtil.getIdentifier(plugin);
}
Integer watcherId = watchIds.get(identifier);
if (watcherId != null) {
moduleEventWatcher.delete(watcherId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.chaosblade.exec.common.aop;

import java.lang.reflect.Method;

import com.alibaba.chaosblade.exec.common.center.ManagerFactory;
import com.alibaba.chaosblade.exec.common.injection.Injector;

/**
* @author Changjun Xiao
*/
public abstract class AfterEnhancer implements Enhancer {

@Override
public void beforeAdvice(String targetName, ClassLoader classLoader, String className, Object object, Method method,
Object[] methodArguments) throws Exception {
return;
}

@Override
public void afterAdvice(String targetName, ClassLoader classLoader, String className, Object object,
Method method, Object[] methodArguments, Object returnObject) throws Exception {
if (!ManagerFactory.getStatusManager().expExists(targetName)) {
return;
}
EnhancerModel model = doAfterAdvice(classLoader, className, object, method, methodArguments, returnObject);
if (model == null) {
return;
}
model.setTarget(targetName).setMethod(method).setObject(object).setMethodArguments(methodArguments);
Injector.inject(model);
}

public abstract EnhancerModel doAfterAdvice(ClassLoader classLoader, String className,
Object object, Method method, Object[] methodArguments,
Object returnObject) throws Exception;
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ public abstract class BeforeEnhancer implements Enhancer {
*/
@Override
public void beforeAdvice(String targetName, ClassLoader classLoader, String className, Object object,
Method method,
Object[] methodArguments)
throws Exception {
Method method, Object[] methodArguments) throws Exception {
if (!ManagerFactory.getStatusManager().expExists(targetName)) {
return;
}
Expand All @@ -65,7 +63,11 @@ public void beforeAdvice(String targetName, ClassLoader classLoader, String clas
* @throws Exception
*/
public abstract EnhancerModel doBeforeAdvice(ClassLoader classLoader, String className, Object object,
Method method,
Object[] methodArguments) throws Exception;
Method method, Object[] methodArguments) throws Exception;

@Override
public void afterAdvice(String targetName, ClassLoader classLoader, String className, Object object,
Method method, Object[] methodArguments, Object returnObject) throws Exception {
return;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,32 @@
public interface Enhancer {

/**
* 方法执行前,增强处理
* Enhanced processing before method execution
*
* @param targetName
* @param classLoader 类加载器
* @param classLoader
* @param className
* @param object 处理的对象
* @param method 对象的方法
* @param methodArguments 对象的方法参数
* @return 自定义结果
* @param object
* @param method
* @param methodArguments
* @throws Exception
*/
void beforeAdvice(String targetName, ClassLoader classLoader, String className, Object object,
Method method,
Object[] methodArguments)
Method method, Object[] methodArguments)
throws Exception;

/**
* Enhanced processing after method execution
*
* @param targetName
* @param classLoader
* @param className
* @param object
* @param method
* @param methodArguments
* @param returnObject
*/
void afterAdvice(String targetName, ClassLoader classLoader, String className, Object object,
Method method, Object[] methodArguments, Object returnObject)
throws Exception;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.alibaba.chaosblade.exec.common.aop;

import com.alibaba.chaosblade.exec.common.model.ModelSpec;
import com.alibaba.chaosblade.exec.common.plugin.MethodPlugin;

/**
* @author Changjun Xiao
Expand All @@ -27,12 +28,16 @@ public class PluginBean implements Plugin {
private ModelSpec modelSpec;
private PointCut pointCut;
private Enhancer enhancer;
private boolean isAfterEvent;

public PluginBean(Plugin plugin) {
this.name = plugin.getName();
this.modelSpec = plugin.getModelSpec();
this.pointCut = new PointCutBean(plugin.getPointCut());
this.enhancer = plugin.getEnhancer();
if (plugin instanceof MethodPlugin) {
this.isAfterEvent = ((MethodPlugin)plugin).isAfterEvent();
}
}

@Override
Expand All @@ -54,4 +59,8 @@ public PointCut getPointCut() {
public Enhancer getEnhancer() {
return this.enhancer;
}

public boolean isAfterEvent() {
return isAfterEvent;
}
}
Loading

0 comments on commit 49a4366

Please sign in to comment.