diff --git a/README.md b/README.md
index 02bbb0d..a698cd0 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# ja-netfilter v1.2.0
+# ja-netfilter v2.0.0
### A javaagent framework
@@ -10,17 +10,7 @@
* some apps support the `JVM Options file`, you can add as a line of the `JVM Options file`.
* **WARNING: DO NOT put some unnecessary whitespace characters!**
-* edit your own rule list config file. The `ja-netfilter` will look for it in the following order(find one and stop searching):
- * passed as args of `-javaagent`. eg: `-javaagent:/absolute/path/to/ja-netfilter.jar=/home/neo/downloads/janf_config.txt`
- * file path in environment variable: `JANF_CONFIG`
- * file path in `java` startup property: `janf.config`. `eg: java -Djanf.config="/home/neo/downloads/janf_config.txt"`
- * some apps support the `JVM Options file`, you can add as a line of the `JVM Options file`. `eg: -Djanf.config="/home/neo/downloads/janf_config.txt"`
- * file path in the same dir as the `ja-netfilter.jar`, no need for additional configuration (**PREFERRED!**)
- * file path in your home directory, named: `.janf_config.txt`. `eg: /home/neo/.janf_config.txt`
- * file path in the subdirectory named `.config` in your home directory. `eg: /home/neo/.config/janf_config.txt`
- * file path in the subdirectory named `.local/etc` in your home directory. `eg: /home/neo/.local/ect/janf_config.txt`
- * file path in the directory named `/usr/local/etc`. `eg: /usr/local/etc/janf_config.txt`
- * file path in the directory named `/etc`. eg: `/etc/janf_config.txt`
+* edit your plugin config files: `${lower plugin name}.conf` file in the `conf` dir where `ja-netfilter.jar` is located.
* run your java application and enjoy~
@@ -28,8 +18,9 @@
```
[ABC]
-# for the specified plugin called "ABC"
+# for the specified section name
+# for example
[URL]
EQUAL,https://someurl
@@ -47,6 +38,7 @@ EQUAL,somedomain
# REGEXP Use regular expressions to match
```
+
## Debug info
* the `ja-netfilter` will **NOT** output debugging information by default
diff --git a/pom.xml b/pom.xml
index bb0505d..612102e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.ja-netfilter
ja-netfilter
- 1.2.0
+ 2.0.0
UTF-8
diff --git a/src/main/java/com/janetfilter/core/Dispatcher.java b/src/main/java/com/janetfilter/core/Dispatcher.java
index 848cf56..b269516 100644
--- a/src/main/java/com/janetfilter/core/Dispatcher.java
+++ b/src/main/java/com/janetfilter/core/Dispatcher.java
@@ -12,7 +12,7 @@ public final class Dispatcher implements ClassFileTransformer {
private final Set classSet = new TreeSet<>();
private final Map> transformerMap = new HashMap<>();
- public void addTransformer(MyTransformer transformer) {
+ public synchronized void addTransformer(MyTransformer transformer) {
String className = transformer.getHookClassName();
classSet.add(className.replace('/', '.'));
List transformers = transformerMap.computeIfAbsent(className, k -> new ArrayList<>());
diff --git a/src/main/java/com/janetfilter/core/Environment.java b/src/main/java/com/janetfilter/core/Environment.java
index 0fbe28f..312fdd2 100644
--- a/src/main/java/com/janetfilter/core/Environment.java
+++ b/src/main/java/com/janetfilter/core/Environment.java
@@ -5,11 +5,13 @@ import java.io.File;
public final class Environment {
private final File baseDir;
private final File agentFile;
+ private final File configDir;
private final File pluginsDir;
public Environment(File agentFile) {
this.agentFile = agentFile;
baseDir = agentFile.getParentFile();
+ configDir = new File(baseDir, "config");
pluginsDir = new File(baseDir, "plugins");
}
@@ -21,6 +23,10 @@ public final class Environment {
return agentFile;
}
+ public File getConfigDir() {
+ return configDir;
+ }
+
public File getPluginsDir() {
return pluginsDir;
}
diff --git a/src/main/java/com/janetfilter/core/Initializer.java b/src/main/java/com/janetfilter/core/Initializer.java
index ceadb51..9d887d6 100644
--- a/src/main/java/com/janetfilter/core/Initializer.java
+++ b/src/main/java/com/janetfilter/core/Initializer.java
@@ -1,32 +1,15 @@
package com.janetfilter.core;
-import com.janetfilter.core.commons.ConfigDetector;
-import com.janetfilter.core.commons.ConfigParser;
import com.janetfilter.core.commons.DebugInfo;
-import com.janetfilter.core.models.FilterConfig;
import com.janetfilter.core.plugin.PluginManager;
-import java.io.File;
import java.lang.instrument.Instrumentation;
import java.util.Set;
public class Initializer {
- public static void init(String args, Instrumentation inst, Environment environment) {
- File configFile = ConfigDetector.detect(environment.getBaseDir(), args);
- if (null == configFile) {
- DebugInfo.output("Could not find any configuration files.");
- } else {
- DebugInfo.output("Current config file: " + configFile.getPath());
- }
-
- try {
- FilterConfig.setCurrent(new FilterConfig(ConfigParser.parse(configFile)));
- } catch (Throwable e) {
- DebugInfo.output(e.getMessage());
- }
-
+ public static void init(Instrumentation inst, Environment environment) {
Dispatcher dispatcher = new Dispatcher();
- new PluginManager(dispatcher, environment).loadPlugins(inst);
+ new PluginManager(inst, dispatcher, environment).loadPlugins();
inst.addTransformer(dispatcher, true);
diff --git a/src/main/java/com/janetfilter/core/Launcher.java b/src/main/java/com/janetfilter/core/Launcher.java
index a2d0b71..dfa71fc 100644
--- a/src/main/java/com/janetfilter/core/Launcher.java
+++ b/src/main/java/com/janetfilter/core/Launcher.java
@@ -9,7 +9,7 @@ import java.net.URL;
import java.util.jar.JarFile;
public class Launcher {
- private static final String VERSION = "v1.2.0";
+ private static final String VERSION = "v2.0.0";
public static void main(String[] args) {
printUsage();
@@ -34,7 +34,7 @@ public class Launcher {
return;
}
- Initializer.init(args, inst, new Environment(agentFile)); // for some custom UrlLoaders
+ Initializer.init(inst, new Environment(agentFile)); // for some custom UrlLoaders
}
private static void printUsage() {
@@ -58,7 +58,7 @@ public class Launcher {
return url.toURI();
}
- String resourcePath = "/b7e909d6ba41ae03fb85af5b8ba702709f5798cf.txt";
+ String resourcePath = "/5a1666cf298cd1d4fa64d62d123af55f5f39024f.txt";
url = Launcher.class.getResource(resourcePath);
if (null == url) {
throw new Exception("Can not locate resource file.");
diff --git a/src/main/java/com/janetfilter/core/commons/ConfigDetector.java b/src/main/java/com/janetfilter/core/commons/ConfigDetector.java
deleted file mode 100644
index 2c1c7be..0000000
--- a/src/main/java/com/janetfilter/core/commons/ConfigDetector.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package com.janetfilter.core.commons;
-
-import com.janetfilter.core.utils.StringUtils;
-
-import java.io.File;
-
-public class ConfigDetector {
- private static final String CONFIG_FILENAME = "janf_config.txt";
-
- public static File detect(File currentDirectory, String args) {
- return detect(currentDirectory.getPath(), args);
- }
-
- public static File detect(String currentDirectory, String args) {
- File configFile = tryFile(args); // by javaagent argument
-
- if (null == configFile) {
- configFile = tryFile(System.getenv("JANF_CONFIG")); // by env
- }
-
- if (null == configFile) {
- configFile = tryFile(System.getProperty("janf.config")); // by -D argument
- }
-
- if (null == configFile) {
- configFile = searchDirectory(currentDirectory); // in the same dir as the jar
- }
-
- String userHome = System.getProperty("user.home");
- if (null == configFile) {
- configFile = searchDirectory(userHome, "." + CONFIG_FILENAME); // $HOME/.janf_config.txt
- }
-
- if (null == configFile) {
- configFile = searchDirectory(userHome + File.pathSeparator + ".config"); // $HOME/.config/janf_config.txt
- }
-
- if (null == configFile) {
- configFile = searchDirectory(userHome + File.pathSeparator + ".local" + File.pathSeparator + "/etc"); // $HOME/.local/etc/janf_config.txt
- }
-
- if (null == configFile) {
- configFile = searchDirectory("/usr/local/etc"); // /usr/local/etc/janf_config.txt
- }
-
- if (null == configFile) {
- configFile = searchDirectory("/etc"); // /etc/janf_config.txt
- }
-
- return configFile;
- }
-
- private static File searchDirectory(String dirPath) {
- return searchDirectory(dirPath, CONFIG_FILENAME);
- }
-
- private static File searchDirectory(String dirPath, String filename) {
- if (StringUtils.isEmpty(dirPath)) {
- return null;
- }
-
- File dirFile = new File(dirPath);
- if (!dirFile.isDirectory()) {
- return null;
- }
-
- return tryFile(new File(dirFile, filename));
- }
-
- private static File tryFile(String filePath) {
- if (StringUtils.isEmpty(filePath)) {
- return null;
- }
-
- return tryFile(new File(filePath));
- }
-
- private static File tryFile(File file) {
- if (!file.exists()) {
- return null;
- }
-
- if (!file.isFile()) {
- return null;
- }
-
- if (!file.canRead()) {
- return null;
- }
-
- return file;
- }
-}
diff --git a/src/main/java/com/janetfilter/core/commons/ConfigParser.java b/src/main/java/com/janetfilter/core/commons/ConfigParser.java
index b913df6..a873038 100644
--- a/src/main/java/com/janetfilter/core/commons/ConfigParser.java
+++ b/src/main/java/com/janetfilter/core/commons/ConfigParser.java
@@ -17,7 +17,7 @@ public class ConfigParser {
public static Map> parse(File file) throws Exception {
Map> map = new HashMap<>();
- if (null == file) {
+ if (null == file || !file.exists() || !file.isFile() || !file.canRead()) {
return map;
}
@@ -86,6 +86,7 @@ public class ConfigParser {
}
}
+ DebugInfo.output("Config file loaded: " + file);
return map;
}
}
diff --git a/src/main/java/com/janetfilter/core/commons/DebugInfo.java b/src/main/java/com/janetfilter/core/commons/DebugInfo.java
index f950177..d93a1cd 100644
--- a/src/main/java/com/janetfilter/core/commons/DebugInfo.java
+++ b/src/main/java/com/janetfilter/core/commons/DebugInfo.java
@@ -16,5 +16,6 @@ public class DebugInfo {
String caller = traces.length < 2 ? "UNKNOWN" : traces[1].toString();
System.out.printf(template, DateUtils.formatNow(), caller, content);
+ System.out.flush();
}
}
diff --git a/src/main/java/com/janetfilter/core/models/FilterConfig.java b/src/main/java/com/janetfilter/core/models/FilterConfig.java
deleted file mode 100644
index 85a3ede..0000000
--- a/src/main/java/com/janetfilter/core/models/FilterConfig.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.janetfilter.core.models;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-public class FilterConfig {
- private static FilterConfig current;
-
- private final Map> data;
-
- public FilterConfig(Map> data) {
- this.data = data;
- }
-
- public static FilterConfig getCurrent() {
- return current;
- }
-
- public static void setCurrent(FilterConfig current) {
- FilterConfig.current = current;
- }
-
- public static List getBySection(String section) {
- do {
- if (null == current) {
- break;
- }
-
- if (null == current.data) {
- break;
- }
-
- List list = current.data.get(section);
- if (null == list) {
- break;
- }
-
- return list;
- } while (false);
-
- return new ArrayList<>();
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/janetfilter/core/models/FilterRule.java b/src/main/java/com/janetfilter/core/models/FilterRule.java
index bbacb48..409a645 100644
--- a/src/main/java/com/janetfilter/core/models/FilterRule.java
+++ b/src/main/java/com/janetfilter/core/models/FilterRule.java
@@ -55,9 +55,6 @@ public class FilterRule {
@Override
public String toString() {
- return "{" +
- "type=" + type +
- ", rule='" + rule + '\'' +
- '}';
+ return "{type=" + type + ", rule=" + rule + "}";
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/janetfilter/core/plugin/PluginConfig.java b/src/main/java/com/janetfilter/core/plugin/PluginConfig.java
new file mode 100644
index 0000000..9ca3b01
--- /dev/null
+++ b/src/main/java/com/janetfilter/core/plugin/PluginConfig.java
@@ -0,0 +1,30 @@
+package com.janetfilter.core.plugin;
+
+import com.janetfilter.core.models.FilterRule;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class PluginConfig {
+ private final File file;
+ private final Map> data;
+
+ public PluginConfig(File file, Map> data) {
+ this.file = file;
+ this.data = data;
+ }
+
+ public List getBySection(String section) {
+ return data.getOrDefault(section, new ArrayList<>());
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public Map> getData() {
+ return data;
+ }
+}
diff --git a/src/main/java/com/janetfilter/core/plugin/PluginEntry.java b/src/main/java/com/janetfilter/core/plugin/PluginEntry.java
index 5f838e2..961cc42 100644
--- a/src/main/java/com/janetfilter/core/plugin/PluginEntry.java
+++ b/src/main/java/com/janetfilter/core/plugin/PluginEntry.java
@@ -1,12 +1,11 @@
package com.janetfilter.core.plugin;
import com.janetfilter.core.Environment;
-import com.janetfilter.core.models.FilterRule;
import java.util.List;
public interface PluginEntry {
- default void init(Environment environment, List filterRules) {
+ default void init(Environment environment, PluginConfig config) {
// get plugin config
}
diff --git a/src/main/java/com/janetfilter/core/plugin/PluginManager.java b/src/main/java/com/janetfilter/core/plugin/PluginManager.java
index 2fd8dc5..803edef 100644
--- a/src/main/java/com/janetfilter/core/plugin/PluginManager.java
+++ b/src/main/java/com/janetfilter/core/plugin/PluginManager.java
@@ -2,85 +2,99 @@ package com.janetfilter.core.plugin;
import com.janetfilter.core.Dispatcher;
import com.janetfilter.core.Environment;
+import com.janetfilter.core.commons.ConfigParser;
import com.janetfilter.core.commons.DebugInfo;
-import com.janetfilter.core.models.FilterConfig;
import com.janetfilter.core.utils.StringUtils;
import java.io.File;
import java.lang.instrument.Instrumentation;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public final class PluginManager {
private static final String ENTRY_NAME = "JANF-Plugin-Entry";
+ private final Instrumentation inst;
private final Dispatcher dispatcher;
private final Environment environment;
- public PluginManager(Dispatcher dispatcher, Environment environment) {
+ public PluginManager(Instrumentation inst, Dispatcher dispatcher, Environment environment) {
+ this.inst = inst;
this.dispatcher = dispatcher;
this.environment = environment;
}
- public void loadPlugins(Instrumentation inst) {
- for (Class extends PluginEntry> klass : getAllPluginClasses(inst)) {
- try {
- addPluginEntry(klass);
- } catch (Throwable e) {
- DebugInfo.output("Init plugin failed: " + e.getMessage());
+ public void loadPlugins() {
+ File pluginsDirectory = environment.getPluginsDir();
+ if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) {
+ return;
+ }
+
+ File[] pluginFiles = pluginsDirectory.listFiles((d, n) -> n.endsWith(".jar"));
+ if (null == pluginFiles) {
+ return;
+ }
+
+ try {
+ ExecutorService executorService = Executors.newCachedThreadPool();
+ for (File pluginFile : pluginFiles) {
+ executorService.submit(new PluginLoadTask(pluginFile));
}
+
+ executorService.shutdown();
+ if (!executorService.awaitTermination(30L, TimeUnit.SECONDS)) {
+ throw new RuntimeException("Load plugin timeout");
+ }
+
+ DebugInfo.output("============ All plugins loaded ============");
+ } catch (Throwable e) {
+ DebugInfo.output("Load plugin failed: " + e.getMessage());
}
}
- private List> getAllPluginClasses(Instrumentation inst) {
- List> classes = new ArrayList<>();
+ private class PluginLoadTask implements Runnable {
+ private final File pluginFile;
- do {
- File pluginsDirectory = environment.getPluginsDir();
- if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) {
- break;
- }
+ public PluginLoadTask(File pluginFile) {
+ this.pluginFile = pluginFile;
+ }
- File[] pluginFiles = pluginsDirectory.listFiles((d, n) -> n.endsWith(".jar"));
- if (null == pluginFiles) {
- break;
- }
-
- for (File pluginFile : pluginFiles) {
- try {
- JarFile jarFile = new JarFile(pluginFile);
- Manifest manifest = jarFile.getManifest();
- String entryClass = manifest.getMainAttributes().getValue(ENTRY_NAME);
- if (StringUtils.isEmpty(entryClass)) {
- continue;
- }
-
- PluginClassLoader classLoader = new PluginClassLoader(jarFile);
- Class> klass = Class.forName(entryClass, false, classLoader);
- if (!Arrays.asList(klass.getInterfaces()).contains(PluginEntry.class)) {
- continue;
- }
-
- inst.appendToBootstrapClassLoaderSearch(jarFile);
- classes.add((Class extends PluginEntry>) Class.forName(entryClass));
- } catch (Throwable e) {
- DebugInfo.output("Load plugin failed: " + e.getMessage());
+ @Override
+ public void run() {
+ try {
+ JarFile jarFile = new JarFile(pluginFile);
+ Manifest manifest = jarFile.getManifest();
+ String entryClass = manifest.getMainAttributes().getValue(ENTRY_NAME);
+ if (StringUtils.isEmpty(entryClass)) {
+ return;
}
+
+ PluginClassLoader classLoader = new PluginClassLoader(jarFile);
+ Class> klass = Class.forName(entryClass, false, classLoader);
+ if (!Arrays.asList(klass.getInterfaces()).contains(PluginEntry.class)) {
+ return;
+ }
+
+ synchronized (inst) {
+ inst.appendToBootstrapClassLoaderSearch(jarFile);
+ }
+
+ PluginEntry pluginEntry = (PluginEntry) Class.forName(entryClass).newInstance();
+
+ File configFile = new File(environment.getConfigDir(), pluginEntry.getName().toLowerCase() + ".conf");
+ PluginConfig pluginConfig = new PluginConfig(configFile, ConfigParser.parse(configFile));
+ pluginEntry.init(environment, pluginConfig);
+
+ dispatcher.addTransformers(pluginEntry.getTransformers());
+
+ DebugInfo.output("Plugin loaded: {name=" + pluginEntry.getName() + ", version=" + pluginEntry.getVersion() + ", author=" + pluginEntry.getAuthor() + "}");
+ } catch (Throwable e) {
+ DebugInfo.output("Parse plugin info failed: " + e.getMessage());
}
- } while (false);
-
- return classes;
- }
-
- private void addPluginEntry(Class extends PluginEntry> entryClass) throws Exception {
- PluginEntry pluginEntry = entryClass.newInstance();
-
- pluginEntry.init(environment, FilterConfig.getBySection(pluginEntry.getName()));
- dispatcher.addTransformers(pluginEntry.getTransformers());
-
- DebugInfo.output("Plugin loaded: {name=" + pluginEntry.getName() + ", version=" + pluginEntry.getVersion() + ", author=" + pluginEntry.getAuthor() + "}");
+ }
}
}
diff --git a/src/main/resources/b7e909d6ba41ae03fb85af5b8ba702709f5798cf.txt b/src/main/resources/5a1666cf298cd1d4fa64d62d123af55f5f39024f.txt
similarity index 100%
rename from src/main/resources/b7e909d6ba41ae03fb85af5b8ba702709f5798cf.txt
rename to src/main/resources/5a1666cf298cd1d4fa64d62d123af55f5f39024f.txt