Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: merge record dynamicClass #341

Merged
merged 7 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static void initialize(Instrumentation inst, File agentFile, String agent
File[] extensionFiles = getExtensionJarFiles(agentFile);
classLoader = createAgentClassLoader(agentFile, extensionFiles);
InstrumentationHolder.setAgentClassLoader(classLoader);
InstrumentationHolder.setInstrumentation(inst);
AgentInstaller installer = createAgentInstaller(inst, agentFile, agentArgs);
addJarToLoaderSearch(agentFile, extensionFiles);
installer.install();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,22 @@
import java.lang.ref.WeakReference;
import java.util.concurrent.*;

class WeakCache<K, V> extends ReferenceQueue<K> implements Cache<K, V> {
public class WeakCache<K, V> extends ReferenceQueue<K> implements Cache<K, V> {
final ConcurrentMap<WeakReferenceKey<K>, V> target;

final CleanUpTask<V> cleanUpTask;

public WeakCache() {
this(new ConcurrentHashMap<>());
this(new ConcurrentHashMap<>(), null);
}

public WeakCache(CleanUpTask<V> cleanUpTask) {
this(new ConcurrentHashMap<>(), cleanUpTask);
}

public WeakCache(ConcurrentMap<WeakReferenceKey<K>, V> target) {
public WeakCache(ConcurrentMap<WeakReferenceKey<K>, V> target, CleanUpTask<V> cleanUpTask) {
this.target = target;
this.cleanUpTask = cleanUpTask;
}

public V get(K key) {
Expand All @@ -38,20 +45,31 @@ public void clear() {
target.clear();
}

public boolean containsKey(K key) {
check();
return target.containsKey(new WeakReferenceKey<>(key));
}

void check() {
Reference<?> reference;
while ((reference = poll()) != null) {
target.remove(reference);
final V value = target.remove(reference);
if (cleanUpTask != null && value != null) {
cleanUpTask.cleanUp(value);
}
}
}

static final class WeakReferenceKey<K> extends WeakReference<K> {
private final int hashCode;

WeakReferenceKey(K key) {
this(key, null);
}

WeakReferenceKey(K key, ReferenceQueue<? super K> queue) {
super(key, queue);

hashCode = System.identityHashCode(key);
hashCode = (key == null) ? 0 : System.identityHashCode(key);
}

@Override
Expand All @@ -68,5 +86,12 @@ public boolean equals(Object other) {
return other != null && other.equals(this);
}
}

public interface CleanUpTask<T> {
/**
* @param object object to cleanup
*/
void cleanUp(T object);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class ArexMocker implements Mocker {
private long creationTime;
private Mocker.Target targetRequest;
private Mocker.Target targetResponse;
private boolean merge;
private String operationName;

public ArexMocker() {
Expand Down Expand Up @@ -116,4 +117,12 @@ public void setTargetResponse(Mocker.Target targetResponse) {
public void setOperationName(String operationName) {
this.operationName = operationName;
}

public boolean isMerge() {
return merge;
}

public void setMerge(boolean merge) {
this.merge = merge;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static MockCategoryType createDependency(String name) {

public static MockCategoryType create(String name, boolean entryPoint, boolean skipComparison) {
return CATEGORY_TYPE_MAP.computeIfAbsent(name,
key -> new MockCategoryType(name, entryPoint, skipComparison));
key -> new MockCategoryType(name, entryPoint, skipComparison));
}

public static Collection<MockCategoryType> values() {
Expand Down Expand Up @@ -78,6 +78,9 @@ private MockCategoryType(String name, boolean entryPoint, boolean skipComparison
this.skipComparison = skipComparison;
}

public static MockCategoryType of(String name) {
return CATEGORY_TYPE_MAP.get(name);
}

@Override
public String toString() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,8 @@ default String recordLogTitle() {
default String replayLogTitle() {
return "replay." + getCategoryType().getName();
}

boolean isMerge();

void setMerge(boolean merge);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.function.Function;

public class ArrayUtils {
public static final String[] EMPTY_STRING_ARRAY = new String[0];
private ArrayUtils() {}

public static byte[] addAll(final byte[] array1, final byte... array2) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package io.arex.agent.bootstrap.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.*;

public class CollectionUtil {
private static final List<?> EMPTY_LIST = newArrayList();
Expand Down Expand Up @@ -41,4 +38,42 @@ public static <E> List<E> newArrayList(E... elements) {
Collections.addAll(list, elements);
return list;
}

/**
* split to multiple list by split count
*/
public static <V> List<List<V>> split(List<V> originalList, int splitCount) {
List<List<V>> splitList = new ArrayList<>();
if (isEmpty(originalList)) {
return splitList;
}
int originalSize = originalList.size();
if (originalSize < splitCount || splitCount == 0) {
splitList.add(originalList);
return splitList;
}
for (int i = 0; i < splitCount; i++) {
List<V> list = new ArrayList<>();
splitList.add(list);
}
int index = 0;
for (V value : originalList) {
splitList.get(index).add(value);
index = (index + 1) % splitCount;
}
return splitList;
}

public static <V> List<V> filterNull(List<V> originalList) {
List<V> filterList = new ArrayList<>();
if (isEmpty(originalList)) {
return filterList;
}
for (V element : originalList) {
if (element != null) {
filterList.add(element);
}
}
return filterList;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package io.arex.agent.bootstrap.util;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.params.provider.Arguments.arguments;

import java.util.*;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

class CollectionUtilTest {

Expand Down Expand Up @@ -33,4 +39,33 @@ void newArrayList() {
actualResult = CollectionUtil.newArrayList("test");
assertInstanceOf(ArrayList.class, actualResult);
}

@ParameterizedTest
@MethodSource("splitCase")
void split(List<String> originalList, int splitCount, Predicate<List<List<String>>> predicate) {
assertTrue(predicate.test(CollectionUtil.split(originalList, splitCount)));
}

static Stream<Arguments> splitCase() {
Supplier<List<String>> lessSplitCountList = () -> CollectionUtil.newArrayList("mock");
Supplier<List<String>> normalSplitCountList = () -> CollectionUtil.newArrayList("mock1", "mock2");

Predicate<List<List<String>>> empty = CollectionUtil::isEmpty;
Predicate<List<List<String>>> notEmpty = CollectionUtil::isNotEmpty;

return Stream.of(
arguments(null, 1, empty),
arguments(lessSplitCountList.get(), 2, notEmpty),
arguments(normalSplitCountList.get(), 2, notEmpty)
);
}

@Test
void filterNull() {
List<String> actualResult = CollectionUtil.filterNull(null);
assertEquals(0, actualResult.size());

actualResult = CollectionUtil.filterNull(CollectionUtil.newArrayList("mock"));
assertEquals(1, actualResult.size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import io.arex.agent.bootstrap.util.ConcurrentHashSet;
import io.arex.agent.bootstrap.util.StringUtil;
import io.arex.inst.runtime.model.ArexConstants;
import io.arex.inst.runtime.model.MergeDTO;
import io.arex.inst.runtime.util.MergeRecordReplayUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

public class ArexContext {
Expand All @@ -18,11 +18,13 @@ public class ArexContext {
private final long createTime;
private final AtomicInteger sequence;
private Set<Integer> methodSignatureHashList;
private Map<String, Object> cachedReplayResultMap;
private Map<Integer, List<MergeDTO>> cachedReplayResultMap;
private Map<String, Set<String>> excludeMockTemplate;

private Map<String, Object> attachments = null;

private LinkedBlockingQueue<MergeDTO> mergeRecordQueue;

private boolean isRedirectRequest;
private boolean isInvalidCase;

Expand Down Expand Up @@ -72,7 +74,7 @@ public Set<Integer> getMethodSignatureHashList() {
return methodSignatureHashList;
}

public Map<String, Object> getCachedReplayResultMap() {
public Map<Integer, List<MergeDTO>> getCachedReplayResultMap() {
if (cachedReplayResultMap == null) {
cachedReplayResultMap = new ConcurrentHashMap<>();
}
Expand Down Expand Up @@ -138,6 +140,13 @@ public boolean isRedirectRequest(String referer) {
return isRedirectRequest;
}

public LinkedBlockingQueue<MergeDTO> getMergeRecordQueue() {
if (mergeRecordQueue == null) {
mergeRecordQueue = new LinkedBlockingQueue<>(2048);
}
return mergeRecordQueue;
}

public void clear() {
if (methodSignatureHashList != null) {
methodSignatureHashList.clear();
Expand All @@ -151,5 +160,10 @@ public void clear() {
if (attachments != null) {
attachments.clear();
}
if (mergeRecordQueue != null) {
// async thread merge record (main entry has ended)
MergeRecordReplayUtil.recordRemain(this);
mergeRecordQueue.clear();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public static void registerListener(ContextListener listener) {

private static void publish(ArexContext context, boolean isCreate) {
if (CollectionUtil.isNotEmpty(LISTENERS)) {
LISTENERS.stream().forEach(listener -> {
LISTENERS.forEach(listener -> {
if (isCreate) {
listener.onCreate(context);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.arex.inst.runtime.match;

import io.arex.agent.bootstrap.model.Mocker;
import io.arex.inst.runtime.log.LogManager;
import io.arex.inst.runtime.model.MergeDTO;

public abstract class AbstractMatchStrategy {
static final String MATCH_TITLE = "replay.match.fail";
static final int ACCURATE_MATCH_ORDER = 10;
static final int FUZZY_MATCH_ORDER = 20;
static final int EIGEN_MATCH_ORDER = 30;

public void match(MatchStrategyContext context) {
try {
if (check(context)) {
process(context);
}
} catch (Exception e) {
LogManager.warn(MATCH_TITLE, e);
}
}

private boolean check(MatchStrategyContext context) {
if (context == null || context.getRequestMocker() == null || context.isInterrupt()) {
return false;
}
return valid(context);
}

boolean valid(MatchStrategyContext context) {
return true;
}
abstract int order();
abstract void process(MatchStrategyContext context) throws Exception;

Mocker buildMatchedMocker(Mocker requestMocker, MergeDTO mergeReplayDTO) {
if (mergeReplayDTO == null) {
return null;
}
requestMocker.getTargetResponse().setBody(mergeReplayDTO.getResponse());
requestMocker.getTargetResponse().setType(mergeReplayDTO.getResponseType());
requestMocker.getTargetResponse().setAttributes(mergeReplayDTO.getResponseAttributes());
mergeReplayDTO.setMatched(true);
return requestMocker;
}
}
Loading