fly.li
3 years ago
70 changed files with 4633 additions and 3259 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,45 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.design.record.analyzer.advice.TimeAdvice; |
||||||
|
import com.fr.design.record.analyzer.advice.TrackAdvice; |
||||||
|
import com.fr.record.analyzer.AnalyzerConfiguration; |
||||||
|
import com.fr.record.analyzer.AnalyzerUnit; |
||||||
|
import com.fr.record.analyzer.Assistant; |
||||||
|
import com.fr.record.analyzer.Metrics; |
||||||
|
import com.fr.record.analyzer.Track; |
||||||
|
import com.fr.record.analyzer.configuration.AnalyzerAssemblyFactory; |
||||||
|
import com.fr.stable.ArrayUtils; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.description.type.TypeDescription; |
||||||
|
import com.fr.third.net.bytebuddy.dynamic.DynamicType; |
||||||
|
import com.fr.third.net.bytebuddy.matcher.ElementMatchers; |
||||||
|
import com.fr.third.net.bytebuddy.utility.JavaModule; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/08 |
||||||
|
**/ |
||||||
|
public class DesignerAnalyzer { |
||||||
|
|
||||||
|
private static final AnalyzerUnit ANALYZER = new AnalyzerUnit(); |
||||||
|
|
||||||
|
public static synchronized void init(AnalyzerAssemblyFactory factory, AnalyzerConfiguration... configurations) { |
||||||
|
|
||||||
|
AnalyzerAssemblyFactory redefineFactory = factory.prepare(DesignerAssemblyFactory.getInstance()); |
||||||
|
|
||||||
|
AnalyzerConfiguration defaultConfiguration = AnalyzerConfiguration.create(new Assistant() { |
||||||
|
@Override |
||||||
|
public DynamicType.Builder<?> supply(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { |
||||||
|
return builder |
||||||
|
.visit(Advice.to(TimeAdvice.class).on(ElementMatchers.isAnnotatedWith(Metrics.class))) |
||||||
|
.visit(Advice.to(TrackAdvice.class).on(ElementMatchers.isAnnotatedWith(Track.class))); |
||||||
|
} |
||||||
|
}); |
||||||
|
AnalyzerConfiguration[] allConfigurations = ArrayUtils.add(configurations, defaultConfiguration); |
||||||
|
|
||||||
|
// 准备监听
|
||||||
|
ANALYZER.setAgentListener(new DesignerAnalyzerListener()); |
||||||
|
|
||||||
|
ANALYZER.init(redefineFactory, allConfigurations); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.base.OptimizeUtil; |
||||||
|
import com.fr.concurrent.NamedThreadFactory; |
||||||
|
import com.fr.design.constants.DesignerLaunchStatus; |
||||||
|
import com.fr.design.record.analyzer.advice.DBMonitorAdvice; |
||||||
|
import com.fr.design.record.analyzer.advice.FaultToleranceAdvice; |
||||||
|
import com.fr.design.record.analyzer.advice.FocusAdvice; |
||||||
|
import com.fr.design.record.analyzer.advice.MonitorAdvice; |
||||||
|
import com.fr.design.record.analyzer.advice.PerformancePointAdvice; |
||||||
|
import com.fr.event.Event; |
||||||
|
import com.fr.event.EventDispatcher; |
||||||
|
import com.fr.event.Listener; |
||||||
|
import com.fr.event.Null; |
||||||
|
import com.fr.intelli.metrics.Compute; |
||||||
|
import com.fr.intelli.record.Focus; |
||||||
|
import com.fr.intelli.record.PerformancePoint; |
||||||
|
import com.fr.module.Activator; |
||||||
|
import com.fr.module.extension.Prepare; |
||||||
|
import com.fr.record.analyzer.AnalyzerConfiguration; |
||||||
|
import com.fr.record.analyzer.AnalyzerKey; |
||||||
|
import com.fr.record.analyzer.DBMetrics; |
||||||
|
import com.fr.record.analyzer.FineAnalyzer; |
||||||
|
import com.fr.record.analyzer.advice.AnalyzerAdviceKey; |
||||||
|
import com.fr.record.analyzer.advice.FineAdviceAssistant; |
||||||
|
import com.fr.record.analyzer.configuration.AnalyzerAssemblyFactory; |
||||||
|
import com.fr.record.analyzer.configuration.FineAnalyzerAssemblyFactory; |
||||||
|
import com.fr.stable.collections.CollectionUtils; |
||||||
|
import com.fr.third.net.bytebuddy.matcher.ElementMatchers; |
||||||
|
import com.fr.tolerance.FaultTolerance; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/04 |
||||||
|
**/ |
||||||
|
public class DesignerAnalyzerActivator extends Activator implements Prepare { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
|
||||||
|
OptimizeUtil.open(() -> { |
||||||
|
|
||||||
|
AnalyzerAssemblyFactory basicFactory = createBasicFactory(); |
||||||
|
|
||||||
|
// 兼容逻辑
|
||||||
|
List<AnalyzerConfiguration> backwardsConfigurations = findMutableBackwards(AnalyzerKey.KEY); |
||||||
|
if (!CollectionUtils.isEmpty(backwardsConfigurations)) { |
||||||
|
// 直接初始化,不添加默认值,防止和下面的冲突
|
||||||
|
FineAnalyzer.initDirectly(basicFactory, backwardsConfigurations.toArray(new AnalyzerConfiguration[0])); |
||||||
|
} |
||||||
|
|
||||||
|
// 等页面完全打开后,再进行 retransform, 别影响了启动速度
|
||||||
|
EventDispatcher.listen(DesignerLaunchStatus.STARTUP_COMPLETE, new Listener<Null>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void on(Event event, Null param) { |
||||||
|
|
||||||
|
ExecutorService es = newSingleThreadExecutor(new NamedThreadFactory("designer-analyzer", true)); |
||||||
|
try { |
||||||
|
// 加入 retransform 部分的逻辑
|
||||||
|
List<FineAdviceAssistant> adviceConfigurations = findMutable(AnalyzerAdviceKey.KEY); |
||||||
|
|
||||||
|
if (!CollectionUtils.isEmpty(adviceConfigurations)) { |
||||||
|
AnalyzerConfiguration[] configurations = convertConfigurations(adviceConfigurations); |
||||||
|
es.submit(() -> { |
||||||
|
DesignerAnalyzer.init(basicFactory, configurations); |
||||||
|
}); |
||||||
|
} |
||||||
|
} finally { |
||||||
|
es.shutdown(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private AnalyzerConfiguration[] convertConfigurations(List<FineAdviceAssistant> list) { |
||||||
|
|
||||||
|
return list.stream() |
||||||
|
.map(AnalyzerConfiguration::create) |
||||||
|
.toArray(AnalyzerConfiguration[]::new); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void stop() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void prepare() { |
||||||
|
|
||||||
|
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create( |
||||||
|
ElementMatchers.isAnnotatedWith(Focus.class), |
||||||
|
FocusAdvice.class |
||||||
|
)); |
||||||
|
|
||||||
|
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create( |
||||||
|
ElementMatchers.isAnnotatedWith(Compute.class), |
||||||
|
MonitorAdvice.class |
||||||
|
)); |
||||||
|
|
||||||
|
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create( |
||||||
|
ElementMatchers.isAnnotatedWith(DBMetrics.class), |
||||||
|
DBMonitorAdvice.class |
||||||
|
)); |
||||||
|
|
||||||
|
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create( |
||||||
|
ElementMatchers.isAnnotatedWith(PerformancePoint.class), |
||||||
|
PerformancePointAdvice.class |
||||||
|
)); |
||||||
|
|
||||||
|
addMutable(AnalyzerAdviceKey.KEY, FineAdviceAssistant.create( |
||||||
|
ElementMatchers.isAnnotatedWith(FaultTolerance.class), |
||||||
|
FaultToleranceAdvice.class |
||||||
|
)); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private AnalyzerAssemblyFactory createBasicFactory() { |
||||||
|
|
||||||
|
AnalyzerAssemblyFactory factory = findSingleton(AnalyzerAssemblyFactory.class); |
||||||
|
FineAnalyzerAssemblyFactory basicFactory = new FineAnalyzerAssemblyFactory(); |
||||||
|
basicFactory.prepare(factory); |
||||||
|
return basicFactory; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.record.analyzer.advice.AnalyzerAdvice; |
||||||
|
|
||||||
|
/** |
||||||
|
* 仅作为标志 |
||||||
|
* 没有方法 |
||||||
|
* |
||||||
|
* created by Harrison on 2022/03/04 |
||||||
|
**/ |
||||||
|
public interface DesignerAnalyzerAdvice extends AnalyzerAdvice { |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.third.net.bytebuddy.agent.builder.AgentBuilder; |
||||||
|
import com.fr.third.net.bytebuddy.description.type.TypeDescription; |
||||||
|
import com.fr.third.net.bytebuddy.dynamic.DynamicType; |
||||||
|
import com.fr.third.net.bytebuddy.utility.JavaModule; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/08 |
||||||
|
**/ |
||||||
|
public class DesignerAnalyzerListener extends AgentBuilder.Listener.Adapter { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) { |
||||||
|
FineLoggerFactory.getLogger().debug("Designer-Analyzer transform successfully:{}", typeDescription); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) { |
||||||
|
FineLoggerFactory.getLogger().error("Designer-Analyzer transform error:" + typeName); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.record.analyzer.configuration.AnalyzerAssemblyFactory; |
||||||
|
import com.fr.third.net.bytebuddy.agent.builder.AgentBuilder; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* 装配 Agent 为后置启动 |
||||||
|
* <p>必须在一个线程中处理 retransform 的事务,否则会阻塞整个的线程,导致效果不佳</p> |
||||||
|
* |
||||||
|
* created by Harrison on 2022/03/07 |
||||||
|
**/ |
||||||
|
public class DesignerAssemblyFactory implements AnalyzerAssemblyFactory<Void> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 每次执行 1 个 class 的 retransform |
||||||
|
*/ |
||||||
|
private static final int FIXED_SIZE = 1; |
||||||
|
|
||||||
|
/** |
||||||
|
* 单位 ms |
||||||
|
* 每次间隔 500 ms, 执行一次 |
||||||
|
*/ |
||||||
|
private static final int DELAY_INTERVAL = 500; |
||||||
|
|
||||||
|
private final AgentBuilder.RedefinitionStrategy.BatchAllocator batchAllocator = AgentBuilder.RedefinitionStrategy.BatchAllocator.ForFixedSize.ofSize(FIXED_SIZE); |
||||||
|
|
||||||
|
private final AgentBuilder.RedefinitionStrategy.Listener redefinitionListener = new DelayListener(DELAY_INTERVAL); |
||||||
|
|
||||||
|
public static DesignerAssemblyFactory getInstance() { |
||||||
|
return DesignerAssemblyFactoryHolder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class DesignerAssemblyFactoryHolder { |
||||||
|
private static final DesignerAssemblyFactory INSTANCE = new DesignerAssemblyFactory(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AnalyzerAssemblyFactory<Void> prepare(Void material) { |
||||||
|
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public AgentBuilder assembly(AgentBuilder raw) { |
||||||
|
|
||||||
|
return raw.disableClassFormatChanges() |
||||||
|
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) |
||||||
|
// 每次只 transform 一部分否则会导致 UI 变慢
|
||||||
|
.with(batchAllocator) |
||||||
|
.with(redefinitionListener) |
||||||
|
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE) |
||||||
|
.with(AgentBuilder.TypeStrategy.Default.REDEFINE); |
||||||
|
} |
||||||
|
|
||||||
|
private class DelayListener implements AgentBuilder.RedefinitionStrategy.Listener { |
||||||
|
|
||||||
|
/** |
||||||
|
* 单位 ms |
||||||
|
*/ |
||||||
|
private final int interval; |
||||||
|
|
||||||
|
public DelayListener(int interval) { |
||||||
|
this.interval = interval; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 执行完后,等待一段时间再执行。 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void onBatch(int index, List<Class<?>> batch, List<Class<?>> types) { |
||||||
|
|
||||||
|
try { |
||||||
|
Thread.sleep(interval); |
||||||
|
} catch (Exception ignore) { |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Iterable<? extends List<Class<?>>> onError(int index, List<Class<?>> batch, Throwable throwable, List<Class<?>> types) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onComplete(int amount, List<Class<?>> types, Map<List<Class<?>>, Throwable> failures) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.fr.design.record.analyzer.advice; |
||||||
|
|
||||||
|
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||||
|
import com.fr.general.data.DataModel; |
||||||
|
import com.fr.measure.DBMeterFactory; |
||||||
|
import com.fr.measure.metric.DBMetric; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/07 |
||||||
|
**/ |
||||||
|
public class DBMonitorAdvice implements DesignerAnalyzerAdvice { |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args) { |
||||||
|
|
||||||
|
if (args.length > 1 && args[1] instanceof DataModel) { |
||||||
|
DBMetric meter = ((DataModel) args[1]).getMetric(); |
||||||
|
DBMeterFactory.getMeter().record(meter); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
package com.fr.design.record.analyzer.advice; |
||||||
|
|
||||||
|
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||||
|
import com.fr.record.analyzer.advice.AdviceContext; |
||||||
|
import com.fr.record.analyzer.advice.DefaultAdviceCallable; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
import com.fr.tolerance.FaultTolerance; |
||||||
|
import com.fr.tolerance.FaultToleranceFactory; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/07 |
||||||
|
**/ |
||||||
|
public class FaultToleranceAdvice implements DesignerAnalyzerAdvice { |
||||||
|
|
||||||
|
@Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class) |
||||||
|
public static boolean onMethodEnter(@Advice.Local("context") AdviceContext adviceContext) throws Exception { |
||||||
|
|
||||||
|
adviceContext = AdviceContext |
||||||
|
.builder() |
||||||
|
.onAdviceCall() |
||||||
|
.build(); |
||||||
|
// 如果是切面调用,则忽视当前方法
|
||||||
|
return adviceContext.isOnAdviceCall(); |
||||||
|
} |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, |
||||||
|
@Advice.Origin Method method, |
||||||
|
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, |
||||||
|
@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result, |
||||||
|
@Advice.Local("context")AdviceContext adviceContext) throws Exception { |
||||||
|
|
||||||
|
// 如果是切面调用,则忽视不继续 exit
|
||||||
|
if (adviceContext != null && adviceContext.isOnAdviceCall()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
FaultTolerance faultTolerance = method.getAnnotation(FaultTolerance.class); |
||||||
|
Callable<Object> callable = new DefaultAdviceCallable<>(self, method, args); |
||||||
|
result = FaultToleranceFactory.getInstance().getScene(faultTolerance.scene()).getProcessor().execute(self, callable, args); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.fr.design.record.analyzer.advice; |
||||||
|
|
||||||
|
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||||
|
import com.fr.intelli.record.Focus; |
||||||
|
import com.fr.intelli.record.FocusPoint; |
||||||
|
import com.fr.intelli.record.FocusPolicy; |
||||||
|
import com.fr.log.counter.DefaultLimitedMetric; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/07 |
||||||
|
**/ |
||||||
|
public class FocusAdvice implements DesignerAnalyzerAdvice { |
||||||
|
|
||||||
|
private static final String FOCUS_POINT_ID_PREFIX = "function_"; |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.Origin Method method, |
||||||
|
@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result) throws Exception { |
||||||
|
|
||||||
|
if (FocusPolicy.IGNORE == result) { |
||||||
|
return; |
||||||
|
} |
||||||
|
Focus focus = method.getAnnotation(Focus.class); |
||||||
|
String id = FOCUS_POINT_ID_PREFIX + focus.id(); |
||||||
|
DefaultLimitedMetric.INSTANCE.submit(FocusPoint.create(id, focus.text(), focus.source()), id); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
package com.fr.design.record.analyzer.advice; |
||||||
|
|
||||||
|
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||||
|
import com.fr.general.GeneralUtils; |
||||||
|
import com.fr.intelli.measure.Estimator; |
||||||
|
import com.fr.intelli.metrics.Compute; |
||||||
|
import com.fr.intelli.metrics.SupervisoryConfig; |
||||||
|
import com.fr.intelli.record.Measurable; |
||||||
|
import com.fr.intelli.record.MeasureObject; |
||||||
|
import com.fr.intelli.record.MeasureUnit; |
||||||
|
import com.fr.intelli.record.MetricRegistry; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.measure.DBMeterFactory; |
||||||
|
import com.fr.stable.ArrayUtils; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.stable.web.Session; |
||||||
|
import com.fr.stable.web.SessionProvider; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
import com.fr.web.core.SessionPoolManager; |
||||||
|
import com.fr.web.session.SessionLocalManager; |
||||||
|
|
||||||
|
import java.lang.annotation.Annotation; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
import java.util.UUID; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/07 |
||||||
|
**/ |
||||||
|
public class MonitorAdvice implements DesignerAnalyzerAdvice { |
||||||
|
|
||||||
|
private static final Pattern P = Pattern.compile("-?\\d+"); |
||||||
|
private static final int MIN_ERROR_CODE = 10000000; |
||||||
|
|
||||||
|
@Advice.OnMethodEnter |
||||||
|
public static void onMethodEnter(@Advice.Origin Method method, |
||||||
|
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, |
||||||
|
@Advice.Local("startTime") Long startTime, |
||||||
|
@Advice.Local("registeredSession") Boolean registeredSession) { |
||||||
|
|
||||||
|
startTime = (System.currentTimeMillis()); |
||||||
|
registeredSession = (findSessionAnnotation(method, args)); |
||||||
|
} |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, |
||||||
|
@Advice.Origin Method method, |
||||||
|
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, |
||||||
|
@Advice.Thrown(typing = Assigner.Typing.DYNAMIC) Exception e, |
||||||
|
@Advice.Local("startTime") Long startTime, |
||||||
|
@Advice.Local("registeredSession") Boolean registeredSession) throws Exception { |
||||||
|
|
||||||
|
String error = StringUtils.EMPTY; |
||||||
|
|
||||||
|
try { |
||||||
|
|
||||||
|
if (e != null) { |
||||||
|
try { |
||||||
|
error = getErrorContent(e); |
||||||
|
} catch (Exception ignore) { |
||||||
|
} |
||||||
|
} |
||||||
|
} finally { |
||||||
|
try { |
||||||
|
if (self instanceof Measurable) { |
||||||
|
long consume = System.currentTimeMillis() - startTime; |
||||||
|
Compute once = method.getAnnotation(Compute.class); |
||||||
|
Measurable measurable = (Measurable) self; |
||||||
|
MeasureObject measureObject = MeasureObject.create(); |
||||||
|
recordMemory(once, measurable, measureObject); |
||||||
|
recordSQL(once, measureObject); |
||||||
|
measureObject.consume(consume); |
||||||
|
measureObject.error(error); |
||||||
|
String id = UUID.randomUUID().toString(); |
||||||
|
List<Object> newArgs = new ArrayList<>(Arrays.asList(args)); |
||||||
|
newArgs.add(id); |
||||||
|
recordSQLDetail(id); |
||||||
|
if (measurable instanceof Estimator) { |
||||||
|
measurable.asyncDurable(measureObject, newArgs.toArray()); |
||||||
|
} else { |
||||||
|
Object message = null; |
||||||
|
try { |
||||||
|
message = measurable.durableEntity(measureObject, newArgs.toArray()); |
||||||
|
} catch (Throwable throwable) { |
||||||
|
FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable); |
||||||
|
} |
||||||
|
if (message != null) { |
||||||
|
MetricRegistry.getMetric().submit(message); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (Exception ignore) { |
||||||
|
//埋点信息入库失败应该不能影响业务流程
|
||||||
|
} finally { |
||||||
|
if (registeredSession) { |
||||||
|
// 如果上面记录了,这里就要释放
|
||||||
|
SessionLocalManager.releaseSession(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static String getErrorContent(Exception e) { |
||||||
|
int errorCode = GeneralUtils.objectToNumber( |
||||||
|
extractCodeFromString(e.getMessage()) |
||||||
|
).intValue(); |
||||||
|
// 提取字符串中的第一个数字,最小的错误码为10000000
|
||||||
|
return e.getClass().getName() + ":" + (errorCode >= MIN_ERROR_CODE ? errorCode : StringUtils.EMPTY); |
||||||
|
} |
||||||
|
|
||||||
|
public static String extractCodeFromString(String errorMsg) { |
||||||
|
Matcher m = P.matcher(errorMsg); |
||||||
|
if (m.find()) { |
||||||
|
return m.group(); |
||||||
|
} |
||||||
|
return StringUtils.EMPTY; |
||||||
|
} |
||||||
|
|
||||||
|
public static void recordSQLDetail(String uuid) { |
||||||
|
DBMeterFactory.getMeter().submit(uuid); |
||||||
|
} |
||||||
|
|
||||||
|
public static void recordSQL(Compute once, MeasureObject measureObject) { |
||||||
|
if (SupervisoryConfig.getInstance().isEnableMeasureSql() && once.computeSql()) { |
||||||
|
measureObject.sqlTime(SessionLocalManager.getSqlTime()); |
||||||
|
measureObject.sql(SessionLocalManager.getSql()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void recordMemory(Compute once, Measurable measurable, MeasureObject measureObject) { |
||||||
|
if (SupervisoryConfig.getInstance().isEnableMeasureMemory() && once.computeMemory()) { |
||||||
|
MeasureUnit unit = measurable.measureUnit(); |
||||||
|
measureObject.memory(unit.measureMemory()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean findSessionAnnotation(Method method, Object[] args) { |
||||||
|
Annotation[][] all = method.getParameterAnnotations(); |
||||||
|
int len = ArrayUtils.getLength(args); |
||||||
|
for (int i = 0; i < len; i++) { |
||||||
|
Annotation[] current = all[i]; |
||||||
|
for (Annotation annotation : current) { |
||||||
|
if (annotation.annotationType().equals(Session.class)) { |
||||||
|
SessionLocalManager.setSession( |
||||||
|
SessionPoolManager.getSessionIDInfor(GeneralUtils.objectToString(args[i]), SessionProvider.class)); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
package com.fr.design.record.analyzer.advice; |
||||||
|
|
||||||
|
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||||
|
import com.fr.intelli.record.ConsumePoint; |
||||||
|
import com.fr.intelli.record.MetricRegistry; |
||||||
|
import com.fr.intelli.record.PerformancePoint; |
||||||
|
import com.fr.intelli.record.PerformancePointRecord; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/07 |
||||||
|
**/ |
||||||
|
public class PerformancePointAdvice implements DesignerAnalyzerAdvice { |
||||||
|
|
||||||
|
@Advice.OnMethodEnter |
||||||
|
public static void onMethodEnter(@Advice.Local("startTime") Long startTime) { |
||||||
|
|
||||||
|
startTime = (System.currentTimeMillis()); |
||||||
|
} |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, |
||||||
|
@Advice.Origin Method method, |
||||||
|
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, |
||||||
|
@Advice.Local("startTime") Long startTime) { |
||||||
|
|
||||||
|
PerformancePoint point = method.getAnnotation(PerformancePoint.class); |
||||||
|
String id = point.id(); |
||||||
|
long endTime = System.currentTimeMillis(); |
||||||
|
long consume = endTime - startTime; |
||||||
|
if (self instanceof PerformancePointRecord) { |
||||||
|
PerformancePointRecord measurable = (PerformancePointRecord) self; |
||||||
|
List<Object> newArgs = new ArrayList<Object>(Arrays.asList(args)); |
||||||
|
ConsumePoint consumePoint = ConsumePoint.create(id, startTime, endTime, consume, point.source()); |
||||||
|
MetricRegistry.getMetric().submit(measurable.recordPoint(consumePoint, newArgs.toArray())); |
||||||
|
} else { |
||||||
|
if (StringUtils.isNotEmpty(id)) { |
||||||
|
MetricRegistry.getMetric().submit(ConsumePoint.create(id, consume, point.source())); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package com.fr.design.record.analyzer.advice; |
||||||
|
|
||||||
|
import com.fr.design.record.analyzer.DesignerAnalyzerAdvice; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.record.analyzer.Metrics; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/08 |
||||||
|
**/ |
||||||
|
public class TimeAdvice implements DesignerAnalyzerAdvice { |
||||||
|
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter |
||||||
|
public static void onMethodEnter(@Advice.Local("startTime") Long startTime) { |
||||||
|
|
||||||
|
startTime = (System.currentTimeMillis()); |
||||||
|
} |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.Origin Method method, |
||||||
|
@Advice.Local("startTime") Long startTime) { |
||||||
|
|
||||||
|
Metrics metrics = method.getAnnotation(Metrics.class); |
||||||
|
Object prefix; |
||||||
|
String description = metrics.description(); |
||||||
|
if ("".equals(description)) { |
||||||
|
prefix = method.getDeclaringClass().getName() + "#" + method.getName(); |
||||||
|
} else { |
||||||
|
prefix = description; |
||||||
|
} |
||||||
|
FineLoggerFactory.getLogger().info("{} took {} ms.", prefix, System.currentTimeMillis() - startTime); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.fr.design.record.analyzer.advice; |
||||||
|
|
||||||
|
import com.fr.intelli.record.MetricRegistry; |
||||||
|
import com.fr.third.javax.persistence.Entity; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/08 |
||||||
|
**/ |
||||||
|
public class TrackAdvice { |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result) { |
||||||
|
|
||||||
|
if (result != null) { |
||||||
|
Class clazz = result.getClass(); |
||||||
|
if (clazz.getAnnotation(Entity.class) != null || result instanceof List) { |
||||||
|
MetricRegistry.getMetric().submit(result); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.fr.env.utils; |
||||||
|
|
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.env.DesignerWorkspaceInfo; |
||||||
|
import com.fr.design.env.LocalDesignerWorkspaceInfo; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2022/3/10 |
||||||
|
*/ |
||||||
|
public class WorkspaceUtils { |
||||||
|
|
||||||
|
private static final String SPECIFY_WORKSPACE = "fr.designer.workspace"; |
||||||
|
|
||||||
|
public static DesignerWorkspaceInfo getWorkspaceInfo() { |
||||||
|
String workspacePath; |
||||||
|
String current = DesignerEnvManager.getEnvManager().getCurEnvName(); |
||||||
|
if (StringUtils.isNotEmpty(workspacePath = System.getProperty(SPECIFY_WORKSPACE))) { |
||||||
|
return LocalDesignerWorkspaceInfo.create(StringUtils.EMPTY, workspacePath); |
||||||
|
} else { |
||||||
|
return DesignerEnvManager.getEnvManager().getWorkspaceInfo(current); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,111 @@ |
|||||||
|
package com.fr.exit; |
||||||
|
|
||||||
|
import com.fr.config.dao.PropertiesConstants; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.CommonUtils; |
||||||
|
import com.fr.stable.StableUtils; |
||||||
|
import com.fr.stable.project.ProjectConstants; |
||||||
|
import com.fr.workspace.WorkContext; |
||||||
|
import java.io.File; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.sql.Blob; |
||||||
|
import java.sql.Connection; |
||||||
|
import java.sql.DriverManager; |
||||||
|
import java.sql.PreparedStatement; |
||||||
|
import java.sql.ResultSet; |
||||||
|
import java.sql.SQLException; |
||||||
|
import java.util.Properties; |
||||||
|
|
||||||
|
/** |
||||||
|
* 设计器关闭前的配置缓存一份到Properties |
||||||
|
* |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2022/3/1 |
||||||
|
*/ |
||||||
|
public class ConfigToPropMigrator { |
||||||
|
|
||||||
|
private static final String SELECT_FOR_ENTITY = "select id, value from fine_conf_entity"; |
||||||
|
|
||||||
|
private static final String SELECT_FOR_CLASSNAME = "select id, classname from fine_conf_classname"; |
||||||
|
|
||||||
|
private static final String SELECT_FOR_XML_ENTITY = "select id, value from fine_conf_xmlentity"; |
||||||
|
|
||||||
|
private static final ConfigToPropMigrator INSTANCE = new ConfigToPropMigrator(); |
||||||
|
|
||||||
|
public static ConfigToPropMigrator getInstance() { |
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
public void execute() { |
||||||
|
|
||||||
|
if (WorkContext.getCurrent().isLocal()) { |
||||||
|
|
||||||
|
String url = "jdbc:hsqldb:file://" + WorkContext.getCurrent().getPath() + "/" + ProjectConstants.EMBED_DB_DIRECTORY + "/finedb/db;hsqldb.tx=mvcc"; |
||||||
|
|
||||||
|
try { |
||||||
|
Class.forName("com.fr.third.org.hsqldb.jdbcDriver"); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
return ; |
||||||
|
} |
||||||
|
|
||||||
|
initDirectory(); |
||||||
|
|
||||||
|
try (Connection c = DriverManager.getConnection(url); |
||||||
|
FileOutputStream entityOut = new FileOutputStream(PropertiesConstants.ENTITY_PROP_PATH); |
||||||
|
FileOutputStream classHelperOut = new FileOutputStream(PropertiesConstants.CLASS_NAME_PROP_PATH); |
||||||
|
FileOutputStream xmlEntityOut = new FileOutputStream(PropertiesConstants.XML_ENTITY_PROP_PATH)) { |
||||||
|
|
||||||
|
processClassOrEntity(c, new Properties(), SELECT_FOR_ENTITY, entityOut); |
||||||
|
processClassOrEntity(c, new Properties(), SELECT_FOR_CLASSNAME, classHelperOut); |
||||||
|
processXmlEntity(c, new Properties(), xmlEntityOut); |
||||||
|
DesignerEnvManager.getEnvManager().setPropertiesUsable(true); |
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
||||||
|
deletePropertiesCache(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initDirectory() { |
||||||
|
File directory = new File(StableUtils.pathJoin(WorkContext.getCurrent().getPath(), ProjectConstants.EMBED_DB_DIRECTORY, ProjectConstants.PROPERTIES_CACHE_FOR_CONFIG)); |
||||||
|
if (!directory.exists()) { |
||||||
|
directory.mkdir(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void processClassOrEntity(Connection c, Properties map, String sql, FileOutputStream outputStream) throws SQLException, IOException { |
||||||
|
PreparedStatement query = c.prepareStatement(sql); |
||||||
|
ResultSet resultSet = query.executeQuery(); |
||||||
|
while (resultSet.next()) { |
||||||
|
String id = resultSet.getString(1); |
||||||
|
String value = resultSet.getString(2); |
||||||
|
if (id != null && value != null) { |
||||||
|
map.setProperty(id, value); |
||||||
|
} |
||||||
|
} |
||||||
|
map.store(outputStream, null); |
||||||
|
} |
||||||
|
|
||||||
|
private void processXmlEntity(Connection c, Properties map, FileOutputStream outputStream) throws SQLException, IOException { |
||||||
|
PreparedStatement query = c.prepareStatement(SELECT_FOR_XML_ENTITY); |
||||||
|
ResultSet resultSet = query.executeQuery(); |
||||||
|
while (resultSet.next()) { |
||||||
|
String id = resultSet.getString(1); |
||||||
|
Blob value = resultSet.getBlob(2); |
||||||
|
byte[] bytes = value.getBytes(1L, (int) value.length()); |
||||||
|
map.setProperty(id, new String(bytes)); |
||||||
|
} |
||||||
|
map.store(outputStream, null); |
||||||
|
} |
||||||
|
|
||||||
|
public void deletePropertiesCache() { |
||||||
|
CommonUtils.deleteFile(new File(PropertiesConstants.ENTITY_PROP_PATH)); |
||||||
|
CommonUtils.deleteFile(new File(PropertiesConstants.XML_ENTITY_PROP_PATH)); |
||||||
|
CommonUtils.deleteFile(new File(PropertiesConstants.CLASS_NAME_PROP_PATH)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.fr.start.event; |
||||||
|
|
||||||
|
import com.fr.event.Event; |
||||||
|
import com.fr.event.Null; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2022/3/7 |
||||||
|
*/ |
||||||
|
public enum LazyStartupEvent implements Event<Null> { |
||||||
|
|
||||||
|
INSTANCE |
||||||
|
} |
@ -0,0 +1,139 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.third.net.bytebuddy.ByteBuddy; |
||||||
|
import com.fr.third.net.bytebuddy.agent.ByteBuddyAgent; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.dynamic.loading.ClassReloadingStrategy; |
||||||
|
import com.fr.third.net.bytebuddy.matcher.ElementMatchers; |
||||||
|
import com.fr.third.org.apache.commons.lang3.time.StopWatch; |
||||||
|
import org.junit.Assert; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* 测试一下,通过 redefine 去处理代码时 |
||||||
|
* 相应的 advice 应该怎么写 |
||||||
|
*/ |
||||||
|
public class BytebuddyRedefineTest { |
||||||
|
|
||||||
|
/** |
||||||
|
* 测试一下是否可以直接抛出异常 |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void testThrowException() { |
||||||
|
|
||||||
|
try { |
||||||
|
ByteBuddyAgent.install(); |
||||||
|
|
||||||
|
new ByteBuddy() |
||||||
|
.redefine(TestClass.class) |
||||||
|
.visit(Advice.to(TestThrowExceptionAdvice.class).on(ElementMatchers.named("testPrint"))) |
||||||
|
.make() |
||||||
|
.load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); |
||||||
|
|
||||||
|
TestClass testClass = new TestClass(); |
||||||
|
testClass.testPrint(); |
||||||
|
} catch (Throwable throwable) { |
||||||
|
Assert.assertNotNull("expected throw exception", throwable); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 测试是否可以直接传值 |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void testTransferValue() { |
||||||
|
|
||||||
|
ByteBuddyAgent.install(); |
||||||
|
|
||||||
|
new ByteBuddy() |
||||||
|
.redefine(TestClass.class) |
||||||
|
.visit(Advice.to(TestTransferValueAdvice.class).on(ElementMatchers.named("testPrint"))) |
||||||
|
.make() |
||||||
|
.load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); |
||||||
|
|
||||||
|
TestClass testClass = new TestClass(); |
||||||
|
String print = testClass.testPrint(); |
||||||
|
|
||||||
|
Assert.assertEquals(10, TestTransferValueAdvice.intField); |
||||||
|
Assert.assertEquals("[test]stringField", TestTransferValueAdvice.stringField); |
||||||
|
Assert.assertEquals("[test]objectField", TestTransferValueAdvice.objectField); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 测试是否可以改变返回值 |
||||||
|
*/ |
||||||
|
@Test |
||||||
|
public void testModifyReturn() { |
||||||
|
|
||||||
|
ByteBuddyAgent.install(); |
||||||
|
|
||||||
|
new ByteBuddy() |
||||||
|
.redefine(TestClass.class) |
||||||
|
.visit(Advice.to(TestModifyReturnAdvice.class).on(ElementMatchers.named("testPrint"))) |
||||||
|
.make() |
||||||
|
.load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); |
||||||
|
|
||||||
|
TestClass testClass = new TestClass(); |
||||||
|
String print = testClass.testPrint(); |
||||||
|
|
||||||
|
Assert.assertEquals("[test]Modify Return Value", print); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testCallable() throws Exception { |
||||||
|
|
||||||
|
ByteBuddyAgent.install(); |
||||||
|
|
||||||
|
new ByteBuddy() |
||||||
|
.redefine(TestClass.class) |
||||||
|
.visit(Advice.to(TestCallableAdvice.class).on(ElementMatchers.named("testPrint"))) |
||||||
|
.make() |
||||||
|
.load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); |
||||||
|
|
||||||
|
TestClass testClass = new TestClass(); |
||||||
|
String print = testClass.testPrint(); |
||||||
|
|
||||||
|
Assert.assertEquals("[test]Callable", print); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testCallablePerformance() throws Exception { |
||||||
|
|
||||||
|
// 千
|
||||||
|
int loop = 1000; |
||||||
|
StopWatch stopWatch = new StopWatch(); |
||||||
|
|
||||||
|
stopWatch.start(); |
||||||
|
TestClass rawClass = new TestClass(); |
||||||
|
for (int i = 0; i < loop; i++) { |
||||||
|
rawClass.testPrint(); |
||||||
|
} |
||||||
|
System.out.printf("raw class run %s cost %s ms \n", loop, stopWatch.getTime(TimeUnit.MILLISECONDS)); |
||||||
|
|
||||||
|
|
||||||
|
ByteBuddyAgent.install(); |
||||||
|
|
||||||
|
new ByteBuddy() |
||||||
|
.redefine(TestClass.class) |
||||||
|
.visit(Advice.to(TestCallableAdvice.class).on(ElementMatchers.named("testPrint"))) |
||||||
|
.make() |
||||||
|
.load(TestClass.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent()); |
||||||
|
|
||||||
|
stopWatch.reset(); |
||||||
|
stopWatch.start(); |
||||||
|
|
||||||
|
TestClass retransformClass = new TestClass(); |
||||||
|
for (int i = 0; i < loop; i++) { |
||||||
|
retransformClass.testPrint(); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.printf("retransformClass class run %s cost %s ms \n", loop, stopWatch.getTime(TimeUnit.MILLISECONDS)); |
||||||
|
|
||||||
|
Assert.assertEquals("[test]Callable", retransformClass.testPrint()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.record.analyzer.advice.AdviceCallable; |
||||||
|
import com.fr.record.analyzer.advice.AdviceContext; |
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
import com.fr.tolerance.FaultTolerance; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/09 |
||||||
|
**/ |
||||||
|
public class TestCallableAdvice { |
||||||
|
|
||||||
|
@Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class) |
||||||
|
public static boolean onMethodEnter(@Advice.Local("context")AdviceContext adviceContext) { |
||||||
|
|
||||||
|
adviceContext = AdviceContext |
||||||
|
.builder() |
||||||
|
.onAdviceCall() |
||||||
|
.build(); |
||||||
|
// 如果是切面调用,则忽视当前方法
|
||||||
|
return adviceContext.isOnAdviceCall(); |
||||||
|
} |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, |
||||||
|
@Advice.Origin Method method, |
||||||
|
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, |
||||||
|
@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result, |
||||||
|
@Advice.Local("context") AdviceContext adviceContext) throws Exception { |
||||||
|
|
||||||
|
// 如果是切面调用,则忽视不继续 exit
|
||||||
|
if (adviceContext != null && adviceContext.isOnAdviceCall()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
FaultTolerance faultTolerance = method.getAnnotation(FaultTolerance.class); |
||||||
|
Callable<Object> callable = new AdviceCallable<Object>() { |
||||||
|
@Override |
||||||
|
public Object call() throws Exception { |
||||||
|
return method.invoke(self, args); |
||||||
|
} |
||||||
|
}; |
||||||
|
result = TestCallableHelper.test(callable); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import java.util.concurrent.Callable; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/09 |
||||||
|
**/ |
||||||
|
public class TestCallableHelper { |
||||||
|
|
||||||
|
public static String test(Callable<Object> callable) throws Exception { |
||||||
|
|
||||||
|
callable.call(); |
||||||
|
return "[test]Callable"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/04 |
||||||
|
**/ |
||||||
|
public class TestClass { |
||||||
|
|
||||||
|
public String testPrint() { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/07 |
||||||
|
**/ |
||||||
|
public class TestModifyReturnAdvice { |
||||||
|
|
||||||
|
@Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class) |
||||||
|
public static int onMethodEnter() throws Exception { |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.This(optional = true, typing = Assigner.Typing.DYNAMIC) Object self, |
||||||
|
@Advice.Origin Method method, |
||||||
|
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] args, |
||||||
|
@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result) throws Exception { |
||||||
|
|
||||||
|
result = "[test]Modify Return Value"; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
import com.fr.third.net.bytebuddy.implementation.bytecode.assign.Assigner; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/07 |
||||||
|
**/ |
||||||
|
public class TestThrowExceptionAdvice { |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.Origin Method method, |
||||||
|
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] arguments, |
||||||
|
@Advice.Thrown(typing = Assigner.Typing.DYNAMIC) Exception e) throws Exception { |
||||||
|
throw new RuntimeException("[test] throw exception in advice"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
package com.fr.design.record.analyzer; |
||||||
|
|
||||||
|
import com.fr.third.net.bytebuddy.asm.Advice; |
||||||
|
|
||||||
|
/** |
||||||
|
* created by Harrison on 2022/03/07 |
||||||
|
**/ |
||||||
|
public class TestTransferValueAdvice { |
||||||
|
|
||||||
|
public static int intField; |
||||||
|
|
||||||
|
public static String stringField; |
||||||
|
|
||||||
|
public static Object objectField; |
||||||
|
|
||||||
|
@Advice.OnMethodEnter |
||||||
|
public static void onMethodEnter(@Advice.Local("int") int intField, |
||||||
|
@Advice.Local("string") String stringField, |
||||||
|
@Advice.Local("Object") Object objectField) { |
||||||
|
|
||||||
|
intField = 10; |
||||||
|
stringField = "[test]stringField"; |
||||||
|
objectField = "[test]objectField"; |
||||||
|
} |
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Exception.class) |
||||||
|
public static void onMethodExit(@Advice.Local("int") int intField, |
||||||
|
@Advice.Local("string") String stringField, |
||||||
|
@Advice.Local("Object") Object objectField) throws Exception { |
||||||
|
|
||||||
|
TestTransferValueAdvice.intField = intField; |
||||||
|
TestTransferValueAdvice.stringField = stringField; |
||||||
|
TestTransferValueAdvice.objectField = objectField; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
package com.fr.start.module.optimized; |
||||||
|
|
||||||
|
import com.fr.config.activator.BaseDBActivator; |
||||||
|
import com.fr.config.dao.DaoSelectorFactory; |
||||||
|
import com.fr.event.Event; |
||||||
|
import com.fr.event.Listener; |
||||||
|
import com.fr.event.Null; |
||||||
|
import com.fr.start.event.LazyStartupEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2022/3/7 |
||||||
|
*/ |
||||||
|
public class BaseDBActivator4Designer extends BaseDBActivator { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
if (DaoSelectorFactory.getDaoSelector().useCacheDao()) { |
||||||
|
listenEvent(LazyStartupEvent.INSTANCE, new Listener<Null>(Integer.MAX_VALUE) { |
||||||
|
@Override |
||||||
|
public void on(Event event, Null param) { |
||||||
|
BaseDBActivator4Designer.super.start(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} else { |
||||||
|
super.start(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.fr.start.module.optimized; |
||||||
|
|
||||||
|
import com.fr.config.activator.ConfigurationActivator; |
||||||
|
import com.fr.config.dao.DaoContext; |
||||||
|
import com.fr.config.dao.DaoSelectorFactory; |
||||||
|
import com.fr.config.dao.impl.PropertiesClassHelperDao; |
||||||
|
import com.fr.config.dao.impl.PropertiesEntityDao; |
||||||
|
import com.fr.config.dao.impl.PropertiesXmlEntityDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2022/3/7 |
||||||
|
*/ |
||||||
|
public class ConfigurationActivator4Designer extends ConfigurationActivator { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initLocalDao() { |
||||||
|
if (DaoSelectorFactory.getDaoSelector().useCacheDao()) { |
||||||
|
DaoContext.setClassHelperDao(new PropertiesClassHelperDao()); |
||||||
|
DaoContext.setEntityDao(new PropertiesEntityDao()); |
||||||
|
DaoContext.setXmlEntityDao(new PropertiesXmlEntityDao()); |
||||||
|
} else { |
||||||
|
super.initLocalDao(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.fr.start.module.optimized; |
||||||
|
|
||||||
|
import com.fr.config.dao.DaoSelectorFactory; |
||||||
|
import com.fr.event.Event; |
||||||
|
import com.fr.event.Listener; |
||||||
|
import com.fr.event.Null; |
||||||
|
import com.fr.report.module.ReportBaseActivator; |
||||||
|
import com.fr.start.event.LazyStartupEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2022/3/16 |
||||||
|
*/ |
||||||
|
public class ReportBaseActivator4Designer extends ReportBaseActivator { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void vcsInit() { |
||||||
|
if (DaoSelectorFactory.getDaoSelector().useCacheDao()) { |
||||||
|
listenEvent(LazyStartupEvent.INSTANCE, new Listener<Null>() { |
||||||
|
@Override |
||||||
|
public void on(Event event, Null param) { |
||||||
|
ReportBaseActivator4Designer.super.vcsInit(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} else { |
||||||
|
super.vcsInit(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
package com.fr.start.module.optimized; |
||||||
|
|
||||||
|
import com.fr.config.dao.DaoSelectorFactory; |
||||||
|
import com.fr.config.dao.swicter.DaoSwitcher; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.event.Event; |
||||||
|
import com.fr.event.Listener; |
||||||
|
import com.fr.event.Null; |
||||||
|
import com.fr.exit.ConfigToPropMigrator; |
||||||
|
import com.fr.stable.db.tenant.TenantDBAdapter; |
||||||
|
import com.fr.start.event.LazyStartupEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author hades |
||||||
|
* @version 11.0 |
||||||
|
* Created by hades on 2022/3/7 |
||||||
|
*/ |
||||||
|
public class TenantDBAdapter4Designer extends TenantDBAdapter { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
if (DaoSelectorFactory.getDaoSelector().useCacheDao()) { |
||||||
|
listenEvent(LazyStartupEvent.INSTANCE, new Listener<Null>() { |
||||||
|
@Override |
||||||
|
public void on(Event event, Null param) { |
||||||
|
TenantDBAdapter4Designer.super.start(); |
||||||
|
afterStart(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
} else { |
||||||
|
super.start(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void afterStart() { |
||||||
|
DesignerEnvManager.getEnvManager().setPropertiesUsable(false); |
||||||
|
DaoSwitcher.executeSwitch(); |
||||||
|
ConfigToPropMigrator.getInstance().deletePropertiesCache(); |
||||||
|
DesignerEnvManager.getEnvManager().saveXMLFile(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue