diff --git a/README.md b/README.md
index 41c4f23..0e1999a 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# ja-netfilter v2.1.1
+# ja-netfilter v2.2.0
### A javaagent framework
@@ -9,12 +9,13 @@
* add as an argument of the `java` command. eg: `java -javaagent:/absolute/path/to/ja-netfilter.jar -jar executable_jar_file.jar`
* 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!**
+* or execute `java -jar /path/to/ja-netfilter.jar` to use `attach mode`.
* edit your plugin config files: `${lower plugin name}.conf` file in the `config` dir where `ja-netfilter.jar` is located.
-* the `config` and `plugins` directory can be specified through **the javaagent args**.
- * eg: `-javaagent:/path/to/ja-netfilter.jar=appName`, your config and plugins directories will be `config-appname` and `plugins-appname`.
- * if no javaagent args, they default to `config` and `plugins`.
- * this mechanism will avoid extraneous and bloated `config` and `plugins`.
+* the `config`, `logs` and `plugins` directories can be specified through **the javaagent args**.
+ * eg: `-javaagent:/path/to/ja-netfilter.jar=appName`, your config, logs and plugins directories will be `config-appname`, `logs-appname` and `plugins-appname`.
+ * if no javaagent args, they default to `config`, `logs` and `plugins`.
+ * this mechanism will avoid extraneous and bloated `config`, `logs` and `plugins`.
* run your java application and enjoy~
@@ -46,8 +47,9 @@ EQUAL,somedomain
## Debug info
* the `ja-netfilter` will **NOT** output debugging information by default
-* add environment variable `JANF_DEBUG=1` and start to enable it
-* or add system property `-Djanf.debug=1` to enable it
+* add environment variable `JANF_DEBUG=1` (log level) and start to enable it
+* or add system property `-Djanf.debug=1` (log level) to enable it
+* log level: `NONE=0`, `DEBUG=1`, `INFO=2`, `WARN=3`, `ERROR=4`;
## Plugin system
diff --git a/pom.xml b/pom.xml
index 956d137..74973f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.ja-netfilter
ja-netfilter
- 2.1.1
+ 2.2.0
ja-netfilter
A javaagent framework
@@ -82,6 +82,7 @@
neo
com.janetfilter.core.Launcher
+ com.janetfilter.core.Launcher
com.janetfilter.core.Launcher
true
true
@@ -163,4 +164,13 @@
+
+
+ com.sun
+ tools
+ 1.8
+ system
+ ${java.home}/../lib/tools.jar
+
+
diff --git a/src/main/java/com/janetfilter/core/Dispatcher.java b/src/main/java/com/janetfilter/core/Dispatcher.java
index 2d44b23..cd36e29 100644
--- a/src/main/java/com/janetfilter/core/Dispatcher.java
+++ b/src/main/java/com/janetfilter/core/Dispatcher.java
@@ -70,7 +70,7 @@ public final class Dispatcher implements ClassFileTransformer {
classFileBuffer = transformer.transform(className, classFileBuffer, order++);
}
} catch (Throwable e) {
- DebugInfo.output("Transform class failed: " + className, e);
+ DebugInfo.error("Transform class failed: " + className, e);
}
} while (false);
diff --git a/src/main/java/com/janetfilter/core/Environment.java b/src/main/java/com/janetfilter/core/Environment.java
index 0d873f0..5983972 100644
--- a/src/main/java/com/janetfilter/core/Environment.java
+++ b/src/main/java/com/janetfilter/core/Environment.java
@@ -1,14 +1,17 @@
package com.janetfilter.core;
+import com.janetfilter.core.utils.ProcessUtils;
import com.janetfilter.core.utils.StringUtils;
import java.io.File;
public final class Environment {
+ private final String pid;
private final File baseDir;
private final File agentFile;
private final File configDir;
private final File pluginsDir;
+ private final File logsDir;
private final String nativePrefix;
public Environment(File agentFile) {
@@ -22,13 +25,20 @@ public final class Environment {
if (StringUtils.isEmpty(app)) {
configDir = new File(baseDir, "config");
pluginsDir = new File(baseDir, "plugins");
+ logsDir = new File(baseDir, "logs");
} else {
app = app.toLowerCase();
configDir = new File(baseDir, "config-" + app);
pluginsDir = new File(baseDir, "plugins-" + app);
+ logsDir = new File(baseDir, "logs-" + app);
}
nativePrefix = StringUtils.randomMethodName(15) + "_";
+ pid = ProcessUtils.currentId();
+ }
+
+ public String getPid() {
+ return pid;
}
public File getBaseDir() {
@@ -47,6 +57,10 @@ public final class Environment {
return pluginsDir;
}
+ public File getLogsDir() {
+ return logsDir;
+ }
+
public String getNativePrefix() {
return nativePrefix;
}
@@ -54,11 +68,13 @@ public final class Environment {
@Override
public String toString() {
return "Environment: {" +
- "\n\tbaseDir=" + baseDir +
- ", \n\tagentFile=" + agentFile +
- ", \n\tconfigDir=" + configDir +
- ", \n\tpluginsDir=" + pluginsDir +
- ", \n\tnativePrefix=" + nativePrefix +
+ "\n\tpid = " + pid +
+ ", \n\tbaseDir = " + baseDir +
+ ", \n\tagentFile = " + agentFile +
+ ", \n\tconfigDir = " + configDir +
+ ", \n\tpluginsDir = " + pluginsDir +
+ ", \n\tlogsDir = " + logsDir +
+ ", \n\tnativePrefix = " + nativePrefix +
"\n}";
}
}
diff --git a/src/main/java/com/janetfilter/core/Initializer.java b/src/main/java/com/janetfilter/core/Initializer.java
index 9d609cc..7634f5c 100644
--- a/src/main/java/com/janetfilter/core/Initializer.java
+++ b/src/main/java/com/janetfilter/core/Initializer.java
@@ -8,7 +8,8 @@ import java.util.Set;
public class Initializer {
public static void init(Instrumentation inst, Environment environment) {
- DebugInfo.output(environment.toString());
+ DebugInfo.useFile(environment.getLogsDir());
+ DebugInfo.info(environment.toString());
Dispatcher dispatcher = new Dispatcher();
new PluginManager(inst, dispatcher, environment).loadPlugins();
@@ -26,7 +27,7 @@ public class Initializer {
try {
inst.retransformClasses(c);
} catch (Throwable e) {
- DebugInfo.output("Retransform class failed: " + name, e);
+ DebugInfo.error("Retransform class failed: " + name, e);
}
}
}
diff --git a/src/main/java/com/janetfilter/core/Launcher.java b/src/main/java/com/janetfilter/core/Launcher.java
index e0cd5a1..9893400 100644
--- a/src/main/java/com/janetfilter/core/Launcher.java
+++ b/src/main/java/com/janetfilter/core/Launcher.java
@@ -1,25 +1,49 @@
package com.janetfilter.core;
+import com.janetfilter.core.attach.VMLauncher;
+import com.janetfilter.core.attach.VMSelector;
import com.janetfilter.core.commons.DebugInfo;
+import com.janetfilter.core.utils.WhereIsUtils;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.net.URI;
-import java.net.URL;
import java.util.jar.JarFile;
public class Launcher {
- private static final String VERSION = "v2.1.1";
+ public static final String ATTACH_ARG = "--attach";
+ public static final String VERSION = "v2.2.0";
private static boolean loaded = false;
public static void main(String[] args) {
+ URI jarURI;
+ try {
+ jarURI = WhereIsUtils.getJarURI();
+ } catch (Throwable e) {
+ DebugInfo.error("Can not locate `ja-netfilter` jar file.", e);
+ return;
+ }
+
+ String jarPath = jarURI.getPath();
+ if (args.length > 1 && args[0].equals(ATTACH_ARG)) {
+ VMLauncher.attachVM(jarPath, args[1], args.length > 2 ? args[2] : null);
+ return;
+ }
+
printUsage();
+
+ try {
+ new VMSelector(new File(jarPath)).select();
+ } catch (Throwable e) {
+ System.err.println(" ERROR: Select virtual machine failed.");
+ e.printStackTrace(System.err);
+ }
}
public static void premain(String args, Instrumentation inst) {
if (loaded) {
- DebugInfo.output("WARN: You have multiple `ja-netfilter` as -javaagent.");
+ DebugInfo.warn("You have multiple `ja-netfilter` as javaagent.");
return;
}
@@ -28,9 +52,9 @@ public class Launcher {
URI jarURI;
try {
loaded = true;
- jarURI = getJarURI();
+ jarURI = WhereIsUtils.getJarURI();
} catch (Throwable e) {
- DebugInfo.output("ERROR: Can not locate ja-netfilter jar file.", e);
+ DebugInfo.error("Can not locate `ja-netfilter` jar file.", e);
return;
}
@@ -38,13 +62,17 @@ public class Launcher {
try {
inst.appendToBootstrapClassLoaderSearch(new JarFile(agentFile));
} catch (Throwable e) {
- DebugInfo.output("ERROR: Can not access ja-netfilter jar file.", e);
+ DebugInfo.error("Can not access `ja-netfilter` jar file.", e);
return;
}
Initializer.init(inst, new Environment(agentFile, args)); // for some custom UrlLoaders
}
+ public static void agentmain(String args, Instrumentation inst) {
+ premain(args, inst);
+ }
+
private static void printUsage() {
String content = "\n ============================================================================ \n" +
"\n" +
@@ -57,28 +85,5 @@ public class Launcher {
" ============================================================================ \n\n";
System.out.print(content);
- System.out.flush();
- }
-
- private static URI getJarURI() throws Exception {
- URL url = Launcher.class.getProtectionDomain().getCodeSource().getLocation();
- if (null != url) {
- return url.toURI();
- }
-
- String resourcePath = "/4cc9c353c626d6510ca855ab6907ed7f64400257.txt";
- url = Launcher.class.getResource(resourcePath);
- if (null == url) {
- throw new Exception("Can not locate resource file.");
- }
-
- String path = url.getPath();
- if (!path.endsWith("!" + resourcePath)) {
- throw new Exception("Invalid resource path.");
- }
-
- path = path.substring(0, path.length() - resourcePath.length() - 1);
-
- return new URI(path);
}
}
diff --git a/src/main/java/com/janetfilter/core/attach/VMDescriptor.java b/src/main/java/com/janetfilter/core/attach/VMDescriptor.java
new file mode 100644
index 0000000..52fff73
--- /dev/null
+++ b/src/main/java/com/janetfilter/core/attach/VMDescriptor.java
@@ -0,0 +1,51 @@
+package com.janetfilter.core.attach;
+
+public class VMDescriptor {
+ private String id;
+ private String className;
+ private String args;
+ private Boolean old = true;
+
+ public VMDescriptor(String id, String className, String args) {
+ this.id = id;
+ this.className = className;
+ this.args = args;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ public String getArgs() {
+ return args;
+ }
+
+ public void setArgs(String args) {
+ this.args = args;
+ }
+
+ public Boolean getOld() {
+ return old;
+ }
+
+ public void setOld(Boolean old) {
+ this.old = old;
+ }
+
+ @Override
+ public String toString() {
+ return id + " " + className;
+ }
+}
diff --git a/src/main/java/com/janetfilter/core/attach/VMLauncher.java b/src/main/java/com/janetfilter/core/attach/VMLauncher.java
new file mode 100644
index 0000000..f5aacaf
--- /dev/null
+++ b/src/main/java/com/janetfilter/core/attach/VMLauncher.java
@@ -0,0 +1,81 @@
+package com.janetfilter.core.attach;
+
+import com.janetfilter.core.Launcher;
+import com.janetfilter.core.utils.ProcessUtils;
+import com.janetfilter.core.utils.WhereIsUtils;
+import com.sun.tools.attach.VirtualMachine;
+
+import java.io.File;
+import java.io.IOException;
+
+public class VMLauncher {
+ public static void attachVM(String agentFile, String pid, String args) {
+ try {
+ VirtualMachine vm = VirtualMachine.attach(pid);
+ vm.loadAgent(agentFile, args);
+ vm.detach();
+ } catch (IOException e) {
+ if (e.getMessage().startsWith("Non-numeric value found")) {
+ System.out.println("WARN: The jdk used by `ja-netfilter` does not match the attached jdk version");
+ }
+ } catch (Throwable e) {
+ System.err.println("Attach failed: " + pid);
+ e.printStackTrace(System.err);
+ return;
+ }
+
+ System.out.println("ATTACHED SUCCESSFULLY: " + pid);
+ }
+
+ public static void launch(File thisJar, VMDescriptor descriptor, String args) throws Exception {
+ File javaCommand = WhereIsUtils.findJava();
+ if (null == javaCommand) {
+ throw new Exception("Can not locate java command, unable to start attach mode.");
+ }
+
+ ProcessBuilder pb;
+ double version = Double.parseDouble(System.getProperty("java.specification.version"));
+ if (version > 1.8D) {
+ pb = buildProcess(javaCommand, thisJar, descriptor.getId(), args);
+ } else {
+ File toolsJar = WhereIsUtils.findToolsJar();
+ if (null == toolsJar) {
+ throw new Exception("Can not locate tools.jar file, unable to start attach mode.");
+ }
+
+ pb = buildProcess(javaCommand, thisJar, descriptor.getId(), args, toolsJar);
+ }
+
+ int exitValue = ProcessUtils.start(pb);
+ if (0 != exitValue) {
+ throw new Exception("Attach mode failed: " + exitValue);
+ }
+ }
+
+ private static ProcessBuilder buildProcess(File java, File thisJar, String id, String args) {
+ String[] cmdArray = new String[]{
+ java.getAbsolutePath(),
+ "-Djanf.debug=" + System.getProperty("janf.debug", "0"),
+ "-jar",
+ thisJar.getAbsolutePath(),
+ Launcher.ATTACH_ARG,
+ id, args
+ };
+
+ return new ProcessBuilder(cmdArray);
+ }
+
+ private static ProcessBuilder buildProcess(File java, File thisJar, String id, String args, File toolsJar) {
+ String[] cmdArray = new String[]{
+ java.getAbsolutePath(),
+ "-Djanf.debug=" + System.getProperty("janf.debug", "0"),
+ "-Xbootclasspath/a:" + toolsJar.getAbsolutePath(),
+ "-jar",
+ thisJar.getAbsolutePath(),
+ Launcher.ATTACH_ARG,
+ id, args
+ };
+
+ return new ProcessBuilder(cmdArray);
+ }
+}
diff --git a/src/main/java/com/janetfilter/core/attach/VMSelector.java b/src/main/java/com/janetfilter/core/attach/VMSelector.java
new file mode 100644
index 0000000..4dbcbaf
--- /dev/null
+++ b/src/main/java/com/janetfilter/core/attach/VMSelector.java
@@ -0,0 +1,124 @@
+package com.janetfilter.core.attach;
+
+import com.janetfilter.core.utils.DateUtils;
+import com.janetfilter.core.utils.ProcessUtils;
+import com.janetfilter.core.utils.WhereIsUtils;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class VMSelector {
+ private final File thisJar;
+ private List descriptors;
+
+ public VMSelector(File thisJar) {
+ this.thisJar = thisJar;
+ }
+
+ private List getVMList() throws Exception {
+ File jpsCommand = WhereIsUtils.findJPS();
+ if (null == jpsCommand) {
+ throw new Exception("jps command not found");
+ }
+
+ List list = new ArrayList<>();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ProcessUtils.start(new ProcessBuilder(jpsCommand.getAbsolutePath(), "-lv"), bos);
+
+ String line;
+ BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bos.toByteArray())));
+ while ((line = reader.readLine()) != null) {
+ list.add(line);
+ }
+
+ String processId = ProcessUtils.currentId();
+ return list.stream()
+ .map(s -> {
+ String[] section = (s + " ").split(" ", 3);
+ return new VMDescriptor(section[0].trim(), section[1].trim(), section[2].trim());
+ })
+ .filter(d -> !d.getId().equals(processId) && !"sun.tools.jps.Jps".equals(d.getClassName()) && !"jdk.jcmd/sun.tools.jps.Jps".equals(d.getClassName()))
+ .sorted(Comparator.comparingInt(d -> Integer.parseInt(d.getId())))
+ .collect(Collectors.toList());
+ }
+
+ private String getInput() throws IOException {
+ return new BufferedReader(new InputStreamReader(System.in)).readLine().trim();
+ }
+
+ private void processSelect() throws Exception {
+ System.out.print(" Select: ");
+ String input = getInput();
+
+ switch (input) {
+ case "Q":
+ case "q":
+ System.exit(0);
+ case "R":
+ case "r":
+ System.out.println(" =========================== " + DateUtils.formatDateTime() + " ============================");
+ select();
+ return;
+ case "":
+ processSelect();
+ return;
+ default:
+ int index;
+ try {
+ index = Integer.parseInt(input);
+ } catch (NumberFormatException e) {
+ invalidInput(input);
+ return;
+ }
+
+ if (index < 1) {
+ invalidInput(input);
+ return;
+ }
+
+ if (index > descriptors.size()) {
+ invalidInput(input);
+ return;
+ }
+
+ System.out.print(" Agent args: ");
+ input = getInput();
+ try {
+ VMLauncher.launch(thisJar, descriptors.get(index - 1), input);
+ } catch (Exception e) {
+ System.err.println("> Attach to: " + index + " failed.");
+ e.printStackTrace(System.err);
+ return;
+ }
+ break;
+ }
+ }
+
+ private void invalidInput(String input) throws Exception {
+ System.err.println("> Invalid input: " + input);
+ processSelect();
+ }
+
+ public void select() throws Exception {
+ boolean first = null == descriptors;
+ List temp = getVMList();
+ if (null != descriptors && !descriptors.isEmpty()) {
+ temp.forEach(d -> d.setOld(descriptors.stream().anyMatch(d1 -> d.getId().equals(d1.getId()))));
+ }
+
+ descriptors = temp;
+ System.out.println(" Java Virtual Machine List: (Select and attach" + (first ? "" : ", + means the new one") + ")");
+
+ int index = 1;
+ for (VMDescriptor d : descriptors) {
+ System.out.printf(" %3d]:%s%s %s%n", index++, d.getOld() ? " " : "+", d.getId(), d.getClassName());
+ }
+ System.out.println(" r]: ");
+ System.out.println(" q]: ");
+
+ processSelect();
+ }
+}
diff --git a/src/main/java/com/janetfilter/core/commons/ConfigParser.java b/src/main/java/com/janetfilter/core/commons/ConfigParser.java
index a873038..c47fc62 100644
--- a/src/main/java/com/janetfilter/core/commons/ConfigParser.java
+++ b/src/main/java/com/janetfilter/core/commons/ConfigParser.java
@@ -80,13 +80,13 @@ public class ConfigParser {
}
map.get(lastSection).add(rule);
- DebugInfo.output("Add section: " + lastSection + ", rule: " + rule);
+ DebugInfo.debug("Add section: " + lastSection + ", rule: " + rule);
break;
}
}
}
- DebugInfo.output("Config file loaded: " + file);
+ DebugInfo.debug("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 7127313..1d1e990 100644
--- a/src/main/java/com/janetfilter/core/commons/DebugInfo.java
+++ b/src/main/java/com/janetfilter/core/commons/DebugInfo.java
@@ -1,18 +1,95 @@
package com.janetfilter.core.commons;
import com.janetfilter.core.utils.DateUtils;
+import com.janetfilter.core.utils.ProcessUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
public class DebugInfo {
- private static final boolean DEBUG = "1".equals(System.getenv("JANF_DEBUG")) || "1".equals(System.getProperty("janf.debug"));
private static final String CLASS_NAME = DebugInfo.class.getName();
- private static final String LOG_TEMPLATE = "[%s] %s DEBUG : %s%n";
+ private static final String LOG_TEMPLATE = "[%s] %s %s : %s%n";
+ private static final Level LOG_LEVEL;
+ private static File logFile;
+
+ static {
+ Level level = Level.of(System.getProperty("janf.debug"));
+ LOG_LEVEL = Level.NONE == level ? Level.of(System.getenv("JANF_DEBUG")) : level;
+ }
+
+ public static void useFile(File dir) {
+ if (LOG_LEVEL == Level.NONE || null == dir) {
+ return;
+ }
+
+ if (!dir.exists() && !dir.mkdirs()) {
+ error("Can't make directory: " + dir);
+ return;
+ }
+
+ if (!dir.isDirectory()) {
+ error("It's not a directory: " + dir);
+ return;
+ }
+
+ if (!dir.canWrite()) {
+ error("Read-only directory: " + dir);
+ return;
+ }
+
+ File file = new File(dir, String.format("%s-%s.log", DateUtils.formatDate(), ProcessUtils.currentId()));
+ if (file.exists()) {
+ error("Log file exists: " + file);
+ return;
+ }
+
+ logFile = file;
+ }
+
+ public static void debug(String content, Throwable e) {
+ output(Level.DEBUG, content, e);
+ }
+
+ public static void debug(String content) {
+ debug(content, null);
+ }
+
+ public static void info(String content, Throwable e) {
+ output(Level.INFO, content, e);
+ }
+
+ public static void info(String content) {
+ info(content, null);
+ }
+
+ public static void warn(String content, Throwable e) {
+ output(Level.WARN, content, e);
+ }
+
+ public static void warn(String content) {
+ warn(content, null);
+ }
+
+ public static void error(String content, Throwable e) {
+ output(Level.ERROR, content, e);
+ }
+
+ public static void error(String content) {
+ error(content, null);
+ }
public static void output(String content) {
- output(content, null);
+ debug(content);
}
public static void output(String content, Throwable e) { // No logger lib required
- if (!DEBUG) {
+ debug(content, e);
+ }
+
+ public static void output(Level level, String content, Throwable e) { // No logger lib required
+ if (Level.NONE == LOG_LEVEL || level.ordinal() < LOG_LEVEL.ordinal()) {
return;
}
@@ -26,15 +103,74 @@ public class DebugInfo {
}
}
- String outContent = String.format(LOG_TEMPLATE, DateUtils.formatNow(), caller, content);
+ String outContent = String.format(LOG_TEMPLATE, DateUtils.formatDateTime(), caller, level, content);
if (null == e) {
- System.out.print(outContent);
+ writeContent(outContent);
return;
}
synchronized (DebugInfo.class) {
- System.out.print(outContent);
- e.printStackTrace(System.err);
+ writeContent(outContent, System.err);
+ writeException(e);
+ }
+ }
+
+ private static void writeContent(String content) {
+ writeContent(content, System.out);
+ }
+
+ private static void writeContent(String content, PrintStream fallback) {
+ if (null == logFile) {
+ fallback.print(content);
+ return;
+ }
+
+ try (PrintStream ps = new PrintStream(new FileOutputStream(logFile, true))) {
+ ps.print(content);
+ } catch (IOException e) {
+ fallback.println(content);
+ }
+ }
+
+ private static void writeException(Throwable e) {
+ writeException(e, System.err);
+ }
+
+ private static void writeException(Throwable e, PrintStream fallback) {
+ if (null == logFile) {
+ e.printStackTrace(fallback);
+ return;
+ }
+
+ try (PrintStream ps = new PrintStream(new FileOutputStream(logFile, true))) {
+ e.printStackTrace(ps);
+ } catch (IOException ex) {
+ e.printStackTrace(fallback);
+ }
+ }
+
+ private enum Level {
+ NONE, DEBUG, INFO, WARN, ERROR;
+
+ public static Level of(String valueStr) {
+ if (null == valueStr) {
+ return NONE;
+ }
+
+ int value;
+ try {
+ value = Integer.parseInt(valueStr);
+ } catch (NumberFormatException e) {
+ return NONE;
+ }
+
+ for (Level level : values()) {
+ if (level.ordinal() == value) {
+ return level;
+ }
+ }
+
+ return NONE;
}
}
}
diff --git a/src/main/java/com/janetfilter/core/plugin/PluginManager.java b/src/main/java/com/janetfilter/core/plugin/PluginManager.java
index 264637e..54c1a53 100644
--- a/src/main/java/com/janetfilter/core/plugin/PluginManager.java
+++ b/src/main/java/com/janetfilter/core/plugin/PluginManager.java
@@ -52,9 +52,9 @@ public final class PluginManager {
throw new RuntimeException("Load plugin timeout");
}
- DebugInfo.output(String.format("============ All plugins loaded, %.2fs elapsed ============", (System.currentTimeMillis() - startTime) / 1000D));
+ DebugInfo.debug(String.format("============ All plugins loaded, %.2fs elapsed ============", (System.currentTimeMillis() - startTime) / 1000D));
} catch (Throwable e) {
- DebugInfo.output("Load plugin failed", e);
+ DebugInfo.error("Load plugin failed", e);
}
}
@@ -93,9 +93,9 @@ public final class PluginManager {
dispatcher.addTransformers(pluginEntry.getTransformers());
- DebugInfo.output("Plugin loaded: {name=" + pluginEntry.getName() + ", version=" + pluginEntry.getVersion() + ", author=" + pluginEntry.getAuthor() + "}");
+ DebugInfo.debug("Plugin loaded: {name=" + pluginEntry.getName() + ", version=" + pluginEntry.getVersion() + ", author=" + pluginEntry.getAuthor() + "}");
} catch (Throwable e) {
- DebugInfo.output("Parse plugin info failed", e);
+ DebugInfo.error("Parse plugin info failed", e);
}
}
}
diff --git a/src/main/java/com/janetfilter/core/utils/DateUtils.java b/src/main/java/com/janetfilter/core/utils/DateUtils.java
index 0ce40a0..c798b98 100644
--- a/src/main/java/com/janetfilter/core/utils/DateUtils.java
+++ b/src/main/java/com/janetfilter/core/utils/DateUtils.java
@@ -14,10 +14,18 @@ public class DateUtils {
return FULL_DF.format(date);
}
+ public static String formatDateTime() {
+ return FULL_DF.format(new Date());
+ }
+
public static String formatDate(Date date) {
return DATE_DF.format(date);
}
+ public static String formatDate() {
+ return formatDate(new Date());
+ }
+
public static String formatTime(Date date) {
return TIME_DF.format(date);
}
@@ -33,8 +41,4 @@ public class DateUtils {
public static Date parseDateTime(String dateTimeStr) throws ParseException {
return FULL_DF.parse(dateTimeStr);
}
-
- public static String formatNow() {
- return formatDateTime(new Date());
- }
}
diff --git a/src/main/java/com/janetfilter/core/utils/ProcessUtils.java b/src/main/java/com/janetfilter/core/utils/ProcessUtils.java
new file mode 100644
index 0000000..f4a1fb8
--- /dev/null
+++ b/src/main/java/com/janetfilter/core/utils/ProcessUtils.java
@@ -0,0 +1,75 @@
+package com.janetfilter.core.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ProcessUtils {
+ private static String processId;
+
+ public synchronized static String currentId() {
+ if (null == processId) {
+ String name = ManagementFactory.getRuntimeMXBean().getName() + "@";
+
+ processId = name.split("@", 2)[0];
+ }
+
+ return processId;
+ }
+
+ public static int start(ProcessBuilder pb) throws Exception {
+ return start(pb, System.out, System.err);
+ }
+
+ public static int start(ProcessBuilder pb, OutputStream out) throws Exception {
+ return start(pb, out, null);
+ }
+
+ public static int start(ProcessBuilder pb, OutputStream out, OutputStream err) throws Exception {
+ Process p = pb.start();
+
+ List threads = new ArrayList<>();
+ if (null != out) {
+ threads.add(new Thread(new RedirectOutput(p.getInputStream(), out)));
+ }
+ if (null != err) {
+ threads.add(new Thread(new RedirectOutput(p.getErrorStream(), err)));
+ }
+
+ for (Thread thread : threads) {
+ thread.start();
+ }
+ for (Thread thread : threads) {
+ thread.join();
+ }
+
+ return p.waitFor();
+ }
+
+ static class RedirectOutput implements Runnable {
+ private static final int BUFF_SIZE = 1024;
+ private final InputStream origin;
+ private final OutputStream dest;
+
+ RedirectOutput(InputStream origin, OutputStream dest) {
+ this.origin = origin;
+ this.dest = dest;
+ }
+
+ public void run() {
+ int length;
+ byte[] buffer = new byte[BUFF_SIZE];
+
+ try {
+ while ((length = origin.read(buffer)) != -1) {
+ dest.write(buffer, 0, length);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("ERROR: Redirect output failed.", e);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/janetfilter/core/utils/WhereIsUtils.java b/src/main/java/com/janetfilter/core/utils/WhereIsUtils.java
new file mode 100644
index 0000000..6f45e82
--- /dev/null
+++ b/src/main/java/com/janetfilter/core/utils/WhereIsUtils.java
@@ -0,0 +1,82 @@
+package com.janetfilter.core.utils;
+
+import com.janetfilter.core.Launcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+
+public class WhereIsUtils {
+ private static final String JAVA_HOME = System.getProperty("java.home");
+
+ public static File findJPS() {
+ String[] paths = new String[]{"bin/jps", "bin/jps.exe", "../bin/jps", "../bin/jps.exe"};
+
+ for (String path : paths) {
+ File file = new File(JAVA_HOME, path);
+ if (file.exists() && file.isFile() && file.canExecute()) {
+ return getCanonicalFile(file);
+ }
+ }
+
+ return null;
+ }
+
+ public static File findJava() {
+ String[] paths = new String[]{"bin/java", "bin/java.exe", "../bin/java", "../bin/java.exe"};
+
+ for (String path : paths) {
+ File file = new File(JAVA_HOME, path);
+ if (file.exists() && file.isFile() && file.canExecute()) {
+ return getCanonicalFile(file);
+ }
+ }
+
+ return null;
+ }
+
+ public static File findToolsJar() {
+ String[] paths = new String[]{"lib/tools.jar", "../lib/tools.jar", "../../lib/tools.jar"};
+
+ for (String path : paths) {
+ File file = new File(JAVA_HOME, path);
+ if (file.exists() && file.isFile()) {
+ return getCanonicalFile(file);
+ }
+ }
+
+ return null;
+ }
+
+ public static URI getJarURI() throws Exception {
+ URL url = Launcher.class.getProtectionDomain().getCodeSource().getLocation();
+ if (null != url) {
+ return url.toURI();
+ }
+
+ String resourcePath = "/288daf08f4ba46dfde71b7f0624b0ad7f234a67a.txt";
+ url = Launcher.class.getResource(resourcePath);
+ if (null == url) {
+ throw new Exception("Can not locate resource file.");
+ }
+
+ String path = url.getPath();
+ if (!path.endsWith("!" + resourcePath)) {
+ throw new Exception("Invalid resource path.");
+ }
+
+ path = path.substring(0, path.length() - resourcePath.length() - 1);
+
+ return new URI(path);
+
+ }
+
+ private static File getCanonicalFile(File file) {
+ try {
+ return file.getCanonicalFile();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+}
diff --git a/src/main/resources/4cc9c353c626d6510ca855ab6907ed7f64400257.txt b/src/main/resources/288daf08f4ba46dfde71b7f0624b0ad7f234a67a.txt
similarity index 100%
rename from src/main/resources/4cc9c353c626d6510ca855ab6907ed7f64400257.txt
rename to src/main/resources/288daf08f4ba46dfde71b7f0624b0ad7f234a67a.txt