Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
@@ -0,0 +1,65 @@
package com.taobao.arthas.core.util;

import com.taobao.arthas.core.shell.term.impl.http.session.LRUCache;
import java.util.regex.Pattern;

/**
* 正则表达式缓存管理器
* 用于缓存编译后的正则表达式对象,避免重复编译的开销
*/
public class RegexCacheManager {
private static final RegexCacheManager INSTANCE = new RegexCacheManager();

// 使用LRUCache缓存编译后的正则表达式
private final LRUCache<String, Pattern> regexCache;

// 缓存大小限制
private static final int MAX_CACHE_SIZE = 100;

private RegexCacheManager() {
// 初始化LRUCache,设置最大缓存大小
this.regexCache = new LRUCache<>(MAX_CACHE_SIZE);
}

public static RegexCacheManager getInstance() {
return INSTANCE;
}

/**
* 获取正则表达式Pattern对象,优先从缓存获取,缓存未命中则编译并缓存
*/
public Pattern getPattern(String regex) {
if (regex == null || regex.isEmpty()) {
return null;
}

// 从LRUCache获取
Pattern pattern = regexCache.get(regex);
if (pattern != null) {
return pattern;
}

// 缓存未命中,编译正则表达式
// 不捕获PatternSyntaxException,让异常向上抛出,以便及时发现无效的正则表达式
pattern = Pattern.compile(regex);
// 缓存编译结果
regexCache.put(regex, pattern);

return pattern;
}

/**
* 清理缓存
*/
public void clearCache() {
regexCache.clear();
}

/**
* 获取缓存大小
*/
public int getCacheSize() {
return regexCache.usedEntries();
}

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
package com.taobao.arthas.core.util.matcher;

import com.taobao.arthas.core.util.RegexCacheManager;
import java.util.regex.Pattern;

/**
* regex matcher
* @author ralf0131 2017-01-06 13:16.
*/
public class RegexMatcher implements Matcher<String> {

private final String pattern;
private volatile Pattern compiledPattern;

public RegexMatcher(String pattern) {
this.pattern = pattern;
}

@Override
public boolean matching(String target) {
return null != target
&& null != pattern
&& target.matches(pattern);
if (null == target || null == pattern) {
return false;
}

// 在第一次matching时才编译正则表达式
if (compiledPattern == null) {
compiledPattern = RegexCacheManager.getInstance().getPattern(pattern);
}

return compiledPattern != null && compiledPattern.matcher(target).matches();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.taobao.arthas.core.util;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
* RegexCacheManager测试类
*/
public class RegexCacheManagerTest {
private RegexCacheManager cacheManager;

@Before
public void setUp() {
// 获取单例实例
cacheManager = RegexCacheManager.getInstance();
// 清理缓存,确保测试环境干净
cacheManager.clearCache();
}

/**
* 测试基本缓存功能
*/
@Test
public void testBasicCacheFunctionality() {
// 测试缓存未命中的情况
String regex1 = ".*Test.*";
Pattern pattern1 = cacheManager.getPattern(regex1);
Assert.assertNotNull(pattern1);
Assert.assertEquals(1, cacheManager.getCacheSize());

// 测试缓存命中的情况
Pattern pattern1Cached = cacheManager.getPattern(regex1);
Assert.assertNotNull(pattern1Cached);
Assert.assertSame(pattern1, pattern1Cached); // 应该是同一个对象
Assert.assertEquals(1, cacheManager.getCacheSize()); // 缓存大小应该保持不变

// 测试多个正则表达式
String regex2 = "^Test.*";
Pattern pattern2 = cacheManager.getPattern(regex2);
Assert.assertNotNull(pattern2);
Assert.assertEquals(2, cacheManager.getCacheSize());

// 测试空正则表达式
Pattern nullPattern = cacheManager.getPattern(null);
Assert.assertNull(nullPattern);

Pattern emptyPattern = cacheManager.getPattern("");
Assert.assertNull(emptyPattern);
}

/**
* 测试LRU淘汰策略
*/
@Test
public void testLRUEvictionPolicy() {
// 生成多个正则表达式,超过最大缓存大小
int maxCacheSize = 100;
for (int i = 0; i < maxCacheSize + 5; i++) {
String regex = "TestRegex" + i;
Pattern pattern = cacheManager.getPattern(regex);
Assert.assertNotNull(pattern);
}

// 缓存大小应该等于最大缓存大小
Assert.assertEquals(maxCacheSize, cacheManager.getCacheSize()); // 100 是实际的最大缓存大小

// 测试访问顺序,确保LRU策略生效
String firstRegex = "TestRegex0";

// 再次访问第一个正则表达式,使其成为最近使用的
Pattern firstPattern = cacheManager.getPattern(firstRegex);
Assert.assertNotNull(firstPattern);

// 再添加一个新的正则表达式,应该淘汰最久未使用的
String newRegex = "NewTestRegex";
Pattern newPattern = cacheManager.getPattern(newRegex);
Assert.assertNotNull(newPattern);

// 第一个正则表达式应该仍然在缓存中(因为刚被访问过)
Pattern firstPatternAgain = cacheManager.getPattern(firstRegex);
Assert.assertNotNull(firstPatternAgain);
}

/**
* 测试缓存清理功能
*/
@Test
public void testCacheClear() {
// 添加一些缓存项
cacheManager.getPattern(".*Test1");
cacheManager.getPattern(".*Test2");
Assert.assertTrue(cacheManager.getCacheSize() > 0);

// 清理缓存
cacheManager.clearCache();
Assert.assertEquals(0, cacheManager.getCacheSize());

// 清理后应该可以重新添加缓存项
Pattern pattern = cacheManager.getPattern(".*Test3");
Assert.assertNotNull(pattern);
Assert.assertEquals(1, cacheManager.getCacheSize());
}

/**
* 测试无效正则表达式处理
*/
@Test
public void testInvalidRegexHandling() {
// 测试无效的正则表达式,应该抛出PatternSyntaxException
String invalidRegex = "[a-z";
try {
cacheManager.getPattern(invalidRegex);
} catch (Exception e) {
// 验证抛出的是PatternSyntaxException
Assert.assertTrue("Expected PatternSyntaxException but got " + e.getClass().getName(), e instanceof PatternSyntaxException);
}

// 测试另一个无效的正则表达式,应该抛出PatternSyntaxException
String anotherInvalidRegex = "(a-z";
try {
cacheManager.getPattern(anotherInvalidRegex);
} catch (Exception e) {
// 验证抛出的是PatternSyntaxException
Assert.assertTrue("Expected PatternSyntaxException but got " + e.getClass().getName(), e instanceof PatternSyntaxException);
}

// 确保缓存大小没有增加
Assert.assertEquals("无效正则表达式不应该被缓存", 0, cacheManager.getCacheSize());
}

}