From bd88a390e1ac8bf8b080e01dad9550c8da70675b Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 26 Nov 2019 21:44:02 +0800 Subject: [PATCH 1/6] =?UTF-8?q?[javalib-io-binary]=20=E4=BA=8C=E8=BF=9B?= =?UTF-8?q?=E5=88=B6=E5=BA=8F=E5=88=97=E5=8C=96/=E5=8F=8D=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E5=B7=A5=E5=85=B7=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- javalib-io-binary/pom.xml | 4 - .../io/github/dunwu/javalib/io/FstDemo.java | 48 +++++++- .../dunwu/javalib/io/JdkSerializeDemo.java | 46 ++++++- .../io/github/dunwu/javalib/io/KryoDemo.java | 114 ++++++++++++++++++ .../dunwu/javalib/io/bean/BeanUtils.java | 8 +- .../dunwu/javalib/io/bean/TestBean.java | 2 +- ...est.java => SerializePerformanceTest.java} | 26 +++- 7 files changed, 229 insertions(+), 19 deletions(-) create mode 100644 javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java rename javalib-io-binary/src/{test => main}/java/io/github/dunwu/javalib/io/bean/BeanUtils.java (82%) rename javalib-io-binary/src/{test => main}/java/io/github/dunwu/javalib/io/bean/TestBean.java (97%) rename javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/{SerializeTest.java => SerializePerformanceTest.java} (58%) diff --git a/javalib-io-binary/pom.xml b/javalib-io-binary/pom.xml index 7560afa2..ec43b90d 100644 --- a/javalib-io-binary/pom.xml +++ b/javalib-io-binary/pom.xml @@ -32,10 +32,6 @@ 5.0.0-RC4 - - org.projectlombok - lombok - io.github.dunwu dunwu-common diff --git a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/FstDemo.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/FstDemo.java index 35bfe03f..9f3362e9 100644 --- a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/FstDemo.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/FstDemo.java @@ -3,9 +3,11 @@ import org.nustaq.serialization.FSTConfiguration; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; /** - * fast-serialization lib Demo + * FST 序列化/反序列化示例 * * @author Zhang Peng * @see FST @@ -15,11 +17,38 @@ public class FstDemo { private static FSTConfiguration DEFAULT_CONFIG = FSTConfiguration.createDefaultConfiguration(); - public static byte[] serialize(T obj) { + /** + * 将对象序列化为 byte 数组 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的 byte 数组 + */ + public static byte[] writeToBytes(T obj) { return DEFAULT_CONFIG.asByteArray(obj); } - public static T deserialize(byte[] bytes, Class clazz) throws IOException { + /** + * 将对象序列化为 byte 数组后,再使用 Base64 编码 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的字符串 + */ + public static String writeToString(T obj) { + byte[] bytes = writeToBytes(obj); + return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); + } + + /** + * 将 byte 数组反序列化为原对象 + * + * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromBytes(byte[] bytes, Class clazz) throws IOException { Object obj = DEFAULT_CONFIG.asObject(bytes); if (clazz.isInstance(obj)) { return (T) obj; @@ -28,4 +57,17 @@ public static T deserialize(byte[] bytes, Class clazz) throws IOException } } + /** + * 将字符串反序列化为原对象,先使用 Base64 解码 + * + * @param str {@link #writeToString} 方法序列化后的字符串 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromString(String str, Class clazz) throws IOException { + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + return readFromBytes(Base64.getDecoder().decode(bytes), clazz); + } + } diff --git a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/JdkSerializeDemo.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/JdkSerializeDemo.java index c9012827..7f834572 100644 --- a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/JdkSerializeDemo.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/JdkSerializeDemo.java @@ -1,6 +1,8 @@ package io.github.dunwu.javalib.io; import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Base64; /** * JDK 默认序列化、反序列化机制示例 @@ -10,7 +12,14 @@ */ public class JdkSerializeDemo { - public static byte[] serialize(T obj) throws IOException { + /** + * 将对象序列化为 byte 数组 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的 byte 数组 + */ + public static byte[] writeToBytes(T obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); @@ -20,7 +29,27 @@ public static byte[] serialize(T obj) throws IOException { return bytes; } - public static T deserialize(byte[] bytes, Class clazz) throws IOException, ClassNotFoundException { + /** + * 将对象序列化为 byte 数组后,再使用 Base64 编码 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的字符串 + */ + public static String writeToString(T obj) throws IOException { + byte[] bytes = writeToBytes(obj); + return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); + } + + /** + * 将 byte 数组反序列化为原对象 + * + * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromBytes(byte[] bytes, Class clazz) throws IOException, ClassNotFoundException { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bais); Object obj = ois.readObject(); @@ -33,4 +62,17 @@ public static T deserialize(byte[] bytes, Class clazz) throws IOException } } + /** + * 将字符串反序列化为原对象,先使用 Base64 解码 + * + * @param str {@link #writeToString} 方法序列化后的字符串 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromString(String str, Class clazz) throws IOException, ClassNotFoundException { + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + return readFromBytes(Base64.getDecoder().decode(bytes), clazz); + } + } diff --git a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java new file mode 100644 index 00000000..052eb43c --- /dev/null +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java @@ -0,0 +1,114 @@ +package io.github.dunwu.javalib.io; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; +import org.objenesis.strategy.StdInstantiatorStrategy; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * Kyro 序列化/反序列化示例 + * + * @author Zhang Peng + * @author Kryo 使用指南 + * @since 2019-11-26 + */ +public class KryoDemo { + + // 每个线程的 Kryo 实例 + private static final ThreadLocal kryoLocal = ThreadLocal.withInitial(() -> { + Kryo kryo = new Kryo(); + + /** + * 不要轻易改变这里的配置!更改之后,序列化的格式就会发生变化, + * 上线的同时就必须清除 Redis 里的所有缓存, + * 否则那些缓存再回来反序列化的时候,就会报错 + */ + //支持对象循环引用(否则会栈溢出) + kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置 + + //不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册) + kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置 + + //Fix the NPE bug when deserializing Collections. + ((DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy()) + .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy()); + + return kryo; + }); + + /** + * 获得当前线程的 Kryo 实例 + * + * @return 当前线程的 Kryo 实例 + */ + public static Kryo getInstance() { + return kryoLocal.get(); + } + + /** + * 将对象序列化为 byte 数组 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的 byte 数组 + */ + public static byte[] writeToBytes(T obj) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Output output = new Output(byteArrayOutputStream); + + Kryo kryo = getInstance(); + kryo.writeObject(output, obj); + output.flush(); + + return byteArrayOutputStream.toByteArray(); + } + + /** + * 将对象序列化为 byte 数组后,再使用 Base64 编码 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的字符串 + */ + public static String writeToString(T obj) { + byte[] bytes = writeToBytes(obj); + return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); + } + + /** + * 将 byte 数组反序列化为原对象 + * + * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + @SuppressWarnings("unchecked") + public static T readFromBytes(byte[] bytes, Class clazz) { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + Input input = new Input(byteArrayInputStream); + + Kryo kryo = getInstance(); + return (T) kryo.readObject(input, clazz); + } + + /** + * 将字符串反序列化为原对象,先使用 Base64 解码 + * + * @param str {@link #writeToString} 方法序列化后的字符串 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromString(String str, Class clazz) { + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + return readFromBytes(Base64.getDecoder().decode(bytes), clazz); + } + +} diff --git a/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/bean/BeanUtils.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/BeanUtils.java similarity index 82% rename from javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/bean/BeanUtils.java rename to javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/BeanUtils.java index 3c7e3be3..58534b10 100644 --- a/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/bean/BeanUtils.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/BeanUtils.java @@ -1,6 +1,7 @@ package io.github.dunwu.javalib.io.bean; -import java.sql.Date; +import io.github.dunwu.util.time.DateExtUtils; + import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; @@ -14,15 +15,14 @@ public class BeanUtils { public static TestBean initJdk8Bean() { String[] strArray = { "a", "b", "c" }; Integer[] intArray = { 1, 2, 3, 4, 5 }; - List intList = new ArrayList<>(); - intList.addAll(Arrays.asList(intArray)); + List intList = new ArrayList<>(Arrays.asList(intArray)); Map map = new HashMap<>(); map.put("name", "jack"); map.put("age", 18); map.put("length", 175.3f); TestBean bean = new TestBean(); - Date date = Date.valueOf("2019-11-22"); LocalDateTime localDateTime = LocalDateTime.of(2000, 1, 1, 12, 0, 0); + Date date = DateExtUtils.localDateTime2Date(localDateTime); LocalDate localDate = LocalDate.of(1949, 10, 1); bean.setI1(10).setI2(1024).setF1(0.5f).setD1(100.0) .setDate1(date).setDate2(localDateTime).setDate3(localDate) diff --git a/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/bean/TestBean.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/TestBean.java similarity index 97% rename from javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/bean/TestBean.java rename to javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/TestBean.java index f47c3cb5..b2cfa3f6 100644 --- a/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/bean/TestBean.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/TestBean.java @@ -50,7 +50,7 @@ public class TestBean implements Serializable { private Map map; - public static enum Color { + public enum Color { RED, YELLOW, BLUE diff --git a/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializeTest.java b/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializePerformanceTest.java similarity index 58% rename from javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializeTest.java rename to javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializePerformanceTest.java index 5816ac3d..ff9a4462 100644 --- a/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializeTest.java +++ b/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializePerformanceTest.java @@ -9,10 +9,12 @@ import static org.assertj.core.api.Assertions.assertThat; /** + * 序列化、反序列化性能测试 + * * @author Zhang Peng * @since 2019-11-22 */ -public class SerializeTest { +public class SerializePerformanceTest { private static final int BATCH_SIZE = 100000; @@ -21,9 +23,9 @@ public void testJdkSerialize() throws IOException, ClassNotFoundException { long begin = System.currentTimeMillis(); for (int i = 0; i < BATCH_SIZE; i++) { TestBean oldBean = BeanUtils.initJdk8Bean(); - byte[] bytes = JdkSerializeDemo.serialize(oldBean); + byte[] bytes = JdkSerializeDemo.writeToBytes(oldBean); assertThat(bytes).isNotEmpty(); - TestBean newBean = JdkSerializeDemo.deserialize(bytes, TestBean.class); + TestBean newBean = JdkSerializeDemo.readFromBytes(bytes, TestBean.class); assertThat(newBean).isNotNull(); } long end = System.currentTimeMillis(); @@ -35,13 +37,27 @@ public void testFst() throws IOException { long begin = System.currentTimeMillis(); for (int i = 0; i < BATCH_SIZE; i++) { TestBean oldBean = BeanUtils.initJdk8Bean(); - byte[] bytes = FstDemo.serialize(oldBean); + byte[] bytes = FstDemo.writeToBytes(oldBean); assertThat(bytes).isNotEmpty(); - TestBean newBean = FstDemo.deserialize(bytes, TestBean.class); + TestBean newBean = FstDemo.readFromBytes(bytes, TestBean.class); assertThat(newBean).isNotNull(); } long end = System.currentTimeMillis(); System.out.printf("FST 序列化/反序列化耗时:%s", (end - begin)); } + @Test + public void testKryo() throws IOException { + long begin = System.currentTimeMillis(); + for (int i = 0; i < BATCH_SIZE; i++) { + TestBean oldBean = BeanUtils.initJdk8Bean(); + byte[] bytes = KryoDemo.writeToBytes(oldBean); + assertThat(bytes).isNotEmpty(); + TestBean newBean = KryoDemo.readFromBytes(bytes, TestBean.class); + assertThat(newBean).isNotNull(); + } + long end = System.currentTimeMillis(); + System.out.printf("Kryo 序列化/反序列化耗时:%s", (end - begin)); + } + } From 1005e481508b27ea120be16aa72c081b33957226 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 26 Nov 2019 21:49:09 +0800 Subject: [PATCH 2/6] update docs --- docs/javalib/javalib-binary.md | 199 +++++++++++++++++++++++++++++++-- 1 file changed, 192 insertions(+), 7 deletions(-) diff --git a/docs/javalib/javalib-binary.md b/docs/javalib/javalib-binary.md index 45ecbee7..2b240ca7 100644 --- a/docs/javalib/javalib-binary.md +++ b/docs/javalib/javalib-binary.md @@ -108,15 +108,48 @@ Java 自身的序列化方式具有以下缺点: 示例: ```java +import org.nustaq.serialization.FSTConfiguration; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + public class FstDemo { private static FSTConfiguration DEFAULT_CONFIG = FSTConfiguration.createDefaultConfiguration(); - public static byte[] serialize(T obj) { + /** + * 将对象序列化为 byte 数组 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的 byte 数组 + */ + public static byte[] writeToBytes(T obj) { return DEFAULT_CONFIG.asByteArray(obj); } - public static T deserialize(byte[] bytes, Class clazz) throws IOException { + /** + * 将对象序列化为 byte 数组后,再使用 Base64 编码 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的字符串 + */ + public static String writeToString(T obj) { + byte[] bytes = writeToBytes(obj); + return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); + } + + /** + * 将 byte 数组反序列化为原对象 + * + * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromBytes(byte[] bytes, Class clazz) throws IOException { Object obj = DEFAULT_CONFIG.asObject(bytes); if (clazz.isInstance(obj)) { return (T) obj; @@ -125,19 +158,171 @@ public class FstDemo { } } + /** + * 将字符串反序列化为原对象,先使用 Base64 解码 + * + * @param str {@link #writeToString} 方法序列化后的字符串 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromString(String str, Class clazz) throws IOException { + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + return readFromBytes(Base64.getDecoder().decode(bytes), clazz); + } + } ``` 测试: ```java -// 序列化 -byte[] bytes = JdkSerializeDemo.serialize(oldBean); -// 反序列化 -TestBean newBean = JdkSerializeDemo.deserialize(bytes, TestBean.class); +long begin = System.currentTimeMillis(); +for (int i = 0; i < BATCH_SIZE; i++) { + TestBean oldBean = BeanUtils.initJdk8Bean(); + byte[] bytes = FstDemo.writeToBytes(oldBean); + TestBean newBean = FstDemo.readFromBytes(bytes, TestBean.class); +} +long end = System.currentTimeMillis(); +System.out.printf("FST 序列化/反序列化耗时:%s", (end - begin)); ``` -## TODO +## Kryo 应用 + +### 引入依赖 + +```xml + + com.esotericsoftware + kryo + 5.0.0-RC4 + +``` + +### Kryo API + +示例: + +```java +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; +import org.objenesis.strategy.StdInstantiatorStrategy; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class KryoDemo { + + // 每个线程的 Kryo 实例 + private static final ThreadLocal kryoLocal = ThreadLocal.withInitial(() -> { + Kryo kryo = new Kryo(); + + /** + * 不要轻易改变这里的配置!更改之后,序列化的格式就会发生变化, + * 上线的同时就必须清除 Redis 里的所有缓存, + * 否则那些缓存再回来反序列化的时候,就会报错 + */ + //支持对象循环引用(否则会栈溢出) + kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置 + + //不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册) + kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置 + + //Fix the NPE bug when deserializing Collections. + ((DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy()) + .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy()); + + return kryo; + }); + + /** + * 获得当前线程的 Kryo 实例 + * + * @return 当前线程的 Kryo 实例 + */ + public static Kryo getInstance() { + return kryoLocal.get(); + } + + /** + * 将对象序列化为 byte 数组 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的 byte 数组 + */ + public static byte[] writeToBytes(T obj) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Output output = new Output(byteArrayOutputStream); + + Kryo kryo = getInstance(); + kryo.writeObject(output, obj); + output.flush(); + + return byteArrayOutputStream.toByteArray(); + } + + /** + * 将对象序列化为 byte 数组后,再使用 Base64 编码 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的字符串 + */ + public static String writeToString(T obj) { + byte[] bytes = writeToBytes(obj); + return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); + } + + /** + * 将 byte 数组反序列化为原对象 + * + * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + @SuppressWarnings("unchecked") + public static T readFromBytes(byte[] bytes, Class clazz) { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + Input input = new Input(byteArrayInputStream); + + Kryo kryo = getInstance(); + return (T) kryo.readObject(input, clazz); + } + + /** + * 将字符串反序列化为原对象,先使用 Base64 解码 + * + * @param str {@link #writeToString} 方法序列化后的字符串 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromString(String str, Class clazz) { + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + return readFromBytes(Base64.getDecoder().decode(bytes), clazz); + } + +} +``` + +测试: + +```java +long begin = System.currentTimeMillis(); +for (int i = 0; i < BATCH_SIZE; i++) { + TestBean oldBean = BeanUtils.initJdk8Bean(); + byte[] bytes = KryoDemo.writeToBytes(oldBean); + TestBean newBean = KryoDemo.readFromBytes(bytes, TestBean.class); +} +long end = System.currentTimeMillis(); +System.out.printf("Kryo 序列化/反序列化耗时:%s", (end - begin)); +``` ## 参考资料 From 914e6ae3e7453321bfa0892401cfb8367d60150a Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Wed, 27 Nov 2019 18:36:08 +0800 Subject: [PATCH 3/6] update docs --- README.md | 2 + docs/README.md | 2 + docs/javalib/java-log.md | 2 +- docs/javatool/build/maven/maven-deploy.md | 2 +- docs/javatool/build/maven/maven-pom.md | 2 +- docs/javatool/build/maven/maven-quickstart.md | 2 +- docs/javatool/build/maven/maven-settings.md | 2 +- docs/javatool/test/jmeter.md | 204 ++++++++++++++++++ docs/sidebar.md | 2 + ...1\351\200\201\350\257\267\346\261\202.jmx" | 181 ++++++++++++++++ 10 files changed, 396 insertions(+), 5 deletions(-) create mode 100644 docs/javatool/test/jmeter.md create mode 100644 "settings/jmeter/\350\257\273\345\217\226CSV\345\271\266\346\234\211\345\272\217\347\232\204\345\217\221\351\200\201\350\257\267\346\261\202.jmx" diff --git a/README.md b/README.md index 0b14c3b4..0e3260d2 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ - Java IDE - [Intellij IDEA 使用指南](docs/javatool/ide/intellij.md) - [Eclipse 使用指南](docs/javatool/ide/eclipse.md) +- **测试** + - [Jmeter 应用指南](docs/javatool/test/jmeter.md) ## [JavaCore 教程](https://dunwu.github.io/javacore/) diff --git a/docs/README.md b/docs/README.md index b208bbf4..85beb372 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,6 +39,8 @@ - Java IDE - [Intellij IDEA 使用指南](javatool/ide/intellij.md) - [Eclipse 使用指南](javatool/ide/eclipse.md) +- **测试** + - [Jmeter 应用指南](javatool/test/jmeter.md) ## [JavaCore 教程](https://dunwu.github.io/javacore/) diff --git a/docs/javalib/java-log.md b/docs/javalib/java-log.md index bba76bf2..bb46459f 100644 --- a/docs/javalib/java-log.md +++ b/docs/javalib/java-log.md @@ -6,7 +6,7 @@ > > 我们先来逐一了解一下主流日志工具。 > -> :notebook: 本文已归档到:「[blog](https://github.com/dunwu/blog)」 +> 📓 本文已归档到:「[blog](https://github.com/dunwu/blog)」 diff --git a/docs/javatool/build/maven/maven-deploy.md b/docs/javatool/build/maven/maven-deploy.md index 3a449167..b51affaf 100644 --- a/docs/javatool/build/maven/maven-deploy.md +++ b/docs/javatool/build/maven/maven-deploy.md @@ -1,6 +1,6 @@ # Maven 教程之发布 jar 到私服或中央仓库 -> :notebook: 本文已归档到:「[blog](https://github.com/dunwu/blog/blob/master/source/_posts/java/javatool/build/maven/)」 +> 📓 本文已归档到:「[blog](https://github.com/dunwu/blog/blob/master/source/_posts/java/javatool/build/maven/)」 diff --git a/docs/javatool/build/maven/maven-pom.md b/docs/javatool/build/maven/maven-pom.md index 08a5928c..f1d8e308 100644 --- a/docs/javatool/build/maven/maven-pom.md +++ b/docs/javatool/build/maven/maven-pom.md @@ -1,6 +1,6 @@ # Maven 教程之 pom.xml 详解 -> :notebook: 本文已归档到:「[blog](https://github.com/dunwu/blog/blob/master/source/_posts/java/javatool/build/maven/)」 +> 📓 本文已归档到:「[blog](https://github.com/dunwu/blog/blob/master/source/_posts/java/javatool/build/maven/)」 diff --git a/docs/javatool/build/maven/maven-quickstart.md b/docs/javatool/build/maven/maven-quickstart.md index 228ee6c6..2110ef48 100644 --- a/docs/javatool/build/maven/maven-quickstart.md +++ b/docs/javatool/build/maven/maven-quickstart.md @@ -1,6 +1,6 @@ # Maven 教程之入门指南 -> :notebook: 本文已归档到:「[blog](https://github.com/dunwu/blog/blob/master/source/_posts/java/javatool/build/maven/)」 +> 📓 本文已归档到:「[blog](https://github.com/dunwu/blog/blob/master/source/_posts/java/javatool/build/maven/)」 diff --git a/docs/javatool/build/maven/maven-settings.md b/docs/javatool/build/maven/maven-settings.md index 59500afa..924eaa4a 100644 --- a/docs/javatool/build/maven/maven-settings.md +++ b/docs/javatool/build/maven/maven-settings.md @@ -1,6 +1,6 @@ # Maven 教程之 settings.xml 详解 -> :notebook: 本文已归档到:「[blog](https://github.com/dunwu/blog/blob/master/source/_posts/java/javatool/build/maven/)」 +> 📓 本文已归档到:「[blog](https://github.com/dunwu/blog/blob/master/source/_posts/java/javatool/build/maven/)」 diff --git a/docs/javatool/test/jmeter.md b/docs/javatool/test/jmeter.md new file mode 100644 index 00000000..d5d7630d --- /dev/null +++ b/docs/javatool/test/jmeter.md @@ -0,0 +1,204 @@ +# JMeter 应用指南 + +> [Jmeter](https://github.com/apache/jmeter) 是一款基于 Java 开发的功能和性能测试软件。 +> +> 🎁 本文编辑时的最新版本为:5.1.1 + +## 1. 简介 + +[Jmeter](https://github.com/apache/jmeter) 是一款使用 Java 开发的功能和性能测试软件。 + +### 1.1. 特性 + +Jmeter 能够加载和性能测试许多不同的应用程序/服务器/协议类型: + +- 网络 - HTTP,HTTPS(Java,NodeJS,PHP,ASP.NET 等) +- SOAP / REST Web 服务 +- FTP 文件 +- 通过 JDBC 的数据库 +- LDAP +- 通过 JMS 的面向消息的中间件(MOM) +- 邮件-SMTP(S),POP3(S)和 IMAP(S) +- 本机命令或 Shell 脚本 +- TCP 协议 +- Java 对象 + +### 1.2. 工作流 + +Jmeter 的工作原理是仿真用户向服务器发送请求,并收集服务器应答信息并计算统计信息。 + +Jmeter 的工作流如下图所示: + +![](http://dunwu.test.upcdn.net/cs/java/javaweb/technology/test/jmeter-workflow.png!zp) + +### 1.3. 主要元素 + +Jmeter 的主要元素如下: + +- **`测试计划(Test Plan)`** - 可以将测试计划视为 JMeter 的测试脚本 。测试计划由测试元素组成,例如线程组,逻辑控制器,样本生成控制器,监听器,定时器,断言和配置元素。 +- **`线程组(Thread Group)`** - 线程组的作用是:模拟大量用户负载的运行场景。 + - 设置线程数 + - 设置加速期 + - 设置执行测试的次数 +- **`控制器(Controllers)`** - 可以分为两大类: + - **`采样器(Sampler)`** - 采样器的作用是模拟用户对目标服务器发送请求。 采样器是必须将组件添加到测试计划中的,因为它只能让 JMeter 知道需要将哪种类型的请求发送到服务器。 请求可以是 HTTP,HTTP(s),FTP,TCP,SMTP,SOAP 等。 + - **`逻辑控制器`** - 逻辑控制器的作用是:控制多个请求发送的循环次数及顺序等。 +- **`监听器(Listeners)`** - 监听器的作用是:收集测试结果信息。如查看结果树、汇总报告等。 +- **`计时器(Timers)`** - 计时器的作用是:控制多个请求发送的时间频次。 +- **`配置元素(Configuration Elements)`** - 配置元素的工作与采样器的工作类似。但是,它不发送请求,而是提供预备的数据等,如 CSV、函数助手。 +- **`预处理器元素(Pre-Processor Elements)`** - 预处理器元素在采样器发出请求之前执行,如果预处理器附加到采样器元素,那么它将在该采样器元素运行之前执行。预处理器元素用于在运行之前准备环境及参数。 +- **`后处理器元素(Post-Processor Elements)`** - 后处理器元素是在发送采样器请求之后执行的元素,常用于处理响应数据。 + +![](http://dunwu.test.upcdn.net/cs/java/javaweb/technology/test/jmeter-elements.png!zp) + +> 📌 提示: +> +> Jmeter 元素的数量关系大致如下: +> +> 1. 脚本中最多只能有一个测试计划。 +> 2. 测试计划中至少要有一个线程组。 +> 3. 线程组中至少要有一个取样器。 +> 4. 线程组中至少要有一个监听器。 + +## 2. 安装 + +### 2.1. 环境要求 + +- 必要的。Jmeter 基于 JDK8 开发,所以必须运行在 JDK8 环境。 + + - JDK8 + +- 可选的。有些 jar 包不是 Jmeter 提供的,如果需要相应的功能,需要自行下载并置于 `lib` 目录。 + - JDBC + - JMS + - [Bouncy Castle](http://www.bouncycastle.org/test_releases.html) + +### 2.2. 下载 + +进入 [**Jmeter 官网下载地址**](https://jmeter.apache.org/download_jmeter.cgi) 选择需要版本进行下载。 + +### 2.3. 启动 + +解压 Jmeter 压缩包,进入 bin 目录 + +Unix 类系统运行 `jmeter` ;Windows 系统运行 `jmeter.bat` + +![image-20191024104517721](http://dunwu.test.upcdn.net/snap/jmeter/image-20191024104517721.png!zp) + +## 3. 使用 + +### 3.1. 创建测试计划 + +> 🔔 注意: +> +> - 在运行整个测试计划之前,应保存测试计划。 +> +> - JMeter 的测试计划以 `.jmx` 扩展文件的形式保存。 + +#### 3.1.1. 创建线程组 + +- 在“测试计划”上右键 【添加】=>【线程(用户)】=>【线程组】。 + +- 设置线程数和循环次数 + +![image-20191024105545736](http://dunwu.test.upcdn.net/snap/jmeter/image-20191024105545736.png!zp) + +#### 3.1.2. 配置原件 + +- 在新建的线程组上右键 【添加】=>【配置元件】=>【HTTP 请求默认值】。 + +- 填写协议、服务器名称或 IP、端口号 + +![image-20191024110016264](http://dunwu.test.upcdn.net/snap/jmeter/image-20191024110016264.png!zp) + +#### 3.1.3. 构造 HTTP 请求 + +- 在“线程组”上右键 【添加-】=>【取样器】=>【HTTP 请求】。 + +- 填写协议、服务器名称或 IP、端口号(如果配置了 HTTP 请求默认值可以忽略) +- 填写方法、路径 +- 填写参数、消息体数据、文件上传 + +![image-20191024110953063](http://dunwu.test.upcdn.net/snap/jmeter/image-20191024110953063.png!zp) + +#### 3.1.4. 添加 HTTP 请求头 + +- 在“线程组”上右键 【添加】=>【配置元件】=>【HTTP 信息头管理器】 +- 由于我的测试例中传输的数据为 json 形式,所以设置键值对 `Content-Type`:`application/json` + +![image-20191024111825226](http://dunwu.test.upcdn.net/snap/jmeter/image-20191024111825226.png!zp) + +#### 3.1.5. 添加断言 + +- 在“线程组”上右键 【添加】=>【断言】=>【 响应断言 】 +- 在我的案例中,以 HTTP 应答状态码为 200 来判断请求是否成功 + +![image-20191024112335130](http://dunwu.test.upcdn.net/snap/jmeter/image-20191024112335130.png!zp) + +#### 3.1.6. 添加察看结果树 + +- 在“线程组”上右键 【添加】=>【监听器】=>【察看结果树】 +- 直接点击运行,就可以查看测试结果 + +![image-20191024113849270](http://dunwu.test.upcdn.net/snap/jmeter/image-20191024113849270.png!zp) + +#### 3.1.7. 添加汇总报告 + +- 在“线程组”上右键 【添加】=>【监听器】=>【汇总报告】 +- 直接点击运行,就可以查看测试结果 + +![image-20191024114016424](http://dunwu.test.upcdn.net/snap/jmeter/image-20191024114016424.png!zp) + +#### 3.1.8. 保存测试计划 + +执行测试计划前,GUI 会提示先保存配置为 `jmx` 文件。 + +### 3.2. 执行测试计划 + +官方建议不要直接使用 GUI 来执行测试计划,这种模式指适用于创建测试计划和 debug。 + +执行测试计划应该使用命令行模式,语法形式如下: + +```bash +jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder] +``` + +执行测试计划后,在 `-e -o` 参数后指定的 web 报告目录下,可以找到测试报告内容。在浏览器中打开 `index.html` 文件,可以看到如下报告: + +![image-20191024120233058](http://dunwu.test.upcdn.net/snap/jmeter/image-20191024120233058.png!zp) + +## 4. 问题 + +### 4.1. 如何读取本地 txt/csv 文件作为请求参数 + +参考:[Jmeter 读取本地 txt/csv 文件作为请求参数,实现接口自动化](https://www.jianshu.com/p/3b2d3b643415) + +(1)依次点击【添加】=>【配置元件】=>【CSV 数据文件设置】 + +配置如下所示: + +![image-20191127175820747](http://dunwu.test.upcdn.net/snap/image-20191127175820747.png) + +重要配置说明(其他配置根据实际情况填): + +- 文件名:输入需要导入的数据文件位置。 +- 文件编码:设为 UTF-8,避免乱码。 +- 变量名称:使用 `,` 分隔输入变量列表。如截图中设置了两个变量 `a` 和 `b` + +(2)在 HTTP 请求的消息体数据中配置参数 + +``` +[{"a":"${a}","b":"${b}"}] +``` + +### 4.2. 如何有序发送数据 + +依次点击【添加】=>【逻辑控制器】=>【事务控制器】 + +## 5. 参考资料 + +- [Jmeter 官网](https://jmeter.apache.org/) +- [Jmeter Github](https://github.com/apache/jmeter) +- [Jmeter 性能测试入门](https://www.cnblogs.com/TankXiao/p/4045439.html) +- [易百教程 - Jmeter 教程](https://www.yiibai.com/jmeter) +- [Jmeter 读取本地 txt/csv 文件作为请求参数,实现接口自动化](https://www.jianshu.com/p/3b2d3b643415) diff --git a/docs/sidebar.md b/docs/sidebar.md index 3daa33c8..a7dd1365 100644 --- a/docs/sidebar.md +++ b/docs/sidebar.md @@ -28,6 +28,8 @@ - Java IDE - [Intellij IDEA 使用指南](javatool/ide/intellij.md) - [Eclipse 使用指南](javatool/ide/eclipse.md) +- **测试工具** + - [Jmeter 应用指南](javatool/test/jmeter.md) ## [JavaCore 教程](https://dunwu.github.io/javacore/) diff --git "a/settings/jmeter/\350\257\273\345\217\226CSV\345\271\266\346\234\211\345\272\217\347\232\204\345\217\221\351\200\201\350\257\267\346\261\202.jmx" "b/settings/jmeter/\350\257\273\345\217\226CSV\345\271\266\346\234\211\345\272\217\347\232\204\345\217\221\351\200\201\350\257\267\346\261\202.jmx" new file mode 100644 index 00000000..8704dde0 --- /dev/null +++ "b/settings/jmeter/\350\257\273\345\217\226CSV\345\271\266\346\234\211\345\272\217\347\232\204\345\217\221\351\200\201\350\257\267\346\261\202.jmx" @@ -0,0 +1,181 @@ + + + + + + false + true + false + + + + + + + + continue + + false + 1 + + 100 + 1 + false + + + + + + + + + localhost + 8080 + http + + + 6 + + + + + + true + + + + false + [{"a":"${a}","b":"${b}"}] + = + + + + + + + + /getMessage + POST + true + false + true + false + + + + + + + + + Content-Type + application/json + + + + + + + 200 + + + Assertion.response_code + false + 8 + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + , + UTF-8 + D:/Temp/data.csv + true + true + true + shareMode.all + true + a,b + + + + false + false + + + + + + From fecc0c26185995f8197b8d505c5a6057f999ca17 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Fri, 29 Nov 2019 18:10:14 +0800 Subject: [PATCH 4/6] update docs --- .gitignore | 2 + README.md | 48 +- docs/README.md | 50 +- docs/book.json | 69 +++ docs/cover.jpg | Bin 0 -> 117622 bytes docs/index.html | 1 - docs/javalib/dozer.md | 18 +- docs/javalib/freemark.md | 20 +- docs/javalib/javalib-binary.md | 11 +- docs/javalib/{java-log.md => javalib-log.md} | 34 +- .../javalib/{java-util.md => javalib-util.md} | 0 docs/javalib/javamail.md | 34 +- docs/javalib/jsoup.md | 188 ++++--- docs/javalib/junit.md | 20 +- docs/javalib/lombok.md | 2 +- docs/javalib/mockito.md | 58 +- docs/javalib/reflections.md | 4 +- docs/javalib/thumbnailator.md | 36 +- docs/javalib/zxing.md | 24 +- docs/javatool/build/ant.md | 16 +- docs/javatool/build/maven/README.md | 5 +- docs/javatool/build/maven/maven-action.md | 28 +- docs/javatool/build/maven/maven-deploy.md | 14 +- docs/javatool/build/maven/maven-pom.md | 38 +- docs/javatool/build/maven/maven-quickstart.md | 28 +- docs/javatool/build/maven/maven-settings.md | 28 +- .../maven/plugins/maven-checkstyle-plugin.md | 425 --------------- .../build/maven/plugins/maven-checkstyle.md | 425 --------------- docs/javatool/elastic/README.md | 45 ++ docs/javatool/elastic/elastic-beats-ops.md | 238 +++++++++ .../elastic/elastic-elasticsearch-ops.md | 133 +++++ docs/javatool/elastic/elastic-kibana-ops.md | 346 ++++++++++++ docs/javatool/elastic/elastic-logstash-ops.md | 495 ++++++++++++++++++ docs/javatool/elastic/elastic-quickstart.md | 276 ++++++++++ docs/javatool/ide/README.md | 4 + docs/javatool/ide/eclipse.md | 2 +- docs/javatool/ide/intellij.md | 39 +- docs/javatool/ide/vscode.md | 2 +- docs/package.json | 36 +- docs/sidebar.md | 79 +-- javalib-bean/README.md | 2 +- .../io/github/dunwu/javalib/io/KryoDemo.java | 2 +- 42 files changed, 2028 insertions(+), 1297 deletions(-) create mode 100644 docs/book.json create mode 100644 docs/cover.jpg rename docs/javalib/{java-log.md => javalib-log.md} (97%) rename docs/javalib/{java-util.md => javalib-util.md} (100%) delete mode 100644 docs/javatool/build/maven/plugins/maven-checkstyle-plugin.md delete mode 100644 docs/javatool/build/maven/plugins/maven-checkstyle.md create mode 100644 docs/javatool/elastic/README.md create mode 100644 docs/javatool/elastic/elastic-beats-ops.md create mode 100644 docs/javatool/elastic/elastic-elasticsearch-ops.md create mode 100644 docs/javatool/elastic/elastic-kibana-ops.md create mode 100644 docs/javatool/elastic/elastic-logstash-ops.md create mode 100644 docs/javatool/elastic/elastic-quickstart.md create mode 100644 docs/javatool/ide/README.md diff --git a/.gitignore b/.gitignore index b8875768..4a99239d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ hs_err_pid* # maven plugin temp files .flattened-pom.xml +package-lock.json # ------------------------------- javascript ------------------------------- @@ -47,6 +48,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* bundle*.js +book.pdf # ------------------------------- intellij ------------------------------- diff --git a/README.md b/README.md index 0e3260d2..7de9e8d7 100644 --- a/README.md +++ b/README.md @@ -9,36 +9,42 @@ > 各种主流 Java 库的应用。 -- [Dozer 使用指南](docs/javalib/dozer.md) -- [Freemark 使用指南](docs/javalib/freemark.md) +- [Dozer 应用指南](docs/javalib/dozer.md) +- [Freemark 应用指南](docs/javalib/freemark.md) - [Java 与 JSON](docs/javalib/javalib-json.md) -- [细说 Java 主流日志工具库](docs/javalib/java-log.md) -- [细说 Java 主流工具包](docs/javalib/java-util.md) -- [JavaMail 使用指南](docs/javalib/javamail.md) -- [Jsoup 使用指南](docs/javalib/jsoup.md) -- [JUnit5 使用指南](docs/javalib/junit.md) -- [Lombok 使用指南](docs/javalib/lombok.md) -- [Mockito 使用指南](docs/javalib/mockito.md) -- [Reflections 使用指南](docs/javalib/reflections.md) -- [Thumbnailator 使用指南](docs/javalib/thumbnailator.md) -- [ZXing 使用指南](docs/javalib/zxing.md) +- [细说 Java 主流日志工具库](docs/javalib/javalib-log.md) +- [细说 Java 主流工具包](docs/javalib/javalib-util.md) +- [JavaMail 应用指南](docs/javalib/javamail.md) +- [Jsoup 应用指南](docs/javalib/jsoup.md) +- [JUnit5 应用指南](docs/javalib/junit.md) +- [Lombok 应用指南](docs/javalib/lombok.md) +- [Mockito 应用指南](docs/javalib/mockito.md) +- [Reflections 应用指南](docs/javalib/reflections.md) +- [Thumbnailator 应用指南](docs/javalib/thumbnailator.md) +- [ZXing 应用指南](docs/javalib/zxing.md) ## [JavaTool](docs/javatool/README.md) > 各种主流 Java 工具的应用。 - [构建工具](docs/javatool/build/README.md) - - Maven 教程 + - [Maven 教程 📚](docs/javatool/build/maven/README.md) - [Maven 快速指南](docs/javatool/build/maven/maven-quickstart.md) - [Maven 教程之 pom.xml 详解](docs/javatool/build/maven/maven-pom.md) - [Maven 教程之 settings.xml 详解](docs/javatool/build/maven/maven-settings.md) - [Maven 实战问题和最佳实践](docs/javatool/build/maven/maven-action.md) - [Maven 教程之发布 jar 到私服或中央仓库](docs/javatool/build/maven/maven-deploy.md) - - [Maven 插件之代码检查](docs/javatool/build/maven/plugins/maven-checkstyle.md) + - [Maven 插件之代码检查](docs/javatool/build/maven/maven-checkstyle-plugin.md) - [Ant 简易教程](docs/javatool/build/ant.md) -- Java IDE - - [Intellij IDEA 使用指南](docs/javatool/ide/intellij.md) - - [Eclipse 使用指南](docs/javatool/ide/eclipse.md) +- [搜索引擎 Elastic](docs/javatool/elastic/README.md) + - [Elastic 技术栈快速入门](docs/javatool/elastic/elastic-quickstart.md) + - [Elasticsearch 运维](docs/javatool/elastic/elastic-elasticsearch-ops.md) + - [Beats 运维](docs/javatool/elastic/elastic-beats-ops.md) + - [Kibana 运维](docs/javatool/elastic/elastic-kibana-ops.md) + - [Logstash 运维](docs/javatool/elastic/elastic-logstash-ops.md) +- [Java IDE](docs/javatool/ide/README.md) + - [Intellij IDEA 应用指南](docs/javatool/ide/intellij.md) + - [Eclipse 应用指南](docs/javatool/ide/eclipse.md) - **测试** - [Jmeter 应用指南](docs/javatool/test/jmeter.md) @@ -62,10 +68,10 @@ ## 相关技术栈 -- :1234: [db-tutorial](https://dunwu.github.io/db-tutorial/) - 是对数据库领域开发经验的总结。内容包含:关系型数据库和 Nosql 理论、Mysql、Redis 等。 -- :dart: [algorithm-tutorial](https://dunwu.github.io/algorithm-tutorial/) - 是对数据结构和算法的总结。内容包含:一些基本的数据结构、算法。 -- :penguin: [linux-tutorial](https://github.com/dunwu/linux-tutorial) - 是对 Linux 操作系统的经验总结。内容包含:Linux 常用命令;各种常见软件的 Linux 环境安装配置;运维、部署脚本;Shell、Python 语法教程;Git、Docker 教程。 -- :art: [frontend-tutorial](https://github.com/dunwu/frontend-tutorial) - 前端教程 +- [db-tutorial](https://dunwu.github.io/db-tutorial/) - 是对数据库领域开发经验的总结。内容包含:关系型数据库和 Nosql 理论、Mysql、Redis 等。 +- [algorithm-tutorial](https://dunwu.github.io/algorithm-tutorial/) - 是对数据结构和算法的总结。内容包含:一些基本的数据结构、算法。 +- [linux-tutorial](https://github.com/dunwu/linux-tutorial) - 是对 Linux 操作系统的经验总结。内容包含:Linux 常用命令;各种常见软件的 Linux 环境安装配置;运维、部署脚本;Shell、Python 语法教程;Git、Docker 教程。 +- [frontend-tutorial](https://github.com/dunwu/frontend-tutorial) - 前端教程 --- diff --git a/docs/README.md b/docs/README.md index 85beb372..2375d56d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,37 +9,43 @@ > 各种主流 Java 库的应用。 -- [Dozer 使用指南](javalib/dozer.md) -- [Freemark 使用指南](javalib/freemark.md) +- [Dozer 应用指南](javalib/dozer.md) +- [Freemark 应用指南](javalib/freemark.md) - [Java 与 JSON](javalib/javalib-json.md) -- [细说 Java 主流日志工具库](javalib/java-log.md) -- [细说 Java 主流工具包](javalib/java-util.md) -- [JavaMail 使用指南](javalib/javamail.md) -- [Jsoup 使用指南](javalib/jsoup.md) -- [JUnit5 使用指南](javalib/junit.md) -- [Lombok 使用指南](javalib/lombok.md) -- [Mockito 使用指南](javalib/mockito.md) -- [Reflections 使用指南](javalib/reflections.md) -- [Thumbnailator 使用指南](javalib/thumbnailator.md) -- [ZXing 使用指南](javalib/zxing.md) +- [细说 Java 主流日志工具库](javalib/javalib-log.md) +- [细说 Java 主流工具包](javalib/javalib-util.md) +- [JavaMail 应用指南](javalib/javamail.md) +- [Jsoup 应用指南](javalib/jsoup.md) +- [JUnit5 应用指南](javalib/junit.md) +- [Lombok 应用指南](javalib/lombok.md) +- [Mockito 应用指南](javalib/mockito.md) +- [Reflections 应用指南](javalib/reflections.md) +- [Thumbnailator 应用指南](javalib/thumbnailator.md) +- [ZXing 应用指南](javalib/zxing.md) ## [JavaTool](javatool/README.md) > 各种主流 Java 工具的应用。 - [构建工具](javatool/build/README.md) - - Maven 教程 + - [Maven 教程 📚](javatool/build/maven/README.md) - [Maven 快速指南](javatool/build/maven/maven-quickstart.md) - [Maven 教程之 pom.xml 详解](javatool/build/maven/maven-pom.md) - [Maven 教程之 settings.xml 详解](javatool/build/maven/maven-settings.md) - [Maven 实战问题和最佳实践](javatool/build/maven/maven-action.md) - [Maven 教程之发布 jar 到私服或中央仓库](javatool/build/maven/maven-deploy.md) - - [Maven 插件之代码检查](javatool/build/maven/plugins/maven-checkstyle.md) + - [Maven 插件之代码检查](javatool/build/maven/maven-checkstyle-plugin.md) - [Ant 简易教程](javatool/build/ant.md) -- Java IDE - - [Intellij IDEA 使用指南](javatool/ide/intellij.md) - - [Eclipse 使用指南](javatool/ide/eclipse.md) -- **测试** +- [搜索引擎 Elastic](javatool/elastic/README.md) + - [Elastic 技术栈快速入门](javatool/elastic/elastic-quickstart.md) + - [Elasticsearch 运维](javatool/elastic/elastic-elasticsearch-ops.md) + - [Beats 运维](javatool/elastic/elastic-beats-ops.md) + - [Kibana 运维](javatool/elastic/elastic-kibana-ops.md) + - [Logstash 运维](javatool/elastic/elastic-logstash-ops.md) +- [Java IDE](javatool/ide/README.md) + - [Intellij IDEA 应用指南](javatool/ide/intellij.md) + - [Eclipse 应用指南](javatool/ide/eclipse.md) +- **测试工具** - [Jmeter 应用指南](javatool/test/jmeter.md) ## [JavaCore 教程](https://dunwu.github.io/javacore/) @@ -62,10 +68,10 @@ ## 相关技术栈 -- :1234: [db-tutorial](https://dunwu.github.io/db-tutorial/) - 是对数据库领域开发经验的总结。内容包含:关系型数据库和 Nosql 理论、Mysql、Redis 等。 -- :dart: [algorithm-tutorial](https://dunwu.github.io/algorithm-tutorial/) - 是对数据结构和算法的总结。内容包含:一些基本的数据结构、算法。 -- :penguin: [linux-tutorial](https://github.com/dunwu/linux-tutorial) - 是对 Linux 操作系统的经验总结。内容包含:Linux 常用命令;各种常见软件的 Linux 环境安装配置;运维、部署脚本;Shell、Python 语法教程;Git、Docker 教程。 -- :art: [frontend-tutorial](https://github.com/dunwu/frontend-tutorial) - 前端教程 +- [db-tutorial](https://dunwu.github.io/db-tutorial/) - 是对数据库领域开发经验的总结。内容包含:关系型数据库和 Nosql 理论、Mysql、Redis 等。 +- [algorithm-tutorial](https://dunwu.github.io/algorithm-tutorial/) - 是对数据结构和算法的总结。内容包含:一些基本的数据结构、算法。 +- [linux-tutorial](https://github.com/dunwu/linux-tutorial) - 是对 Linux 操作系统的经验总结。内容包含:Linux 常用命令;各种常见软件的 Linux 环境安装配置;运维、部署脚本;Shell、Python 语法教程;Git、Docker 教程。 +- [frontend-tutorial](https://github.com/dunwu/frontend-tutorial) - 前端教程 --- diff --git a/docs/book.json b/docs/book.json new file mode 100644 index 00000000..69ecaf5c --- /dev/null +++ b/docs/book.json @@ -0,0 +1,69 @@ +{ + "gitbook": "3.2.2", + "title": "java-tutorial", + "language": "zh-hans", + "root": "./", + "structure": { + "summary": "sidebar.md" + }, + "links": { + "sidebar": { + "java-tutorial": "https://github.com/dunwu/java-tutorial" + } + }, + "plugins": [ + "-lunr", + "-search", + "advanced-emoji@^0.2.2", + "anchor-navigation-ex@1.0.10", + "anchors@^0.7.1", + "edit-link@^2.0.2", + "expandable-chapters-small@^0.1.7", + "github@^2.0.0", + "search-plus@^0.0.11", + "simple-page-toc@^0.1.1", + "splitter@^0.0.8", + "tbfed-pagefooter@^0.0.1" + ], + "pluginsConfig": { + "anchor-navigation-ex": { + "showLevel": false, + "associatedWithSummary": true, + "multipleH1": true, + "mode": "float", + "isRewritePageTitle": false, + "float": { + "showLevelIcon": false, + "level1Icon": "fa fa-hand-o-right", + "level2Icon": "fa fa-hand-o-right", + "level3Icon": "fa fa-hand-o-right" + }, + "pageTop": { + "showLevelIcon": false, + "level1Icon": "fa fa-hand-o-right", + "level2Icon": "fa fa-hand-o-right", + "level3Icon": "fa fa-hand-o-right" + } + }, + "edit-link": { + "base": "https://github.com/dunwu/java-tutorial/blob/master/docs", + "label": "编辑此页面" + }, + "github": { + "url": "https://github.com/dunwu" + }, + "simple-page-toc": { + "maxDepth": 4, + "skipFirstH1": true + }, + "sharing": { + "weibo": true, + "all": ["weibo"] + }, + "tbfed-pagefooter": { + "copyright": "Copyright © Zhang Peng 2017", + "modify_label": "该文件上次修订时间:", + "modify_format": "YYYY-MM-DD HH:mm:ss" + } + } +} diff --git a/docs/cover.jpg b/docs/cover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d859125628b0cfd75dc162521f1e1a1725ac3e1e GIT binary patch literal 117622 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

    =$U#GEL(k; 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 diff --git a/docs/javalib/dozer.md b/docs/javalib/dozer.md index e93b517c..154d3329 100644 --- a/docs/javalib/dozer.md +++ b/docs/javalib/dozer.md @@ -1,4 +1,4 @@ -# Dozer 使用指南 +# Dozer 应用指南 这篇文章是本人在阅读 Dozer 官方文档(5.5.1 版本,官网已经一年多没更新了)的过程中,整理下来我认为比较基础的应用场景。 @@ -8,17 +8,17 @@ - [简介](#简介) - [安装](#安装) - - [引入 jar 包](#引入-jar-包) - - [Eclipse 插件](#eclipse-插件) + - [引入 jar 包](#引入-jar-包) + - [Eclipse 插件](#eclipse-插件) - [使用](#使用) - - [准备](#准备) - - [Dozer 的配置](#dozer-的配置) - - [与 Spring 整合](#与-spring-整合) + - [准备](#准备) + - [Dozer 的配置](#dozer-的配置) + - [与 Spring 整合](#与-spring-整合) - [Dozer 支持的数据类型转换](#dozer-支持的数据类型转换) - [Dozer 的映射配置](#dozer-的映射配置) - - [用注解来配置映射](#用注解来配置映射) - - [用 API 来配置映射](#用-api-来配置映射) - - [用 XML 来配置映射](#用-xml-来配置映射) + - [用注解来配置映射](#用注解来配置映射) + - [用 API 来配置映射](#用-api-来配置映射) + - [用 XML 来配置映射](#用-xml-来配置映射) - [参考](#参考) diff --git a/docs/javalib/freemark.md b/docs/javalib/freemark.md index 0739bcd0..3d9cc432 100644 --- a/docs/javalib/freemark.md +++ b/docs/javalib/freemark.md @@ -1,21 +1,21 @@ -# Freemark 使用指南 +# Freemark 应用指南 > FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML 网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个 Java 类库,是一款程序员可以嵌入他们所开发产品的组件。 - [简介](#简介) - - [什么是 Freemark?](#什么是-freemark) + - [什么是 Freemark?](#什么是-freemark) - [入门](#入门) - - [创建 Configuration 实例](#创建-configuration-实例) - - [创建数据模型](#创建数据模型) - - [获取模板](#获取模板) - - [合并模板和数据模型](#合并模板和数据模型) - - [完整示例](#完整示例) + - [创建 Configuration 实例](#创建-configuration-实例) + - [创建数据模型](#创建数据模型) + - [获取模板](#获取模板) + - [合并模板和数据模型](#合并模板和数据模型) + - [完整示例](#完整示例) - [基础](#基础) - - [数值](#数值) - - [类型](#类型) - - [模板](#模板) + - [数值](#数值) + - [类型](#类型) + - [模板](#模板) - [参考资料](#参考资料) diff --git a/docs/javalib/javalib-binary.md b/docs/javalib/javalib-binary.md index 2b240ca7..6374a93b 100644 --- a/docs/javalib/javalib-binary.md +++ b/docs/javalib/javalib-binary.md @@ -6,14 +6,14 @@ 原因很简单,就是 Java 默认的序列化机制(`ObjectInputStream` 和 `ObjectOutputStream`)具有很多缺点。 -> 不了解 Java 默认的序列化机制,可以参考:[Java 序列化](https://github.com/dunwu/javacore/blob/master/docs/io/Java序列化.md) +> 不了解 Java 默认的序列化机制,可以参考:[Java 序列化](https://github.com/dunwu/javacore/blob/master/docs/io/Java序列化.md) Java 自身的序列化方式具有以下缺点: - **无法跨语言使用 **。这点最为致命,对于很多需要跨语言通信的异构系统来说,不能跨语言序列化,即意味着完全无法通信(彼此数据不能识别,当然无法交互了)。 -- **序列化的性能不高**。序列化后的数据体积较大,这大大影响存储和传输的效率。 -- 序列化一定需要实现 `Serializable` 接口。 -- 需要关注 `serialVersionUID`。 +- **序列化的性能不高**。序列化后的数据体积较大,这大大影响存储和传输的效率。 +- 序列化一定需要实现 `Serializable` 接口。 +- 需要关注 `serialVersionUID`。 引入二进制序列化库就是为了解决这些问题,这在 RPC 应用中尤为常见。 @@ -86,6 +86,7 @@ Java 自身的序列化方式具有以下缺点: **(2)选型建议** - 如果需要跨语言通信,那么可以考虑:Protobuf、Thrift、Hession。 + - [thrift](https://github.com/apache/thrift)、[protobuf](https://github.com/protocolbuffers/protobuf) - 适用于对性能敏感,对开发体验要求不高的内部系统。 - [hessian](http://hessian.caucho.com/doc/hessian-overview.xtp) - 适用于对开发体验敏感,性能有要求的内外部系统。 @@ -334,4 +335,4 @@ System.out.printf("Kryo 序列化/反序列化耗时:%s", (end - begin)); - [Hessian 官网](http://hessian.caucho.com/) - [FST Github](https://github.com/RuedigerMoeller/fast-serialization) - **文章** - - [java序列化框架对比](https://www.jianshu.com/p/937883b6b2e5) + - [java 序列化框架对比](https://www.jianshu.com/p/937883b6b2e5) diff --git a/docs/javalib/java-log.md b/docs/javalib/javalib-log.md similarity index 97% rename from docs/javalib/java-log.md rename to docs/javalib/javalib-log.md index bb46459f..26a755da 100644 --- a/docs/javalib/java-log.md +++ b/docs/javalib/javalib-log.md @@ -11,28 +11,28 @@ - [日志框架](#日志框架) - - [java.util.logging (JUL)](#javautillogging-jul) - - [Log4j](#log4j) - - [Logback](#logback) - - [Log4j2](#log4j2) - - [Log4j vs Logback vs Log4j2](#log4j-vs-logback-vs-log4j2) + - [java.util.logging (JUL)](#javautillogging-jul) + - [Log4j](#log4j) + - [Logback](#logback) + - [Log4j2](#log4j2) + - [Log4j vs Logback vs Log4j2](#log4j-vs-logback-vs-log4j2) - [日志门面](#日志门面) - - [common-logging](#common-logging) - - [slf4j](#slf4j) - - [common-logging vs slf4j](#common-logging-vs-slf4j) - - [总结](#总结) + - [common-logging](#common-logging) + - [slf4j](#slf4j) + - [common-logging vs slf4j](#common-logging-vs-slf4j) + - [总结](#总结) - [实施日志解决方案](#实施日志解决方案) - - [引入 jar 包](#引入-jar-包) - - [使用 API](#使用-api) + - [引入 jar 包](#引入-jar-包) + - [使用 API](#使用-api) - [log4j2 配置](#log4j2-配置) - [logback 配置](#logback-配置) - - [``](#configuration) - - [``](#appender) - - [``](#logger) - - [``](#root) - - [完整的 logback.xml 参考示例](#完整的-logbackxml-参考示例) + - [``](#configuration) + - [``](#appender) + - [``](#logger) + - [``](#root) + - [完整的 logback.xml 参考示例](#完整的-logbackxml-参考示例) - [log4j 配置](#log4j-配置) - - [完整的 log4j.xml 参考示例](#完整的-log4jxml-参考示例) + - [完整的 log4j.xml 参考示例](#完整的-log4jxml-参考示例) - [参考](#参考) diff --git a/docs/javalib/java-util.md b/docs/javalib/javalib-util.md similarity index 100% rename from docs/javalib/java-util.md rename to docs/javalib/javalib-util.md diff --git a/docs/javalib/javamail.md b/docs/javalib/javamail.md index ebd0d561..1ee24c17 100644 --- a/docs/javalib/javamail.md +++ b/docs/javalib/javamail.md @@ -1,26 +1,26 @@ -# JavaMail 使用指南 +# JavaMail 应用指南 - [简介](#简介) - - [邮件相关的标准](#邮件相关的标准) - - [JavaMail 简介](#javamail-简介) - - [邮件传输过程](#邮件传输过程) - - [Message 结构](#message-结构) + - [邮件相关的标准](#邮件相关的标准) + - [JavaMail 简介](#javamail-简介) + - [邮件传输过程](#邮件传输过程) + - [Message 结构](#message-结构) - [JavaMail 的核心类](#javamail-的核心类) - - [java.util.Properties 类(属性对象)](#javautilproperties-类属性对象) - - [javax.mail.Session 类(会话对象)](#javaxmailsession-类会话对象) - - [javax.mail.Transport 类(邮件传输)](#javaxmailtransport-类邮件传输) - - [javax.mail.Store 类(邮件存储 )](#javaxmailstore-类邮件存储-) - - [javax.mail.Message 类(消息对象)](#javaxmailmessage-类消息对象) - - [javax.mail.Address 类(地址)](#javaxmailaddress-类地址) - - [Authenticator 类(认证者)](#authenticator-类认证者) + - [java.util.Properties 类(属性对象)](#javautilproperties-类属性对象) + - [javax.mail.Session 类(会话对象)](#javaxmailsession-类会话对象) + - [javax.mail.Transport 类(邮件传输)](#javaxmailtransport-类邮件传输) + - [javax.mail.Store 类(邮件存储 )](#javaxmailstore-类邮件存储-) + - [javax.mail.Message 类(消息对象)](#javaxmailmessage-类消息对象) + - [javax.mail.Address 类(地址)](#javaxmailaddress-类地址) + - [Authenticator 类(认证者)](#authenticator-类认证者) - [实例](#实例) - - [发送文本邮件](#发送文本邮件) - - [发送 HTML 格式的邮件](#发送-html-格式的邮件) - - [发送带附件的邮件](#发送带附件的邮件) - - [获取邮箱中的邮件](#获取邮箱中的邮件) - - [转发邮件](#转发邮件) + - [发送文本邮件](#发送文本邮件) + - [发送 HTML 格式的邮件](#发送-html-格式的邮件) + - [发送带附件的邮件](#发送带附件的邮件) + - [获取邮箱中的邮件](#获取邮箱中的邮件) + - [转发邮件](#转发邮件) diff --git a/docs/javalib/jsoup.md b/docs/javalib/jsoup.md index 25c4dd60..8cdcd0c4 100644 --- a/docs/javalib/jsoup.md +++ b/docs/javalib/jsoup.md @@ -1,25 +1,24 @@ -# Jsoup 使用指南 - +# Jsoup 应用指南 - [简介](#简介) - [加载](#加载) - - [从HTML字符串加载一个文档](#从html字符串加载一个文档) - - [解析一个body片断](#解析一个body片断) - - [从URL加载一个文档](#从url加载一个文档) - - [从一个文件加载一个文档](#从一个文件加载一个文档) + - [从 HTML 字符串加载一个文档](#从-html-字符串加载一个文档) + - [解析一个 body 片断](#解析一个-body-片断) + - [从 URL 加载一个文档](#从-url-加载一个文档) + - [从一个文件加载一个文档](#从一个文件加载一个文档) - [解析](#解析) - - [使用DOM方法来遍历一个文档](#使用dom方法来遍历一个文档) - - [使用选择器语法来查找元素](#使用选择器语法来查找元素) - - [从元素抽取属性,文本和HTML](#从元素抽取属性文本和html) - - [处理URLs](#处理urls) + - [使用 DOM 方法来遍历一个文档](#使用-dom-方法来遍历一个文档) + - [使用选择器语法来查找元素](#使用选择器语法来查找元素) + - [从元素抽取属性,文本和 HTML](#从元素抽取属性文本和-html) + - [处理 URLs](#处理-urls) - [数据修改](#数据修改) - - [设置属性的值](#设置属性的值) - - [设置一个元素的HTML内容](#设置一个元素的html内容) - - [设置元素的文本内容](#设置元素的文本内容) -- [HTML清理](#html清理) - - [消除不受信任的HTML (来防止XSS攻击)](#消除不受信任的html-来防止xss攻击) + - [设置属性的值](#设置属性的值) + - [设置一个元素的 HTML 内容](#设置一个元素的-html-内容) + - [设置元素的文本内容](#设置元素的文本内容) +- [HTML 清理](#html-清理) + - [消除不受信任的 HTML (来防止 XSS 攻击)](#消除不受信任的-html-来防止-xss-攻击) - [参考](#参考) @@ -38,7 +37,7 @@ jsoup 是基于 MIT 协议发布的,可放心使用于商业项目。 ## 加载 -### 从HTML字符串加载一个文档 +### 从 HTML 字符串加载一个文档 使用静态 `Jsoup.parse(String html)` 方法或 `Jsoup.parse(String html, String baseUri)` 示例代码: @@ -50,18 +49,17 @@ Document doc = Jsoup.parse(html); > **说明** > -> `parse(String html, String baseUri)` 这方法能够将输入的HTML解析为一个新的文档 (Document),参数 baseUri 是用来将相对 URL 转成绝对URL,并指定从哪个网站获取文档。如这个方法不适用,你可以使用 `parse(String html)` 方法来解析成HTML字符串如上面的示例。 -> -> 只要解析的不是空字符串,就能返回一个结构合理的文档,其中包含(至少) 一个head和一个body元素。 +> `parse(String html, String baseUri)` 这方法能够将输入的 HTML 解析为一个新的文档 (Document),参数 baseUri 是用来将相对 URL 转成绝对 URL,并指定从哪个网站获取文档。如这个方法不适用,你可以使用 `parse(String html)` 方法来解析成 HTML 字符串如上面的示例。 > -> 一旦拥有了一个Document,你就可以使用Document中适当的方法或它父类 `Element`和`Node`中的方法来取得相关数据。 +> 只要解析的不是空字符串,就能返回一个结构合理的文档,其中包含(至少) 一个 head 和一个 body 元素。 > +> 一旦拥有了一个 Document,你就可以使用 Document 中适当的方法或它父类 `Element`和`Node`中的方法来取得相关数据。 -### 解析一个body片断 +### 解析一个 body 片断 **问题** -假如你有一个HTML片断 (比如. 一个 `div` 包含一对 `p` 标签; 一个不完整的HTML文档) 想对它进行解析。这个HTML片断可以是用户提交的一条评论或在一个CMS页面中编辑body部分。 +假如你有一个 HTML 片断 (比如. 一个 `div` 包含一对 `p` 标签; 一个不完整的 HTML 文档) 想对它进行解析。这个 HTML 片断可以是用户提交的一条评论或在一个 CMS 页面中编辑 body 部分。 **办法** @@ -75,16 +73,15 @@ Element body = doc.body(); > **说明** > -> `parseBodyFragment` 方法创建一个空壳的文档,并插入解析过的HTML到`body`元素中。假如你使用正常的 `Jsoup.parse(String html)` 方法,通常你也可以得到相同的结果,但是明确将用户输入作为 body片段处理,以确保用户所提供的任何糟糕的HTML都将被解析成body元素。 -> -> `Document.body()` 方法能够取得文档body元素的所有子元素,与 `doc.getElementsByTag("body")`相同。 +> `parseBodyFragment` 方法创建一个空壳的文档,并插入解析过的 HTML 到`body`元素中。假如你使用正常的 `Jsoup.parse(String html)` 方法,通常你也可以得到相同的结果,但是明确将用户输入作为 body 片段处理,以确保用户所提供的任何糟糕的 HTML 都将被解析成 body 元素。 > +> `Document.body()` 方法能够取得文档 body 元素的所有子元素,与 `doc.getElementsByTag("body")`相同。 -#### 保证安全Stay safe +#### 保证安全 Stay safe -假如你可以让用户输入HTML内容,那么要小心避免跨站脚本攻击。利用基于 `Whitelist` 的清除器和 `clean(String bodyHtml, Whitelist whitelist)`方法来清除用户输入的恶意内容。 +假如你可以让用户输入 HTML 内容,那么要小心避免跨站脚本攻击。利用基于 `Whitelist` 的清除器和 `clean(String bodyHtml, Whitelist whitelist)`方法来清除用户输入的恶意内容。 -### 从URL加载一个文档 +### 从 URL 加载一个文档 使用 `Jsoup.connect(String url)`方法 @@ -94,8 +91,7 @@ Document doc = Jsoup.connect("http://example.com/").get(); > **说明** > -> `connect(String url)` 方法创建一个新的 `Connection`, 和 `get()` 取得和解析一个HTML文件。如果从该URL获取HTML时发生错误,便会抛出 IOException,应适当处理。 -> +> `connect(String url)` 方法创建一个新的 `Connection`, 和 `get()` 取得和解析一个 HTML 文件。如果从该 URL 获取 HTML 时发生错误,便会抛出 IOException,应适当处理。 `Connection` 接口还提供一个方法链来解决特殊请求,具体如下: @@ -119,24 +115,23 @@ Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/"); > **说明** > -> `parse(File in, String charsetName, String baseUri)` 这个方法用来加载和解析一个HTML文件。如在加载文件的时候发生错误,将抛出IOException,应作适当处理。 +> `parse(File in, String charsetName, String baseUri)` 这个方法用来加载和解析一个 HTML 文件。如在加载文件的时候发生错误,将抛出 IOException,应作适当处理。 > -> `baseUri` 参数用于解决文件中URLs是相对路径的问题。如果不需要可以传入一个空的字符串。 +> `baseUri` 参数用于解决文件中 URLs 是相对路径的问题。如果不需要可以传入一个空的字符串。 > > 另外还有一个方法`parse(File in, String charsetName)` ,它使用文件的路径做为 `baseUri`。 这个方法适用于如果被解析文件位于网站的本地文件系统,且相关链接也指向该文件系统。 -> ## 解析 -### 使用DOM方法来遍历一个文档 +### 使用 DOM 方法来遍历一个文档 **问题** -你有一个HTML文档要从中提取数据,并了解这个HTML文档的结构。 +你有一个 HTML 文档要从中提取数据,并了解这个 HTML 文档的结构。 **方法** -将HTML解析成一个`Document`之后,就可以使用类似于DOM的方法进行操作。示例代码: +将 HTML 解析成一个`Document`之后,就可以使用类似于 DOM 的方法进行操作。示例代码: ```java File input = new File("/tmp/input.html"); @@ -171,12 +166,12 @@ for (Element link : links) { - `attributes()`获取所有属性 - `id()`, `className()` and `classNames()` - `text()`获取文本内容`text(String value)` 设置文本内容 -- `html()`获取元素内HTML`html(String value)`设置元素内的HTML内容 -- `outerHtml()`获取元素外HTML内容 -- `data()`获取数据内容(例如:script和style标签) +- `html()`获取元素内 HTML`html(String value)`设置元素内的 HTML 内容 +- `outerHtml()`获取元素外 HTML 内容 +- `data()`获取数据内容(例如:script 和 style 标签) - `tag()` and `tagName()` -####操作HTML和文本 +####操作 HTML 和文本 - `append(String html)`, `prepend(String html)` - `appendText(String text)`, `prependText(String text)` @@ -187,7 +182,7 @@ for (Element link : links) { **问题** -你想使用类似于CSS或jQuery的语法来查找和操作元素。 +你想使用类似于 CSS 或 jQuery 的语法来查找和操作元素。 **方法** @@ -209,64 +204,63 @@ Elements resultLinks = doc.select("h3.r > a"); //在h3元素之后的a元素 > **说明** > -> jsoup elements对象支持类似于[CSS](http://www.w3.org/TR/2009/PR-css3-selectors-20091215/) (或[jquery](http://jquery.com/))的选择器语法,来实现非常强大和灵活的查找功能。. +> jsoup elements 对象支持类似于[CSS](http://www.w3.org/TR/2009/PR-css3-selectors-20091215/) (或[jquery](http://jquery.com/))的选择器语法,来实现非常强大和灵活的查找功能。. > > 这个`select` 方法在`Document`, `Element`,或`Elements`对象中都可以使用。且是上下文相关的,因此可实现指定元素的过滤,或者链式选择访问。 > -> Select方法将返回一个`Elements`集合,并提供一组方法来抽取和处理结果。 -> +> Select 方法将返回一个`Elements`集合,并提供一组方法来抽取和处理结果。 -####Selector选择器概述 +####Selector 选择器概述 - `tagname`: 通过标签查找元素,比如:`a` - `ns|tag`: 通过标签在命名空间查找元素,比如:可以用 `fb|name` 语法来查找 `` 元素 -- `#id`: 通过ID查找元素,比如:`#logo` -- `.class`: 通过class名称查找元素,比如:`.masthead` +- `#id`: 通过 ID 查找元素,比如:`#logo` +- `.class`: 通过 class 名称查找元素,比如:`.masthead` - `[attribute]`: 利用属性查找元素,比如:`[href]` -- `[^attr]`: 利用属性名前缀来查找元素,比如:可以用`[^data-]` 来查找带有HTML5 Dataset属性的元素 +- `[^attr]`: 利用属性名前缀来查找元素,比如:可以用`[^data-]` 来查找带有 HTML5 Dataset 属性的元素 - `[attr=value]`: 利用属性值来查找元素,比如:`[width=500]` - `[attr^=value]`, `[attr$=value]`, `[attr*=value]`: 利用匹配属性值开头、结尾或包含属性值来查找元素,比如:`[href*=/path/]` - `[attr\~=regex]`: 利用属性值匹配正则表达式来查找元素,比如: `img[src\~=(?i)\.(png|jpe?g)]` - `*`: 这个符号将匹配所有元素 -####Selector选择器组合使用 +####Selector 选择器组合使用 - `el##id`: 元素+ID,比如: `div##logo` - `el.class`: 元素+class,比如: `div.masthead` - `el[attr]`: 元素+class,比如: `a[href]` - 任意组合,比如:`a[href].highlight` - `ancestor child`: 查找某个元素下子元素,比如:可以用`.body p` 查找在"body"元素下的所有`p`元素 -- `parent > child`: 查找某个父元素下的直接子元素,比如:可以用`div.content > p` 查找 `p` 元素,也可以用`body > *` 查找body标签下所有直接子元素 -- `siblingA + siblingB`: 查找在A元素之前第一个同级元素B,比如:`div.head + div` -- `siblingA \~ siblingX`: 查找A元素之前的同级X元素,比如:`h1 \~ p` +- `parent > child`: 查找某个父元素下的直接子元素,比如:可以用`div.content > p` 查找 `p` 元素,也可以用`body > *` 查找 body 标签下所有直接子元素 +- `siblingA + siblingB`: 查找在 A 元素之前第一个同级元素 B,比如:`div.head + div` +- `siblingA \~ siblingX`: 查找 A 元素之前的同级 X 元素,比如:`h1 \~ p` - `el, el, el`:多个选择器组合,查找匹配任一选择器的唯一元素,例如:`div.masthead, div.logo` -####伪选择器selectors +####伪选择器 selectors -- `:lt(n)`: 查找哪些元素的同级索引值(它的位置在DOM树中是相对于它的父节点)小于n,比如:`td:lt(3)` 表示小于三列的元素 -- `:gt(n)`:查找哪些元素的同级索引值大于`n``,比如`: `div p:gt(2)`表示哪些div中有包含2个以上的p元素 -- `:eq(n)`: 查找哪些元素的同级索引值与`n`相等,比如:`form input:eq(1)`表示包含一个input标签的Form元素 -- `:has(seletor)`: 查找匹配选择器包含元素的元素,比如:`div:has(p)`表示哪些div包含了p元素 +- `:lt(n)`: 查找哪些元素的同级索引值(它的位置在 DOM 树中是相对于它的父节点)小于 n,比如:`td:lt(3)` 表示小于三列的元素 +- `:gt(n)`:查找哪些元素的同级索引值大于` n``,比如 `: `div p:gt(2)`表示哪些 div 中有包含 2 个以上的 p 元素 +- `:eq(n)`: 查找哪些元素的同级索引值与`n`相等,比如:`form input:eq(1)`表示包含一个 input 标签的 Form 元素 +- `:has(seletor)`: 查找匹配选择器包含元素的元素,比如:`div:has(p)`表示哪些 div 包含了 p 元素 - `:not(selector)`: 查找与选择器不匹配的元素,比如: `div:not(.logo)` 表示不包含 class=logo 元素的所有 div 列表 - `:contains(text)`: 查找包含给定文本的元素,搜索不区分大不写,比如: `p:contains(jsoup)` - `:containsOwn(text)`: 查找直接包含给定文本的元素 - `:matches(regex)`: 查找哪些元素的文本匹配指定的正则表达式,比如:`div:matches((?i)login)` - `:matchesOwn(regex)`: 查找自身包含文本匹配指定正则表达式的元素 -- 注意:上述伪选择器索引是从0开始的,也就是说第一个元素索引值为0,第二个元素index为1等 +- 注意:上述伪选择器索引是从 0 开始的,也就是说第一个元素索引值为 0,第二个元素 index 为 1 等 -可以查看`Selector` API参考来了解更详细的内容 +可以查看`Selector` API 参考来了解更详细的内容 -### 从元素抽取属性,文本和HTML +### 从元素抽取属性,文本和 HTML **问题** -在解析获得一个Document实例对象,并查找到一些元素之后,你希望取得在这些元素中的数据。 +在解析获得一个 Document 实例对象,并查找到一些元素之后,你希望取得在这些元素中的数据。 **方法** - 要取得一个属性的值,可以使用`Node.attr(String key)` 方法 - 对于一个元素中的文本,可以使用`Element.text()`方法 -- 对于要取得元素或属性中的HTML内容,可以使用`Element.html()`, 或 `Node.outerHtml()`方法 +- 对于要取得元素或属性中的 HTML 内容,可以使用`Element.html()`, 或 `Node.outerHtml()`方法 示例: @@ -279,7 +273,7 @@ String text = doc.body().text(); // "An example link"//取得字符串中的文 String linkHref = link.attr("href"); // "http://example.com/"//取得链接地址 String linkText = link.text(); // "example""//取得链接地址中的文本 -String linkOuterH = link.outerHtml(); +String linkOuterH = link.outerHtml(); // "example" String linkInnerH = link.html(); // "example"//取得链接内的html内容 ``` @@ -292,25 +286,24 @@ String linkInnerH = link.html(); // "example"//取得链接内的html内 > - `Element.tagName()` > - `Element.className()` and `Element.hasClass(String className)` > -> 这些访问器方法都有相应的setter方法来更改数据 -> +> 这些访问器方法都有相应的 setter 方法来更改数据 **参见** - `Element`和`Elements`集合类的参考文档 -- [URLs处理](http://www.open-open.com/jsoup/working-with-urls.htm) -- [使用CSS选择器语法来查找元素](http://www.open-open.com/jsoup/selector-syntax.htm) +- [URLs 处理](http://www.open-open.com/jsoup/working-with-urls.htm) +- [使用 CSS 选择器语法来查找元素](http://www.open-open.com/jsoup/selector-syntax.htm) -### 处理URLs +### 处理 URLs **问题** -你有一个包含相对URLs路径的HTML文档,需要将这些相对路径转换成绝对路径的URLs。 +你有一个包含相对 URLs 路径的 HTML 文档,需要将这些相对路径转换成绝对路径的 URLs。 **方法** 1. 在你解析文档时确保有指定`base URI`,然后 -2. 使用 `abs:` 属性前缀来取得包含`base URI`的绝对路径。代码如下: +2. 使用 `abs:` 属性前缀来取得包含`base URI`的绝对路径。代码如下: ```java Document doc = Jsoup.connect("http://www.open-open.com").get(); @@ -323,14 +316,13 @@ String absHref = link.attr("abs:href"); // "http://www.open-open.com/" > **说明** > -> 在HTML元素中,URLs 经常写成相对于文档位置的相对路径: `...`. 当你使用 `Node.attr(String key)` 方法来取得a元素的href属性时,它将直接返回在HTML源码中指定定的值。 +> 在 HTML 元素中,URLs 经常写成相对于文档位置的相对路径: `...`. 当你使用 `Node.attr(String key)` 方法来取得 a 元素的 href 属性时,它将直接返回在 HTML 源码中指定定的值。 > -> 假如你需要取得一个绝对路径,需要在属性名前加 `abs:` 前缀。这样就可以返回包含根路径的URL地址`attr("abs:href")` +> 假如你需要取得一个绝对路径,需要在属性名前加 `abs:` 前缀。这样就可以返回包含根路径的 URL 地址`attr("abs:href")` > -> 因此,在解析HTML文档时,定义base URI非常重要。 +> 因此,在解析 HTML 文档时,定义 base URI 非常重要。 > > 如果你不想使用`abs:` 前缀,还有一个方法能够实现同样的功能 `Node.absUrl(String key)`。 -> ## 数据修改 @@ -346,7 +338,7 @@ String absHref = link.attr("abs:href"); // "http://www.open-open.com/" 假如你需要修改一个元素的 `class` 属性,可以使用 `Element.addClass(String className)` 和`Element.removeClass(String className)` 方法。 -`Elements` 提供了批量操作元素属性和class的方法,比如:要为div中的每一个a元素都添加一个`rel="nofollow"` 可以使用如下方法: +`Elements` 提供了批量操作元素属性和 class 的方法,比如:要为 div 中的每一个 a 元素都添加一个`rel="nofollow"` 可以使用如下方法: ``` doc.select("div.comments a").attr("rel", "nofollow"); @@ -360,17 +352,16 @@ doc.select("div.comments a").attr("rel", "nofollow"); > ``` > doc.select("div.masthead").attr("title", "jsoup").addClass("round-box"); > ``` -> -### 设置一个元素的HTML内容 +### 设置一个元素的 HTML 内容 **问题** -你需要一个元素中的HTML内容 +你需要一个元素中的 HTML 内容 **方法** -可以使用`Element`中的HTML设置方法具体如下: +可以使用`Element`中的 HTML 设置方法具体如下: ```java Element div = doc.select("div").first(); //

    @@ -386,21 +377,19 @@ span.wrap("
  • "); > **说明** > -> - `Element.html(String html)` 这个方法将先清除元素中的HTML内容,然后用传入的HTML代替。 -> - `Element.prepend(String first)` 和 `Element.append(String last)` 方法用于在分别在元素内部HTML的前面和后面添加HTML内容 -> - `Element.wrap(String around)` 对元素包裹一个外部HTML内容。 -> +> - `Element.html(String html)` 这个方法将先清除元素中的 HTML 内容,然后用传入的 HTML 代替。 +> - `Element.prepend(String first)` 和 `Element.append(String last)` 方法用于在分别在元素内部 HTML 的前面和后面添加 HTML 内容 +> - `Element.wrap(String around)` 对元素包裹一个外部 HTML 内容。 > > **参见** > -> 可以查看API参考文档中 `Element.prependElement(String tag)`和`Element.appendElement(String tag)` 方法来创建新的元素并作为文档的子元素插入其中。 -> +> 可以查看 API 参考文档中 `Element.prependElement(String tag)`和`Element.appendElement(String tag)` 方法来创建新的元素并作为文档的子元素插入其中。 ### 设置元素的文本内容 **问题** -你需要修改一个HTML文档中的文本内容 +你需要修改一个 HTML 文档中的文本内容 **方法** @@ -418,26 +407,25 @@ div.append(" Last"); > > 文本设置方法与 [HTML setter](http://jsoup.org/cookbook/modifying-data/set-html) 方法一样: > -> - `Element.text(String text)` 将清除一个元素中的内部HTML内容,然后提供的文本进行代替 -> - `Element.prepend(String first)` 和 `Element.append(String last)` 将分别在元素的内部html前后添加文本节点。 -> -> 对于传入的文本如果含有像 `<`, `>` 等这样的字符,将以文本处理,而非HTML。 +> - `Element.text(String text)` 将清除一个元素中的内部 HTML 内容,然后提供的文本进行代替 +> - `Element.prepend(String first)` 和 `Element.append(String last)` 将分别在元素的内部 html 前后添加文本节点。 > +> 对于传入的文本如果含有像 `<`, `>` 等这样的字符,将以文本处理,而非 HTML。 -## HTML清理 +## HTML 清理 -### 消除不受信任的HTML (来防止XSS攻击) +### 消除不受信任的 HTML (来防止 XSS 攻击) **问题** -在做网站的时候,经常会提供用户评论的功能。有些不怀好意的用户,会搞一些脚本到评论内容中,而这些脚本可能会破坏整个页面的行为,更严重的是获取一些机要信息,此时需要清理该HTML,以避免跨站脚本[cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting)攻击(XSS)。 +在做网站的时候,经常会提供用户评论的功能。有些不怀好意的用户,会搞一些脚本到评论内容中,而这些脚本可能会破坏整个页面的行为,更严重的是获取一些机要信息,此时需要清理该 HTML,以避免跨站脚本[cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting)攻击(XSS)。 **方法** -使用jsoup HTML `Cleaner` 方法进行清除,但需要指定一个可配置的 `Whitelist`。 +使用 jsoup HTML `Cleaner` 方法进行清除,但需要指定一个可配置的 `Whitelist`。 ```java -String unsafe = +String unsafe = "

    Link

    "; String safe = Jsoup.clean(unsafe, Whitelist.basic()); // now:

    Link

    @@ -447,7 +435,7 @@ String safe = Jsoup.clean(unsafe, Whitelist.basic()); XSS 又叫 CSS (Cross Site Script) ,跨站脚本攻击。它指的是恶意攻击者往 Web 页面里插入恶意 html 代码,当用户浏览该页之时,嵌入其中 Web 里面的 html 代码会被执行,从而达到恶意攻击用户的特殊目的。XSS 属于被动式的攻击,因为其被动且不好利用,所以许多人常忽略其危害性。所以我们经常只让用户输入纯文本的内容,但这样用户体验就比较差了。 -一个更好的解决方法就是使用一个富文本编辑器 WYSIWYG 如 [CKEditor](http://ckeditor.com/) 和 [TinyMCE](http://tinymce.moxiecode.com/)。这些可以输出 HTML 并能够让用户可视化编辑。虽然他们可以在客户端进行校验,但是这样还不够安全,需要在服务器端进行校验并清除有害的HTML代码,这样才能确保输入到你网站的HTML是安全的。否则,攻击者能够绕过客户端的 Javascript 验证,并注入不安全的 HMTL 直接进入您的网站。 +一个更好的解决方法就是使用一个富文本编辑器 WYSIWYG 如 [CKEditor](http://ckeditor.com/) 和 [TinyMCE](http://tinymce.moxiecode.com/)。这些可以输出 HTML 并能够让用户可视化编辑。虽然他们可以在客户端进行校验,但是这样还不够安全,需要在服务器端进行校验并清除有害的 HTML 代码,这样才能确保输入到你网站的 HTML 是安全的。否则,攻击者能够绕过客户端的 Javascript 验证,并注入不安全的 HMTL 直接进入您的网站。 jsoup 的 whitelist 清理器能够在服务器端对用户输入的 HTML 进行过滤,只输出一些安全的标签和属性。 @@ -457,17 +445,17 @@ jsoup 提供了一系列的 `Whitelist` 基本配置,能够满足大多数要 **参见** -- 参阅[XSS cheat sheet](http://ha.ckers.org/xss.html) ,有一个例子可以了解为什么不能使用正则表达式,而采用安全的whitelist parser-based清理器才是正确的选择。 +- 参阅[XSS cheat sheet](http://ha.ckers.org/xss.html) ,有一个例子可以了解为什么不能使用正则表达式,而采用安全的 whitelist parser-based 清理器才是正确的选择。 - 参阅`Cleaner` ,了解如何返回一个 `Document` 对象,而不是字符串 -- 参阅`Whitelist`,了解如何创建一个自定义的whitelist +- 参阅`Whitelist`,了解如何创建一个自定义的 whitelist - [nofollow](http://en.wikipedia.org/wiki/Nofollow) 链接属性了解 ## 参考 -- [jsoup github托管代码](https://github.com/jhy/jsoup) +- [jsoup github 托管代码](https://github.com/jhy/jsoup) - [jsoup Cookbook](https://jsoup.org/cookbook/) - [jsoup Cookbook(中文版)](http://www.open-open.com/jsoup/) -- [不错的jsoup学习笔记](https://github.com/code4craft/jsoup-learning) +- [不错的 jsoup 学习笔记](https://github.com/code4craft/jsoup-learning) diff --git a/docs/javalib/junit.md b/docs/javalib/junit.md index 9be0f89e..3441c771 100644 --- a/docs/javalib/junit.md +++ b/docs/javalib/junit.md @@ -1,4 +1,4 @@ -# JUnit5 使用指南 +# JUnit5 应用指南 > version: junit5 @@ -7,15 +7,15 @@ - [1. 安装](#1-安装) - [2. JUnit 注解](#2-junit-注解) - [3. 编写单元测试](#3-编写单元测试) - - [3.1. 基本的单元测试类和方法](#31-基本的单元测试类和方法) - - [3.2. 定制测试类和方法的显示名称](#32-定制测试类和方法的显示名称) - - [3.3. 断言(Assertions)](#33-断言assertions) - - [3.4. 假想(Assumptions)](#34-假想assumptions) - - [3.5. 禁用](#35-禁用) - - [3.6. 测试条件](#36-测试条件) - - [3.7. 嵌套测试](#37-嵌套测试) - - [3.8. 重复测试](#38-重复测试) - - [3.9. 参数化测试](#39-参数化测试) + - [3.1. 基本的单元测试类和方法](#31-基本的单元测试类和方法) + - [3.2. 定制测试类和方法的显示名称](#32-定制测试类和方法的显示名称) + - [3.3. 断言(Assertions)](#33-断言assertions) + - [3.4. 假想(Assumptions)](#34-假想assumptions) + - [3.5. 禁用](#35-禁用) + - [3.6. 测试条件](#36-测试条件) + - [3.7. 嵌套测试](#37-嵌套测试) + - [3.8. 重复测试](#38-重复测试) + - [3.9. 参数化测试](#39-参数化测试) - [4. 引用和引申](#4-引用和引申) diff --git a/docs/javalib/lombok.md b/docs/javalib/lombok.md index ead65cc0..3b9709fa 100644 --- a/docs/javalib/lombok.md +++ b/docs/javalib/lombok.md @@ -1,4 +1,4 @@ -# Lombok 使用指南 +# Lombok 应用指南 diff --git a/docs/javalib/mockito.md b/docs/javalib/mockito.md index 3a0d31d1..ca3657e7 100644 --- a/docs/javalib/mockito.md +++ b/docs/javalib/mockito.md @@ -1,52 +1,12 @@ -# Mockito 使用指南 +# Mockito 应用指南 > Mockito 是一个针对 Java 的 mock 框架。 - - -- [预备知识](#预备知识) -- [使用 mock 对象来进行测试](#使用-mock-对象来进行测试) - - [单元测试的目标和挑战](#单元测试的目标和挑战) - - [测试类的分类](#测试类的分类) - - [Mock 对象的产生](#mock-对象的产生) - - [使用 Mockito 生成 Mock 对象](#使用-mockito-生成-mock-对象) -- [为自己的项目添加 Mockito 依赖](#为自己的项目添加-mockito-依赖) - - [在 Gradle 添加 Mockito 依赖](#在-gradle-添加-mockito-依赖) - - [在 Maven 添加 Mockito 依赖](#在-maven-添加-mockito-依赖) - - [在 Eclipse IDE 使用 Mockito](#在-eclipse-ide-使用-mockito) - - [以 OSGi 或者 Eclipse 插件形式添加 Mockito 依赖](#以-osgi-或者-eclipse-插件形式添加-mockito-依赖) -- [使用 Mockito API](#使用-mockito-api) - - [静态引用](#静态引用) - - [使用 Mockito 创建和配置 mock 对象](#使用-mockito-创建和配置-mock-对象) - - [配置 mock](#配置-mock) - - [验证 mock 对象方法是否被调用](#验证-mock-对象方法是否被调用) - - [使用 Spy 封装 java 对象](#使用-spy-封装-java-对象) - - [使用 @InjectMocks 在 Mockito 中进行依赖注入](#使用-injectmocks-在-mockito-中进行依赖注入) - - [捕捉参数](#捕捉参数) - - [Mockito 的限制](#mockito-的限制) -- [在 Android 中使用 Mockito](#在-android-中使用-mockito) -- [实例:使用 Mockito 写一个 Instrumented Unit Test](#实例使用-mockito-写一个-instrumented-unit-test) - - [创建一个测试的 Android 应用](#创建一个测试的-android-应用) - - [在 app/build.gradle 文件中添加 Mockito 依赖](#在-appbuildgradle-文件中添加-mockito-依赖) - - [创建测试](#创建测试) -- [实例:使用 Mockito 创建一个 mock 对象](#实例使用-mockito-创建一个-mock-对象) - - [目标](#目标) - - [创建一个 Twitter API 的例子](#创建一个-twitter-api-的例子) - - [模拟 ITweet 的实例](#模拟-itweet-的实例) - - [验证方法调用](#验证方法调用) - - [验证](#验证) -- [模拟静态方法](#模拟静态方法) - - [使用 Powermock 来模拟静态方法](#使用-powermock-来模拟静态方法) - - [用封装的方法代替 Powermock](#用封装的方法代替-powermock) -- [引用和引申](#引用和引申) - - - ## 预备知识 如果需要往下学习,你需要先理解 Junit 框架中的单元测试。 -如果你不熟悉 JUnit,请查看下面的教程: +如果你不熟悉 JUnit,请看 [Junit 教程](http://www.vogella.com/tutorials/JUnit/article.html) ## 使用 mock 对象来进行测试 @@ -83,9 +43,7 @@ _Mockito_ 是一个流行 mock 框架,可以和 JUnit 结合起来使用。Moc 1. 模拟并替换测试代码中外部依赖 2. 执行测试代码 -3. 验证测试代码是否被正确的执行 - -
    +3. 验证测试代码是否被正确的执行 0 ## 为自己的项目添加 Mockito 依赖 @@ -100,7 +58,7 @@ dependencies { testCompile "org.mockito:mockito-core:2.0.57-beta" } ### 在 Maven 添加 Mockito 依赖 -需要在 Maven 声明依赖,您可以在 [http://search.maven.org](http://search.maven.org/) 网站中搜索 g:"org.mockito", a:"mockito-core" 来得到具体的声明方式。 +需要在 Maven 声明依赖,您可以在 [http://search.maven.org](http://search.maven.org/) 网站中搜索 `g:"org.mockito", a:"mockito-core"` 来得到具体的声明方式。 ### 在 Eclipse IDE 使用 Mockito @@ -110,9 +68,7 @@ Eclipse IDE 支持 Gradle 和 Maven 两种构建工具,所以在 Eclipse IDE 在 Eclipse RCP 应用依赖通常可以在 p2 update 上得到。Orbit 是一个很好的第三方仓库,我们可以在里面寻找能在 Eclipse 上使用的应用和插件。 -Orbit 仓库地址:http://download.eclipse.org/tools/orbit/downloads - -
    +Orbit 仓库地址:[http://download.eclipse.org/tools/orbit/downloads](http://download.eclipse.org/tools/orbit/downloads) ## 使用 Mockito API @@ -336,7 +292,7 @@ public class ArticleManagerTest { 1. 创建 ArticleManager 实例并注入 Mock 对象 -更多的详情可以查看 . +更多的详情可以查看 [http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/InjectMocks.html](http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/InjectMocks.html) ### 捕捉参数 @@ -420,7 +376,7 @@ public static Intent createQuery(Context context, String query, String value) { ### 在 app/build.gradle 文件中添加 Mockito 依赖 -``` +```java dependencies { // Mockito 和 JUnit 的依赖 // instrumentation unit tests on the JVM diff --git a/docs/javalib/reflections.md b/docs/javalib/reflections.md index 7cc875b9..e24c081d 100644 --- a/docs/javalib/reflections.md +++ b/docs/javalib/reflections.md @@ -1,4 +1,4 @@ -# Reflections 使用指南 +# Reflections 应用指南 @@ -92,7 +92,7 @@ Set getters = getAllMethods(someClass, withModifier(Modifier.PUBLIC), withPrefix("get"), withParametersCount(0)); //or -Set listMethodsFromCollectionToBoolean = +Set listMethodsFromCollectionToBoolean = getAllMethods(List.class, withParametersAssignableTo(Collection.class), withReturnType(boolean.class)); diff --git a/docs/javalib/thumbnailator.md b/docs/javalib/thumbnailator.md index 472ef1c6..b7b42ff2 100644 --- a/docs/javalib/thumbnailator.md +++ b/docs/javalib/thumbnailator.md @@ -1,19 +1,18 @@ -# Thumbnailator 使用指南 - +# Thumbnailator 应用指南 - [简介](#简介) - [核心 API](#核心-api) - - [Thumbnails](#thumbnails) - - [Thumbnails.Builder](#thumbnailsbuilder) - - [工作流](#工作流) + - [Thumbnails](#thumbnails) + - [Thumbnails.Builder](#thumbnailsbuilder) + - [工作流](#工作流) - [实战](#实战) - - [安装](#安装) - - [图片缩放](#图片缩放) - - [图片旋转](#图片旋转) - - [加水印](#加水印) - - [批量处理图片](#批量处理图片) + - [安装](#安装) + - [图片缩放](#图片缩放) + - [图片旋转](#图片旋转) + - [加水印](#加水印) + - [批量处理图片](#批量处理图片) - [参考](#参考) @@ -58,7 +57,7 @@ public static Builder fromInputStreams(Iterable fromImages(Iterable images) {...} ``` -很显然,**Thumbnails 允许通过传入文件名、文件、网络图的URL、图片流、图片缓存多种方式来初始化构造器**。 +很显然,**Thumbnails 允许通过传入文件名、文件、网络图的 URL、图片流、图片缓存多种方式来初始化构造器**。 因此,你可以根据实际需求来灵活的选择图片的输入方式。 @@ -68,7 +67,7 @@ public static Builder fromImages(Iterable images) `Thumbnails.Builder` 是 `Thumbnails` 的内部静态类。它用于设置生成缩略图任务的相关参数。 -***注:`Thumbnails.Builder` 的构造函数是私有函数。所以,它只允许通过 `Thumbnails` 的实例化函数来进行初始化。*** +**_注:`Thumbnails.Builder` 的构造函数是私有函数。所以,它只允许通过 `Thumbnails` 的实例化函数来进行初始化。_** #### 设置参数的函数 @@ -81,16 +80,16 @@ public Builder size(int width, int height) { updateStatus(Properties.SIZE, Status.ALREADY_SET); updateStatus(Properties.SCALE, Status.CANNOT_SET); - + validateDimensions(width, height); this.width = width; this.height = height; - + return this; } ``` -通过返回this指针,使得设置参数函数可以以链式调用的方式来使用,形式如下: +通过返回 this 指针,使得设置参数函数可以以链式调用的方式来使用,形式如下: ```java Thumbnails.of(new File("original.jpg")) @@ -138,7 +137,7 @@ Thumbnailator 的工作步骤十分简单,可分为三步: 3. **输出**:`Thumbnails.Builder` 输出图片文件或图片流。 -> 更多详情可以参考: [Thumbnailator 官网javadoc](https://coobird.github.io/thumbnailator/javadoc/0.4.8/) +> 更多详情可以参考: [Thumbnailator 官网 javadoc](https://coobird.github.io/thumbnailator/javadoc/0.4.8/) ## 实战 @@ -148,7 +147,7 @@ Thumbnailator 生成什么样的图片,是根据设置参数来决定的。 ### 安装 -maven项目中引入依赖: +maven 项目中引入依赖: ```xml @@ -157,6 +156,7 @@ maven项目中引入依赖: [0.4, 0.5) ``` + ### 图片缩放 `Thumbnails.Builder` 的 `size` 函数可以设置新图片精确的宽度和高度,也可以用 `scale` 函数设置缩放比例。 @@ -176,9 +176,11 @@ Thumbnails.of("oldFile.png") ``` **oldFile.png** +
    **newFile_scale_1.0_0.5.png** +
    ### 图片旋转 diff --git a/docs/javalib/zxing.md b/docs/javalib/zxing.md index b1168062..bd74f3da 100644 --- a/docs/javalib/zxing.md +++ b/docs/javalib/zxing.md @@ -1,30 +1,29 @@ -# ZXing 使用指南 - +# ZXing 应用指南 - [简介](#简介) - [实战](#实战) - - [安装](#安装) - - [生成二维码图片](#生成二维码图片) - - [解析二维码图片](#解析二维码图片) + - [安装](#安装) + - [生成二维码图片](#生成二维码图片) + - [解析二维码图片](#解析二维码图片) - [参考](#参考) ## 简介 -`ZXing` 是一个开源 Java 类库用于解析多种格式的 1D/2D 条形码。目标是能够对QR编码、Data Matrix、UPC的1D条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE和Android。 +`ZXing` 是一个开源 Java 类库用于解析多种格式的 1D/2D 条形码。目标是能够对 QR 编码、Data Matrix、UPC 的 1D 条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE 和 Android。 -官网:[ZXing github仓库](https://github.com/zxing/zxing) +官网:[ZXing github 仓库](https://github.com/zxing/zxing) ## 实战 -***本例演示如何在一个非 android 的 Java 项目中使用 ZXing 来生成、解析二维码图片。*** +**_本例演示如何在一个非 android 的 Java 项目中使用 ZXing 来生成、解析二维码图片。_** ### 安装 -maven项目只需引入依赖: +maven 项目只需引入依赖: ```xml @@ -39,13 +38,13 @@ maven项目只需引入依赖: ``` -如果非maven项目,就去官网下载发布版本:[下载地址](https://github.com/zxing/zxing/releases) +如果非 maven 项目,就去官网下载发布版本:[下载地址](https://github.com/zxing/zxing/releases) ### 生成二维码图片 ZXing 生成二维码图片有以下步骤: -1. `com.google.zxing.MultiFormatWriter` 根据内容以及图像编码参数生成图像2D矩阵。 +1. `com.google.zxing.MultiFormatWriter` 根据内容以及图像编码参数生成图像 2D 矩阵。 2. ​ `com.google.zxing.client.j2se.MatrixToImageWriter` 根据图像矩阵生成图片文件或图片缓存 `BufferedImage` 。 ```java @@ -70,7 +69,6 @@ ZXing 解析二维码图片有以下步骤: 3. `com.google.zxing.MultiFormatReader` 根据图像解码参数来解析 `com.google.zxing.BinaryBitmap` 。 - ```java public String decode(String filepath) throws IOException, NotFoundException { BufferedImage bufferedImage = ImageIO.read(new FileInputStream(filepath)); @@ -92,4 +90,4 @@ public String decode(String filepath) throws IOException, NotFoundException { ## 参考 -[ZXing github仓库](https://github.com/zxing/zxing) +[ZXing github 仓库](https://github.com/zxing/zxing) diff --git a/docs/javatool/build/ant.md b/docs/javatool/build/ant.md index c55db8cd..44ccfd7c 100644 --- a/docs/javatool/build/ant.md +++ b/docs/javatool/build/ant.md @@ -4,16 +4,16 @@ - [简介](#简介) - [下载和安装](#下载和安装) - - [下载](#下载) - - [配置环境变量](#配置环境变量) - - [验证](#验证) + - [下载](#下载) + - [配置环境变量](#配置环境变量) + - [验证](#验证) - [例子](#例子) - [关键元素](#关键元素) - - [Project 元素](#project-元素) - - [Target 元素](#target-元素) - - [Task 元素](#task-元素) - - [Property 元素](#property-元素) - - [extension-point 元素](#extension-point-元素) + - [Project 元素](#project-元素) + - [Target 元素](#target-元素) + - [Task 元素](#task-元素) + - [Property 元素](#property-元素) + - [extension-point 元素](#extension-point-元素) - [参考资料](#参考资料) diff --git a/docs/javatool/build/maven/README.md b/docs/javatool/build/maven/README.md index a1fa5a18..76441c2a 100644 --- a/docs/javatool/build/maven/README.md +++ b/docs/javatool/build/maven/README.md @@ -9,8 +9,7 @@ ## 学习资料 - 官网 - - [Maven 官网](https://maven.apache.org/) + - [Maven 官网](https://maven.apache.org/) - [Maven Github](https://github.com/apache/maven) - 书 - - [《Maven 实战》](https://book.douban.com/subject/5345682/) - 国内最权威的Maven专家的力作,唯一一本哦! - + - [《Maven 实战》](https://book.douban.com/subject/5345682/) - 国内最权威的 Maven 专家的力作,唯一一本哦! diff --git a/docs/javatool/build/maven/maven-action.md b/docs/javatool/build/maven/maven-action.md index f748dc7e..7dc13051 100644 --- a/docs/javatool/build/maven/maven-action.md +++ b/docs/javatool/build/maven/maven-action.md @@ -3,21 +3,21 @@ - [1. 常见问题](#1-常见问题) - - [1.1. dependencies 和 dependencyManagement,plugins 和 pluginManagement 有什么区别?](#11-dependencies-和-dependencymanagementplugins-和-pluginmanagement-有什么区别) - - [1.2. IDEA 修改 JDK 版本后编译报错](#12-idea-修改-jdk-版本后编译报错) - - [1.3. 重复引入依赖](#13-重复引入依赖) - - [1.4. 如何打包一个可以直接运行的 Spring Boot jar 包](#14-如何打包一个可以直接运行的-spring-boot-jar-包) - - [1.5. 去哪儿找 maven dependency ?](#15-去哪儿找-maven-dependency-) - - [1.6. 如何指定编码?](#16-如何指定编码) - - [1.7. 如何指定 JDK 版本?](#17-如何指定-jdk-版本) - - [1.8. 如何避免将 dependency 打包到构件中?](#18-如何避免将-dependency-打包到构件中) - - [1.9. 如何跳过单元测试](#19-如何跳过单元测试) - - [1.10. IDEA 修改 JDK 版本后编译报错](#110-idea-修改-jdk-版本后编译报错) - - [1.11. 重复引入依赖](#111-重复引入依赖) - - [1.12. 如何引入本地 jar](#112-如何引入本地-jar) - - [1.13. 如何排除依赖](#113-如何排除依赖) + - [1.1. dependencies 和 dependencyManagement,plugins 和 pluginManagement 有什么区别?](#11-dependencies-和-dependencymanagementplugins-和-pluginmanagement-有什么区别) + - [1.2. IDEA 修改 JDK 版本后编译报错](#12-idea-修改-jdk-版本后编译报错) + - [1.3. 重复引入依赖](#13-重复引入依赖) + - [1.4. 如何打包一个可以直接运行的 Spring Boot jar 包](#14-如何打包一个可以直接运行的-spring-boot-jar-包) + - [1.5. 去哪儿找 maven dependency ?](#15-去哪儿找-maven-dependency-) + - [1.6. 如何指定编码?](#16-如何指定编码) + - [1.7. 如何指定 JDK 版本?](#17-如何指定-jdk-版本) + - [1.8. 如何避免将 dependency 打包到构件中?](#18-如何避免将-dependency-打包到构件中) + - [1.9. 如何跳过单元测试](#19-如何跳过单元测试) + - [1.10. IDEA 修改 JDK 版本后编译报错](#110-idea-修改-jdk-版本后编译报错) + - [1.11. 重复引入依赖](#111-重复引入依赖) + - [1.12. 如何引入本地 jar](#112-如何引入本地-jar) + - [1.13. 如何排除依赖](#113-如何排除依赖) - [2. 最佳实践](#2-最佳实践) - - [2.1. 通过 bom 统一管理版本](#21-通过-bom-统一管理版本) + - [2.1. 通过 bom 统一管理版本](#21-通过-bom-统一管理版本) diff --git a/docs/javatool/build/maven/maven-deploy.md b/docs/javatool/build/maven/maven-deploy.md index b51affaf..30780a77 100644 --- a/docs/javatool/build/maven/maven-deploy.md +++ b/docs/javatool/build/maven/maven-deploy.md @@ -5,14 +5,14 @@ - [发布 jar 包到中央仓库](#发布-jar-包到中央仓库) - - [在 Sonatype 创建 Issue](#在-sonatype-创建-issue) - - [使用 GPG 生成公私钥对](#使用-gpg-生成公私钥对) - - [Maven 配置](#maven-配置) - - [部署和发布](#部署和发布) + - [在 Sonatype 创建 Issue](#在-sonatype-创建-issue) + - [使用 GPG 生成公私钥对](#使用-gpg-生成公私钥对) + - [Maven 配置](#maven-配置) + - [部署和发布](#部署和发布) - [部署 maven 私服](#部署-maven-私服) - - [下载安装 Nexus](#下载安装-nexus) - - [启动停止 Nexus](#启动停止-nexus) - - [使用 Nexus](#使用-nexus) + - [下载安装 Nexus](#下载安装-nexus) + - [启动停止 Nexus](#启动停止-nexus) + - [使用 Nexus](#使用-nexus) - [参考资料](#参考资料) diff --git a/docs/javatool/build/maven/maven-pom.md b/docs/javatool/build/maven/maven-pom.md index f1d8e308..8b96a92b 100644 --- a/docs/javatool/build/maven/maven-pom.md +++ b/docs/javatool/build/maven/maven-pom.md @@ -5,30 +5,30 @@ - [简介](#简介) - - [什么是 pom?](#什么是-pom) - - [pom 配置一览](#pom-配置一览) + - [什么是 pom?](#什么是-pom) + - [pom 配置一览](#pom-配置一览) - [基本配置](#基本配置) - - [maven 坐标](#maven-坐标) + - [maven 坐标](#maven-坐标) - [依赖配置](#依赖配置) - - [dependencies](#dependencies) - - [parent](#parent) - - [dependencyManagement](#dependencymanagement) - - [modules](#modules) - - [properties](#properties) + - [dependencies](#dependencies) + - [parent](#parent) + - [dependencyManagement](#dependencymanagement) + - [modules](#modules) + - [properties](#properties) - [构建配置](#构建配置) - - [build](#build) - - [reporting](#reporting) + - [build](#build) + - [reporting](#reporting) - [项目信息](#项目信息) - [环境配置](#环境配置) - - [issueManagement](#issuemanagement) - - [ciManagement](#cimanagement) - - [mailingLists](#mailinglists) - - [scm](#scm) - - [prerequisites](#prerequisites) - - [repositories](#repositories) - - [pluginRepositories](#pluginrepositories) - - [distributionManagement](#distributionmanagement) - - [profiles](#profiles) + - [issueManagement](#issuemanagement) + - [ciManagement](#cimanagement) + - [mailingLists](#mailinglists) + - [scm](#scm) + - [prerequisites](#prerequisites) + - [repositories](#repositories) + - [pluginRepositories](#pluginrepositories) + - [distributionManagement](#distributionmanagement) + - [profiles](#profiles) - [参考资料](#参考资料) diff --git a/docs/javatool/build/maven/maven-quickstart.md b/docs/javatool/build/maven/maven-quickstart.md index 2110ef48..e8a64796 100644 --- a/docs/javatool/build/maven/maven-quickstart.md +++ b/docs/javatool/build/maven/maven-quickstart.md @@ -5,23 +5,23 @@ - [简介](#简介) - - [Maven 是什么](#maven-是什么) - - [Maven 的生命周期](#maven-的生命周期) - - [Maven 的标准工程结构](#maven-的标准工程结构) - - [Maven 的"约定优于配置"](#maven-的约定优于配置) - - [Maven 的版本规范](#maven-的版本规范) + - [Maven 是什么](#maven-是什么) + - [Maven 的生命周期](#maven-的生命周期) + - [Maven 的标准工程结构](#maven-的标准工程结构) + - [Maven 的"约定优于配置"](#maven-的约定优于配置) + - [Maven 的版本规范](#maven-的版本规范) - [安装](#安装) - - [本地仓储配置](#本地仓储配置) + - [本地仓储配置](#本地仓储配置) - [第一个 Maven 工程](#第一个-maven-工程) - - [在 Intellij 中创建 Maven 工程](#在-intellij-中创建-maven-工程) - - [在 Eclipse 中创建 Maven 工程](#在-eclipse-中创建-maven-工程) + - [在 Intellij 中创建 Maven 工程](#在-intellij-中创建-maven-工程) + - [在 Eclipse 中创建 Maven 工程](#在-eclipse-中创建-maven-工程) - [使用指导](#使用指导) - - [如何添加依赖](#如何添加依赖) - - [如何寻找 jar 包](#如何寻找-jar-包) - - [如何使用 Maven 插件(Plugin)](#如何使用-maven-插件plugin) - - [如何一次编译多个工程](#如何一次编译多个工程) - - [常用 Maven 插件](#常用-maven-插件) - - [常用 Maven 命令](#常用-maven-命令) + - [如何添加依赖](#如何添加依赖) + - [如何寻找 jar 包](#如何寻找-jar-包) + - [如何使用 Maven 插件(Plugin)](#如何使用-maven-插件plugin) + - [如何一次编译多个工程](#如何一次编译多个工程) + - [常用 Maven 插件](#常用-maven-插件) + - [常用 Maven 命令](#常用-maven-命令) - [引用和引申](#引用和引申) diff --git a/docs/javatool/build/maven/maven-settings.md b/docs/javatool/build/maven/maven-settings.md index 924eaa4a..87c67d43 100644 --- a/docs/javatool/build/maven/maven-settings.md +++ b/docs/javatool/build/maven/maven-settings.md @@ -5,21 +5,21 @@ - [简介](#简介) - - [settings.xml 有什么用?](#settingsxml-有什么用) - - [settings.xml 文件位置](#settingsxml-文件位置) - - [配置优先级](#配置优先级) + - [settings.xml 有什么用?](#settingsxml-有什么用) + - [settings.xml 文件位置](#settingsxml-文件位置) + - [配置优先级](#配置优先级) - [settings.xml 元素详解](#settingsxml-元素详解) - - [顶级元素概览](#顶级元素概览) - - [LocalRepository](#localrepository) - - [InteractiveMode](#interactivemode) - - [UsePluginRegistry](#usepluginregistry) - - [Offline](#offline) - - [PluginGroups](#plugingroups) - - [Servers](#servers) - - [Mirrors](#mirrors) - - [Proxies](#proxies) - - [Profiles](#profiles) - - [ActiveProfiles](#activeprofiles) + - [顶级元素概览](#顶级元素概览) + - [LocalRepository](#localrepository) + - [InteractiveMode](#interactivemode) + - [UsePluginRegistry](#usepluginregistry) + - [Offline](#offline) + - [PluginGroups](#plugingroups) + - [Servers](#servers) + - [Mirrors](#mirrors) + - [Proxies](#proxies) + - [Profiles](#profiles) + - [ActiveProfiles](#activeprofiles) - [参考资料](#参考资料) diff --git a/docs/javatool/build/maven/plugins/maven-checkstyle-plugin.md b/docs/javatool/build/maven/plugins/maven-checkstyle-plugin.md deleted file mode 100644 index 4d7e80da..00000000 --- a/docs/javatool/build/maven/plugins/maven-checkstyle-plugin.md +++ /dev/null @@ -1,425 +0,0 @@ -# Maven 插件之代码检查 - - - -- [maven-checkstyle-plugin](#maven-checkstyle-plugin) - - [定义 checkstyle.xml](#定义-checkstylexml) - - [配置 pom.xml](#配置-pomxml) -- [maven-pmd-plugin](#maven-pmd-plugin) - - [配置 pom.xml](#配置-pomxml-1) -- [参考资料](#参考资料) - - - -## maven-checkstyle-plugin - -> **maven-checkstyle-plugin,用于检测代码中不符合规范的地方。** - -### 定义 checkstyle.xml - -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -### 配置 pom.xml - -```xml - - - ... - - config/maven_checks.xml - - ... - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.0 - - - - validate - validate - - - style/checkstyle.xml - UTF-8 - true - true - false - - - check - - - - - - - org.apache.maven.plugins - maven-jxr-plugin - 2.3 - - - - ... - -``` - -其中可以修改使用的检查规则文件路径,插件默认提供了四个规则文件可以直接使用,无需手动下载: - -- config/sun_checks.xml - Sun Microsystems Definition (default). -- config/maven_checks.xml - Maven Development Definitions. -- config/turbine_checks.xml - Turbine Development Definitions. -- config/avalon_checks.xml - Avalon Development Definitions. - -配置好后,可以执行 `mvn clean checkstyle:check` 检查代码。 - -## maven-pmd-plugin - -> maven-pmd-plugin 是阿里编程规范检查插件。 - -### 配置 pom.xml - -参考 https://github.com/alibaba/p3c/blob/master/p3c-pmd/pom.xml 配置 - -```xml - - org.apache.maven.plugins - maven-pmd-plugin - 3.11.0 - - ${project.build.sourceEncoding} - ${maven.compiler.target} - true - - rulesets/java/ali-comment.xml - rulesets/java/ali-concurrent.xml - rulesets/java/ali-constant.xml - rulesets/java/ali-exception.xml - rulesets/java/ali-flowcontrol.xml - rulesets/java/ali-naming.xml - rulesets/java/ali-oop.xml - rulesets/java/ali-orm.xml - rulesets/java/ali-other.xml - rulesets/java/ali-set.xml - - true - - - - verify - - check - - - - - - com.alibaba.p3c - p3c-pmd - 2.0.0 - - - - -``` - -配置好后,可以执行 `mvn clean pmd:check` 检查代码。 - -## 参考资料 - -- https://maven.apache.org/plugins/maven-checkstyle-plugin/ -- https://maven.apache.org/jxr/maven-jxr-plugin/ -- https://www.jianshu.com/p/557b975ae40d -- 阿里巴巴编程规范 - - https://github.com/alibaba/p3c - - https://github.com/alibaba/p3c/blob/master/p3c-pmd/pom.xml diff --git a/docs/javatool/build/maven/plugins/maven-checkstyle.md b/docs/javatool/build/maven/plugins/maven-checkstyle.md deleted file mode 100644 index 4d7e80da..00000000 --- a/docs/javatool/build/maven/plugins/maven-checkstyle.md +++ /dev/null @@ -1,425 +0,0 @@ -# Maven 插件之代码检查 - - - -- [maven-checkstyle-plugin](#maven-checkstyle-plugin) - - [定义 checkstyle.xml](#定义-checkstylexml) - - [配置 pom.xml](#配置-pomxml) -- [maven-pmd-plugin](#maven-pmd-plugin) - - [配置 pom.xml](#配置-pomxml-1) -- [参考资料](#参考资料) - - - -## maven-checkstyle-plugin - -> **maven-checkstyle-plugin,用于检测代码中不符合规范的地方。** - -### 定义 checkstyle.xml - -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -### 配置 pom.xml - -```xml - - - ... - - config/maven_checks.xml - - ... - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.0 - - - - validate - validate - - - style/checkstyle.xml - UTF-8 - true - true - false - - - check - - - - - - - org.apache.maven.plugins - maven-jxr-plugin - 2.3 - - - - ... - -``` - -其中可以修改使用的检查规则文件路径,插件默认提供了四个规则文件可以直接使用,无需手动下载: - -- config/sun_checks.xml - Sun Microsystems Definition (default). -- config/maven_checks.xml - Maven Development Definitions. -- config/turbine_checks.xml - Turbine Development Definitions. -- config/avalon_checks.xml - Avalon Development Definitions. - -配置好后,可以执行 `mvn clean checkstyle:check` 检查代码。 - -## maven-pmd-plugin - -> maven-pmd-plugin 是阿里编程规范检查插件。 - -### 配置 pom.xml - -参考 https://github.com/alibaba/p3c/blob/master/p3c-pmd/pom.xml 配置 - -```xml - - org.apache.maven.plugins - maven-pmd-plugin - 3.11.0 - - ${project.build.sourceEncoding} - ${maven.compiler.target} - true - - rulesets/java/ali-comment.xml - rulesets/java/ali-concurrent.xml - rulesets/java/ali-constant.xml - rulesets/java/ali-exception.xml - rulesets/java/ali-flowcontrol.xml - rulesets/java/ali-naming.xml - rulesets/java/ali-oop.xml - rulesets/java/ali-orm.xml - rulesets/java/ali-other.xml - rulesets/java/ali-set.xml - - true - - - - verify - - check - - - - - - com.alibaba.p3c - p3c-pmd - 2.0.0 - - - - -``` - -配置好后,可以执行 `mvn clean pmd:check` 检查代码。 - -## 参考资料 - -- https://maven.apache.org/plugins/maven-checkstyle-plugin/ -- https://maven.apache.org/jxr/maven-jxr-plugin/ -- https://www.jianshu.com/p/557b975ae40d -- 阿里巴巴编程规范 - - https://github.com/alibaba/p3c - - https://github.com/alibaba/p3c/blob/master/p3c-pmd/pom.xml diff --git a/docs/javatool/elastic/README.md b/docs/javatool/elastic/README.md new file mode 100644 index 00000000..f0b11fba --- /dev/null +++ b/docs/javatool/elastic/README.md @@ -0,0 +1,45 @@ +# Elastic 技术栈 + +> **Elastic 技术栈通常被用来作为日志中心。** +> +> ELK 是 elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/products/elasticsearch) 、[Logstash](https://www.elastic.co/products/logstash) 、[Kibana](https://www.elastic.co/products/kibana) 的首字母组合。 +> +> [ElasticSearch](https://www.elastic.co/products/elasticsearch) 是一个基于 [Lucene](http://lucene.apache.org/core/documentation.html) 构建的开源,分布式,RESTful 搜索引擎。 +> +> [Logstash](https://www.elastic.co/products/logstash) 传输和处理你的日志、事务或其他数据。 +> +> [Kibana](https://www.elastic.co/products/kibana) 将 Elasticsearch 的数据分析并渲染为可视化的报表。 +> +> Elastic 技术栈,在 ELK 的基础上扩展了一些新的产品,如:[Beats](https://www.elastic.co/products/beats) 、[X-Pack](https://www.elastic.co/products/x-pack) 。 + +## 内容 + +- [Elastic 技术栈快速入门](elastic-quickstart.md) +- [Elasticsearch 运维](elastic-elasticsearch-ops.md) +- [Beats 运维](elastic-beats-ops.md) +- [Kibana 运维](elastic-kibana-ops.md) +- [Logstash 运维](elastic-logstash-ops.md) + +## 学习资源 + +- **官方资源** + - [Elasticsearch 官网](https://www.elastic.co/cn/products/elasticsearch) + - [Elasticsearch Github](https://github.com/elastic/elasticsearch) + - [Elasticsearch 官方文档](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) + - [Logstash 官网](https://www.elastic.co/cn/products/logstash) + - [Logstash Github](https://github.com/elastic/logstash) + - [Logstash 官方文档](https://www.elastic.co/guide/en/logstash/current/index.html) + - [Kibana 官网](https://www.elastic.co/cn/products/kibana) + - [Kibana Github](https://github.com/elastic/kibana) + - [Kibana 官方文档](https://www.elastic.co/guide/en/kibana/current/index.html) + - [Beats 官网](https://www.elastic.co/cn/products/beats) + - [Beats Github](https://github.com/elastic/beats) + - [Beats 官方文档](https://www.elastic.co/guide/en/beats/libbeat/current/index.html) +- **第三方工具** + - [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) +- **教程** + - [Elasticsearch 权威指南(中文版)](https://es.xiaoleilu.com/index.html) + - [ELK Stack 权威指南](https://github.com/chenryn/logstash-best-practice-cn) +- **博文** + - [Elasticsearch+Logstash+Kibana 教程](https://www.cnblogs.com/xing901022/p/4704319.html) + - [ELK(Elasticsearch、Logstash、Kibana)安装和配置](https://github.com/judasn/Linux-Tutorial/blob/master/ELK-Install-And-Settings.md) diff --git a/docs/javatool/elastic/elastic-beats-ops.md b/docs/javatool/elastic/elastic-beats-ops.md new file mode 100644 index 00000000..19ad8fd9 --- /dev/null +++ b/docs/javatool/elastic/elastic-beats-ops.md @@ -0,0 +1,238 @@ +# Filebeat 运维 + +> Beats 平台集合了多种单一用途数据采集器。它们从成百上千或成千上万台机器和系统向 Logstash 或 Elasticsearch 发送数据。 +> +> 因为我只接触过 Filebeat,所有本文仅介绍 Filebeat 的日常运维。 + +## 1. Filebeat 安装 + +### 1.1. 环境要求 + +> 版本:Elastic Stack 7.4 + +### 1.2. 安装步骤 + +Unix / Linux 系统建议使用下面方式安装,因为比较通用。 + +``` +wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.1.1-linux-x86_64.tar.gz +tar -zxf filebeat-6.1.1-linux-x86_64.tar.gz +``` + +> 更多内容可以参考:[filebeat-installation](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html) + +## 2. Filebeat 配置 + +> 首先,必须要知道的是:`filebeat.yml` 是 filebeat 的配置文件。其路径会因为你安装方式而有所不同。 +> +> Beat 所有系列产品的配置文件都基于 [YAML](http://www.yaml.org/) 格式,FileBeat 当然也不例外。 +> +> 更多 filebeat 配置内容可以参考:[配置 filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/configuring-howto-filebeat.html) +> +> 更多 filebeat.yml 文件格式内容可以参考:[filebeat.yml 文件格式](https://www.elastic.co/guide/en/beats/libbeat/6.1/config-file-format.html) + +filebeat.yml 部分配置示例: + +```yml +filebeat: + prospectors: + - type: log + paths: + - /var/log/*.log + multiline: + pattern: '^[' + match: after +``` + +### 2.1. 重要配置项 + +> 下面我将列举 Filebeat 的较为重要的配置项。 +> +> 如果想了解更多配置信息,可以参考: +> +> 更多 filebeat 配置内容可以参考:[配置 filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/configuring-howto-filebeat.html) +> +> 更多 filebeat.yml 文件格式内容可以参考:[filebeat.yml 文件格式](https://www.elastic.co/guide/en/beats/libbeat/6.1/config-file-format.html) + +#### 2.1.1. filebeat.prospectors + +(文件监视器)用于指定需要关注的文件。 + +**示例** + +```yaml +filebeat.prospectors: + - type: log + enabled: true + paths: + - /var/log/*.log +``` + +#### 2.1.2. output.elasticsearch + +如果你希望使用 filebeat 直接向 elasticsearch 输出数据,需要配置 output.elasticsearch 。 + +**示例** + +```yaml +output.elasticsearch: + hosts: ['192.168.1.42:9200'] +``` + +#### 2.1.3. output.logstash + +如果你希望使用 filebeat 向 logstash 输出数据,然后由 logstash 再向 elasticsearch 输出数据,需要配置 output.logstash。 + +> **注意** +> +> 相比于向 elasticsearch 输出数据,个人更推荐向 logstash 输出数据。 +> +> 因为 logstash 和 filebeat 一起工作时,如果 logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。这样,可以减少管道超负荷的情况。 + +**示例** + +```yaml +output.logstash: + hosts: ['127.0.0.1:5044'] +``` + +此外,还需要在 logstash 的配置文件(如 logstash.conf)中指定 beats input 插件: + +```yaml +input { + beats { + port => 5044 # 此端口需要与 filebeat.yml 中的端口相同 + } +} + +# The filter part of this file is commented out to indicate that it is +# optional. +# filter { +# +# } + +output { + elasticsearch { + hosts => "localhost:9200" + manage_template => false + index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}" + document_type => "%{[@metadata][type]}" + } +} +``` + +#### 2.1.4. setup.kibana + +如果打算使用 Filebeat 提供的 Kibana 仪表板,需要配置 setup.kibana 。 + +**示例** + +```yaml +setup.kibana: + host: 'localhost:5601' +``` + +#### 2.1.5. setup.template.settings + +在 Elasticsearch 中,[索引模板](https://www.elastic.co/guide/en/elasticsearch/reference/6.1/indices-templates.html)用于定义设置和映射,以确定如何分析字段。 + +在 Filebeat 中,setup.template.settings 用于配置索引模板。 + +Filebeat 推荐的索引模板文件由 Filebeat 软件包安装。如果您接受 filebeat.yml 配置文件中的默认配置,Filebeat 在成功连接到 Elasticsearch 后自动加载模板。 + +您可以通过在 Filebeat 配置文件中配置模板加载选项来禁用自动模板加载,或加载自己的模板。您还可以设置选项来更改索引和索引模板的名称。 + +> **参考** +> +> 更多内容可以参考:[filebeat-template](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-template.html) +> +> **说明** +> +> 如无必要,使用 Filebeat 配置文件中的默认索引模板即可。 + +#### 2.1.6. setup.dashboards + +Filebeat 附带了示例 Kibana 仪表板。在使用仪表板之前,您需要创建索引模式 `filebeat- *`,并将仪表板加载到 Kibana 中。为此,您可以运行 `setup` 命令或在 `filebeat.yml` 配置文件中配置仪表板加载。 + +为了在 Kibana 中加载 Filebeat 的仪表盘,需要在 `filebeat.yml` 配置中启动开关: + +``` +setup.dashboards.enabled: true +``` + +> **参考** +> +> 更多内容可以参考:[configuration-dashboards](https://www.elastic.co/guide/en/beats/filebeat/current/configuration-dashboards.html) + +## 3. Filebeat 命令 + +filebeat 提供了一系列命令来完成各种功能。 + +执行命令方式: + +```bash +./filebeat COMMAND +``` + +> **参考** +> +> 更多内容可以参考:[command-line-options](https://www.elastic.co/guide/en/beats/filebeat/current/command-line-options.html) +> +> **说明** +> +> 个人认为命令行没有必要一一掌握,因为绝大部分功能都可以通过配置来完成。且通过命令行指定功能这种方式要求每次输入同样参数,不利于固化启动方式。 +> +> 最重要的当然是启动命令 run 了。 +> +> **示例** 指定配置文件启动 +> +> ```bash +> ./filebeat run -e -c filebeat.yml -d "publish" +> ./filebeat -e -c filebeat.yml -d "publish" # run 可以省略 +> ``` + +## 4. Filebeat 模块 + +> [Filebeat](https://www.elastic.co/cn/products/beats/filebeat) 和 [Metricbeat](https://www.elastic.co/cn/products/beats/metricbeat) 内部集成了一系列模块,用以简化常见日志格式(例如 NGINX、Apache 或诸如 Redis 或 Docker 等系统指标)的收集、解析和可视化过程。 + +- 配置 elasticsearch 和 kibana + +``` +output.elasticsearch: + hosts: ["myEShost:9200"] + username: "elastic" + password: "elastic" +setup.kibana: + host: "mykibanahost:5601" + username: "elastic" + password: "elastic +``` + +> username 和 password 是可选的,如果不需要认证则不填。 + +- 初始化环境 + +执行下面命令,filebeat 会加载推荐索引模板。 + +``` +./filebeat setup -e +``` + +- 指定模块 + +执行下面命令,指定希望加载的模块。 + +``` +./filebeat -e --modules system,nginx,mysql +``` + +> 更多内容可以参考: +> +> - [配置 filebeat 模块](https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-modules.html) +> - [filebeat 支持模块](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html) + +## 5. 参考资料 + +- [Beats 官网](https://www.elastic.co/cn/products/beats) +- [Beats Github](https://github.com/elastic/beats) +- [Beats 官方文档](https://www.elastic.co/guide/en/beats/libbeat/current/index.html) diff --git a/docs/javatool/elastic/elastic-elasticsearch-ops.md b/docs/javatool/elastic/elastic-elasticsearch-ops.md new file mode 100644 index 00000000..8caab8ca --- /dev/null +++ b/docs/javatool/elastic/elastic-elasticsearch-ops.md @@ -0,0 +1,133 @@ +# Elasticsearch 运维 + +> [Elasticsearch](https://github.com/elastic/elasticsearch) 是一个分布式、RESTful 风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。 作为 Elastic Stack 的核心,它集中存储您的数据,帮助您发现意料之中以及意料之外的情况。 + +## 部署 + +### 安装步骤 + +> [Elasticsearch 官方开源版本安装说明](https://www.elastic.co/cn/downloads/elasticsearch-oss) + +(1)下载解压 + +访问 [官方下载地址](https://www.elastic.co/cn/downloads/elasticsearch-oss) ,选择需要的版本,下载解压到本地。 + +(2)运行 + +运行 `bin/elasticsearch` (Windows 系统上运行 `bin\elasticsearch.bat` ) + +(3)访问 + +执行 `curl http://localhost:9200/` 测试服务是否启动 + +### 集群规划 + +ElasticSearch 集群需要根据业务实际情况去合理规划。 + +需要考虑的问题点: + +- 集群部署几个节点? +- 有多少个索引? +- 每个索引有多大数据量? +- 每个索引有多少个分片? + +一个参考规划: + +- 3 台机器,每台机器是 6 核 64G 的。 +- 我们 es 集群的日增量数据大概是 2000 万条,每天日增量数据大概是 500MB,每月增量数据大概是 6 亿,15G。目前系统已经运行了几个月,现在 es 集群里数据总量大概是 100G 左右。 +- 目前线上有 5 个索引(这个结合你们自己业务来,看看自己有哪些数据可以放 es 的),每个索引的数据量大概是 20G,所以这个数据量之内,我们每个索引分配的是 8 个 shard,比默认的 5 个 shard 多了 3 个 shard。 + +## FAQ + +### elasticsearch 不允许以 root 权限来运行 + +**问题:**在 Linux 环境中,elasticsearch 不允许以 root 权限来运行。 + +如果以 root 身份运行 elasticsearch,会提示这样的错误: + +``` +can not run elasticsearch as root +``` + +**解决方法:**使用非 root 权限账号运行 elasticsearch + +```bash +# 创建用户组 +groupadd elk +# 创建新用户,-g elk 设置其用户组为 elk,-p elk 设置其密码为 elk +useradd elk -g elk -p elk +# 更改 /opt 文件夹及内部文件的所属用户及组为 elk:elk +chown -R elk:elk /opt # 假设你的 elasticsearch 安装在 opt 目录下 +# 切换账号 +su elk +``` + +### vm.max_map_count 不低于 262144 + +**问题:**`vm.max_map_count` 表示虚拟内存大小,它是一个内核参数。elasticsearch 默认要求 `vm.max_map_count` 不低于 262144。 + +``` +max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] +``` + +**解决方法:** + +你可以执行以下命令,设置 `vm.max_map_count` ,但是重启后又会恢复为原值。 + +``` +sysctl -w vm.max_map_count=262144 +``` + +持久性的做法是在 `/etc/sysctl.conf` 文件中修改 `vm.max_map_count` 参数: + +``` +echo "vm.max_map_count=262144" > /etc/sysctl.conf +sysctl -p +``` + +> **注意** +> +> 如果运行环境为 docker 容器,可能会限制执行 sysctl 来修改内核参数。 +> +> 这种情况下,你只能选择直接修改宿主机上的参数了。 + +### nofile 不低于 65536 + +**问题:** `nofile` 表示进程允许打开的最大文件数。elasticsearch 进程要求可以打开的最大文件数不低于 65536。 + +``` +max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536] +``` + +**解决方法:** + +在 `/etc/security/limits.conf` 文件中修改 `nofile` 参数: + +``` +echo "* soft nofile 65536" > /etc/security/limits.conf +echo "* hard nofile 131072" > /etc/security/limits.conf +``` + +### nproc 不低于 2048 + +**问题:** `nproc` 表示最大线程数。elasticsearch 要求最大线程数不低于 2048。 + +``` +max number of threads [1024] for user [user] is too low, increase to at least [2048] +``` + +**解决方法:** + +在 `/etc/security/limits.conf` 文件中修改 `nproc` 参数: + +``` +echo "* soft nproc 2048" > /etc/security/limits.conf +echo "* hard nproc 4096" > /etc/security/limits.conf +``` + +## 参考资料 + +- [Elasticsearch 官网](https://www.elastic.co/cn/products/elasticsearch) +- [Elasticsearch Github](https://github.com/elastic/elasticsearch) +- [Elasticsearch 官方文档](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) +- [Install Elasticsearch with RPM]( https://www.elastic.co/guide/en/elasticsearch/reference/current/rpm.html#rpm) \ No newline at end of file diff --git a/docs/javatool/elastic/elastic-kibana-ops.md b/docs/javatool/elastic/elastic-kibana-ops.md new file mode 100644 index 00000000..ebc0b6c6 --- /dev/null +++ b/docs/javatool/elastic/elastic-kibana-ops.md @@ -0,0 +1,346 @@ +# Kibana 运维 + +> 通过 Kibana,您可以对自己的 Elasticsearch 进行可视化,还可以在 Elastic Stack 中进行导航,这样您便可以进行各种操作了,从跟踪查询负载,到理解请求如何流经您的整个应用,都能轻松完成。 + +## 1. 安装 + +### 1.1. 环境要求 + +> 版本:Elastic Stack 7.4 + +### 1.2. 安装步骤 + +安装步骤如下: + +1. 在 [kibana 官方下载地址](https://www.elastic.co/downloads/kibana)下载所需版本包并解压到本地。 +2. 修改 `config/kibana.yml` 配置文件,设置 `elasticsearch.url` 指向 Elasticsearch 实例。 +3. 运行 `bin/kibana` (Windows 上运行 `bin\kibana.bat`) +4. 在浏览器上访问 http://localhost:5601 + +## 2. 使用 + +### 2.1. 检索 + +单击侧面导航栏中的 `检索(Discover)` ,可以显示 `Kibana` 的数据查询功能功能。 + +

    + +在搜索栏中,您可以输入 Elasticsearch 查询条件来搜索您的数据。您可以在 `Discover` 页面中浏览结果并在 `Visualize` 页面中创建已保存搜索条件的可视化。 + +当前索引模式显示在查询栏下方。索引模式确定提交查询时搜索哪些索引。要搜索一组不同的索引,请从下拉菜单中选择不同的模式。要添加索引模式(index pattern),请转至 `Management/Kibana/Index Patterns` 并单击 `Add New`。 + +您可以使用字段名称和您感兴趣的值构建搜索。对于数字字段,可以使用比较运算符,如大于(>),小于(<)或等于(=)。您可以将元素与逻辑运算符 `AND`,`OR` 和 `NOT` 链接,全部使用大写。 + +默认情况下,每个匹配文档都显示所有字段。要选择要显示的文档字段,请将鼠标悬停在“可用字段”列表上,然后单击要包含的每个字段旁边的添加按钮。例如,如果只添加 account_number,则显示将更改为包含五个帐号的简单列表: + +

    + +kibana 的搜索栏遵循 [query-string-syntax](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax) 文档中所说明的查询语义。 + +这里说明一些最基本的查询语义。 + +查询字符串会被解析为一系列的术语和运算符。一个术语可以是一个单词(如:quick、brown)或用双引号包围的短语(如"quick brown")。 + +查询操作允许您自定义搜索 - 下面介绍了可用的选项。 + +#### 2.1.1. 字段名称 + +正如查询字符串查询中所述,将在搜索条件中搜索 default_field,但可以在查询语法中指定其他字段: + +例如: + +- 查询 `status` 字段中包含 `active` 关键字 + +``` +status:active +``` + +- `title` 字段包含 `quick` 或 `brown` 关键字。如果您省略 `OR` 运算符,则将使用默认运算符 + +``` +title:(quick OR brown) +title:(quick brown) +``` + +- author 字段查找精确的短语 "john smith",即精确查找。 + +``` +author:"John Smith" +``` + +- 任意字段 `book.title`,`book.content` 或 `book.date` 都包含 `quick` 或 `brown`(注意我们需要如何使用 `\*` 表示通配符) + +``` +book.\*:(quick brown) +``` + +- title 字段包含任意非 null 值 + +``` +_exists_:title +``` + +#### 2.1.2. 通配符 + +ELK 提供了 ? 和 \* 两个通配符。 + +- `?` 表示任意单个字符; +- `*` 表示任意零个或多个字符。 + +``` +qu?ck bro* +``` + +> **注意:通配符查询会使用大量的内存并且执行性能较为糟糕,所以请慎用。** > **提示**:纯通配符 \* 被写入 [exsits](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html) 查询,从而提高了查询效率。因此,通配符 `field:*` 将匹配包含空值的文档,如:`{“field”:“”}`,但是如果字段丢失或显示将值置为 null 则不匹配,如:`“field”:null}` > **提示**:在一个单词的开头(例如:`*ing`)使用通配符这种方式的查询量特别大,因为索引中的所有术语都需要检查,以防万一匹配。通过将 `allow_leading_wildcard` 设置为 `false`,可以禁用。 + +#### 2.1.3. 正则表达式 + +可以通过 `/` 将正则表达式包裹在查询字符串中进行查询 + +例: + +``` +name:/joh?n(ath[oa]n)/ +``` + +支持的正则表达式语义可以参考:[Regular expression syntax](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#regexp-syntax) + +#### 2.1.4. 模糊查询 + +我们可以使用 `~` 运算符来进行模糊查询。 + +例: + +假设我们实际想查询 + +``` +quick brown forks +``` + +但是,由于拼写错误,我们的查询关键字变成如下情况,依然可以查到想要的结果。 + +``` +quikc\~ brwn\~ foks\~ +``` + +这种模糊查询使用 Damerau-Levenshtein 距离来查找所有匹配最多两个更改的项。所谓的更改是指单个字符的插入,删除或替换,或者两个相邻字符的换位。 + +默认编辑距离为 `2`,但编辑距离为 `1` 应足以捕捉所有人类拼写错误的 80%。它可以被指定为: + +``` +quikc\~1 +``` + +#### 2.1.5. 近似检索 + +尽管短语查询(例如,`john smith`)期望所有的词条都是完全相同的顺序,但是近似查询允许指定的单词进一步分开或以不同的顺序排列。与模糊查询可以为单词中的字符指定最大编辑距离一样,近似搜索也允许我们指定短语中单词的最大编辑距离: + +例 + +``` +"fox quick"\~5 +``` + +字段中的文本越接近查询字符串中指定的原始顺序,该文档就越被认为是相关的。当与上面的示例查询相比时,短语 `"quick fox"` 将被认为比 `"quick brown fox"` 更近似查询条件。 + +#### 2.1.6. 范围 + +可以为日期,数字或字符串字段指定范围。闭区间范围用方括号 `[min TO max]` 和开区间范围用花括号 `{min TO max}` 来指定。 + +我们不妨来看一些示例。 + +- 2012 年的所有日子 + +``` +date:[2012-01-01 TO 2012-12-31] +``` + +- 数字 1 到 5 + +``` +count:[1 TO 5] +``` + +- 在 `alpha` 和 `omega` 之间的标签,不包括 `alpha` 和 `omega` + +``` +tag:{alpha TO omega} +``` + +- 10 以上的数字 + +``` +count:[10 TO *] +``` + +- 2012 年以前的所有日期 + +``` +date:{* TO 2012-01-01} +``` + +此外,开区间和闭区间也可以组合使用 + +- 数组 1 到 5,但不包括 5 + +``` +count:[1 TO 5} +``` + +一边无界的范围也可以使用以下语法: + +``` +age:>10 +age:>=10 +age:<10 +age:<=10 +``` + +当然,你也可以使用 AND 运算符来得到连个查询结果的交集 + +``` +age:(>=10 AND <20) +age:(+>=10 +<20) +``` + +#### 2.1.7. Boosting + +使用操作符 `^` 使一个术语比另一个术语更相关。例如,如果我们想查找所有有关狐狸的文档,但我们对狐狸特别感兴趣: + +``` +quick^2 fox +``` + +默认提升值是 1,但可以是任何正浮点数。 0 到 1 之间的提升减少了相关性。 + +增强也可以应用于短语或组: + +``` +"john smith"^2 (foo bar)^4 +``` + +#### 2.1.8. 布尔操作 + +默认情况下,只要一个词匹配,所有词都是可选的。搜索 `foo bar baz` 将查找包含 `foo` 或 `bar` 或 `baz` 中的一个或多个的任何文档。我们已经讨论了上面的`default_operator`,它允许你强制要求所有的项,但也有布尔运算符可以在查询字符串本身中使用,以提供更多的控制。 + +首选的操作符是 `+`(此术语必须存在)和 `-` (此术语不得存在)。所有其他条款是可选的。例如,这个查询: + +``` +quick brown +fox -news +``` + +这条查询意味着: + +- fox 必须存在 +- news 必须不存在 +- quick 和 brown 是可有可无的 + +熟悉的运算符 `AND`,`OR` 和 `NOT`(也写成 `&&`,`||` 和 `!`)也被支持。然而,这些操作符有一定的优先级:`NOT` 优先于 `AND`,`AND` 优先于 `OR`。虽然 `+` 和 `-` 仅影响运算符右侧的术语,但 `AND` 和 `OR` 会影响左侧和右侧的术语。 + +#### 2.1.9. 分组 + +多个术语或子句可以用圆括号组合在一起,形成子查询 + +``` +(quick OR brown) AND fox +``` + +可以使用组来定位特定的字段,或者增强子查询的结果: + +``` +status:(active OR pending) title:(full text search)^2 +``` + +#### 2.1.10. 保留字 + +如果你需要使用任何在你的查询本身中作为操作符的字符(而不是作为操作符),那么你应该用一个反斜杠来转义它们。例如,要搜索(1 + 1)= 2,您需要将查询写为 `\(1\+1\)\=2` + +保留字符是:`+ - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /` + +无法正确地转义这些特殊字符可能会导致语法错误,从而阻止您的查询运行。 + +#### 2.1.11. 空查询 + +如果查询字符串为空或仅包含空格,则查询将生成一个空的结果集。 + +### 2.2. 可视化 + +要想使用可视化的方式展示您的数据,请单击侧面导航栏中的 `可视化(Visualize)`。 + +Visualize 工具使您能够以多种方式(如饼图、柱状图、曲线图、分布图等)查看数据。要开始使用,请点击蓝色的 `Create a visualization` 或 `+` 按钮。 + +![https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-landing.png](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-landing.png) + +有许多可视化类型可供选择。 + +![https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-wizard-step-1.png](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-wizard-step-1.png) + +下面,我们来看创建几个图标示例: + +#### 2.2.1. Pie + +您可以从保存的搜索中构建可视化文件,也可以输入新的搜索条件。要输入新的搜索条件,首先需要选择一个索引模式来指定要搜索的索引。 + +默认搜索匹配所有文档。最初,一个“切片”包含整个饼图: + +![https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-pie-1.png](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-pie-1.png) + +要指定在图表中展示哪些数据,请使用 Elasticsearch 存储桶聚合。分组汇总只是将与您的搜索条件相匹配的文档分类到不同的分类中,也称为分组。 + +为每个范围定义一个存储桶: + +1. 单击 `Split Slices`。 +2. 在 `Aggregation` 列表中选择 `Terms`。_注意:这里的 Terms 是 Elk 采集数据时定义好的字段或标签。_ +3. 在 `Field` 列表中选择 `level.keyword`。 +4. 点击 ![images/apply-changes-button.png](https://www.elastic.co/guide/en/kibana/6.1/images/apply-changes-button.png) 按钮来更新图表。 + +![image.png](https://upload-images.jianshu.io/upload_images/3101171-7fb2042dc6d59520.png) + +完成后,如果想要保存这个图表,可以点击页面最上方一栏中的 `Save` 按钮。 + +#### 2.2.2. Vertical Bar + +我们在展示一下如何创建柱状图。 + +1. 点击蓝色的 `Create a visualization` 或 `+` 按钮。选择 `Vertical Bar` +2. 选择索引模式。由于您尚未定义任何 bucket ,因此您会看到一个大栏,显示与默认通配符查询匹配的文档总数。 +3. 指定 Y 轴所代表的字段 +4. 指定 X 轴所代表的字段 +5. 点击 ![images/apply-changes-button.png](https://www.elastic.co/guide/en/kibana/6.1/images/apply-changes-button.png) 按钮来更新图表。 + +![image.png](https://upload-images.jianshu.io/upload_images/3101171-5aa7627284c19a56.png) + +完成后,如果想要保存这个图表,可以点击页面最上方一栏中的 `Save` 按钮。 + +### 2.3. 报表 + +`报表(Dashboard)` 可以整合和共享 `Visualize` 集合。 + +1. 点击侧面导航栏中的 Dashboard。 +2. 点击添加显示保存的可视化列表。 +3. 点击之前保存的 `Visualize`,然后点击列表底部的小向上箭头关闭可视化列表。 +4. 将鼠标悬停在可视化对象上会显示允许您编辑,移动,删除和调整可视化对象大小的容器控件。 + +## 3. FAQ + +### 3.1. Kibana No Default Index Pattern Warning + +**问题:**安装 ELK 后,访问 kibana 页面时,提示以下错误信息: + +``` +Warning No default index pattern. You must select or create one to continue. +... +Unable to fetch mapping. Do you have indices matching the pattern? +``` + +这就说明 logstash 没有把日志写入到 elasticsearch。 + +**解决方法:** + +检查 logstash 与 elasticsearch 之间的通讯是否有问题,一般问题就出在这。 + +## 4. 参考资料 + +- [Kibana 官网](https://www.elastic.co/cn/products/kibana) +- [Kibana Github](https://github.com/elastic/kibana) +- [Kibana 官方文档](https://www.elastic.co/guide/en/kibana/current/index.html) diff --git a/docs/javatool/elastic/elastic-logstash-ops.md b/docs/javatool/elastic/elastic-logstash-ops.md new file mode 100644 index 00000000..14bf17cb --- /dev/null +++ b/docs/javatool/elastic/elastic-logstash-ops.md @@ -0,0 +1,495 @@ +# Logstash 运维 + +> [Logstash](https://github.com/elastic/logstash) 是开源的服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到您最喜欢的“存储库”中。 + +## 1. 安装 + +### 1.1. 安装步骤 + +安装步骤如下: + +1. 在 [logstash 官方下载地址](https://www.elastic.co/downloads/logstash)下载所需版本包并解压到本地。 + +2. 添加一个 `logstash.conf` 文件,指定要使用的插件以及每个插件的设置。举个简单的例子: + +``` +input { stdin { } } +output { + elasticsearch { hosts => ["localhost:9200"] } + stdout { codec => rubydebug } +} +``` + +3. 运行 `bin/logstash -f logstash.conf` (Windows 上运行`bin/logstash.bat -f logstash.conf`) + +## 2. 配置 + +### 2.1. 设置文件 + +- **`logstash.yml`**:logstash 的默认启动配置文件 +- **`jvm.options`**:logstash 的 JVM 配置文件。 +- **`startup.options`** (Linux):包含系统安装脚本在 `/usr/share/logstash/bin` 中使用的选项为您的系统构建适当的启动脚本。安装 Logstash 软件包时,系统安装脚本将在安装过程结束时执行,并使用 `startup.options` 中指定的设置来设置用户,组,服务名称和服务描述等选项。 + +### 2.2. logstash.yml 设置项 + +节选部分设置项,更多项请参考:https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html + +| 参数 | 描述 | 默认值 | +| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `node.name` | 节点名 | 机器的主机名 | +| `path.data` | Logstash 及其插件用于任何持久性需求的目录。 | `LOGSTASH_HOME/data` | +| `pipeline.workers` | 同时执行管道的过滤器和输出阶段的工作任务数量。如果发现事件正在备份,或 CPU 未饱和,请考虑增加此数字以更好地利用机器处理能力。 | Number of the host’s CPU cores | +| `pipeline.batch.size` | 尝试执行过滤器和输出之前,单个工作线程从输入收集的最大事件数量。较大的批量处理大小一般来说效率更高,但是以增加的内存开销为代价。您可能必须通过设置 `LS_HEAP_SIZE` 变量来有效使用该选项来增加 JVM 堆大小。 | `125` | +| `pipeline.batch.delay` | 创建管道事件批处理时,在将一个尺寸过小的批次发送给管道工作任务之前,等待每个事件需要多长时间(毫秒)。 | `5` | +| `pipeline.unsafe_shutdown` | 如果设置为 true,则即使在内存中仍存在 inflight 事件时,也会强制 Logstash 在关闭期间退出。默认情况下,Logstash 将拒绝退出,直到所有接收到的事件都被推送到输出。启用此选项可能会导致关机期间数据丢失。 | `false` | +| `path.config` | 主管道的 Logstash 配置路径。如果您指定一个目录或通配符,配置文件将按字母顺序从目录中读取。 | Platform-specific. See [[dir-layout\]](https://github.com/elastic/logstash/blob/6.1/docs/static/settings-file.asciidoc#dir-layout). | +| `config.string` | 包含用于主管道的管道配置的字符串。使用与配置文件相同的语法。 | None | +| `config.test_and_exit` | 设置为 true 时,检查配置是否有效,然后退出。请注意,使用此设置不会检查 grok 模式的正确性。 Logstash 可以从目录中读取多个配置文件。如果将此设置与 log.level:debug 结合使用,则 Logstash 将记录组合的配置文件,并注掉其源文件的配置块。 | `false` | +| `config.reload.automatic` | 设置为 true 时,定期检查配置是否已更改,并在配置更改时重新加载配置。这也可以通过 SIGHUP 信号手动触发。 | `false` | +| `config.reload.interval` | Logstash 检查配置文件更改的时间间隔。 | `3s` | +| `config.debug` | 设置为 true 时,将完全编译的配置显示为调试日志消息。您还必须设置`log.level:debug`。警告:日志消息将包括任何传递给插件配置作为明文的“密码”选项,并可能导致明文密码出现在您的日志! | `false` | +| `config.support_escapes` | 当设置为 true 时,带引号的字符串将处理转义字符。 | `false` | +| `modules` | 配置时,模块必须处于上表所述的嵌套 YAML 结构中。 | None | +| `http.host` | 绑定地址 | `"127.0.0.1"` | +| `http.port` | 绑定端口 | `9600` | +| `log.level` | 日志级别。有效选项:fatal > error > warn > info > debug > trace | `info` | +| `log.format` | 日志格式。json (JSON 格式)或 plain (原对象) | `plain` | +| `path.logs` | Logstash 自身日志的存储路径 | `LOGSTASH_HOME/logs` | +| `path.plugins` | 在哪里可以找到自定义的插件。您可以多次指定此设置以包含多个路径。 | | + +## 3. 启动 + +### 3.1. 命令行 + +通过命令行启动 logstash 的方式如下: + +``` +bin/logstash [options] +``` + +其中 [options] 是您可以指定用于控制 Logstash 执行的命令行标志。 + +在命令行上设置的任何标志都会覆盖 Logstash 设置文件(`logstash.yml`)中的相应设置,但设置文件本身不会更改。 + +> **注** +> +> 虽然可以通过指定命令行参数的方式,来控制 logstash 的运行方式,但显然这么做很麻烦。 +> +> 建议通过指定配置文件的方式,来控制 logstash 运行,启动命令如下: +> +> ``` +> bin/logstash -f logstash.conf +> ``` +> +> 若想了解更多的命令行参数细节,请参考:https://www.elastic.co/guide/en/logstash/current/running-logstash-command-line.html + +### 3.2. 配置文件 + +上节,我们了解到,logstash 可以执行 `bin/logstash -f logstash.conf` ,按照配置文件中的参数去覆盖默认设置文件(`logstash.yml`)中的设置。 + +这节,我们就来学习一下这个配置文件如何配置参数。 + +#### 3.2.1. 配置文件结构 + +在工作原理一节中,我们已经知道了 Logstash 主要有三个工作阶段 input 、filter、output。而 logstash 配置文件文件结构也与之相对应: + +``` +input {} + +filter {} + +output {} +``` + +> 每个部分都包含一个或多个插件的配置选项。如果指定了多个过滤器,则会按照它们在配置文件中的显示顺序应用它们。 + +#### 3.2.2. 插件配置 + +插件的配置由插件名称和插件的一个设置块组成。 + +下面的例子中配置了两个输入文件配置: + +``` +input { + file { + path => "/var/log/messages" + type => "syslog" + } + + file { + path => "/var/log/apache/access.log" + type => "apache" + } +} +``` + +您可以配置的设置因插件类型而异。你可以参考: [Input Plugins](https://www.elastic.co/guide/en/logstash/current/input-plugins.html), [Output Plugins](https://www.elastic.co/guide/en/logstash/current/output-plugins.html), [Filter Plugins](https://www.elastic.co/guide/en/logstash/current/filter-plugins.html), 和 [Codec Plugins](https://www.elastic.co/guide/en/logstash/current/codec-plugins.html) 。 + +#### 3.2.3. 值类型 + +一个插件可以要求设置的值是一个特定的类型,比如布尔值,列表或哈希值。以下值类型受支持。 + +- Array + +``` + users => [ {id => 1, name => bob}, {id => 2, name => jane} ] +``` + +- Lists + +``` + path => [ "/var/log/messages", "/var/log/*.log" ] + uris => [ "http://elastic.co", "http://example.net" ] +``` + +- Boolean + +``` + ssl_enable => true +``` + +- Bytes + +``` + my_bytes => "1113" # 1113 bytes + my_bytes => "10MiB" # 10485760 bytes + my_bytes => "100kib" # 102400 bytes + my_bytes => "180 mb" # 180000000 bytes +``` + +- Codec + +``` + codec => "json" +``` + +- Hash + +``` +match => { + "field1" => "value1" + "field2" => "value2" + ... +} +``` + +- Number + +``` + port => 33 +``` + +- Password + +``` + my_password => "password" +``` + +- URI + +``` + my_uri => "http://foo:bar@example.net" +``` + +- Path + +``` + my_path => "/tmp/logstash" +``` + +- String + +* 转义字符 + +## 4. 插件 + +### 4.1. input + +> Logstash 支持各种输入选择 ,可以在同一时间从众多常用来源捕捉事件。能够以连续的流式传输方式,轻松地从您的日志、指标、Web 应用、数据存储以及各种 AWS 服务采集数据。 + +#### 4.1.1. 常用 input 插件 + +- **file**:从文件系统上的文件读取,就像 UNIX 命令 `tail -0F` 一样 +- **syslog:**在众所周知的端口 514 上侦听系统日志消息,并根据 RFC3164 格式进行解析 +- **redis:**从 redis 服务器读取,使用 redis 通道和 redis 列表。 Redis 经常用作集中式 Logstash 安装中的“代理”,它将来自远程 Logstash“托运人”的 Logstash 事件排队。 +- **beats:**处理由 Filebeat 发送的事件。 + +更多详情请见:[Input Plugins](https://www.elastic.co/guide/en/logstash/current/input-plugins.html) + +### 4.2. filter + +> 过滤器是 Logstash 管道中的中间处理设备。如果符合特定条件,您可以将条件过滤器组合在一起,对事件执行操作。 + +#### 4.2.1. 常用 filter 插件 + +- **grok:**解析和结构任意文本。 Grok 目前是 Logstash 中将非结构化日志数据解析为结构化和可查询的最佳方法。 +- **mutate:**对事件字段执行一般转换。您可以重命名,删除,替换和修改事件中的字段。 + +- **drop:**完全放弃一个事件,例如调试事件。 + +- **clone:**制作一个事件的副本,可能会添加或删除字段。 + +- **geoip:**添加有关 IP 地址的地理位置的信息(也可以在 Kibana 中显示惊人的图表!) + +更多详情请见:[Filter Plugins](https://www.elastic.co/guide/en/logstash/current/filter-plugins.html) + +### 4.3. output + +> 输出是 Logstash 管道的最后阶段。一个事件可以通过多个输出,但是一旦所有输出处理完成,事件就完成了执行。 + +#### 4.3.1. 常用 output 插件 + +- **elasticsearch:**将事件数据发送给 Elasticsearch(推荐模式)。 +- **file:**将事件数据写入文件或磁盘。 +- **graphite:**将事件数据发送给 graphite(一个流行的开源工具,存储和绘制指标。 http://graphite.readthedocs.io/en/latest/)。 +- **statsd:**将事件数据发送到 statsd (这是一种侦听统计数据的服务,如计数器和定时器,通过 UDP 发送并将聚合发送到一个或多个可插入的后端服务)。 + +更多详情请见:[Output Plugins](https://www.elastic.co/guide/en/logstash/current/output-plugins.html) + +### 4.4. codec + +用于格式化对应的内容。 + +#### 4.4.1. 常用 codec 插件 + +- **json:**以 JSON 格式对数据进行编码或解码。 +- **multiline:**将多行文本事件(如 java 异常和堆栈跟踪消息)合并为单个事件。 + +更多插件请见:[Codec Plugins](https://www.elastic.co/guide/en/logstash/current/codec-plugins.html) + +## 5. 实战 + +前面的内容都是对 Logstash 的介绍和原理说明。接下来,我们来实战一些常见的应用场景。 + +### 5.1. 传输控制台数据 + +> stdin input 插件从标准输入读取事件。这是最简单的 input 插件,一般用于测试场景。 + +**应用** + +(1)创建 `logstash-input-stdin.conf` : + +``` +input { stdin { } } +output { + elasticsearch { hosts => ["localhost:9200"] } + stdout { codec => rubydebug } +} +``` + +更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-stdin.html + +(2)执行 logstash,使用 `-f` 来指定你的配置文件: + +``` +bin/logstash -f logstash-input-stdin.conf +``` + +### 5.2. 传输 logback 日志 + +> elk 默认使用的 Java 日志工具是 log4j2 ,并不支持 logback 和 log4j。 +> +> 想使用 logback + logstash ,可以使用 [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) 。[logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) 提供了 UDP / TCP / 异步方式来传输日志数据到 logstash。 +> +> 如果你使用的是 log4j ,也不是不可以用这种方式,只要引入桥接 jar 包即可。如果你对 log4j 、logback ,或是桥接 jar 包不太了解,可以参考我的这篇博文:[细说 Java 主流日志工具库](https://github.com/dunwu/JavaStack/blob/master/docs/javalib/java-log.md) 。 + +#### 5.2.1. TCP 应用 + +1. logstash 配置 + + (1)创建 `logstash-input-tcp.conf` : + +``` +input { + # stdin { } + tcp { + # host:port就是上面appender中的 destination, + # 这里其实把logstash作为服务,开启9250端口接收logback发出的消息 + host => "127.0.0.1" port => 9250 mode => "server" tags => ["tags"] codec => json_lines + } +} +output { + elasticsearch { hosts => ["localhost:9200"] } + stdout { codec => rubydebug } +} +``` + +更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-tcp.html + +(2)执行 logstash,使用 `-f` 来指定你的配置文件:`bin/logstash -f logstash-input-udp.conf` + +2. java 应用配置 + + (1)在 Java 应用的 pom.xml 中引入 jar 包: + +```xml + + net.logstash.logback + logstash-logback-encoder + 4.11 + + + + + ch.qos.logback + logback-core + 1.2.3 + + + ch.qos.logback + logback-classic + 1.2.3 + + + ch.qos.logback + logback-access + 1.2.3 + +``` + +(2)接着,在 logback.xml 中添加 appender + +```xml + + + 192.168.28.32:9251 + + + + + +``` + +大功告成,此后,`io.github.dunwu.spring` 包中的 TRACE 及以上级别的日志信息都会被定向输出到 logstash 服务。 + +

    + +接下来,就是 logback 的具体使用 ,如果对此不了解,不妨参考一下我的这篇博文:[细说 Java 主流日志工具库](https://github.com/dunwu/JavaStack/blob/master/docs/javalib/java-log.md) 。 + +**实例:**[我的 logback.xml](https://github.com/dunwu/JavaStack/blob/master/codes/javatool/src/main/resources/logback.xml) + +#### 5.2.2. UDP 应用 + +UDP 和 TCP 的使用方式大同小异。 + +1. logstash 配置 + + (1)创建 `logstash-input-udp.conf` : + +``` +input { +udp { + port => 9250 + codec => json +} +} +output { + elasticsearch { hosts => ["localhost:9200"] } + stdout { codec => rubydebug } +} +``` + +更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-udp.html + +(2)执行 logstash,使用 `-f` 来指定你的配置文件:`bin/logstash -f logstash-input-udp.conf` + +2. java 应用配置 + + (1)在 Java 应用的 pom.xml 中引入 jar 包: + + 与 **TCP 应用** 一节中的引入依赖包完全相同。 + + (2)接着,在 logback.xml 中添加 appender + +```xml + + 192.168.28.32 + 9250 + + + + +``` + +(3)接下来,就是 logback 的具体使用 ,如果对此不了解,不妨参考一下我的这篇博文:[细说 Java 主流日志工具库](https://github.com/dunwu/JavaStack/blob/master/docs/javalib/java-log.md) 。 + +**实例:**[我的 logback.xml](https://github.com/dunwu/JavaStack/blob/master/codes/javatool/src/main/resources/logback.xml) + +### 5.3. 传输文件 + +> 在 Java Web 领域,需要用到一些重要的工具,例如 Tomcat 、Nginx 、Mysql 等。这些不属于业务应用,但是它们的日志数据对于定位问题、分析统计同样很重要。这时无法使用 logback 方式将它们的日志传输到 logstash。 +> +> 如何采集这些日志文件呢?别急,你可以使用 logstash 的 file input 插件。 +> +> 需要注意的是,传输文件这种方式,必须在日志所在的机器上部署 logstash 。 + +**应用** + +logstash 配置 + +(1)创建 `logstash-input-file.conf` : + +``` +input { + file { + path => ["/var/log/nginx/access.log"] + type => "nginx-access-log" + start_position => "beginning" + } +} + +output { + if [type] == "nginx-access-log" { + elasticsearch { + hosts => ["localhost:9200"] + index => "nginx-access-log" + } + } +} +``` + +(2)执行 logstash,使用 `-f` 来指定你的配置文件:`bin/logstash -f logstash-input-file.conf` + +更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-file.html + +## 6. 小技巧 + +### 6.1. 启动、终止应用 + +如果你的 logstash 每次都是通过指定配置文件方式启动。不妨建立一个启动脚本。 + +``` +# cd xxx 进入 logstash 安装目录下的 bin 目录 +logstash -f logstash.conf +``` + +如果你的 logstash 运行在 linux 系统下,不妨使用 nohup 来启动一个守护进程。这样做的好处在于,即使关闭终端,应用仍会运行。 + +**创建 startup.sh** + +``` +nohup ./logstash -f logstash.conf >> nohup.out 2>&1 & +``` + +终止应用没有什么好方法,你只能使用 ps -ef | grep logstash ,查出进程,将其 kill 。不过,我们可以写一个脚本来干这件事: + +**创建 shutdown.sh** + +脚本不多解释,请自行领会作用。 + +``` +PID=`ps -ef | grep logstash | awk '{ print $2}' | head -n 1` +kill -9 ${PID} +``` + +## 7. 参考资料 + +- [Logstash 官网](https://www.elastic.co/cn/products/logstash) +- [Logstash Github](https://github.com/elastic/logstash) +- [Logstash 官方文档](https://www.elastic.co/guide/en/logstash/current/index.html) +- [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) +- [ELK Stack 权威指南](https://github.com/chenryn/logstash-best-practice-cn) +- [ELK(Elasticsearch、Logstash、Kibana)安装和配置](https://github.com/judasn/Linux-Tutorial/blob/master/ELK-Install-And-Settings.md) diff --git a/docs/javatool/elastic/elastic-quickstart.md b/docs/javatool/elastic/elastic-quickstart.md new file mode 100644 index 00000000..be6effad --- /dev/null +++ b/docs/javatool/elastic/elastic-quickstart.md @@ -0,0 +1,276 @@ +# Elastic 快速入门 + +> 开源协议:[Apache 2.0](https://github.com/elastic/elasticsearch/tree/7.4/licenses/APACHE-LICENSE-2.0.txt) + +## 1. 简介 + +### 1.1. Elastic Stack 是什么 + +**Elastic Stack** 即 **ELK Stack**。 + +ELK 是指 Elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/cn/products/elasticsearch) 、[Logstash](https://www.elastic.co/cn/products/logstash) 、[Kibana](https://www.elastic.co/cn/products/kibana) 的首字母组合。 + +- Elasticsearch 是一个搜索和分析引擎。 +- Logstash 是服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如 Elasticsearch 等“存储库”中。 +- Kibana 则可以让用户在 Elasticsearch 中使用图形和图表对数据进行可视化。 + +而 Elastic Stack 是 ELK Stack 的更新换代产品,最新产品引入了轻量型的单一功能数据采集器,并把它们叫做 [Beats](https://www.elastic.co/cn/products/beats)。 + +### 1.2. 为什么使用 Elastic Stack + +对于有一定规模的公司来说,通常会很多个应用,并部署在大量的服务器上。运维和开发人员常常需要通过查看日志来定位问题。如果应用是集群化部署,试想如果登录一台台服务器去查看日志,是多么费时费力。 + +而通过 ELK 这套解决方案,可以同时实现日志收集、日志搜索和日志分析的功能。 + +### 1.3. Elastic Stack 架构 + +

    + +> **说明** +> +> 以上是 Elastic Stack 的一个架构图。从图中可以清楚的看到数据流向。 +> +> - [Beats](https://www.elastic.co/products/beats) 是单一用途的数据传输平台,它可以将多台机器的数据发送到 Logstash 或 ElasticSearch。但 Beats 并不是不可或缺的一环,所以本文中暂不介绍。 +> - [Logstash](https://www.elastic.co/products/logstash) 是一个动态数据收集管道。支持以 TCP/UDP/HTTP 多种方式收集数据(也可以接受 Beats 传输来的数据),并对数据做进一步丰富或提取字段处理。 +> - [ElasticSearch](https://www.elastic.co/products/elasticsearch) 是一个基于 JSON 的分布式的搜索和分析引擎。作为 ELK 的核心,它集中存储数据。 +> +> - [Kibana](https://www.elastic.co/products/kibana) 是 ELK 的用户界面。它将收集的数据进行可视化展示(各种报表、图形化数据),并提供配置、管理 ELK 的界面。 + +## 2. ElasticSearch + +> [Elasticsearch](https://github.com/elastic/elasticsearch) 是一个分布式、RESTful 风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。 作为 Elastic Stack 的核心,它集中存储您的数据,帮助您发现意料之中以及意料之外的情况。 + +### 2.1. ElasticSearch 简介 + +[Elasticsearch](https://github.com/elastic/elasticsearch) 基于搜索库 [Lucene](https://github.com/apache/lucene-solr) 开发。ElasticSearch 隐藏了 Lucene 的复杂性,提供了简单易用的 REST API / Java API 接口(另外还有其他语言的 API 接口)。 + +ElasticSearch 可以视为一个文档存储,它**将复杂数据结构序列化为 JSON 存储**。 + +**ElasticSearch 是近乎于实时的全文搜素**,这是指: + +- 从写入数据到数据可以被搜索,存在较小的延迟(大概是 1s) +- 基于 ES 执行搜索和分析可以达到秒级 + +#### 2.1.1. 核心概念 + +- **`索引(Index)`** 可以认为是文档(document)的优化集合。 +- 每个 **`文档(document)`** 都是字段(field)的集合。 +- **`字段(field)`** 是包含数据的键值对。 +- 默认情况下,Elasticsearch 对每个字段中的所有数据建立索引,并且每个索引字段都具有专用的优化数据结构。 +- 每个索引里可以有一个或者多个类型(type)。`类型(type)` 是 index 的一个逻辑分类, +- 当单台机器不足以存储大量数据时,Elasticsearch 可以将一个索引中的数据切分为多个 **`分片(shard)`** 。 **`分片(shard)`** 分布在多台服务器上存储。有了 shard 就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。每个 shard 都是一个 lucene index。 +- 任何一个服务器随时可能故障或宕机,此时 shard 可能就会丢失,因此可以为每个 shard 创建多个 **`副本(replica)`**。replica 可以在 shard 故障时提供备用服务,保证数据不丢失,多个 replica 还可以提升搜索操作的吞吐量和性能。primary shard(建立索引时一次设置,不能修改,默认 5 个),replica shard(随时修改数量,默认 1 个),默认每个索引 10 个 shard,5 个 primary shard,5 个 replica shard,最小的高可用配置,是 2 台服务器。 + +### 2.2. ElasticSearch 原理 + +#### 2.2.1. ES 写数据过程 + +- 客户端选择一个 node 发送请求过去,这个 node 就是 `coordinating node`(协调节点)。 +- `coordinating node` 对 document 进行**路由**,将请求转发给对应的 node(有 primary shard)。 +- 实际的 node 上的 `primary shard` 处理请求,然后将数据同步到 `replica node`。 +- `coordinating node` 如果发现 `primary node` 和所有 `replica node` 都搞定之后,就返回响应结果给客户端。 + +![es-write](https://github.com/doocs/advanced-java/raw/master/images/es-write.png) + +#### 2.2.2. es 读数据过程 + +可以通过 `doc id` 来查询,会根据 `doc id` 进行 hash,判断出来当时把 `doc id` 分配到了哪个 shard 上面去,从那个 shard 去查询。 + +- 客户端发送请求到**任意**一个 node,成为 `coordinate node`。 +- `coordinate node` 对 `doc id` 进行哈希路由,将请求转发到对应的 node,此时会使用 `round-robin` **随机轮询算法**,在 `primary shard` 以及其所有 replica 中随机选择一个,让读请求负载均衡。 +- 接收请求的 node 返回 document 给 `coordinate node`。 +- `coordinate node` 返回 document 给客户端。 + +#### 2.2.3. 写数据底层原理 + +![es-write-detail](https://github.com/doocs/advanced-java/raw/master/images/es-write-detail.png) + +先写入内存 buffer,在 buffer 里的时候数据是搜索不到的;同时将数据写入 translog 日志文件。 + +如果 buffer 快满了,或者到一定时间,就会将内存 buffer 数据 `refresh` 到一个新的 `segment file` 中,但是此时数据不是直接进入 `segment file` 磁盘文件,而是先进入 `os cache` 。这个过程就是 `refresh`。 + +每隔 1 秒钟,es 将 buffer 中的数据写入一个**新的** `segment file`,每秒钟会产生一个**新的磁盘文件** `segment file`,这个 `segment file` 中就存储最近 1 秒内 buffer 中写入的数据。 + +但是如果 buffer 里面此时没有数据,那当然不会执行 refresh 操作,如果 buffer 里面有数据,默认 1 秒钟执行一次 refresh 操作,刷入一个新的 segment file 中。 + +操作系统里面,磁盘文件其实都有一个东西,叫做 `os cache`,即操作系统缓存,就是说数据写入磁盘文件之前,会先进入 `os cache`,先进入操作系统级别的一个内存缓存中去。只要 `buffer` 中的数据被 refresh 操作刷入 `os cache`中,这个数据就可以被搜索到了。 + +为什么叫 es 是**准实时**的? `NRT`,全称 `near real-time`。默认是每隔 1 秒 refresh 一次的,所以 es 是准实时的,因为写入的数据 1 秒之后才能被看到。可以通过 es 的 `restful api` 或者 `java api`,**手动**执行一次 refresh 操作,就是手动将 buffer 中的数据刷入 `os cache`中,让数据立马就可以被搜索到。只要数据被输入 `os cache` 中,buffer 就会被清空了,因为不需要保留 buffer 了,数据在 translog 里面已经持久化到磁盘去一份了。 + +重复上面的步骤,新的数据不断进入 buffer 和 translog,不断将 `buffer` 数据写入一个又一个新的 `segment file` 中去,每次 `refresh` 完 buffer 清空,translog 保留。随着这个过程推进,translog 会变得越来越大。当 translog 达到一定长度的时候,就会触发 `commit` 操作。 + +commit 操作发生第一步,就是将 buffer 中现有数据 `refresh` 到 `os cache` 中去,清空 buffer。然后,将一个 `commit point` 写入磁盘文件,里面标识着这个 `commit point` 对应的所有 `segment file`,同时强行将 `os cache` 中目前所有的数据都 `fsync` 到磁盘文件中去。最后**清空** 现有 translog 日志文件,重启一个 translog,此时 commit 操作完成。 + +这个 commit 操作叫做 `flush`。默认 30 分钟自动执行一次 `flush`,但如果 translog 过大,也会触发 `flush`。flush 操作就对应着 commit 的全过程,我们可以通过 es api,手动执行 flush 操作,手动将 os cache 中的数据 fsync 强刷到磁盘上去。 + +translog 日志文件的作用是什么?你执行 commit 操作之前,数据要么是停留在 buffer 中,要么是停留在 os cache 中,无论是 buffer 还是 os cache 都是内存,一旦这台机器死了,内存中的数据就全丢了。所以需要将数据对应的操作写入一个专门的日志文件 `translog` 中,一旦此时机器宕机,再次重启的时候,es 会自动读取 translog 日志文件中的数据,恢复到内存 buffer 和 os cache 中去。 + +translog 其实也是先写入 os cache 的,默认每隔 5 秒刷一次到磁盘中去,所以默认情况下,可能有 5 秒的数据会仅仅停留在 buffer 或者 translog 文件的 os cache 中,如果此时机器挂了,会**丢失** 5 秒钟的数据。但是这样性能比较好,最多丢 5 秒的数据。也可以将 translog 设置成每次写操作必须是直接 `fsync` 到磁盘,但是性能会差很多。 + +实际上你在这里,如果面试官没有问你 es 丢数据的问题,你可以在这里给面试官炫一把,你说,其实 es 第一是准实时的,数据写入 1 秒后可以搜索到;可能会丢失数据的。有 5 秒的数据,停留在 buffer、translog os cache、segment file os cache 中,而不在磁盘上,此时如果宕机,会导致 5 秒的**数据丢失**。 + +**总结一下**,数据先写入内存 buffer,然后每隔 1s,将数据 refresh 到 os cache,到了 os cache 数据就能被搜索到(所以我们才说 es 从写入到能被搜索到,中间有 1s 的延迟)。每隔 5s,将数据写入 translog 文件(这样如果机器宕机,内存数据全没,最多会有 5s 的数据丢失),translog 大到一定程度,或者默认每隔 30mins,会触发 commit 操作,将缓冲区的数据都 flush 到 segment file 磁盘文件中。 + +> 数据写入 segment file 之后,同时就建立好了倒排索引。 + +#### 2.2.4. 删除/更新数据底层原理 + +如果是删除操作,commit 的时候会生成一个 `.del` 文件,里面将某个 doc 标识为 `deleted` 状态,那么搜索的时候根据 `.del` 文件就知道这个 doc 是否被删除了。 + +如果是更新操作,就是将原来的 doc 标识为 `deleted` 状态,然后新写入一条数据。 + +buffer 每 refresh 一次,就会产生一个 `segment file`,所以默认情况下是 1 秒钟一个 `segment file`,这样下来 `segment file` 会越来越多,此时会定期执行 merge。每次 merge 的时候,会将多个 `segment file` 合并成一个,同时这里会将标识为 `deleted` 的 doc 给**物理删除掉**,然后将新的 `segment file` 写入磁盘,这里会写一个 `commit point`,标识所有新的 `segment file`,然后打开 `segment file` 供搜索使用,同时删除旧的 `segment file`。 + +#### 2.2.5. 底层 lucene + +简单来说,lucene 就是一个 jar 包,里面包含了封装好的各种建立倒排索引的算法代码。我们用 Java 开发的时候,引入 lucene jar,然后基于 lucene 的 api 去开发就可以了。 + +通过 lucene,我们可以将已有的数据建立索引,lucene 会在本地磁盘上面,给我们组织索引的数据结构。 + +#### 2.2.6. 倒排索引 + +在搜索引擎中,每个文档都有一个对应的文档 ID,文档内容被表示为一系列关键词的集合。例如,文档 1 经过分词,提取了 20 个关键词,每个关键词都会记录它在文档中出现的次数和出现位置。 + +那么,倒排索引就是**关键词到文档** ID 的映射,每个关键词都对应着一系列的文件,这些文件中都出现了关键词。 + +举个栗子。 + +有以下文档: + +| DocId | Doc | +| ----- | ---------------------------------------------- | +| 1 | 谷歌地图之父跳槽 Facebook | +| 2 | 谷歌地图之父加盟 Facebook | +| 3 | 谷歌地图创始人拉斯离开谷歌加盟 Facebook | +| 4 | 谷歌地图之父跳槽 Facebook 与 Wave 项目取消有关 | +| 5 | 谷歌地图之父拉斯加盟社交网站 Facebook | + +对文档进行分词之后,得到以下**倒排索引**。 + +| WordId | Word | DocIds | +| ------ | -------- | --------- | +| 1 | 谷歌 | 1,2,3,4,5 | +| 2 | 地图 | 1,2,3,4,5 | +| 3 | 之父 | 1,2,4,5 | +| 4 | 跳槽 | 1,4 | +| 5 | Facebook | 1,2,3,4,5 | +| 6 | 加盟 | 2,3,5 | +| 7 | 创始人 | 3 | +| 8 | 拉斯 | 3,5 | +| 9 | 离开 | 3 | +| 10 | 与 | 4 | +| .. | .. | .. | + +另外,实用的倒排索引还可以记录更多的信息,比如文档频率信息,表示在文档集合中有多少个文档包含某个单词。 + +那么,有了倒排索引,搜索引擎可以很方便地响应用户的查询。比如用户输入查询 `Facebook`,搜索系统查找倒排索引,从中读出包含这个单词的文档,这些文档就是提供给用户的搜索结果。 + +要注意倒排索引的两个重要细节: + +- 倒排索引中的所有词项对应一个或多个文档; +- 倒排索引中的词项**根据字典顺序升序排列** + +> 上面只是一个简单的栗子,并没有严格按照字典顺序升序排列。 + +## 3. Logstash + +> [Logstash](https://github.com/elastic/logstash) 是开源的服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到您最喜欢的“存储库”中。 + +### 3.1. Logstash 简介 + +Logstash 可以传输和处理你的日志、事务或其他数据。 + +Logstash 是 Elasticsearch 的最佳数据管道。 + +Logstash 是插件式管理模式,在输入、过滤、输出以及编码过程中都可以使用插件进行定制。Logstash 社区有超过 200 种可用插件。 + +### 3.2. Logstash 原理 + +Logstash 有两个必要元素:`input` 和 `output` ,一个可选元素:`filter`。 + +这三个元素,分别代表 Logstash 事件处理的三个阶段:输入 > 过滤器 > 输出。 + +

    + +- **input** - 负责从数据源采集数据。 +- **`filter`** - 将数据修改为你指定的格式或内容。 +- **`output`** - 将数据传输到目的地。 + +在实际应用场景中,通常输入、输出、过滤器不止一个。Logstash 的这三个元素都使用插件式管理方式,用户可以根据应用需要,灵活的选用各阶段需要的插件,并组合使用。 + +## 4. Beats + +> **[Beats](https://github.com/elastic/beats) 是安装在服务器上的数据中转代理**。 +> +> Beats 可以将数据直接传输到 Elasticsearch 或传输到 Logstash 。 + +

    + +Beats 有多种类型,可以根据实际应用需要选择合适的类型。 + +常用的类型有: + +- **Packetbeat:**网络数据包分析器,提供有关您的应用程序服务器之间交换的事务的信息。 +- **Filebeat:**从您的服务器发送日志文件。 +- **Metricbeat:**是一个服务器监视代理程序,它定期从服务器上运行的操作系统和服务收集指标。 +- **Winlogbeat:**提供 Windows 事件日志。 + +### 4.1. Filebeat 简介 + +> _由于本人仅接触过 Filebeat,所以本文只介绍 Beats 组件中的 Filebeat。_ + +相比 Logstash,FileBeat 更加轻量化。 + +在任何环境下,应用程序都有停机的可能性。 Filebeat 读取并转发日志行,如果中断,则会记住所有事件恢复联机状态时所在位置。 + +Filebeat 带有内部模块(auditd,Apache,Nginx,System 和 MySQL),可通过一个指定命令来简化通用日志格式的收集,解析和可视化。 + +FileBeat 不会让你的管道超负荷。FileBeat 如果是向 Logstash 传输数据,当 Logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。 + +

    + +### 4.2. Filebeat 原理 + +Filebeat 有两个主要组件: + +- `harvester`:负责读取一个文件的内容。它会逐行读取文件内容,并将内容发送到输出目的地。 +- `prospector`:负责管理 harvester 并找到所有需要读取的文件源。比如类型是日志,prospector 就会遍历制定路径下的所有匹配要求的文件。 + +```yaml +filebeat.prospectors: + - type: log + paths: + - /var/log/*.log + - /var/path2/*.log +``` + +Filebeat 保持每个文件的状态,并经常刷新注册表文件中的磁盘状态。状态用于记住 harvester 正在读取的最后偏移量,并确保发送所有日志行。 + +Filebeat 将每个事件的传递状态存储在注册表文件中。所以它能保证事件至少传递一次到配置的输出,没有数据丢失。 + +## 5. 运维 + +- [ElasticSearch 运维](elastic-elasticsearch-ops.md) +- [Logstash 运维](elastic-logstash-ops.md) +- [Kibana 运维](elastic-kibana-ops.md) +- [Beats 运维](elastic-beats-ops.md) + +## 6. 参考资料 + +- **官方资源** + - [Elasticsearch 官网](https://www.elastic.co/cn/products/elasticsearch) + - [Elasticsearch Github](https://github.com/elastic/elasticsearch) + - [Elasticsearch 官方文档](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) + - [Logstash 官网](https://www.elastic.co/cn/products/logstash) + - [Logstash Github](https://github.com/elastic/logstash) + - [Logstash 官方文档](https://www.elastic.co/guide/en/logstash/current/index.html) + - [Kibana 官网](https://www.elastic.co/cn/products/kibana) + - [Kibana Github](https://github.com/elastic/kibana) + - [Kibana 官方文档](https://www.elastic.co/guide/en/kibana/current/index.html) + - [Beats 官网](https://www.elastic.co/cn/products/beats) + - [Beats Github](https://github.com/elastic/beats) + - [Beats 官方文档](https://www.elastic.co/guide/en/beats/libbeat/current/index.html) +- **文章** + - [什么是 ELK Stack?](https://www.elastic.co/cn/what-is/elk-stack) + - https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/es-introduction.md + - [es-write-query-search](https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/es-write-query-search.md) diff --git a/docs/javatool/ide/README.md b/docs/javatool/ide/README.md new file mode 100644 index 00000000..87a90fa3 --- /dev/null +++ b/docs/javatool/ide/README.md @@ -0,0 +1,4 @@ +# Java IDE + +- [Intellij IDEA 应用指南](intellij.md) +- [Eclipse 应用指南](eclipse.md) diff --git a/docs/javatool/ide/eclipse.md b/docs/javatool/ide/eclipse.md index 569ec7f7..c0d8e740 100644 --- a/docs/javatool/ide/eclipse.md +++ b/docs/javatool/ide/eclipse.md @@ -1,4 +1,4 @@ -# Eclipse 使用指南 +# Eclipse 应用指南 diff --git a/docs/javatool/ide/intellij.md b/docs/javatool/ide/intellij.md index 06130126..15f09cf3 100644 --- a/docs/javatool/ide/intellij.md +++ b/docs/javatool/ide/intellij.md @@ -1,14 +1,4 @@ -# Intellij IDEA 使用指南 - - - -- [快捷键](#快捷键) -- [插件](#插件) -- [个性化](#个性化) -- [破解](#破解) -- [参考资料](#参考资料) - - +# Intellij IDEA 应用指南 ## 快捷键 @@ -16,7 +6,7 @@ IntelliJ IDEA 作为一个以快捷键为中心的 IDE,为大多数操作建议了键盘快捷键。在这个主题中,您可以找到最不可缺少的列表,使 IntelliJ IDEA 轻松实现第一步。 -**核心快捷键表** +核心快捷键表: | 操作 | 快捷键 | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | @@ -194,13 +184,13 @@ IntelliJ IDEA 作为一个以快捷键为中心的 IDE,为大多数操作建 #### VCS/Local History -| 快捷键 | 介绍 | -| ------------------------------------------------- | -------------------------------------------------- | -| Ctrl + K | 版本控制提交项目,需要此项目有加入到版本控制才可用 | -| Ctrl + T | 版本控制更新项目,需要此项目有加入到版本控制才可用 | -| Alt + `| 显示版本控制常用操作菜单弹出层`(必备)` | -| Alt + Shift + C | 查看最近操作项目的变化情况列表 | -| Alt + Shift + N | 选择/添加 task`(必备)` | +| 快捷键 | 介绍 | +| --------------- | -------------------------------------------------- | +| Ctrl + K | 版本控制提交项目,需要此项目有加入到版本控制才可用 | +| Ctrl + T | 版本控制更新项目,需要此项目有加入到版本控制才可用 | +| `Alt + |` | 显示版本控制常用操作菜单弹出层`(必备)` | +| Alt + Shift + C | 查看最近操作项目的变化情况列表 | +| Alt + Shift + N | 选择/添加 task`(必备)` | #### Live Templates @@ -230,7 +220,7 @@ IntelliJ IDEA 作为一个以快捷键为中心的 IDE,为大多数操作建 推荐几个比较好用的插件 -- [Key promoter](https://plugins.jetbrains.com/plugin/4455?pr=idea) 快捷键提示 +- [Key promoter](https://plugins.jetbrains.com/plugin/4455?pr=idea) [快捷键提示](https://plugins.jetbrains.com/plugin/4455?pr=idea) - [CamelCase](https://plugins.jetbrains.com/plugin/7160?pr=idea) 驼峰式命名和下划线命名交替变化 - [CheckStyle-IDEA](https://plugins.jetbrains.com/plugin/1065?pr=idea) 代码规范检查 - [FindBugs-IDEA](https://plugins.jetbrains.com/plugin/3847?pr=idea)潜在 Bug 检查 @@ -249,9 +239,7 @@ IntelliJ IDEA 作为一个以快捷键为中心的 IDE,为大多数操作建 [intellij-colors-solarized](https://github.com/jkaving/intellij-colors-solarized) 个人觉得这种色彩搭配十分优雅 -下载地址:https://github.com/altercation/solarized - -
    +[下载地址](https://github.com/altercation/solarized) ## 破解 @@ -277,6 +265,5 @@ Windows 版本是 exe 程序,将其设为开机自动启动即可,别告诉 ## 参考资料 -[IntelliJ-IDEA-Tutorial](https://github.com/judasn/IntelliJ-IDEA-Tutorial) - -[极客学院 - Intellij IDEA 使用教程](http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/) +- [IntelliJ-IDEA-Tutorial](https://github.com/judasn/IntelliJ-IDEA-Tutorial) +- [极客学院 - Intellij IDEA 使用教程](http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/) diff --git a/docs/javatool/ide/vscode.md b/docs/javatool/ide/vscode.md index f295a73c..3e0b7ac0 100644 --- a/docs/javatool/ide/vscode.md +++ b/docs/javatool/ide/vscode.md @@ -1,4 +1,4 @@ -# vscode 使用指南 +# vscode 应用指南 ## 快捷键 diff --git a/docs/package.json b/docs/package.json index b2d684d8..79e0a91a 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,7 +1,33 @@ { - "name": "java-tutorial", - "version": "1.0.0", - "scripts": { - "start": "docsify serve ./ --port 4000" - } + "name": "java-tutorial", + "author": "Zhang Peng", + "homepage": "http://dunwu.github.io/java-tutorial", + "repository": { + "type": "git", + "url": "git@github.com:dunwu/java-tutorial.git" + }, + "scripts": { + "start": "docsify serve ./ --port 4000", + "clean": "rimraf _book", + "install": "gitbook install", + "serve": "gitbook serve", + "build": "npm run clean & gitbook build", + "pdf": "gitbook pdf" + }, + "dependencies": { + "gitbook-plugin-advanced-emoji": "^0.2.2", + "gitbook-plugin-anchor-navigation-ex": "^1.0.10", + "gitbook-plugin-anchors": "^0.7.1", + "gitbook-plugin-edit-link": "^2.0.2", + "gitbook-plugin-expandable-chapters-small": "^0.1.7", + "gitbook-plugin-github": "^2.0.0", + "gitbook-plugin-search-plus": "0.0.11", + "gitbook-plugin-simple-page-toc": "^0.1.2", + "gitbook-plugin-splitter": "0.0.8", + "gitbook-plugin-tbfed-pagefooter": "0.0.1" + }, + "devDependencies": { + "gh-pages": "^2.1.1", + "rimraf": "^3.0.0" + } } diff --git a/docs/sidebar.md b/docs/sidebar.md index a7dd1365..f2828d6f 100644 --- a/docs/sidebar.md +++ b/docs/sidebar.md @@ -1,40 +1,45 @@ -## [JavaLib](javalib/README.md) +## 前言 -- [Dozer 使用指南](javalib/dozer.md) -- [Freemark 使用指南](javalib/freemark.md) -- [细说 Java 主流 JSON 库](javalib/javalib-json.md) -- [细说 Java 主流日志工具库](javalib/java-log.md) -- [细说 Java 主流工具包](javalib/java-util.md) -- [JavaMail 使用指南](javalib/javamail.md) -- [Jsoup 使用指南](javalib/jsoup.md) -- [JUnit5 使用指南](javalib/junit.md) -- [Lombok 使用指南](javalib/lombok.md) -- [Mockito 使用指南](javalib/mockito.md) -- [Reflections 使用指南](javalib/reflections.md) -- [Thumbnailator 使用指南](javalib/thumbnailator.md) -- [ZXing 使用指南](javalib/zxing.md) +- [说明](README.md) -## [JavaTool](javatool/README.md) +## Java 教程 -- [构建工具](javatool/build/README.md) - - Maven 教程 - - [Maven 快速指南](javatool/build/maven/maven-quickstart.md) - - [Maven 教程之 pom.xml 详解](javatool/build/maven/maven-pom.md) - - [Maven 教程之 settings.xml 详解](javatool/build/maven/maven-settings.md) - - [Maven 实战问题和最佳实践](javatool/build/maven/maven-action.md) - - [Maven 教程之发布 jar 到私服或中央仓库](javatool/build/maven/maven-deploy.md) - - [Maven 插件之代码检查](javatool/build/maven/plugins/maven-checkstyle.md) - - [Ant 简易教程](javatool/build/ant.md) -- Java IDE - - [Intellij IDEA 使用指南](javatool/ide/intellij.md) - - [Eclipse 使用指南](javatool/ide/eclipse.md) -- **测试工具** - - [Jmeter 应用指南](javatool/test/jmeter.md) - -## [JavaCore 教程](https://dunwu.github.io/javacore/) - -## [JavaWeb 教程](https://dunwu.github.io/javaweb/) - -## [Spring 教程](https://dunwu.github.io/spring-tutorial/) - -## [Spring Boot 教程](https://dunwu.github.io/spring-boot-tutorial/) +- [JavaLib](javalib/README.md) + - [Dozer 应用指南](javalib/dozer.md) + - [Freemark 应用指南](javalib/freemark.md) + - [细说 Java 主流 JSON 库](javalib/javalib-json.md) + - [细说 Java 主流日志工具库](javalib/javalib-log.md) + - [细说 Java 主流工具包](javalib/javalib-util.md) + - [JavaMail 应用指南](javalib/javamail.md) + - [Jsoup 应用指南](javalib/jsoup.md) + - [JUnit5 应用指南](javalib/junit.md) + - [Lombok 应用指南](javalib/lombok.md) + - [Mockito 应用指南](javalib/mockito.md) + - [Reflections 应用指南](javalib/reflections.md) + - [Thumbnailator 应用指南](javalib/thumbnailator.md) + - [ZXing 应用指南](javalib/zxing.md) +- [JavaTool](javatool/README.md) + - [构建工具](javatool/build/README.md) + - [Maven 教程 📚](javatool/build/maven/README.md) + - [Maven 快速指南](javatool/build/maven/maven-quickstart.md) + - [Maven 教程之 pom.xml 详解](javatool/build/maven/maven-pom.md) + - [Maven 教程之 settings.xml 详解](javatool/build/maven/maven-settings.md) + - [Maven 实战问题和最佳实践](javatool/build/maven/maven-action.md) + - [Maven 教程之发布 jar 到私服或中央仓库](javatool/build/maven/maven-deploy.md) + - [Maven 插件之代码检查](javatool/build/maven/maven-checkstyle-plugin.md) + - [Ant 简易教程](javatool/build/ant.md) + - [搜索引擎 Elastic](javatool/elastic/README.md) + - [Elastic 技术栈快速入门](javatool/elastic/elastic-quickstart.md) + - [Elasticsearch 运维](javatool/elastic/elastic-elasticsearch-ops.md) + - [Beats 运维](javatool/elastic/elastic-beats-ops.md) + - [Kibana 运维](javatool/elastic/elastic-kibana-ops.md) + - [Logstash 运维](javatool/elastic/elastic-logstash-ops.md) + - [Java IDE](javatool/ide/README.md) + - [Intellij IDEA 应用指南](javatool/ide/intellij.md) + - [Eclipse 应用指南](javatool/ide/eclipse.md) + - **测试工具** + - [Jmeter 应用指南](javatool/test/jmeter.md) +- [JavaCore 教程 📚](https://dunwu.github.io/javacore/) +- [JavaWeb 教程 📚](https://dunwu.github.io/javaweb/) +- [Spring 教程 📚](https://dunwu.github.io/spring-tutorial/) +- [Spring Boot 教程 📚](https://dunwu.github.io/spring-boot-tutorial/) diff --git a/javalib-bean/README.md b/javalib-bean/README.md index ead65cc0..3b9709fa 100644 --- a/javalib-bean/README.md +++ b/javalib-bean/README.md @@ -1,4 +1,4 @@ -# Lombok 使用指南 +# Lombok 应用指南 diff --git a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java index 052eb43c..683be9b5 100644 --- a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java @@ -15,7 +15,7 @@ * Kyro 序列化/反序列化示例 * * @author Zhang Peng - * @author Kryo 使用指南 + * @author Kryo 应用指南 * @since 2019-11-26 */ public class KryoDemo { From 4e9a5deee435855738a1022de341ce803915b490 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 10 Dec 2019 19:18:11 +0800 Subject: [PATCH 5/6] update docs --- docs/javalib/elastic/elastic-quickstart.md | 130 +++++++++ docs/javalib/jmh.md | 325 +++++++++++++++++++++ 2 files changed, 455 insertions(+) create mode 100644 docs/javalib/elastic/elastic-quickstart.md create mode 100644 docs/javalib/jmh.md diff --git a/docs/javalib/elastic/elastic-quickstart.md b/docs/javalib/elastic/elastic-quickstart.md new file mode 100644 index 00000000..708ca212 --- /dev/null +++ b/docs/javalib/elastic/elastic-quickstart.md @@ -0,0 +1,130 @@ +# Elastic 技术栈快速入门 + +> 开源协议:[Apache 2.0](https://github.com/elastic/elasticsearch/tree/7.4/licenses/APACHE-LICENSE-2.0.txt) + +## 1. 简介 + +### 1.1. Elastic Stack 是什么 + +**Elastic Stack** 即 **ELK Stack**。 + +ELK 是指 Elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/cn/products/elasticsearch) 、[Logstash](https://www.elastic.co/cn/products/logstash) 、[Kibana](https://www.elastic.co/cn/products/kibana) 的首字母组合。 + +- Elasticsearch 是一个搜索和分析引擎。 +- Logstash 是服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如 Elasticsearch 等“存储库”中。 +- Kibana 则可以让用户在 Elasticsearch 中使用图形和图表对数据进行可视化。 + +而 Elastic Stack 是 ELK Stack 的更新换代产品,最新产品引入了轻量型的单一功能数据采集器,并把它们叫做 [Beats](https://www.elastic.co/cn/products/beats)。 + +### 1.2. 为什么使用 Elastic Stack + +对于有一定规模的公司来说,通常会很多个应用,并部署在大量的服务器上。运维和开发人员常常需要通过查看日志来定位问题。如果应用是集群化部署,试想如果登录一台台服务器去查看日志,是多么费时费力。 + +而通过 ELK 这套解决方案,可以同时实现日志收集、日志搜索和日志分析的功能。 + +### 1.3. Elastic Stack 架构 + +

    + +> **说明** +> +> 以上是 Elastic Stack 的一个架构图。从图中可以清楚的看到数据流向。 +> +> - [Beats](https://www.elastic.co/products/beats) 是单一用途的数据传输平台,它可以将多台机器的数据发送到 Logstash 或 ElasticSearch。但 Beats 并不是不可或缺的一环,所以本文中暂不介绍。 +> - [Logstash](https://www.elastic.co/products/logstash) 是一个动态数据收集管道。支持以 TCP/UDP/HTTP 多种方式收集数据(也可以接受 Beats 传输来的数据),并对数据做进一步丰富或提取字段处理。 +> - [ElasticSearch](https://www.elastic.co/products/elasticsearch) 是一个基于 JSON 的分布式的搜索和分析引擎。作为 ELK 的核心,它集中存储数据。 +> +> - [Kibana](https://www.elastic.co/products/kibana) 是 ELK 的用户界面。它将收集的数据进行可视化展示(各种报表、图形化数据),并提供配置、管理 ELK 的界面。 + +## 2. Logstash + +> [Logstash](https://github.com/elastic/logstash) 是开源的服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到您最喜欢的“存储库”中。 + +### 2.1. Logstash 简介 + +Logstash 可以传输和处理你的日志、事务或其他数据。 + +Logstash 是 Elasticsearch 的最佳数据管道。 + +Logstash 是插件式管理模式,在输入、过滤、输出以及编码过程中都可以使用插件进行定制。Logstash 社区有超过 200 种可用插件。 + +### 2.2. Logstash 原理 + +Logstash 有两个必要元素:`input` 和 `output` ,一个可选元素:`filter`。 + +这三个元素,分别代表 Logstash 事件处理的三个阶段:输入 > 过滤器 > 输出。 + +

    + +- **input** - 负责从数据源采集数据。 +- **`filter`** - 将数据修改为你指定的格式或内容。 +- **`output`** - 将数据传输到目的地。 + +在实际应用场景中,通常输入、输出、过滤器不止一个。Logstash 的这三个元素都使用插件式管理方式,用户可以根据应用需要,灵活的选用各阶段需要的插件,并组合使用。 + +## 3. Beats + +> **[Beats](https://github.com/elastic/beats) 是安装在服务器上的数据中转代理**。 +> +> Beats 可以将数据直接传输到 Elasticsearch 或传输到 Logstash 。 + +

    + +Beats 有多种类型,可以根据实际应用需要选择合适的类型。 + +常用的类型有: + +- **Packetbeat:**网络数据包分析器,提供有关您的应用程序服务器之间交换的事务的信息。 +- **Filebeat:**从您的服务器发送日志文件。 +- **Metricbeat:**是一个服务器监视代理程序,它定期从服务器上运行的操作系统和服务收集指标。 +- **Winlogbeat:**提供 Windows 事件日志。 + +### 3.1. Filebeat 简介 + +> _由于本人仅接触过 Filebeat,所以本文只介绍 Beats 组件中的 Filebeat。_ + +相比 Logstash,FileBeat 更加轻量化。 + +在任何环境下,应用程序都有停机的可能性。 Filebeat 读取并转发日志行,如果中断,则会记住所有事件恢复联机状态时所在位置。 + +Filebeat 带有内部模块(auditd,Apache,Nginx,System 和 MySQL),可通过一个指定命令来简化通用日志格式的收集,解析和可视化。 + +FileBeat 不会让你的管道超负荷。FileBeat 如果是向 Logstash 传输数据,当 Logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。 + +

    + +### 3.2. Filebeat 原理 + +Filebeat 有两个主要组件: + +- `harvester`:负责读取一个文件的内容。它会逐行读取文件内容,并将内容发送到输出目的地。 +- `prospector`:负责管理 harvester 并找到所有需要读取的文件源。比如类型是日志,prospector 就会遍历制定路径下的所有匹配要求的文件。 + +```yaml +filebeat.prospectors: + - type: log + paths: + - /var/log/*.log + - /var/path2/*.log +``` + +Filebeat 保持每个文件的状态,并经常刷新注册表文件中的磁盘状态。状态用于记住 harvester 正在读取的最后偏移量,并确保发送所有日志行。 + +Filebeat 将每个事件的传递状态存储在注册表文件中。所以它能保证事件至少传递一次到配置的输出,没有数据丢失。 + +## 4. 运维 + +- [ElasticSearch 运维](elastic-elasticsearch-ops.md) +- [Logstash 运维](elastic-logstash-ops.md) +- [Kibana 运维](elastic-kibana-ops.md) +- [Beats 运维](elastic-beats-ops.md) + +## 5. 参考资料 + +- **官方** + - [Elastic 官方](https://www.elastic.co/guide/index.html) + - [Elasticsearch github](https://github.com/elastic/elasticsearch) + - [Logstash github](https://github.com/elastic/logstash) + - [Kibana github](https://github.com/elastic/kibana) +- **文章** + - [什么是 ELK Stack?](https://www.elastic.co/cn/what-is/elk-stack) diff --git a/docs/javalib/jmh.md b/docs/javalib/jmh.md new file mode 100644 index 00000000..36b23beb --- /dev/null +++ b/docs/javalib/jmh.md @@ -0,0 +1,325 @@ +# JMH 应用指南 + +## 简介 + +### 什么是 JMH + +[JMH](http://openjdk.java.net/projects/code-tools/jmh/) 即 Java Microbenchmark Harness,这是专门用于进行代码的微基准测试的一套工具 API。 + +JMH 由 OpenJDK/Oracle 里面那群开发了 Java 编译器的大牛们所开发 。何谓 Micro Benchmark 呢? 简单地说就是在 method 层面上的 benchmark,精度可以精确到 **微秒级**。 + +### 为什么需要 JMH + +#### 死码消除 + +所谓死码,是指注释的代码,不可达的代码块,可达但不被使用的代码等等 。 + +#### 常量折叠与常量传播 + +[常量折叠](https://zh.wikipedia.org/wiki/常數折疊#常數傳播) (Constant folding) 是一个在编译时期简化常数的一个过程,常数在表示式中仅仅代表一个简单的数值,就像是整数 `2`,若是一个变数从未被修改也可作为常数,或者直接将一个变数被明确地被标注为常数,例如下面的描述: + +### JMH 的注意点 + +- 测试前需要预热。 +- 防止无用代码进入测试方法中。 +- 并发测试。 +- 测试结果呈现。 + +### 应用场景 + +1. 当你已经找出了热点函数,而需要对热点函数进行进一步的优化时,就可以使用 JMH 对优化的效果进行定量的分析。 +2. 想定量地知道某个函数需要执行多长时间,以及执行时间和输入 n 的相关性 +3. 一个函数有两种不同实现(例如 JSON 序列化/反序列化有 Jackson 和 Gson 实现),不知道哪种实现性能更好 + +### JMH 概念 + +- `Iteration` - iteration 是 JMH 进行测试的最小单位,包含一组 invocations。 +- `Invocation` - 一次 benchmark 方法调用。 +- `Operation` - benchmark 方法中,被测量操作的执行。如果被测试的操作在 benchmark 方法中循环执行,可以使用`@OperationsPerInvocation`表明循环次数,使测试结果为单次 operation 的性能。 +- `Warmup` - 在实际进行 benchmark 前先进行预热。因为某个函数被调用多次之后,JIT 会对其进行编译,通过预热可以使测量结果更加接近真实情况。 + +## 快速入门 + +### 添加 maven 依赖 + +```xml + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + +``` + +### 测试代码 + +```java +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.*; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 3) +@Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS) +@Threads(8) +@Fork(2) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class StringBuilderBenchmark { + + @Benchmark + public void testStringAdd() { + String a = ""; + for (int i = 0; i < 10; i++) { + a += i; + } + // System.out.println(a); + } + + @Benchmark + public void testStringBuilderAdd() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + // System.out.println(sb.toString()); + } + + public static void main(String[] args) throws RunnerException { + Options options = new OptionsBuilder() + .include(StringBuilderBenchmark.class.getSimpleName()) + .output("d:/Benchmark.log") + .build(); + new Runner(options).run(); + } + +} +``` + +### 执行 JMH + +#### 命令行 + +(1)初始化 **benchmarking** 工程 + +``` +$ mvn archetype:generate \ + -DinteractiveMode=false \ + -DarchetypeGroupId=org.openjdk.jmh \ + -DarchetypeArtifactId=jmh-java-benchmark-archetype \ + -DgroupId=org.sample \ + -DartifactId=test \ + -Dversion=1.0 +``` + +(2)构建 benchmark + +``` +$ cd test/ +$ mvn clean install +``` + +(3)运行 benchmark + +``` +$ java -jar target/benchmarks.jar +``` + +#### 执行 main 方法 + +执行 main 方法,耐心等待测试结果,最终会生成一个测试报告,内容大致如下; + +``` +# JMH version: 1.22 +# VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13 +# VM invoker: C:\Program Files\Java\jdk1.8.0_181\jre\bin\java.exe +# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\lib\idea_rt.jar=58635:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\bin -Dfile.encoding=UTF-8 +# Warmup: 3 iterations, 10 s each +# Measurement: 10 iterations, 5 s each +# Timeout: 10 min per iteration +# Threads: 8 threads, will synchronize iterations +# Benchmark mode: Throughput, ops/time +# Benchmark: io.github.dunwu.javalib.jmh.StringBuilderBenchmark.testStringAdd + +# Run progress: 0.00% complete, ETA 00:05:20 +# Fork: 1 of 2 +# Warmup Iteration 1: 21803.050 ops/ms +# Warmup Iteration 2: 22501.860 ops/ms +# Warmup Iteration 3: 20953.944 ops/ms +Iteration 1: 21627.645 ops/ms +Iteration 2: 21215.269 ops/ms +Iteration 3: 20863.282 ops/ms +Iteration 4: 21617.715 ops/ms +Iteration 5: 21695.645 ops/ms +Iteration 6: 21886.784 ops/ms +Iteration 7: 21986.899 ops/ms +Iteration 8: 22389.540 ops/ms +Iteration 9: 22507.313 ops/ms +Iteration 10: 22124.133 ops/ms + +# Run progress: 25.00% complete, ETA 00:04:02 +# Fork: 2 of 2 +# Warmup Iteration 1: 22262.108 ops/ms +# Warmup Iteration 2: 21567.804 ops/ms +# Warmup Iteration 3: 21787.002 ops/ms +Iteration 1: 21598.970 ops/ms +Iteration 2: 22486.133 ops/ms +Iteration 3: 22157.834 ops/ms +Iteration 4: 22321.827 ops/ms +Iteration 5: 22477.063 ops/ms +Iteration 6: 22154.760 ops/ms +Iteration 7: 21561.095 ops/ms +Iteration 8: 22194.863 ops/ms +Iteration 9: 22493.844 ops/ms +Iteration 10: 22568.078 ops/ms + + +Result "io.github.dunwu.javalib.jmh.StringBuilderBenchmark.testStringAdd": + 21996.435 ±(99.9%) 412.955 ops/ms [Average] + (min, avg, max) = (20863.282, 21996.435, 22568.078), stdev = 475.560 + CI (99.9%): [21583.480, 22409.390] (assumes normal distribution) + + +# JMH version: 1.22 +# VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13 +# VM invoker: C:\Program Files\Java\jdk1.8.0_181\jre\bin\java.exe +# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\lib\idea_rt.jar=58635:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\bin -Dfile.encoding=UTF-8 +# Warmup: 3 iterations, 10 s each +# Measurement: 10 iterations, 5 s each +# Timeout: 10 min per iteration +# Threads: 8 threads, will synchronize iterations +# Benchmark mode: Throughput, ops/time +# Benchmark: io.github.dunwu.javalib.jmh.StringBuilderBenchmark.testStringBuilderAdd + +# Run progress: 50.00% complete, ETA 00:02:41 +# Fork: 1 of 2 +# Warmup Iteration 1: 241500.886 ops/ms +# Warmup Iteration 2: 134206.032 ops/ms +# Warmup Iteration 3: 86907.846 ops/ms +Iteration 1: 86143.339 ops/ms +Iteration 2: 74725.356 ops/ms +Iteration 3: 72316.121 ops/ms +Iteration 4: 77319.716 ops/ms +Iteration 5: 83469.256 ops/ms +Iteration 6: 87712.360 ops/ms +Iteration 7: 79421.899 ops/ms +Iteration 8: 80867.839 ops/ms +Iteration 9: 82619.163 ops/ms +Iteration 10: 87026.928 ops/ms + +# Run progress: 75.00% complete, ETA 00:01:20 +# Fork: 2 of 2 +# Warmup Iteration 1: 228342.337 ops/ms +# Warmup Iteration 2: 124737.248 ops/ms +# Warmup Iteration 3: 82598.851 ops/ms +Iteration 1: 86877.318 ops/ms +Iteration 2: 89388.624 ops/ms +Iteration 3: 88523.558 ops/ms +Iteration 4: 87547.332 ops/ms +Iteration 5: 88376.087 ops/ms +Iteration 6: 88848.837 ops/ms +Iteration 7: 85998.124 ops/ms +Iteration 8: 86796.998 ops/ms +Iteration 9: 87994.726 ops/ms +Iteration 10: 87784.453 ops/ms + + +Result "io.github.dunwu.javalib.jmh.StringBuilderBenchmark.testStringBuilderAdd": + 84487.902 ±(99.9%) 4355.525 ops/ms [Average] + (min, avg, max) = (72316.121, 84487.902, 89388.624), stdev = 5015.829 + CI (99.9%): [80132.377, 88843.427] (assumes normal distribution) + + +# Run complete. Total time: 00:05:23 + +REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on +why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial +experiments, perform baseline and negative tests that provide experimental control, make sure +the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. +Do not assume the numbers tell you what you want them to tell. + +Benchmark Mode Cnt Score Error Units +StringBuilderBenchmark.testStringAdd thrpt 20 21996.435 ± 412.955 ops/ms +StringBuilderBenchmark.testStringBuilderAdd thrpt 20 84487.902 ± 4355.525 ops/ms +``` + +## API + +下面来了解一下 jmh 常用 API + +### @BenchmarkMode + +基准测试类型。这里选择的是 `Throughput` 也就是吞吐量。根据源码点进去,每种类型后面都有对应的解释,比较好理解,吞吐量会得到单位时间内可以进行的操作数。 + +- `Throughput` - 整体吞吐量,例如“1 秒内可以执行多少次调用”。 +- `AverageTime` - 调用的平均时间,例如“每次调用平均耗时 xxx 毫秒”。 +- `SampleTime` - 随机取样,最后输出取样结果的分布,例如“99%的调用在 xxx 毫秒以内,99.99%的调用在 xxx 毫秒以内” +- `SingleShotTime` - 以上模式都是默认一次 iteration 是 1s,唯有 SingleShotTime 是只运行一次。往往同时把 warmup 次数设为 0,用于测试冷启动时的性能。 +- `All` - 所有模式 + +### @Warmup + +上面我们提到了,进行基准测试前需要进行预热。一般我们前几次进行程序测试的时候都会比较慢, 所以要让程序进行几轮预热,保证测试的准确性。其中的参数 iterations 也就非常好理解了,就是预热轮数。 + +为什么需要预热?因为 JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM 会尝试将其编译成为机器码从而提高执行速度。所以为了让 benchmark 的结果更加接近真实情况就需要进行预热。 + +### @Measurement + +度量,其实就是一些基本的测试参数。 + +- `iterations` - 进行测试的轮次 +- `time` - 每轮进行的时长 +- `timeUnit` - 时长单位 + +都是一些基本的参数,可以根据具体情况调整。一般比较重的东西可以进行大量的测试,放到服务器上运行。 + +### @Threads + +每个进程中的测试线程,这个非常好理解,根据具体情况选择,一般为 cpu 乘以 2。 + +### @Fork + +进行 fork 的次数。如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。 + +### @OutputTimeUnit + +这个比较简单了,基准测试结果的时间类型。一般选择秒、毫秒、微秒。 + +### @Benchmark + +方法级注解,表示该方法是需要进行 benchmark 的对象,用法和 JUnit 的 @Test 类似。 + +### @Param + +属性级注解,@Param 可以用来指定某项参数的多种情况。特别适合用来测试一个函数在不同的参数输入的情况下的性能。 + +### @Setup + +方法级注解,这个注解的作用就是我们需要在测试之前进行一些准备工作,比如对一些数据的初始化之类的。 + +### @TearDown + +方法级注解,这个注解的作用就是我们需要在测试之后进行一些结束工作,比如关闭线程池,数据库连接等的,主要用于资源的回收等。 + +### @State + +当使用 @Setup 参数的时候,必须在类上加这个参数,不然会提示无法运行。 + +State 用于声明某个类是一个“状态”,然后接受一个 Scope 参数用来表示该状态的共享范围。 因为很多 benchmark 会需要一些表示状态的类,JMH 允许你把这些类以依赖注入的方式注入到 benchmark 函数里。Scope 主要分为三种。 + +- `Thread` - 该状态为每个线程独享。 +- `Group` - 该状态为同一个组里面所有线程共享。 +- `Benchmark` - 该状态在所有线程间共享。 + +关于 State 的用法,官方的 code sample 里有比较好的[例子](http://hg.openjdk.java.net/code-tools/jmh/file/cb9aa824b55a/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_03_States.java)。 + +## 参考资料 + +- [jmh 官方示例](http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/) +- [Java 微基准测试框架 JMH](https://www.xncoding.com/2018/01/07/java/jmh.html) +- [JAVA 拾遗 — JMH 与 8 个测试陷阱](https://www.cnkirito.moe/java-jmh/) From f9c7830e3c7fc96759fd192fd113b6a32e162790 Mon Sep 17 00:00:00 2001 From: Zhang Peng Date: Tue, 10 Dec 2019 20:25:54 +0800 Subject: [PATCH 6/6] update codes --- .editorconfig | 4 +- docs/book.json | 136 +- docs/index.html | 508 +- javalib-bean/pom.xml | 102 +- .../github/dunwu/javalib/lombok/DataDemo.java | 6 +- .../javalib/lombok/EqualsAndHashCodeDemo.java | 54 +- .../javalib/lombok/GetterAndSetterDemo.java | 10 +- .../dunwu/javalib/lombok/NonNullDemo.java | 8 +- .../github/dunwu/javalib/lombok/Person.java | 6 +- .../dunwu/javalib/lombok/ToStringDemo.java | 16 +- .../dunwu/javalib/lombok/LombokTest.java | 188 +- javalib-cli/pom.xml | 144 +- .../io/github/dunwu/javalib/AnsiSystem.java | 436 +- .../java/io/github/dunwu/javalib/CliDemo.java | 76 +- .../java/io/github/dunwu/javalib/CliUtil.java | 30 +- .../github/dunwu/javalib/SystemInfoUtil.java | 458 +- .../dunwu/javalib/constant/AnsiBgColor.java | 56 +- .../dunwu/javalib/constant/AnsiColor.java | 56 +- .../dunwu/javalib/constant/AnsiElement.java | 10 +- .../dunwu/javalib/constant/AnsiSgr.java | 40 +- .../github/dunwu/javalib/constant/Color.java | 48 +- .../io/github/dunwu/javalib/CliUtilTests.java | 10 +- .../github/dunwu/javalib/SystemInfoTest.java | 582 +- javalib-io-binary/pom.xml | 122 +- .../io/github/dunwu/javalib/io/FstDemo.java | 100 +- .../dunwu/javalib/io/JdkSerializeDemo.java | 118 +- .../io/github/dunwu/javalib/io/KryoDemo.java | 180 +- .../dunwu/javalib/io/bean/BeanUtils.java | 34 +- .../dunwu/javalib/io/bean/TestBean.java | 34 +- .../javalib/io/SerializePerformanceTest.java | 86 +- javalib-io-json/pom.xml | 146 +- .../github/dunwu/javalib/json/bean/Group.java | 48 +- .../github/dunwu/javalib/json/bean/User.java | 28 +- .../json/fastjson/FastjsonFeildBean.java | 232 +- .../json/fastjson/JacksonAnnotationBean.java | 60 +- .../javalib/json/gson/GsonAnnotationBean.java | 76 +- .../javalib/json/gson/VersionedClass.java | 20 +- .../github/dunwu/javalib/json/TestBean.java | 32 +- .../github/dunwu/javalib/json/TestBean2.java | 28 +- .../json/fastjson/FastjsonCaseTests.java | 68 +- .../fastjson/FastjsonPerformanceTests.java | 58 +- .../javalib/json/gson/GsonCaseTests.java | 106 +- .../json/gson/GsonPerformanceTests.java | 62 +- .../json/jackson/JacksonPerformanceTests.java | 62 +- .../dunwu/javalib/json/util/BeanUtils.java | 96 +- javalib-io/pom.xml | 118 +- .../dunwu/javalib/serialize/FstDemo.java | 24 +- .../javalib/serialize/JdkSerializeDemo.java | 42 +- .../io/github/dunwu/javalib/bean/Person.java | 50 +- .../json/fastjson/FastjsonAnnotationBean.java | 162 +- .../javalib/json/fastjson/FastjsonTests.java | 60 +- .../json/jackson/JacksonAnnotationBean.java | 60 +- .../javalib/json/jackson/JacksonTests.java | 138 +- .../javalib/serialize/SerializeTest.java | 58 +- javalib-log/javalib-log-log4j/pom.xml | 110 +- .../github/dunwu/javalib/log/Log4jDemo.java | 20 +- .../src/main/resources/log4j.xml | 72 +- javalib-log/javalib-log-log4j2/pom.xml | 108 +- .../github/dunwu/javalib/log/Log4j2Demo.java | 20 +- .../src/main/resources/log4j2.xml | 78 +- javalib-log/javalib-log-logback/pom.xml | 84 +- .../github/dunwu/javalib/log/LogbackDemo.java | 20 +- .../src/main/resources/logback.xml | 68 +- javalib-log/pom.xml | 26 +- javalib-mvel/pom.xml | 80 +- .../github/dunwu/javalib/mvel/BasicRule.java | 244 +- .../dunwu/javalib/mvel/DefaultRuleEngine.java | 296 +- .../github/dunwu/javalib/mvel/MvelRule.java | 46 +- .../dunwu/javalib/mvel/MvelRuleSet.java | 50 +- .../io/github/dunwu/javalib/mvel/Rule.java | 110 +- .../dunwu/javalib/mvel/RuleConstant.java | 40 +- .../github/dunwu/javalib/mvel/RuleEngine.java | 90 +- .../dunwu/javalib/mvel/RuleEngineBuilder.java | 78 +- .../dunwu/javalib/mvel/RuleEngineParams.java | 174 +- javalib-mvel/src/main/resources/logback.xml | 274 +- .../github/dunwu/javalib/mvel/ClassTests.java | 50 +- .../dunwu/javalib/mvel/SalaryRuleTest.java | 120 +- .../src/test/resources/SalaryRule.json | 98 +- javalib-office/pom.xml | 47 - .../github/dunwu/javalib/office/WordUtil.java | 263 - .../dunwu/javalib/office/WordUtilTest.java | 112 - javalib-test/pom.xml | 139 +- .../github/dunwu/javalib/jmh/QuickStart.java | 43 + .../javalib/jmh/StringBuilderBenchmark.java | 45 + .../dunwu/javalib/lombok/Calculator.java | 6 +- .../github/dunwu/javalib/lombok/Person.java | 36 +- javalib-test/src/main/resources/logback.xml | 20 +- .../dunwu/javalib/junit4/JUnitTest.java | 98 +- .../dunwu/javalib/junit5/AssertionsTests.java | 237 +- .../javalib/junit5/AssumptionsTests.java | 49 +- .../javalib/junit5/DisplayNameTests.java | 28 +- .../dunwu/javalib/junit5/DynamicTests.java | 188 +- .../dunwu/javalib/junit5/NestedTests.java | 144 +- .../javalib/junit5/ParameterizedTests.java | 31 +- .../dunwu/javalib/junit5/RepeatedTests.java | 75 +- .../dunwu/javalib/junit5/StandardTests.java | 81 +- .../dunwu/javalib/mockito/MockitoTest.java | 419 +- javalib-text/pom.xml | 53 + .../dunwu/javalib/office/PinyinDemo.java | 15 + .../github/dunwu/javalib/office/WordUtil.java | 263 + .../dunwu/javalib/office/WordUtilTest.java | 111 + javatool-embedded-server/pom.xml | 168 +- .../javatool/controller/HelloController.java | 62 +- .../javatool/controller/IndexController.java | 24 +- .../javatool/server/SimpleTomcatServer.java | 52 +- .../dunwu/javatool/server/TomcatServer.java | 222 +- .../src/main/resources/logback.xml | 80 +- .../main/resources/spring/spring-servlet.xml | 24 +- .../src/main/resources/tomcat/conf/server.xml | 56 +- .../src/main/resources/tomcat/conf/web.xml | 9038 ++++++++--------- .../src/main/webapp/WEB-INF/web.xml | 82 +- .../src/main/webapp/views/jsp/hello.jsp | 6 +- .../src/main/webapp/views/jsp/index.jsp | 24 +- javatool-examples/pom.xml | 102 +- .../io/github/dunwu/javatool/ElasticDemo.java | 56 +- .../src/main/resources/log4j.xml | 98 +- .../src/main/resources/logback.xml | 104 +- pom.xml | 42 +- prettier.config.js | 2 +- settings/codestyle/google-codestyle.xml | 1190 +-- settings/codestyle/square-codestyle.xml | 630 +- 121 files changed, 11287 insertions(+), 11126 deletions(-) delete mode 100644 javalib-office/pom.xml delete mode 100644 javalib-office/src/main/java/io/github/dunwu/javalib/office/WordUtil.java delete mode 100644 javalib-office/src/test/java/io/github/dunwu/javalib/office/WordUtilTest.java create mode 100644 javalib-test/src/main/java/io/github/dunwu/javalib/jmh/QuickStart.java create mode 100644 javalib-test/src/main/java/io/github/dunwu/javalib/jmh/StringBuilderBenchmark.java create mode 100644 javalib-text/pom.xml create mode 100644 javalib-text/src/main/java/io/github/dunwu/javalib/office/PinyinDemo.java create mode 100644 javalib-text/src/main/java/io/github/dunwu/javalib/office/WordUtil.java create mode 100644 javalib-text/src/test/java/io/github/dunwu/javalib/office/WordUtilTest.java diff --git a/.editorconfig b/.editorconfig index c7ca29b2..d72a75ea 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,7 +10,7 @@ root = true [*] end_of_line = lf indent_size = 2 -indent_style = tab +indent_style = space max_line_length = 120 charset = utf-8 trim_trailing_whitespace = true @@ -19,7 +19,7 @@ insert_final_newline = true [*.{bat, cmd}] end_of_line = crlf -[*.{java, groovy, kt, sh}] +[*.{java, gradle, groovy, kt, sh}] indent_size = 4 [*.md] diff --git a/docs/book.json b/docs/book.json index 69ecaf5c..d84cc294 100644 --- a/docs/book.json +++ b/docs/book.json @@ -1,69 +1,71 @@ { - "gitbook": "3.2.2", - "title": "java-tutorial", - "language": "zh-hans", - "root": "./", - "structure": { - "summary": "sidebar.md" - }, - "links": { - "sidebar": { - "java-tutorial": "https://github.com/dunwu/java-tutorial" - } - }, - "plugins": [ - "-lunr", - "-search", - "advanced-emoji@^0.2.2", - "anchor-navigation-ex@1.0.10", - "anchors@^0.7.1", - "edit-link@^2.0.2", - "expandable-chapters-small@^0.1.7", - "github@^2.0.0", - "search-plus@^0.0.11", - "simple-page-toc@^0.1.1", - "splitter@^0.0.8", - "tbfed-pagefooter@^0.0.1" - ], - "pluginsConfig": { - "anchor-navigation-ex": { - "showLevel": false, - "associatedWithSummary": true, - "multipleH1": true, - "mode": "float", - "isRewritePageTitle": false, - "float": { - "showLevelIcon": false, - "level1Icon": "fa fa-hand-o-right", - "level2Icon": "fa fa-hand-o-right", - "level3Icon": "fa fa-hand-o-right" - }, - "pageTop": { - "showLevelIcon": false, - "level1Icon": "fa fa-hand-o-right", - "level2Icon": "fa fa-hand-o-right", - "level3Icon": "fa fa-hand-o-right" - } - }, - "edit-link": { - "base": "https://github.com/dunwu/java-tutorial/blob/master/docs", - "label": "编辑此页面" - }, - "github": { - "url": "https://github.com/dunwu" - }, - "simple-page-toc": { - "maxDepth": 4, - "skipFirstH1": true - }, - "sharing": { - "weibo": true, - "all": ["weibo"] - }, - "tbfed-pagefooter": { - "copyright": "Copyright © Zhang Peng 2017", - "modify_label": "该文件上次修订时间:", - "modify_format": "YYYY-MM-DD HH:mm:ss" - } - } + "gitbook": "3.2.2", + "title": "java-tutorial", + "language": "zh-hans", + "root": "./", + "structure": { + "summary": "sidebar.md" + }, + "links": { + "sidebar": { + "java-tutorial": "https://github.com/dunwu/java-tutorial" + } + }, + "plugins": [ + "-lunr", + "-search", + "advanced-emoji@^0.2.2", + "anchor-navigation-ex@1.0.10", + "anchors@^0.7.1", + "edit-link@^2.0.2", + "expandable-chapters-small@^0.1.7", + "github@^2.0.0", + "search-plus@^0.0.11", + "simple-page-toc@^0.1.1", + "splitter@^0.0.8", + "tbfed-pagefooter@^0.0.1" + ], + "pluginsConfig": { + "anchor-navigation-ex": { + "showLevel": false, + "associatedWithSummary": true, + "multipleH1": true, + "mode": "float", + "isRewritePageTitle": false, + "float": { + "showLevelIcon": false, + "level1Icon": "fa fa-hand-o-right", + "level2Icon": "fa fa-hand-o-right", + "level3Icon": "fa fa-hand-o-right" + }, + "pageTop": { + "showLevelIcon": false, + "level1Icon": "fa fa-hand-o-right", + "level2Icon": "fa fa-hand-o-right", + "level3Icon": "fa fa-hand-o-right" + } + }, + "edit-link": { + "base": "https://github.com/dunwu/java-tutorial/blob/master/docs", + "label": "编辑此页面" + }, + "github": { + "url": "https://github.com/dunwu" + }, + "simple-page-toc": { + "maxDepth": 4, + "skipFirstH1": true + }, + "sharing": { + "weibo": true, + "all": [ + "weibo" + ] + }, + "tbfed-pagefooter": { + "copyright": "Copyright © Zhang Peng 2017", + "modify_label": "该文件上次修订时间:", + "modify_format": "YYYY-MM-DD HH:mm:ss" + } + } } diff --git a/docs/index.html b/docs/index.html index a17ffade..577beaaa 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,290 +1,284 @@ - - - Java Tutorial - - - - - - - + - + - + - + - - -
    正在加载...
    - - - - - + @media (max-width: 600px) { + pre { + padding-left: 0 !important; + padding-right: 0 !important; + } + } + + + +
    正在加载...
    + + + + + - - - - - - - + + + + + + + diff --git a/javalib-bean/pom.xml b/javalib-bean/pom.xml index 4a27f6cd..3dc0efcc 100644 --- a/javalib-bean/pom.xml +++ b/javalib-bean/pom.xml @@ -1,60 +1,60 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javalib-bean - 1.0.0 - jar + io.github.dunwu + javalib-bean + 1.0.0 + jar - java-bean - JavaBean 相关的 Lib 示例 + java-bean + JavaBean 相关的 Lib 示例 - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 0.4.8 - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 0.4.8 + - - - org.projectlombok - lombok - + + + org.projectlombok + lombok + - - - junit - junit - test - - - org.assertj - assertj-core - test - - - - - - io.github.dunwu - dunwu-dependencies - ${dunwu.version} - pom - import - - - + + + junit + junit + test + + + org.assertj + assertj-core + test + + + + + + io.github.dunwu + dunwu-dependencies + ${dunwu.version} + pom + import + + + - - - - src/main/resources - - - + + + + src/main/resources + + + diff --git a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/DataDemo.java b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/DataDemo.java index a1ef4365..df20bb38 100644 --- a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/DataDemo.java +++ b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/DataDemo.java @@ -14,10 +14,10 @@ @Data(staticConstructor = "of") public class DataDemo { - private final Person founder; + private final Person founder; - protected List employees; + protected List employees; - private String name; + private String name; } diff --git a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/EqualsAndHashCodeDemo.java b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/EqualsAndHashCodeDemo.java index 9ddef7e6..2bdfd2b3 100644 --- a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/EqualsAndHashCodeDemo.java +++ b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/EqualsAndHashCodeDemo.java @@ -17,41 +17,41 @@ class Parent {} @EqualsAndHashCode(callSuper = true, exclude = { "address", "city", "state", "zip" }) public class EqualsAndHashCodeDemo extends Person { - @NonNull - private String name; + @NonNull + private String name; - @NonNull - private Gender gender; + @NonNull + private Gender gender; - private String ssn; + private String ssn; - private String address; + private String address; - private String city; + private String city; - private String state; + private String state; - private String zip; + private String zip; - public EqualsAndHashCodeDemo(@NonNull String name, @NonNull Gender gender) { - this.name = name; - this.gender = gender; - } + public EqualsAndHashCodeDemo(@NonNull String name, @NonNull Gender gender) { + this.name = name; + this.gender = gender; + } - public EqualsAndHashCodeDemo(@NonNull String name, @NonNull Gender gender, - String ssn, String address, String city, String state, String zip) { - this.name = name; - this.gender = gender; - this.ssn = ssn; - this.address = address; - this.city = city; - this.state = state; - this.zip = zip; - } + public EqualsAndHashCodeDemo(@NonNull String name, @NonNull Gender gender, + String ssn, String address, String city, String state, String zip) { + this.name = name; + this.gender = gender; + this.ssn = ssn; + this.address = address; + this.city = city; + this.state = state; + this.zip = zip; + } - enum Gender { - Male, - Female - } + enum Gender { + Male, + Female + } } diff --git a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/GetterAndSetterDemo.java b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/GetterAndSetterDemo.java index 1e2ddacb..0adfd3e6 100644 --- a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/GetterAndSetterDemo.java +++ b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/GetterAndSetterDemo.java @@ -13,11 +13,11 @@ */ public class GetterAndSetterDemo { - @Getter - @Setter - private boolean employed = true; + @Getter + @Setter + private boolean employed = true; - @Setter(AccessLevel.PROTECTED) - private String name; + @Setter(AccessLevel.PROTECTED) + private String name; } diff --git a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/NonNullDemo.java b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/NonNullDemo.java index 216d6a2e..638eae9b 100644 --- a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/NonNullDemo.java +++ b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/NonNullDemo.java @@ -15,9 +15,9 @@ */ public class NonNullDemo { - @Getter - @Setter - @NonNull - private List members; + @Getter + @Setter + @NonNull + private List members; } diff --git a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/Person.java b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/Person.java index 7c72ea98..f6a28465 100644 --- a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/Person.java +++ b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/Person.java @@ -17,10 +17,10 @@ @EqualsAndHashCode(exclude = { "age", "sex" }) public class Person { - private String name; + private String name; - private Integer age; + private Integer age; - private String sex; + private String sex; } diff --git a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/ToStringDemo.java b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/ToStringDemo.java index fe10470b..ea896d9a 100644 --- a/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/ToStringDemo.java +++ b/javalib-bean/src/main/java/io/github/dunwu/javalib/lombok/ToStringDemo.java @@ -9,16 +9,16 @@ @ToString(callSuper = true, exclude = "someExcludedField") public class ToStringDemo { - private boolean someBoolean = true; + private boolean someBoolean = true; - private String someStringField; + private String someStringField; - private float someExcludedField; + private float someExcludedField; - public ToStringDemo(boolean someBoolean, String someStringField, float someExcludedField) { - this.someBoolean = someBoolean; - this.someStringField = someStringField; - this.someExcludedField = someExcludedField; - } + public ToStringDemo(boolean someBoolean, String someStringField, float someExcludedField) { + this.someBoolean = someBoolean; + this.someStringField = someStringField; + this.someExcludedField = someExcludedField; + } } diff --git a/javalib-bean/src/test/java/io/github/dunwu/javalib/lombok/LombokTest.java b/javalib-bean/src/test/java/io/github/dunwu/javalib/lombok/LombokTest.java index e5055144..1adbe085 100644 --- a/javalib-bean/src/test/java/io/github/dunwu/javalib/lombok/LombokTest.java +++ b/javalib-bean/src/test/java/io/github/dunwu/javalib/lombok/LombokTest.java @@ -18,99 +18,99 @@ */ public class LombokTest { - @Test - public void testGetterAndSetterDemo() { - GetterAndSetterDemo demo = new GetterAndSetterDemo(); - demo.setEmployed(true); - demo.setName("xxx"); - assertThat(demo.isEmployed()).isTrue(); - } - - @Test - public void testToStringDemo() { - ToStringDemo demo = new ToStringDemo(true, "abc", 0.5f); - System.out.println(demo.toString()); - assertThat(demo.toString()).contains("someBoolean=true, someStringField=abc"); - - Person person = new Person(); - person.setName("张三"); - person.setAge(20); - person.setSex("男"); - System.out.println(person.toString()); - assertThat(person.toString()).isEqualTo("Person(name=张三, sex=男)"); - } - - @Test - public void testEqualsAndHashCodeDemo() { - EqualsAndHashCodeDemo demo1 = - new EqualsAndHashCodeDemo("name1", EqualsAndHashCodeDemo.Gender.Female, "ssn", "xxx", "xxx", "xxx", "xxx"); - EqualsAndHashCodeDemo demo2 = - new EqualsAndHashCodeDemo("name1", EqualsAndHashCodeDemo.Gender.Female, "ssn", "ooo", "ooo", "ooo", "ooo"); - assertThat(demo1.equals(demo2)).isTrue(); - - Person person = new Person(); - person.setName("张三"); - person.setAge(20); - person.setSex("男"); - - Person person2 = new Person(); - person2.setName("张三"); - person2.setAge(18); - person2.setSex("男"); - - Person person3 = new Person(); - person3.setName("李四"); - person3.setAge(20); - person3.setSex("男"); - - assertThat(person.equals(person2)).isTrue(); - assertThat(person.equals(person3)).isFalse(); - } - - @Test - public void testDataDemo() { - Person huangshiren = new Person(); - huangshiren.setName("黄世仁"); - huangshiren.setAge(30); - huangshiren.setSex("男"); - Person yangbailao = new Person(); - yangbailao.setName("杨白劳"); - yangbailao.setAge(50); - yangbailao.setSex("男"); - Person xiaobaicai = new Person(); - xiaobaicai.setName("小白菜"); - xiaobaicai.setAge(20); - xiaobaicai.setSex("女"); - - List personList = new ArrayList<>(); - personList.add(yangbailao); - personList.add(xiaobaicai); - - DataDemo demo = DataDemo.of(huangshiren); - demo.setName("黑心农产品公司"); - demo.setEmployees(personList); - assertThat(demo.getName()).isEqualTo("黑心农产品公司"); - assertThat(demo.getFounder()).isEqualTo(huangshiren); - - System.out.println("公司名:" + demo.getName()); - System.out.println("创始人:" + demo.getFounder()); - System.out.println("员工信息"); - assertThat(demo.getEmployees()).contains(yangbailao, xiaobaicai); - demo.getEmployees().forEach(person -> { - System.out.println(person.toString()); - }); - } - - @Test - public void testCleanUp() { - try { - @Cleanup - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - baos.write(new byte[] { 'Y', 'e', 's' }); - System.out.println(baos.toString()); - } catch (IOException e) { - e.printStackTrace(); - } - } + @Test + public void testGetterAndSetterDemo() { + GetterAndSetterDemo demo = new GetterAndSetterDemo(); + demo.setEmployed(true); + demo.setName("xxx"); + assertThat(demo.isEmployed()).isTrue(); + } + + @Test + public void testToStringDemo() { + ToStringDemo demo = new ToStringDemo(true, "abc", 0.5f); + System.out.println(demo.toString()); + assertThat(demo.toString()).contains("someBoolean=true, someStringField=abc"); + + Person person = new Person(); + person.setName("张三"); + person.setAge(20); + person.setSex("男"); + System.out.println(person.toString()); + assertThat(person.toString()).isEqualTo("Person(name=张三, sex=男)"); + } + + @Test + public void testEqualsAndHashCodeDemo() { + EqualsAndHashCodeDemo demo1 = + new EqualsAndHashCodeDemo("name1", EqualsAndHashCodeDemo.Gender.Female, "ssn", "xxx", "xxx", "xxx", "xxx"); + EqualsAndHashCodeDemo demo2 = + new EqualsAndHashCodeDemo("name1", EqualsAndHashCodeDemo.Gender.Female, "ssn", "ooo", "ooo", "ooo", "ooo"); + assertThat(demo1.equals(demo2)).isTrue(); + + Person person = new Person(); + person.setName("张三"); + person.setAge(20); + person.setSex("男"); + + Person person2 = new Person(); + person2.setName("张三"); + person2.setAge(18); + person2.setSex("男"); + + Person person3 = new Person(); + person3.setName("李四"); + person3.setAge(20); + person3.setSex("男"); + + assertThat(person.equals(person2)).isTrue(); + assertThat(person.equals(person3)).isFalse(); + } + + @Test + public void testDataDemo() { + Person huangshiren = new Person(); + huangshiren.setName("黄世仁"); + huangshiren.setAge(30); + huangshiren.setSex("男"); + Person yangbailao = new Person(); + yangbailao.setName("杨白劳"); + yangbailao.setAge(50); + yangbailao.setSex("男"); + Person xiaobaicai = new Person(); + xiaobaicai.setName("小白菜"); + xiaobaicai.setAge(20); + xiaobaicai.setSex("女"); + + List personList = new ArrayList<>(); + personList.add(yangbailao); + personList.add(xiaobaicai); + + DataDemo demo = DataDemo.of(huangshiren); + demo.setName("黑心农产品公司"); + demo.setEmployees(personList); + assertThat(demo.getName()).isEqualTo("黑心农产品公司"); + assertThat(demo.getFounder()).isEqualTo(huangshiren); + + System.out.println("公司名:" + demo.getName()); + System.out.println("创始人:" + demo.getFounder()); + System.out.println("员工信息"); + assertThat(demo.getEmployees()).contains(yangbailao, xiaobaicai); + demo.getEmployees().forEach(person -> { + System.out.println(person.toString()); + }); + } + + @Test + public void testCleanUp() { + try { + @Cleanup + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(new byte[] { 'Y', 'e', 's' }); + System.out.println(baos.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/javalib-cli/pom.xml b/javalib-cli/pom.xml index c92722dd..63f491db 100644 --- a/javalib-cli/pom.xml +++ b/javalib-cli/pom.xml @@ -1,78 +1,78 @@ - 4.0.0 + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - io.github.dunwu - javalib-cli - 1.0.0 + io.github.dunwu + javalib-cli + 1.0.0 - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 0.4.8 - 5.4.0 - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 0.4.8 + 5.4.0 + - - - org.apache.commons - commons-lang3 - - - commons-cli - commons-cli - 1.4 - - - com.github.oshi - oshi-core - 4.1.0 - - - net.java.dev.jna - jna - - - net.java.dev.jna - jna-platform - - - - - net.java.dev.jna - jna - ${jna.version} - - - net.java.dev.jna - jna-platform - ${jna.version} - - - net.java.dev.jna - jna - - - - - junit - junit - test - - - - - - io.github.dunwu - dunwu-dependencies - ${dunwu.version} - pom - import - - - + + + org.apache.commons + commons-lang3 + + + commons-cli + commons-cli + 1.4 + + + com.github.oshi + oshi-core + 4.1.0 + + + net.java.dev.jna + jna + + + net.java.dev.jna + jna-platform + + + + + net.java.dev.jna + jna + ${jna.version} + + + net.java.dev.jna + jna-platform + ${jna.version} + + + net.java.dev.jna + jna + + + + + junit + junit + test + + + + + + io.github.dunwu + dunwu-dependencies + ${dunwu.version} + pom + import + + + diff --git a/javalib-cli/src/main/java/io/github/dunwu/javalib/AnsiSystem.java b/javalib-cli/src/main/java/io/github/dunwu/javalib/AnsiSystem.java index 79f14b0b..3dd7030e 100644 --- a/javalib-cli/src/main/java/io/github/dunwu/javalib/AnsiSystem.java +++ b/javalib-cli/src/main/java/io/github/dunwu/javalib/AnsiSystem.java @@ -15,230 +15,230 @@ */ public class AnsiSystem { - private String code; - - private static final String ENCODE_JOIN = ";"; - - private static final String ENCODE_START = "\033["; + public static final AnsiSystem RED = new AnsiSystem("\033[;31m"); + + public static final AnsiSystem GREEN = new AnsiSystem("\033[;32m"); + + public static final AnsiSystem YELLOW = new AnsiSystem("\033[;33m"); - private static final String ENCODE_END = "m"; + public static final AnsiSystem BLUE = new AnsiSystem("\033[;34m"); - private static final String RESET = "\033[0;m"; + public static final AnsiSystem MAGENTA = new AnsiSystem("\033[;35m"); - public static final AnsiSystem RED = new AnsiSystem("\033[;31m"); + public static final AnsiSystem CYAN = new AnsiSystem("\033[;36m"); - public static final AnsiSystem GREEN = new AnsiSystem("\033[;32m"); + public static final AnsiSystem WHITE = new AnsiSystem("\033[;37m"); - public static final AnsiSystem YELLOW = new AnsiSystem("\033[;33m"); + private static final String ENCODE_JOIN = ";"; - public static final AnsiSystem BLUE = new AnsiSystem("\033[;34m"); + private static final String ENCODE_START = "\033["; - public static final AnsiSystem MAGENTA = new AnsiSystem("\033[;35m"); - - public static final AnsiSystem CYAN = new AnsiSystem("\033[;36m"); - - public static final AnsiSystem WHITE = new AnsiSystem("\033[;37m"); - - public AnsiSystem(String code) { - this.code = code; - } - - public AnsiSystem(AnsiConfig config) { - this.code = encode(config); - } - - public void print(String message) { - System.out.print(code + message + RESET); - } - - public void println(String message) { - System.out.println(code + message + RESET); - } - - private String encode(AnsiConfig config) { - StringBuilder sb = new StringBuilder(); - sb.append(ENCODE_START); - if (config.isBold()) { - sb.append(ENCODE_JOIN).append(AnsiSgr.BOLD.getCode()); - } - if (config.isItalic()) { - sb.append(ENCODE_JOIN).append(AnsiSgr.ITALIC.getCode()); - } - if (config.isUnderline()) { - sb.append(ENCODE_JOIN).append(AnsiSgr.UNDERLINE.getCode()); - } - if (config.isSlowBlink()) { - sb.append(ENCODE_JOIN).append(AnsiSgr.SLOW_BLINK.getCode()); - } else { - if (config.isRapidBlink()) { - sb.append(ENCODE_JOIN).append(AnsiSgr.RAPID_BLINK.getCode()); - } - } - if (config.isReverseVideo()) { - sb.append(ENCODE_JOIN).append(AnsiSgr.REVERSE_VIDEO.getCode()); - } - if (config.isCanceal()) { - sb.append(ENCODE_JOIN).append(AnsiSgr.CONCEAL.getCode()); - } - if (config.getColor() != null) { - AnsiColor color = AnsiColor.valueOf(config.getColor().name()); - if (StringUtils.isNotBlank(color.getCode())) { - sb.append(ENCODE_JOIN).append(color.getCode()); - } - } - if (config.getBgColor() != null) { - AnsiBgColor color = AnsiBgColor.valueOf(config.getBgColor().name()); - if (StringUtils.isNotBlank(color.getCode())) { - sb.append(ENCODE_JOIN).append(color.getCode()); - } - } - sb.append(ENCODE_END); - return sb.toString(); - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - /** - * Ansi 配置 - */ - public static class AnsiConfig { - - private boolean bold; - - private boolean italic; - - private boolean underline; - - private boolean slowBlink; - - private boolean rapidBlink; - - private boolean reverseVideo; - - private boolean canceal; - - private Color color; - - private Color bgColor; - - public AnsiConfig() { - this.bold = false; - this.italic = false; - this.underline = false; - this.slowBlink = false; - this.rapidBlink = false; - this.reverseVideo = false; - this.canceal = false; - this.color = Color.DEFAULT; - this.bgColor = Color.DEFAULT; - } - - public AnsiConfig(boolean bold, boolean italic, boolean underline, boolean slowBlink, boolean rapidBlink, - boolean reverseVideo, boolean canceal, Color color, Color bgColor) { - this.bold = bold; - this.italic = italic; - this.underline = underline; - this.slowBlink = slowBlink; - this.rapidBlink = rapidBlink; - this.reverseVideo = reverseVideo; - this.canceal = canceal; - this.color = color; - this.bgColor = bgColor; - } - - public boolean isBold() { - return bold; - } - - public void setBold(boolean bold) { - this.bold = bold; - } - - public boolean isItalic() { - return italic; - } - - public void setItalic(boolean italic) { - this.italic = italic; - } - - public boolean isUnderline() { - return underline; - } - - public void setUnderline(boolean underline) { - this.underline = underline; - } - - public boolean isSlowBlink() { - return slowBlink; - } - - public void setSlowBlink(boolean slowBlink) { - this.slowBlink = slowBlink; - } - - public boolean isRapidBlink() { - return rapidBlink; - } - - public void setRapidBlink(boolean rapidBlink) { - this.rapidBlink = rapidBlink; - } - - public boolean isReverseVideo() { - return reverseVideo; - } - - public void setReverseVideo(boolean reverseVideo) { - this.reverseVideo = reverseVideo; - } - - public boolean isCanceal() { - return canceal; - } - - public void setCanceal(boolean canceal) { - this.canceal = canceal; - } - - public Color getColor() { - return color; - } - - public void setColor(Color color) { - this.color = color; - } - - public Color getBgColor() { - return bgColor; - } - - public void setBgColor(Color bgColor) { - this.bgColor = bgColor; - } - - @Override - public String toString() { - return "AnsiParam{" + - "bold=" + bold + - ", italic=" + italic + - ", underline=" + underline + - ", slowBlink=" + slowBlink + - ", rapidBlink=" + rapidBlink + - ", reverseVideo=" + reverseVideo + - ", canceal=" + canceal + - ", color=" + color + - ", bgColor=" + bgColor + - '}'; - } - - } + private static final String ENCODE_END = "m"; + + private static final String RESET = "\033[0;m"; + + private String code; + + public AnsiSystem(String code) { + this.code = code; + } + + public AnsiSystem(AnsiConfig config) { + this.code = encode(config); + } + + private String encode(AnsiConfig config) { + StringBuilder sb = new StringBuilder(); + sb.append(ENCODE_START); + if (config.isBold()) { + sb.append(ENCODE_JOIN).append(AnsiSgr.BOLD.getCode()); + } + if (config.isItalic()) { + sb.append(ENCODE_JOIN).append(AnsiSgr.ITALIC.getCode()); + } + if (config.isUnderline()) { + sb.append(ENCODE_JOIN).append(AnsiSgr.UNDERLINE.getCode()); + } + if (config.isSlowBlink()) { + sb.append(ENCODE_JOIN).append(AnsiSgr.SLOW_BLINK.getCode()); + } else { + if (config.isRapidBlink()) { + sb.append(ENCODE_JOIN).append(AnsiSgr.RAPID_BLINK.getCode()); + } + } + if (config.isReverseVideo()) { + sb.append(ENCODE_JOIN).append(AnsiSgr.REVERSE_VIDEO.getCode()); + } + if (config.isCanceal()) { + sb.append(ENCODE_JOIN).append(AnsiSgr.CONCEAL.getCode()); + } + if (config.getColor() != null) { + AnsiColor color = AnsiColor.valueOf(config.getColor().name()); + if (StringUtils.isNotBlank(color.getCode())) { + sb.append(ENCODE_JOIN).append(color.getCode()); + } + } + if (config.getBgColor() != null) { + AnsiBgColor color = AnsiBgColor.valueOf(config.getBgColor().name()); + if (StringUtils.isNotBlank(color.getCode())) { + sb.append(ENCODE_JOIN).append(color.getCode()); + } + } + sb.append(ENCODE_END); + return sb.toString(); + } + + public void print(String message) { + System.out.print(code + message + RESET); + } + + public void println(String message) { + System.out.println(code + message + RESET); + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + /** + * Ansi 配置 + */ + public static class AnsiConfig { + + private boolean bold; + + private boolean italic; + + private boolean underline; + + private boolean slowBlink; + + private boolean rapidBlink; + + private boolean reverseVideo; + + private boolean canceal; + + private Color color; + + private Color bgColor; + + public AnsiConfig() { + this.bold = false; + this.italic = false; + this.underline = false; + this.slowBlink = false; + this.rapidBlink = false; + this.reverseVideo = false; + this.canceal = false; + this.color = Color.DEFAULT; + this.bgColor = Color.DEFAULT; + } + + public AnsiConfig(boolean bold, boolean italic, boolean underline, boolean slowBlink, boolean rapidBlink, + boolean reverseVideo, boolean canceal, Color color, Color bgColor) { + this.bold = bold; + this.italic = italic; + this.underline = underline; + this.slowBlink = slowBlink; + this.rapidBlink = rapidBlink; + this.reverseVideo = reverseVideo; + this.canceal = canceal; + this.color = color; + this.bgColor = bgColor; + } + + @Override + public String toString() { + return "AnsiParam{" + + "bold=" + bold + + ", italic=" + italic + + ", underline=" + underline + + ", slowBlink=" + slowBlink + + ", rapidBlink=" + rapidBlink + + ", reverseVideo=" + reverseVideo + + ", canceal=" + canceal + + ", color=" + color + + ", bgColor=" + bgColor + + '}'; + } + + public boolean isBold() { + return bold; + } + + public void setBold(boolean bold) { + this.bold = bold; + } + + public boolean isItalic() { + return italic; + } + + public void setItalic(boolean italic) { + this.italic = italic; + } + + public boolean isUnderline() { + return underline; + } + + public void setUnderline(boolean underline) { + this.underline = underline; + } + + public boolean isSlowBlink() { + return slowBlink; + } + + public void setSlowBlink(boolean slowBlink) { + this.slowBlink = slowBlink; + } + + public boolean isRapidBlink() { + return rapidBlink; + } + + public void setRapidBlink(boolean rapidBlink) { + this.rapidBlink = rapidBlink; + } + + public boolean isReverseVideo() { + return reverseVideo; + } + + public void setReverseVideo(boolean reverseVideo) { + this.reverseVideo = reverseVideo; + } + + public boolean isCanceal() { + return canceal; + } + + public void setCanceal(boolean canceal) { + this.canceal = canceal; + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color; + } + + public Color getBgColor() { + return bgColor; + } + + public void setBgColor(Color bgColor) { + this.bgColor = bgColor; + } + + } } diff --git a/javalib-cli/src/main/java/io/github/dunwu/javalib/CliDemo.java b/javalib-cli/src/main/java/io/github/dunwu/javalib/CliDemo.java index c8af6af1..7477b9eb 100644 --- a/javalib-cli/src/main/java/io/github/dunwu/javalib/CliDemo.java +++ b/javalib-cli/src/main/java/io/github/dunwu/javalib/CliDemo.java @@ -12,45 +12,45 @@ */ public class CliDemo { - public static void main(String[] args) throws ParseException { + public static void main(String[] args) throws ParseException { - while (true) { - Scanner scanner = new Scanner(System.in); - String param = ""; - if (scanner.hasNext()) { - param = scanner.next(); - } + while (true) { + Scanner scanner = new Scanner(System.in); + String param = ""; + if (scanner.hasNext()) { + param = scanner.next(); + } - switch (param) { - case "date": - AnsiSystem.BLUE.println("date = " + new Date()); - break; - case "area": - AnsiSystem.BLUE.println("area = " + "China"); - break; - case "system": - Properties props = System.getProperties(); - System.out.println("Java的运行环境版本:" + props.getProperty("java.version")); - System.out.println("默认的临时文件路径:" + props.getProperty("java.io.tmpdir")); - System.out.println("操作系统的名称:" + props.getProperty("os.name")); - System.out.println("操作系统的构架:" + props.getProperty("os.arch")); - System.out.println("操作系统的版本:" + props.getProperty("os.version")); - System.out.println("用户的账户名称:" + props.getProperty("user.name")); - System.out.println("用户的主目录:" + props.getProperty("user.home")); - System.out.println("用户的当前工作目录:" + props.getProperty("user.dir")); - System.out.println("操作系统:" + props.getProperty("sun.desktop")); - System.out.println("CPU个数:" + Runtime.getRuntime().availableProcessors()); - System.out.println("虚拟机内存总量:" + Runtime.getRuntime().totalMemory()); - System.out.println("虚拟机空闲内存量:" + Runtime.getRuntime().freeMemory()); - System.out.println("虚拟机使用最大内存量:" + Runtime.getRuntime().maxMemory()); - break; - case "exit": - return; - default: - System.err.println("invalid param"); - break; - } - } - } + switch (param) { + case "date": + AnsiSystem.BLUE.println("date = " + new Date()); + break; + case "area": + AnsiSystem.BLUE.println("area = " + "China"); + break; + case "system": + Properties props = System.getProperties(); + System.out.println("Java的运行环境版本:" + props.getProperty("java.version")); + System.out.println("默认的临时文件路径:" + props.getProperty("java.io.tmpdir")); + System.out.println("操作系统的名称:" + props.getProperty("os.name")); + System.out.println("操作系统的构架:" + props.getProperty("os.arch")); + System.out.println("操作系统的版本:" + props.getProperty("os.version")); + System.out.println("用户的账户名称:" + props.getProperty("user.name")); + System.out.println("用户的主目录:" + props.getProperty("user.home")); + System.out.println("用户的当前工作目录:" + props.getProperty("user.dir")); + System.out.println("操作系统:" + props.getProperty("sun.desktop")); + System.out.println("CPU个数:" + Runtime.getRuntime().availableProcessors()); + System.out.println("虚拟机内存总量:" + Runtime.getRuntime().totalMemory()); + System.out.println("虚拟机空闲内存量:" + Runtime.getRuntime().freeMemory()); + System.out.println("虚拟机使用最大内存量:" + Runtime.getRuntime().maxMemory()); + break; + case "exit": + return; + default: + System.err.println("invalid param"); + break; + } + } + } } diff --git a/javalib-cli/src/main/java/io/github/dunwu/javalib/CliUtil.java b/javalib-cli/src/main/java/io/github/dunwu/javalib/CliUtil.java index e3c68573..1b04607f 100644 --- a/javalib-cli/src/main/java/io/github/dunwu/javalib/CliUtil.java +++ b/javalib-cli/src/main/java/io/github/dunwu/javalib/CliUtil.java @@ -7,22 +7,22 @@ public class CliUtil { - public static void prepare(String[] args) throws Exception { - // commons-cli命令行参数,需要带参数值 - Options options = new Options(); - // sql文件路径 - options.addOption("sql", true, "sql config"); - // 任务名称 - options.addOption("name", true, "job name"); + public static void prepare(String[] args) throws Exception { + // commons-cli命令行参数,需要带参数值 + Options options = new Options(); + // sql文件路径 + options.addOption("sql", true, "sql config"); + // 任务名称 + options.addOption("name", true, "job name"); - // 解析命令行参数 - CommandLineParser parser = new DefaultParser(); - CommandLine cl = parser.parse(options, args); - String sql = cl.getOptionValue("sql"); - String name = cl.getOptionValue("name"); + // 解析命令行参数 + CommandLineParser parser = new DefaultParser(); + CommandLine cl = parser.parse(options, args); + String sql = cl.getOptionValue("sql"); + String name = cl.getOptionValue("name"); - System.out.println("sql : " + sql); - System.out.println("name : " + name); - } + System.out.println("sql : " + sql); + System.out.println("name : " + name); + } } diff --git a/javalib-cli/src/main/java/io/github/dunwu/javalib/SystemInfoUtil.java b/javalib-cli/src/main/java/io/github/dunwu/javalib/SystemInfoUtil.java index fa484fe0..cac4c90a 100644 --- a/javalib-cli/src/main/java/io/github/dunwu/javalib/SystemInfoUtil.java +++ b/javalib-cli/src/main/java/io/github/dunwu/javalib/SystemInfoUtil.java @@ -17,249 +17,249 @@ */ public class SystemInfoUtil { - private static Logger logger = LoggerFactory.getLogger(SystemInfoUtil.class); + private static Logger logger = LoggerFactory.getLogger(SystemInfoUtil.class); - public static void printComputerSystem(final ComputerSystem computerSystem) { - System.out.println("manufacturer: " + computerSystem.getManufacturer()); - System.out.println("model: " + computerSystem.getModel()); - System.out.println("serialnumber: " + computerSystem.getSerialNumber()); - final Firmware firmware = computerSystem.getFirmware(); - System.out.println("firmware:"); - System.out.println(" manufacturer: " + firmware.getManufacturer()); - System.out.println(" name: " + firmware.getName()); - System.out.println(" description: " + firmware.getDescription()); - System.out.println(" version: " + firmware.getVersion()); - System.out.println(" release date: " + (firmware.getReleaseDate() == null ? "unknown" - : firmware.getReleaseDate() == null ? "unknown" : firmware.getReleaseDate())); - final Baseboard baseboard = computerSystem.getBaseboard(); - System.out.println("baseboard:"); - System.out.println(" manufacturer: " + baseboard.getManufacturer()); - System.out.println(" model: " + baseboard.getModel()); - System.out.println(" version: " + baseboard.getVersion()); - System.out.println(" serialnumber: " + baseboard.getSerialNumber()); - } + public static void main(String[] args) { - public static void printProcessor(CentralProcessor processor) { - System.out.println(processor); - System.out.println(" " + processor.getPhysicalProcessorCount() + " physical CPU(s)"); - System.out.println(" " + processor.getLogicalProcessorCount() + " logical CPU(s)"); - System.out.println("Identifier: " + processor.getIdentifier()); - System.out.println("ProcessorID: " + processor.getProcessorID()); - } + logger.info("Initializing System..."); + SystemInfo systemInfo = new SystemInfo(); + HardwareAbstractionLayer hal = systemInfo.getHardware(); + logger.info("Checking computer system..."); + printComputerSystem(hal.getComputerSystem()); + logger.info("Checking Processor..."); + printProcessor(hal.getProcessor()); + logger.info("Checking Memory..."); + printMemory(hal.getMemory()); + logger.info("Checking CPU..."); + printCpu(hal.getProcessor()); + logger.info("Checking Sensors..."); + printSensors(hal.getSensors()); + logger.info("Checking Power sources..."); + printPowerSources(hal.getPowerSources()); + logger.info("Checking Disks..."); + printDisks(hal.getDiskStores()); + logger.info("Checking Network interfaces..."); + printNetworkInterfaces(hal.getNetworkIFs()); + // hardware: displays + logger.info("Checking Displays..."); + printDisplays(hal.getDisplays()); + // hardware: USB devices + logger.info("Checking USB Devices..."); + printUsbDevices(hal.getUsbDevices(true)); + OperatingSystem os = systemInfo.getOperatingSystem(); + System.out.println(os); + logger.info("Checking Processes..."); + printProcesses(os, hal.getMemory()); + logger.info("Checking File System..."); + printFileSystem(os.getFileSystem()); + logger.info("Checking Network parameterss..."); + printNetworkParameters(os.getNetworkParams()); + } - public static void printMemory(GlobalMemory memory) { - System.out.println("以使用内存: " + FormatUtil.formatBytes(memory.getAvailable()) + "总共内存" - + FormatUtil.formatBytes(memory.getTotal())); - } + public static void printComputerSystem(final ComputerSystem computerSystem) { + System.out.println("manufacturer: " + computerSystem.getManufacturer()); + System.out.println("model: " + computerSystem.getModel()); + System.out.println("serialnumber: " + computerSystem.getSerialNumber()); + final Firmware firmware = computerSystem.getFirmware(); + System.out.println("firmware:"); + System.out.println(" manufacturer: " + firmware.getManufacturer()); + System.out.println(" name: " + firmware.getName()); + System.out.println(" description: " + firmware.getDescription()); + System.out.println(" version: " + firmware.getVersion()); + System.out.println(" release date: " + (firmware.getReleaseDate() == null ? "unknown" + : firmware.getReleaseDate() == null ? "unknown" : firmware.getReleaseDate())); + final Baseboard baseboard = computerSystem.getBaseboard(); + System.out.println("baseboard:"); + System.out.println(" manufacturer: " + baseboard.getManufacturer()); + System.out.println(" model: " + baseboard.getModel()); + System.out.println(" version: " + baseboard.getVersion()); + System.out.println(" serialnumber: " + baseboard.getSerialNumber()); + } - public static void printCpu(CentralProcessor processor) { - long[] prevTicks = processor.getSystemCpuLoadTicks(); - System.out.println("CPU, IOWait, and IRQ ticks @ 0 sec:" + Arrays.toString(prevTicks)); - // Wait a second... - Util.sleep(1000); - long[] ticks = processor.getSystemCpuLoadTicks(); - System.out.println("CPU, IOWait, and IRQ ticks @ 1 sec:" + Arrays.toString(ticks)); - long user = - ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()]; - long nice = - ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; - long sys = - ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; - long idle = - ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; - long iowait = - ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; - long irq = - ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; - long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; - long steal = - ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; - long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal; - System.out.format( - "User: %.1f%% Nice: %.1f%% System: %.1f%% Idle: %.1f%% IOwait: %.1f%% IRQ: %.1f%% SoftIRQ: %.1f%% Steal: %.1f%%%n", - 100d * user / totalCpu, 100d * nice / totalCpu, 100d * sys / totalCpu, 100d * idle / totalCpu, - 100d * iowait / totalCpu, 100d * irq / totalCpu, 100d * softirq / totalCpu, 100d * steal / totalCpu); - // System.out.format("CPU load: %.1f%% (counting ticks)%n", processor.getSystemCpuLoadTicks() * 100); - // System.out.format("CPU load: %.1f%% (OS MXBean)%n", processor.getProcessorCpuLoadBetweenTicks() * 100); - double[] loadAverage = processor.getSystemLoadAverage(3); - System.out.println("CPU load averages:" + (loadAverage[0] < 0 ? " N/A" : String.format(" %.2f", loadAverage[0])) - + (loadAverage[1] < 0 ? " N/A" : String.format(" %.2f", loadAverage[1])) - + (loadAverage[2] < 0 ? " N/A" : String.format(" %.2f", loadAverage[2]))); - // per core CPU - StringBuilder procCpu = new StringBuilder("CPU load per processor:"); - double[] load = processor.getProcessorCpuLoadBetweenTicks(processor.getProcessorCpuLoadTicks()); - for (double avg : load) { - procCpu.append(String.format(" %.1f%%", avg * 100)); - } - System.out.println(procCpu.toString()); - } + public static void printProcessor(CentralProcessor processor) { + System.out.println(processor); + System.out.println(" " + processor.getPhysicalProcessorCount() + " physical CPU(s)"); + System.out.println(" " + processor.getLogicalProcessorCount() + " logical CPU(s)"); + System.out.println("Identifier: " + processor.getIdentifier()); + System.out.println("ProcessorID: " + processor.getProcessorID()); + } - public static void printProcesses(OperatingSystem os, GlobalMemory memory) { - System.out.println("Processes: " + os.getProcessCount() + ", Threads: " + os.getThreadCount()); - // Sort by highest CPU - List procs = Arrays.asList(os.getProcesses(5, OperatingSystem.ProcessSort.CPU)); - System.out.println(" PID %CPU %MEM VSZ RSS Name"); - for (int i = 0; i < procs.size() && i < 5; i++) { - OSProcess p = procs.get(i); - System.out.format(" %5d %5.1f %4.1f %9s %9s %s%n", p.getProcessID(), - 100d * (p.getKernelTime() + p.getUserTime()) / p.getUpTime(), - 100d * p.getResidentSetSize() / memory.getTotal(), FormatUtil.formatBytes(p.getVirtualSize()), - FormatUtil.formatBytes(p.getResidentSetSize()), p.getName()); - } - } + public static void printMemory(GlobalMemory memory) { + System.out.println("以使用内存: " + FormatUtil.formatBytes(memory.getAvailable()) + "总共内存" + + FormatUtil.formatBytes(memory.getTotal())); + } - public static void printSensors(Sensors sensors) { - System.out.println("Sensors:"); - System.out.format(" CPU Temperature: %.1f°C%n", sensors.getCpuTemperature()); - System.out.println(" Fan Speeds: " + Arrays.toString(sensors.getFanSpeeds())); - System.out.format(" CPU Voltage: %.1fV%n", sensors.getCpuVoltage()); - } + public static void printCpu(CentralProcessor processor) { + long[] prevTicks = processor.getSystemCpuLoadTicks(); + System.out.println("CPU, IOWait, and IRQ ticks @ 0 sec:" + Arrays.toString(prevTicks)); + // Wait a second... + Util.sleep(1000); + long[] ticks = processor.getSystemCpuLoadTicks(); + System.out.println("CPU, IOWait, and IRQ ticks @ 1 sec:" + Arrays.toString(ticks)); + long user = + ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()]; + long nice = + ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; + long sys = + ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; + long idle = + ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; + long iowait = + ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; + long irq = + ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; + long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] + - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; + long steal = + ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; + long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal; + System.out.format( + "User: %.1f%% Nice: %.1f%% System: %.1f%% Idle: %.1f%% IOwait: %.1f%% IRQ: %.1f%% SoftIRQ: %.1f%% Steal: %.1f%%%n", + 100d * user / totalCpu, 100d * nice / totalCpu, 100d * sys / totalCpu, 100d * idle / totalCpu, + 100d * iowait / totalCpu, 100d * irq / totalCpu, 100d * softirq / totalCpu, 100d * steal / totalCpu); + // System.out.format("CPU load: %.1f%% (counting ticks)%n", processor.getSystemCpuLoadTicks() * 100); + // System.out.format("CPU load: %.1f%% (OS MXBean)%n", processor.getProcessorCpuLoadBetweenTicks() * 100); + double[] loadAverage = processor.getSystemLoadAverage(3); + System.out.println("CPU load averages:" + (loadAverage[0] < 0 ? " N/A" : String.format(" %.2f", loadAverage[0])) + + (loadAverage[1] < 0 ? " N/A" : String.format(" %.2f", loadAverage[1])) + + (loadAverage[2] < 0 ? " N/A" : String.format(" %.2f", loadAverage[2]))); + // per core CPU + StringBuilder procCpu = new StringBuilder("CPU load per processor:"); + double[] load = processor.getProcessorCpuLoadBetweenTicks(processor.getProcessorCpuLoadTicks()); + for (double avg : load) { + procCpu.append(String.format(" %.1f%%", avg * 100)); + } + System.out.println(procCpu.toString()); + } - public static void printPowerSources(PowerSource[] powerSources) { - StringBuilder sb = new StringBuilder("Power: "); - if (powerSources.length == 0) { - sb.append("Unknown"); - } else { - double timeRemaining = powerSources[0].getTimeRemaining(); - if (timeRemaining < -1d) { - sb.append("Charging"); - } else if (timeRemaining < 0d) { - sb.append("Calculating time remaining"); - } else { - sb.append(String.format("%d:%02d remaining", (int) (timeRemaining / 3600), - (int) (timeRemaining / 60) % 60)); - } - } - for (PowerSource pSource : powerSources) { - sb.append(String.format("%n %s @ %.1f%%", pSource.getName(), pSource.getRemainingCapacity() * 100d)); - } - System.out.println(sb.toString()); - } + public static void printSensors(Sensors sensors) { + System.out.println("Sensors:"); + System.out.format(" CPU Temperature: %.1f°C%n", sensors.getCpuTemperature()); + System.out.println(" Fan Speeds: " + Arrays.toString(sensors.getFanSpeeds())); + System.out.format(" CPU Voltage: %.1fV%n", sensors.getCpuVoltage()); + } - public static void printDisks(HWDiskStore[] diskStores) { - System.out.println("Disks:"); - for (HWDiskStore disk : diskStores) { - boolean readwrite = disk.getReads() > 0 || disk.getWrites() > 0; - System.out.format(" %s: (model: %s - S/N: %s) size: %s, reads: %s (%s), writes: %s (%s), xfer: %s ms%n", - disk.getName(), disk.getModel(), disk.getSerial(), - disk.getSize() > 0 ? FormatUtil.formatBytesDecimal(disk.getSize()) : "?", - readwrite ? disk.getReads() : "?", readwrite ? FormatUtil.formatBytes(disk.getReadBytes()) : "?", - readwrite ? disk.getWrites() : "?", readwrite ? FormatUtil.formatBytes(disk.getWriteBytes()) : "?", - readwrite ? disk.getTransferTime() : "?"); - HWPartition[] partitions = disk.getPartitions(); - if (partitions == null) { - // TODO Remove when all OS's implemented - continue; - } - for (HWPartition part : partitions) { - System.out.format(" |-- %s: %s (%s) Maj:Min=%d:%d, size: %s%s%n", part.getIdentification(), - part.getName(), part.getType(), part.getMajor(), part.getMinor(), - FormatUtil.formatBytesDecimal(part.getSize()), - part.getMountPoint().isEmpty() ? "" : " @ " + part.getMountPoint()); - } - } - } + public static void printPowerSources(PowerSource[] powerSources) { + StringBuilder sb = new StringBuilder("Power: "); + if (powerSources.length == 0) { + sb.append("Unknown"); + } else { + double timeRemaining = powerSources[0].getTimeRemaining(); + if (timeRemaining < -1d) { + sb.append("Charging"); + } else if (timeRemaining < 0d) { + sb.append("Calculating time remaining"); + } else { + sb.append(String.format("%d:%02d remaining", (int) (timeRemaining / 3600), + (int) (timeRemaining / 60) % 60)); + } + } + for (PowerSource pSource : powerSources) { + sb.append(String.format("%n %s @ %.1f%%", pSource.getName(), pSource.getRemainingCapacity() * 100d)); + } + System.out.println(sb.toString()); + } - public static void printFileSystem(FileSystem fileSystem) { - System.out.println("File System:"); - System.out.format(" File Descriptors: %d/%d%n", fileSystem.getOpenFileDescriptors(), - fileSystem.getMaxFileDescriptors()); - OSFileStore[] fsArray = fileSystem.getFileStores(); - for (OSFileStore fs : fsArray) { - long usable = fs.getUsableSpace(); - long total = fs.getTotalSpace(); - System.out.format( - " %s (%s) [%s] %s of %s free (%.1f%%) is %s " - + (fs.getLogicalVolume() != null && fs.getLogicalVolume().length() > 0 ? "[%s]" : "%s") - + " and is mounted at %s%n", - fs.getName(), fs.getDescription().isEmpty() ? "file system" : fs.getDescription(), fs.getType(), - FormatUtil.formatBytes(usable), FormatUtil.formatBytes(fs.getTotalSpace()), 100d * usable / total, - fs.getVolume(), fs.getLogicalVolume(), fs.getMount()); - } - } + public static void printDisks(HWDiskStore[] diskStores) { + System.out.println("Disks:"); + for (HWDiskStore disk : diskStores) { + boolean readwrite = disk.getReads() > 0 || disk.getWrites() > 0; + System.out.format(" %s: (model: %s - S/N: %s) size: %s, reads: %s (%s), writes: %s (%s), xfer: %s ms%n", + disk.getName(), disk.getModel(), disk.getSerial(), + disk.getSize() > 0 ? FormatUtil.formatBytesDecimal(disk.getSize()) : "?", + readwrite ? disk.getReads() : "?", readwrite ? FormatUtil.formatBytes(disk.getReadBytes()) : "?", + readwrite ? disk.getWrites() : "?", readwrite ? FormatUtil.formatBytes(disk.getWriteBytes()) : "?", + readwrite ? disk.getTransferTime() : "?"); + HWPartition[] partitions = disk.getPartitions(); + if (partitions == null) { + // TODO Remove when all OS's implemented + continue; + } + for (HWPartition part : partitions) { + System.out.format(" |-- %s: %s (%s) Maj:Min=%d:%d, size: %s%s%n", part.getIdentification(), + part.getName(), part.getType(), part.getMajor(), part.getMinor(), + FormatUtil.formatBytesDecimal(part.getSize()), + part.getMountPoint().isEmpty() ? "" : " @ " + part.getMountPoint()); + } + } + } - public static void printNetworkInterfaces(NetworkIF[] networkIFs) { - System.out.println("Network interfaces:"); - for (NetworkIF net : networkIFs) { - System.out.format(" Name: %s (%s)%n", net.getName(), net.getDisplayName()); - System.out.format(" MAC Address: %s %n", net.getMacaddr()); - System.out.format(" MTU: %s, Speed: %s %n", net.getMTU(), FormatUtil.formatValue(net.getSpeed(), "bps")); - System.out.format(" IPv4: %s %n", Arrays.toString(net.getIPv4addr())); - System.out.format(" IPv6: %s %n", Arrays.toString(net.getIPv6addr())); - boolean hasData = net.getBytesRecv() > 0 || net.getBytesSent() > 0 || net.getPacketsRecv() > 0 - || net.getPacketsSent() > 0; - System.out.format(" Traffic: received %s/%s%s; transmitted %s/%s%s %n", - hasData ? net.getPacketsRecv() + " packets" : "?", - hasData ? FormatUtil.formatBytes(net.getBytesRecv()) : "?", - hasData ? " (" + net.getInErrors() + " err)" : "", - hasData ? net.getPacketsSent() + " packets" : "?", - hasData ? FormatUtil.formatBytes(net.getBytesSent()) : "?", - hasData ? " (" + net.getOutErrors() + " err)" : ""); - } - } + public static void printNetworkInterfaces(NetworkIF[] networkIFs) { + System.out.println("Network interfaces:"); + for (NetworkIF net : networkIFs) { + System.out.format(" Name: %s (%s)%n", net.getName(), net.getDisplayName()); + System.out.format(" MAC Address: %s %n", net.getMacaddr()); + System.out.format(" MTU: %s, Speed: %s %n", net.getMTU(), FormatUtil.formatValue(net.getSpeed(), "bps")); + System.out.format(" IPv4: %s %n", Arrays.toString(net.getIPv4addr())); + System.out.format(" IPv6: %s %n", Arrays.toString(net.getIPv6addr())); + boolean hasData = net.getBytesRecv() > 0 || net.getBytesSent() > 0 || net.getPacketsRecv() > 0 + || net.getPacketsSent() > 0; + System.out.format(" Traffic: received %s/%s%s; transmitted %s/%s%s %n", + hasData ? net.getPacketsRecv() + " packets" : "?", + hasData ? FormatUtil.formatBytes(net.getBytesRecv()) : "?", + hasData ? " (" + net.getInErrors() + " err)" : "", + hasData ? net.getPacketsSent() + " packets" : "?", + hasData ? FormatUtil.formatBytes(net.getBytesSent()) : "?", + hasData ? " (" + net.getOutErrors() + " err)" : ""); + } + } - public static void printNetworkParameters(NetworkParams networkParams) { - System.out.println("Network parameters:"); - System.out.format(" Host name: %s%n", networkParams.getHostName()); - System.out.format(" Domain name: %s%n", networkParams.getDomainName()); - System.out.format(" DNS servers: %s%n", Arrays.toString(networkParams.getDnsServers())); - System.out.format(" IPv4 Gateway: %s%n", networkParams.getIpv4DefaultGateway()); - System.out.format(" IPv6 Gateway: %s%n", networkParams.getIpv6DefaultGateway()); - } + public static void printDisplays(Display[] displays) { + System.out.println("Displays:"); + int i = 0; + for (Display display : displays) { + System.out.println(" Display " + i + ":"); + System.out.println(display.toString()); + i++; + } + } - public static void printDisplays(Display[] displays) { - System.out.println("Displays:"); - int i = 0; - for (Display display : displays) { - System.out.println(" Display " + i + ":"); - System.out.println(display.toString()); - i++; - } - } + public static void printUsbDevices(UsbDevice[] usbDevices) { + System.out.println("USB Devices:"); + for (UsbDevice usbDevice : usbDevices) { + System.out.println(usbDevice.toString()); + } + } - public static void printUsbDevices(UsbDevice[] usbDevices) { - System.out.println("USB Devices:"); - for (UsbDevice usbDevice : usbDevices) { - System.out.println(usbDevice.toString()); - } - } + public static void printProcesses(OperatingSystem os, GlobalMemory memory) { + System.out.println("Processes: " + os.getProcessCount() + ", Threads: " + os.getThreadCount()); + // Sort by highest CPU + List procs = Arrays.asList(os.getProcesses(5, OperatingSystem.ProcessSort.CPU)); + System.out.println(" PID %CPU %MEM VSZ RSS Name"); + for (int i = 0; i < procs.size() && i < 5; i++) { + OSProcess p = procs.get(i); + System.out.format(" %5d %5.1f %4.1f %9s %9s %s%n", p.getProcessID(), + 100d * (p.getKernelTime() + p.getUserTime()) / p.getUpTime(), + 100d * p.getResidentSetSize() / memory.getTotal(), FormatUtil.formatBytes(p.getVirtualSize()), + FormatUtil.formatBytes(p.getResidentSetSize()), p.getName()); + } + } - public static void main(String[] args) { + public static void printFileSystem(FileSystem fileSystem) { + System.out.println("File System:"); + System.out.format(" File Descriptors: %d/%d%n", fileSystem.getOpenFileDescriptors(), + fileSystem.getMaxFileDescriptors()); + OSFileStore[] fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) { + long usable = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + System.out.format( + " %s (%s) [%s] %s of %s free (%.1f%%) is %s " + + (fs.getLogicalVolume() != null && fs.getLogicalVolume().length() > 0 ? "[%s]" : "%s") + + " and is mounted at %s%n", + fs.getName(), fs.getDescription().isEmpty() ? "file system" : fs.getDescription(), fs.getType(), + FormatUtil.formatBytes(usable), FormatUtil.formatBytes(fs.getTotalSpace()), 100d * usable / total, + fs.getVolume(), fs.getLogicalVolume(), fs.getMount()); + } + } - logger.info("Initializing System..."); - SystemInfo systemInfo = new SystemInfo(); - HardwareAbstractionLayer hal = systemInfo.getHardware(); - logger.info("Checking computer system..."); - printComputerSystem(hal.getComputerSystem()); - logger.info("Checking Processor..."); - printProcessor(hal.getProcessor()); - logger.info("Checking Memory..."); - printMemory(hal.getMemory()); - logger.info("Checking CPU..."); - printCpu(hal.getProcessor()); - logger.info("Checking Sensors..."); - printSensors(hal.getSensors()); - logger.info("Checking Power sources..."); - printPowerSources(hal.getPowerSources()); - logger.info("Checking Disks..."); - printDisks(hal.getDiskStores()); - logger.info("Checking Network interfaces..."); - printNetworkInterfaces(hal.getNetworkIFs()); - // hardware: displays - logger.info("Checking Displays..."); - printDisplays(hal.getDisplays()); - // hardware: USB devices - logger.info("Checking USB Devices..."); - printUsbDevices(hal.getUsbDevices(true)); - OperatingSystem os = systemInfo.getOperatingSystem(); - System.out.println(os); - logger.info("Checking Processes..."); - printProcesses(os, hal.getMemory()); - logger.info("Checking File System..."); - printFileSystem(os.getFileSystem()); - logger.info("Checking Network parameterss..."); - printNetworkParameters(os.getNetworkParams()); - } + public static void printNetworkParameters(NetworkParams networkParams) { + System.out.println("Network parameters:"); + System.out.format(" Host name: %s%n", networkParams.getHostName()); + System.out.format(" Domain name: %s%n", networkParams.getDomainName()); + System.out.format(" DNS servers: %s%n", Arrays.toString(networkParams.getDnsServers())); + System.out.format(" IPv4 Gateway: %s%n", networkParams.getIpv4DefaultGateway()); + System.out.format(" IPv6 Gateway: %s%n", networkParams.getIpv6DefaultGateway()); + } } diff --git a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiBgColor.java b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiBgColor.java index 7d255e71..b4bc5475 100644 --- a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiBgColor.java +++ b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiBgColor.java @@ -9,36 +9,36 @@ */ public enum AnsiBgColor implements AnsiElement { - DEFAULT(""), - BLACK("40"), - RED("41"), - GREEN("42"), - YELLOW("43"), - BLUE("44"), - MAGENTA("45"), - CYAN("46"), - WHITE("47"), - BRIGHT_BLACK("100"), - BRIGHT_RED("101"), - BRIGHT_GREEN("102"), - BRIGHT_YELLOW("109"), - BRIGHT_BLUE("104"), - BRIGHT_MAGENTA("105"), - BRIGHT_CYAN("106"), - BRIGHT_WHITE("107"); + DEFAULT(""), + BLACK("40"), + RED("41"), + GREEN("42"), + YELLOW("43"), + BLUE("44"), + MAGENTA("45"), + CYAN("46"), + WHITE("47"), + BRIGHT_BLACK("100"), + BRIGHT_RED("101"), + BRIGHT_GREEN("102"), + BRIGHT_YELLOW("109"), + BRIGHT_BLUE("104"), + BRIGHT_MAGENTA("105"), + BRIGHT_CYAN("106"), + BRIGHT_WHITE("107"); - AnsiBgColor(String code) { - this.code = code; - } + private final String code; - private final String code; + AnsiBgColor(String code) { + this.code = code; + } - public String getCode() { - return code; - } + @Override + public String toString() { + return code; + } - @Override - public String toString() { - return code; - } + public String getCode() { + return code; + } } diff --git a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiColor.java b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiColor.java index adfdf673..752bf2e5 100644 --- a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiColor.java +++ b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiColor.java @@ -9,36 +9,36 @@ */ public enum AnsiColor implements AnsiElement { - DEFAULT(""), - BLACK("30"), - RED("31"), - GREEN("32"), - YELLOW("33"), - BLUE("34"), - MAGENTA("35"), - CYAN("36"), - WHITE("37"), - BRIGHT_BLACK("90"), - BRIGHT_RED("91"), - BRIGHT_GREEN("92"), - BRIGHT_YELLOW("99"), - BRIGHT_BLUE("94"), - BRIGHT_MAGENTA("95"), - BRIGHT_CYAN("96"), - BRIGHT_WHITE("97"); + DEFAULT(""), + BLACK("30"), + RED("31"), + GREEN("32"), + YELLOW("33"), + BLUE("34"), + MAGENTA("35"), + CYAN("36"), + WHITE("37"), + BRIGHT_BLACK("90"), + BRIGHT_RED("91"), + BRIGHT_GREEN("92"), + BRIGHT_YELLOW("99"), + BRIGHT_BLUE("94"), + BRIGHT_MAGENTA("95"), + BRIGHT_CYAN("96"), + BRIGHT_WHITE("97"); - AnsiColor(String code) { - this.code = code; - } + private final String code; - private final String code; + AnsiColor(String code) { + this.code = code; + } - public String getCode() { - return code; - } + @Override + public String toString() { + return code; + } - @Override - public String toString() { - return code; - } + public String getCode() { + return code; + } } diff --git a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiElement.java b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiElement.java index 5c3795e5..3e807ded 100644 --- a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiElement.java +++ b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiElement.java @@ -2,10 +2,10 @@ public interface AnsiElement { - /** - * @return the ANSI escape code - */ - @Override - String toString(); + /** + * @return the ANSI escape code + */ + @Override + String toString(); } diff --git a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiSgr.java b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiSgr.java index 1aefa3eb..27636a10 100644 --- a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiSgr.java +++ b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/AnsiSgr.java @@ -15,28 +15,28 @@ */ public enum AnsiSgr implements AnsiElement { - NORMAL("0"), - BOLD("1"), - FAINT("2"), - ITALIC("3"), - UNDERLINE("4"), - SLOW_BLINK("5"), - RAPID_BLINK("6"), - REVERSE_VIDEO("7"), - CONCEAL("8"); + NORMAL("0"), + BOLD("1"), + FAINT("2"), + ITALIC("3"), + UNDERLINE("4"), + SLOW_BLINK("5"), + RAPID_BLINK("6"), + REVERSE_VIDEO("7"), + CONCEAL("8"); - AnsiSgr(String code) { - this.code = code; - } + private final String code; - private final String code; + AnsiSgr(String code) { + this.code = code; + } - public String getCode() { - return code; - } + @Override + public String toString() { + return code; + } - @Override - public String toString() { - return code; - } + public String getCode() { + return code; + } } diff --git a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/Color.java b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/Color.java index d8c14e4f..28c722ef 100644 --- a/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/Color.java +++ b/javalib-cli/src/main/java/io/github/dunwu/javalib/constant/Color.java @@ -6,31 +6,31 @@ */ public enum Color { - DEFAULT("默认"), - BLACK("黑色"), - RED("红色"), - GREEN("绿色"), - YELLOW("黄色"), - BLUE("蓝色"), - MAGENTA("紫色"), - CYAN("青色"), - WHITE("白色"), - BRIGHT_BLACK("亮黑色"), - BRIGHT_RED("亮红色"), - BRIGHT_GREEN("亮绿色"), - BRIGHT_YELLOW("亮黄色"), - BRIGHT_BLUE("亮蓝色"), - BRIGHT_MAGENTA("亮紫色"), - BRIGHT_CYAN("亮青色"), - BRIGHT_WHITE("亮白色"); + DEFAULT("默认"), + BLACK("黑色"), + RED("红色"), + GREEN("绿色"), + YELLOW("黄色"), + BLUE("蓝色"), + MAGENTA("紫色"), + CYAN("青色"), + WHITE("白色"), + BRIGHT_BLACK("亮黑色"), + BRIGHT_RED("亮红色"), + BRIGHT_GREEN("亮绿色"), + BRIGHT_YELLOW("亮黄色"), + BRIGHT_BLUE("亮蓝色"), + BRIGHT_MAGENTA("亮紫色"), + BRIGHT_CYAN("亮青色"), + BRIGHT_WHITE("亮白色"); - Color(String desc) { - this.desc = desc; - } + private String desc; - private String desc; + Color(String desc) { + this.desc = desc; + } - public String getDesc() { - return desc; - } + public String getDesc() { + return desc; + } } diff --git a/javalib-cli/src/test/java/io/github/dunwu/javalib/CliUtilTests.java b/javalib-cli/src/test/java/io/github/dunwu/javalib/CliUtilTests.java index 24b4c6f8..92181a57 100644 --- a/javalib-cli/src/test/java/io/github/dunwu/javalib/CliUtilTests.java +++ b/javalib-cli/src/test/java/io/github/dunwu/javalib/CliUtilTests.java @@ -8,10 +8,10 @@ */ public class CliUtilTests { - @Test - public void prepare() throws Exception { - String[] args = {"-sql", "select * from aa", "-name", "测试"}; - CliUtil.prepare(args); - } + @Test + public void prepare() throws Exception { + String[] args = { "-sql", "select * from aa", "-name", "测试" }; + CliUtil.prepare(args); + } } diff --git a/javalib-cli/src/test/java/io/github/dunwu/javalib/SystemInfoTest.java b/javalib-cli/src/test/java/io/github/dunwu/javalib/SystemInfoTest.java index b1a33287..c6c28490 100644 --- a/javalib-cli/src/test/java/io/github/dunwu/javalib/SystemInfoTest.java +++ b/javalib-cli/src/test/java/io/github/dunwu/javalib/SystemInfoTest.java @@ -24,297 +24,297 @@ */ public class SystemInfoTest { - private static final Logger logger = LoggerFactory.getLogger(SystemInfoTest.class); + private static final Logger logger = LoggerFactory.getLogger(SystemInfoTest.class); - static List oshi = new ArrayList<>(); - - /** - * Test that this platform is implemented.. - */ - @Test - public void testPlatformEnum() { - assertFalse(PlatformEnum.UNKNOWN.equals(SystemInfo.getCurrentPlatformEnum())); - // Exercise the main method - main(null); - } - - /** - * The main method, demonstrating use of classes. - * - * @param args the arguments (unused) - */ - public static void main(String[] args) { - logger.info("Initializing System..."); - SystemInfo si = new SystemInfo(); - - HardwareAbstractionLayer hal = si.getHardware(); - OperatingSystem os = si.getOperatingSystem(); - - printOperatingSystem(os); - - logger.info("Checking computer system..."); - printComputerSystem(hal.getComputerSystem()); - - logger.info("Checking Processor..."); - printProcessor(hal.getProcessor()); - - logger.info("Checking Memory..."); - printMemory(hal.getMemory()); - - logger.info("Checking CPU..."); - printCpu(hal.getProcessor()); - - logger.info("Checking Processes..."); - printProcesses(os, hal.getMemory()); - - logger.info("Checking Services..."); - printServices(os); - - logger.info("Checking Sensors..."); - printSensors(hal.getSensors()); - - logger.info("Checking Power sources..."); - printPowerSources(hal.getPowerSources()); - - logger.info("Checking Disks..."); - printDisks(hal.getDiskStores()); - - logger.info("Checking File System..."); - printFileSystem(os.getFileSystem()); - - logger.info("Checking Network interfaces..."); - printNetworkInterfaces(hal.getNetworkIFs()); - - logger.info("Checking Network parameters..."); - printNetworkParameters(os.getNetworkParams()); - - // hardware: displays - logger.info("Checking Displays..."); - printDisplays(hal.getDisplays()); - - // hardware: USB devices - logger.info("Checking USB Devices..."); - printUsbDevices(hal.getUsbDevices(true)); - - logger.info("Checking Sound Cards..."); - printSoundCards(hal.getSoundCards()); - - StringBuilder output = new StringBuilder(); - for (int i = 0; i < oshi.size(); i++) { - output.append(oshi.get(i)); - if (oshi.get(i) != null && !oshi.get(i).endsWith("\n")) { - output.append('\n'); - } - } - logger.info("Printing Operating System and Hardware Info:{}{}", '\n', output); - } - - private static void printOperatingSystem(final OperatingSystem os) { - oshi.add(String.valueOf(os)); - oshi.add("Booted: " + Instant.ofEpochSecond(os.getSystemBootTime())); - oshi.add("Uptime: " + FormatUtil.formatElapsedSecs(os.getSystemUptime())); - oshi.add("Running with" + (os.isElevated() ? "" : "out") + " elevated permissions."); - } - - private static void printComputerSystem(final ComputerSystem computerSystem) { - oshi.add("system: " + computerSystem.toString()); - oshi.add(" firmware: " + computerSystem.getFirmware().toString()); - oshi.add(" baseboard: " + computerSystem.getBaseboard().toString()); - } - - private static void printProcessor(CentralProcessor processor) { - oshi.add(processor.toString()); - } - - private static void printMemory(GlobalMemory memory) { - oshi.add("Memory: \n " + memory.toString()); - VirtualMemory vm = memory.getVirtualMemory(); - oshi.add("Swap: \n " + vm.toString()); - PhysicalMemory[] pmArray = memory.getPhysicalMemory(); - if (pmArray.length > 0) { - oshi.add("Physical Memory: "); - for (PhysicalMemory pm : pmArray) { - oshi.add(" " + pm.toString()); - } - } - } - - private static void printCpu(CentralProcessor processor) { - oshi.add("Context Switches/Interrupts: " + processor.getContextSwitches() + " / " + processor.getInterrupts()); - - long[] prevTicks = processor.getSystemCpuLoadTicks(); - long[][] prevProcTicks = processor.getProcessorCpuLoadTicks(); - oshi.add("CPU, IOWait, and IRQ ticks @ 0 sec:" + Arrays.toString(prevTicks)); - // Wait a second... - Util.sleep(1000); - long[] ticks = processor.getSystemCpuLoadTicks(); - oshi.add("CPU, IOWait, and IRQ ticks @ 1 sec:" + Arrays.toString(ticks)); - long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; - long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; - long sys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; - long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; - long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; - long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; - long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; - long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; - long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal; - - oshi.add(String.format( - "User: %.1f%% Nice: %.1f%% System: %.1f%% Idle: %.1f%% IOwait: %.1f%% IRQ: %.1f%% SoftIRQ: %.1f%% Steal: %.1f%%", - 100d * user / totalCpu, 100d * nice / totalCpu, 100d * sys / totalCpu, 100d * idle / totalCpu, - 100d * iowait / totalCpu, 100d * irq / totalCpu, 100d * softirq / totalCpu, 100d * steal / totalCpu)); - oshi.add(String.format("CPU load: %.1f%%", processor.getSystemCpuLoadBetweenTicks(prevTicks) * 100)); - double[] loadAverage = processor.getSystemLoadAverage(3); - oshi.add("CPU load averages:" + (loadAverage[0] < 0 ? " N/A" : String.format(" %.2f", loadAverage[0])) - + (loadAverage[1] < 0 ? " N/A" : String.format(" %.2f", loadAverage[1])) - + (loadAverage[2] < 0 ? " N/A" : String.format(" %.2f", loadAverage[2]))); - // per core CPU - StringBuilder procCpu = new StringBuilder("CPU load per processor:"); - double[] load = processor.getProcessorCpuLoadBetweenTicks(prevProcTicks); - for (double avg : load) { - procCpu.append(String.format(" %.1f%%", avg * 100)); - } - oshi.add(procCpu.toString()); - long freq = processor.getProcessorIdentifier().getVendorFreq(); - if (freq > 0) { - oshi.add("Vendor Frequency: " + FormatUtil.formatHertz(freq)); - } - freq = processor.getMaxFreq(); - if (freq > 0) { - oshi.add("Max Frequency: " + FormatUtil.formatHertz(freq)); - } - long[] freqs = processor.getCurrentFreq(); - if (freqs[0] > 0) { - StringBuilder sb = new StringBuilder("Current Frequencies: "); - for (int i = 0; i < freqs.length; i++) { - if (i > 0) { - sb.append(", "); - } - sb.append(FormatUtil.formatHertz(freqs[i])); - } - oshi.add(sb.toString()); - } - } - - private static void printProcesses(OperatingSystem os, GlobalMemory memory) { - oshi.add("Processes: " + os.getProcessCount() + ", Threads: " + os.getThreadCount()); - // Sort by highest CPU - List procs = Arrays.asList(os.getProcesses(5, ProcessSort.CPU)); - - oshi.add(" PID %CPU %MEM VSZ RSS Name"); - for (int i = 0; i < procs.size() && i < 5; i++) { - OSProcess p = procs.get(i); - oshi.add(String.format(" %5d %5.1f %4.1f %9s %9s %s", p.getProcessID(), - 100d * (p.getKernelTime() + p.getUserTime()) / p.getUpTime(), - 100d * p.getResidentSetSize() / memory.getTotal(), FormatUtil.formatBytes(p.getVirtualSize()), - FormatUtil.formatBytes(p.getResidentSetSize()), p.getName())); - } - } - - private static void printServices(OperatingSystem os) { - oshi.add("Services: "); - oshi.add(" PID State Name"); - // DO 5 each of running and stopped - int i = 0; - for (OSService s : os.getServices()) { - if (s.getState().equals(OSService.State.RUNNING) && i++ < 5) { - oshi.add(String.format(" %5d %7s %s", s.getProcessID(), s.getState(), s.getName())); - } - } - i = 0; - for (OSService s : os.getServices()) { - if (s.getState().equals(OSService.State.STOPPED) && i++ < 5) { - oshi.add(String.format(" %5d %7s %s", s.getProcessID(), s.getState(), s.getName())); - } - } - } - - private static void printSensors(Sensors sensors) { - oshi.add("Sensors: " + sensors.toString()); - } - - private static void printPowerSources(PowerSource[] powerSources) { - StringBuilder sb = new StringBuilder("Power Sources: "); - if (powerSources.length == 0) { - sb.append("Unknown"); - } - for (PowerSource powerSource : powerSources) { - sb.append("\n ").append(powerSource.toString()); - } - oshi.add(sb.toString()); - } - - private static void printDisks(HWDiskStore[] diskStores) { - oshi.add("Disks:"); - for (HWDiskStore disk : diskStores) { - oshi.add(" " + disk.toString()); - - HWPartition[] partitions = disk.getPartitions(); - for (HWPartition part : partitions) { - oshi.add(" |-- " + part.toString()); - } - } - } - - private static void printFileSystem(FileSystem fileSystem) { - oshi.add("File System:"); - - oshi.add(String.format(" File Descriptors: %d/%d", fileSystem.getOpenFileDescriptors(), - fileSystem.getMaxFileDescriptors())); - - OSFileStore[] fsArray = fileSystem.getFileStores(); - for (OSFileStore fs : fsArray) { - long usable = fs.getUsableSpace(); - long total = fs.getTotalSpace(); - oshi.add(String.format( - " %s (%s) [%s] %s of %s free (%.1f%%), %s of %s files free (%.1f%%) is %s " - + (fs.getLogicalVolume() != null && fs.getLogicalVolume().length() > 0 ? "[%s]" : "%s") - + " and is mounted at %s", - fs.getName(), fs.getDescription().isEmpty() ? "file system" : fs.getDescription(), fs.getType(), - FormatUtil.formatBytes(usable), FormatUtil.formatBytes(fs.getTotalSpace()), 100d * usable / total, - FormatUtil.formatValue(fs.getFreeInodes(), ""), FormatUtil.formatValue(fs.getTotalInodes(), ""), - 100d * fs.getFreeInodes() / fs.getTotalInodes(), fs.getVolume(), fs.getLogicalVolume(), - fs.getMount())); - } - } - - private static void printNetworkInterfaces(NetworkIF[] networkIFs) { - StringBuilder sb = new StringBuilder("Network Interfaces:"); - if (networkIFs.length == 0) { - sb.append(" Unknown"); - } - for (NetworkIF net : networkIFs) { - sb.append("\n ").append(net.toString()); - } - oshi.add(sb.toString()); - } - - private static void printNetworkParameters(NetworkParams networkParams) { - oshi.add("Network parameters:\n " + networkParams.toString()); - } - - private static void printDisplays(Display[] displays) { - oshi.add("Displays:"); - int i = 0; - for (Display display : displays) { - oshi.add(" Display " + i + ":"); - oshi.add(String.valueOf(display)); - i++; - } - } - - private static void printUsbDevices(UsbDevice[] usbDevices) { - oshi.add("USB Devices:"); - for (UsbDevice usbDevice : usbDevices) { - oshi.add(String.valueOf(usbDevice)); - } - } - - private static void printSoundCards(SoundCard[] cards) { - oshi.add("Sound Cards:"); - for (SoundCard card : cards) { - oshi.add(" " + String.valueOf(card)); - } - } + static List oshi = new ArrayList<>(); + + /** + * Test that this platform is implemented.. + */ + @Test + public void testPlatformEnum() { + assertFalse(PlatformEnum.UNKNOWN.equals(SystemInfo.getCurrentPlatformEnum())); + // Exercise the main method + main(null); + } + + /** + * The main method, demonstrating use of classes. + * + * @param args the arguments (unused) + */ + public static void main(String[] args) { + logger.info("Initializing System..."); + SystemInfo si = new SystemInfo(); + + HardwareAbstractionLayer hal = si.getHardware(); + OperatingSystem os = si.getOperatingSystem(); + + printOperatingSystem(os); + + logger.info("Checking computer system..."); + printComputerSystem(hal.getComputerSystem()); + + logger.info("Checking Processor..."); + printProcessor(hal.getProcessor()); + + logger.info("Checking Memory..."); + printMemory(hal.getMemory()); + + logger.info("Checking CPU..."); + printCpu(hal.getProcessor()); + + logger.info("Checking Processes..."); + printProcesses(os, hal.getMemory()); + + logger.info("Checking Services..."); + printServices(os); + + logger.info("Checking Sensors..."); + printSensors(hal.getSensors()); + + logger.info("Checking Power sources..."); + printPowerSources(hal.getPowerSources()); + + logger.info("Checking Disks..."); + printDisks(hal.getDiskStores()); + + logger.info("Checking File System..."); + printFileSystem(os.getFileSystem()); + + logger.info("Checking Network interfaces..."); + printNetworkInterfaces(hal.getNetworkIFs()); + + logger.info("Checking Network parameters..."); + printNetworkParameters(os.getNetworkParams()); + + // hardware: displays + logger.info("Checking Displays..."); + printDisplays(hal.getDisplays()); + + // hardware: USB devices + logger.info("Checking USB Devices..."); + printUsbDevices(hal.getUsbDevices(true)); + + logger.info("Checking Sound Cards..."); + printSoundCards(hal.getSoundCards()); + + StringBuilder output = new StringBuilder(); + for (int i = 0; i < oshi.size(); i++) { + output.append(oshi.get(i)); + if (oshi.get(i) != null && !oshi.get(i).endsWith("\n")) { + output.append('\n'); + } + } + logger.info("Printing Operating System and Hardware Info:{}{}", '\n', output); + } + + private static void printOperatingSystem(final OperatingSystem os) { + oshi.add(String.valueOf(os)); + oshi.add("Booted: " + Instant.ofEpochSecond(os.getSystemBootTime())); + oshi.add("Uptime: " + FormatUtil.formatElapsedSecs(os.getSystemUptime())); + oshi.add("Running with" + (os.isElevated() ? "" : "out") + " elevated permissions."); + } + + private static void printComputerSystem(final ComputerSystem computerSystem) { + oshi.add("system: " + computerSystem.toString()); + oshi.add(" firmware: " + computerSystem.getFirmware().toString()); + oshi.add(" baseboard: " + computerSystem.getBaseboard().toString()); + } + + private static void printProcessor(CentralProcessor processor) { + oshi.add(processor.toString()); + } + + private static void printMemory(GlobalMemory memory) { + oshi.add("Memory: \n " + memory.toString()); + VirtualMemory vm = memory.getVirtualMemory(); + oshi.add("Swap: \n " + vm.toString()); + PhysicalMemory[] pmArray = memory.getPhysicalMemory(); + if (pmArray.length > 0) { + oshi.add("Physical Memory: "); + for (PhysicalMemory pm : pmArray) { + oshi.add(" " + pm.toString()); + } + } + } + + private static void printCpu(CentralProcessor processor) { + oshi.add("Context Switches/Interrupts: " + processor.getContextSwitches() + " / " + processor.getInterrupts()); + + long[] prevTicks = processor.getSystemCpuLoadTicks(); + long[][] prevProcTicks = processor.getProcessorCpuLoadTicks(); + oshi.add("CPU, IOWait, and IRQ ticks @ 0 sec:" + Arrays.toString(prevTicks)); + // Wait a second... + Util.sleep(1000); + long[] ticks = processor.getSystemCpuLoadTicks(); + oshi.add("CPU, IOWait, and IRQ ticks @ 1 sec:" + Arrays.toString(ticks)); + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long sys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal; + + oshi.add(String.format( + "User: %.1f%% Nice: %.1f%% System: %.1f%% Idle: %.1f%% IOwait: %.1f%% IRQ: %.1f%% SoftIRQ: %.1f%% Steal: %.1f%%", + 100d * user / totalCpu, 100d * nice / totalCpu, 100d * sys / totalCpu, 100d * idle / totalCpu, + 100d * iowait / totalCpu, 100d * irq / totalCpu, 100d * softirq / totalCpu, 100d * steal / totalCpu)); + oshi.add(String.format("CPU load: %.1f%%", processor.getSystemCpuLoadBetweenTicks(prevTicks) * 100)); + double[] loadAverage = processor.getSystemLoadAverage(3); + oshi.add("CPU load averages:" + (loadAverage[0] < 0 ? " N/A" : String.format(" %.2f", loadAverage[0])) + + (loadAverage[1] < 0 ? " N/A" : String.format(" %.2f", loadAverage[1])) + + (loadAverage[2] < 0 ? " N/A" : String.format(" %.2f", loadAverage[2]))); + // per core CPU + StringBuilder procCpu = new StringBuilder("CPU load per processor:"); + double[] load = processor.getProcessorCpuLoadBetweenTicks(prevProcTicks); + for (double avg : load) { + procCpu.append(String.format(" %.1f%%", avg * 100)); + } + oshi.add(procCpu.toString()); + long freq = processor.getProcessorIdentifier().getVendorFreq(); + if (freq > 0) { + oshi.add("Vendor Frequency: " + FormatUtil.formatHertz(freq)); + } + freq = processor.getMaxFreq(); + if (freq > 0) { + oshi.add("Max Frequency: " + FormatUtil.formatHertz(freq)); + } + long[] freqs = processor.getCurrentFreq(); + if (freqs[0] > 0) { + StringBuilder sb = new StringBuilder("Current Frequencies: "); + for (int i = 0; i < freqs.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(FormatUtil.formatHertz(freqs[i])); + } + oshi.add(sb.toString()); + } + } + + private static void printProcesses(OperatingSystem os, GlobalMemory memory) { + oshi.add("Processes: " + os.getProcessCount() + ", Threads: " + os.getThreadCount()); + // Sort by highest CPU + List procs = Arrays.asList(os.getProcesses(5, ProcessSort.CPU)); + + oshi.add(" PID %CPU %MEM VSZ RSS Name"); + for (int i = 0; i < procs.size() && i < 5; i++) { + OSProcess p = procs.get(i); + oshi.add(String.format(" %5d %5.1f %4.1f %9s %9s %s", p.getProcessID(), + 100d * (p.getKernelTime() + p.getUserTime()) / p.getUpTime(), + 100d * p.getResidentSetSize() / memory.getTotal(), FormatUtil.formatBytes(p.getVirtualSize()), + FormatUtil.formatBytes(p.getResidentSetSize()), p.getName())); + } + } + + private static void printServices(OperatingSystem os) { + oshi.add("Services: "); + oshi.add(" PID State Name"); + // DO 5 each of running and stopped + int i = 0; + for (OSService s : os.getServices()) { + if (s.getState().equals(OSService.State.RUNNING) && i++ < 5) { + oshi.add(String.format(" %5d %7s %s", s.getProcessID(), s.getState(), s.getName())); + } + } + i = 0; + for (OSService s : os.getServices()) { + if (s.getState().equals(OSService.State.STOPPED) && i++ < 5) { + oshi.add(String.format(" %5d %7s %s", s.getProcessID(), s.getState(), s.getName())); + } + } + } + + private static void printSensors(Sensors sensors) { + oshi.add("Sensors: " + sensors.toString()); + } + + private static void printPowerSources(PowerSource[] powerSources) { + StringBuilder sb = new StringBuilder("Power Sources: "); + if (powerSources.length == 0) { + sb.append("Unknown"); + } + for (PowerSource powerSource : powerSources) { + sb.append("\n ").append(powerSource.toString()); + } + oshi.add(sb.toString()); + } + + private static void printDisks(HWDiskStore[] diskStores) { + oshi.add("Disks:"); + for (HWDiskStore disk : diskStores) { + oshi.add(" " + disk.toString()); + + HWPartition[] partitions = disk.getPartitions(); + for (HWPartition part : partitions) { + oshi.add(" |-- " + part.toString()); + } + } + } + + private static void printFileSystem(FileSystem fileSystem) { + oshi.add("File System:"); + + oshi.add(String.format(" File Descriptors: %d/%d", fileSystem.getOpenFileDescriptors(), + fileSystem.getMaxFileDescriptors())); + + OSFileStore[] fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) { + long usable = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + oshi.add(String.format( + " %s (%s) [%s] %s of %s free (%.1f%%), %s of %s files free (%.1f%%) is %s " + + (fs.getLogicalVolume() != null && fs.getLogicalVolume().length() > 0 ? "[%s]" : "%s") + + " and is mounted at %s", + fs.getName(), fs.getDescription().isEmpty() ? "file system" : fs.getDescription(), fs.getType(), + FormatUtil.formatBytes(usable), FormatUtil.formatBytes(fs.getTotalSpace()), 100d * usable / total, + FormatUtil.formatValue(fs.getFreeInodes(), ""), FormatUtil.formatValue(fs.getTotalInodes(), ""), + 100d * fs.getFreeInodes() / fs.getTotalInodes(), fs.getVolume(), fs.getLogicalVolume(), + fs.getMount())); + } + } + + private static void printNetworkInterfaces(NetworkIF[] networkIFs) { + StringBuilder sb = new StringBuilder("Network Interfaces:"); + if (networkIFs.length == 0) { + sb.append(" Unknown"); + } + for (NetworkIF net : networkIFs) { + sb.append("\n ").append(net.toString()); + } + oshi.add(sb.toString()); + } + + private static void printNetworkParameters(NetworkParams networkParams) { + oshi.add("Network parameters:\n " + networkParams.toString()); + } + + private static void printDisplays(Display[] displays) { + oshi.add("Displays:"); + int i = 0; + for (Display display : displays) { + oshi.add(" Display " + i + ":"); + oshi.add(String.valueOf(display)); + i++; + } + } + + private static void printUsbDevices(UsbDevice[] usbDevices) { + oshi.add("USB Devices:"); + for (UsbDevice usbDevice : usbDevices) { + oshi.add(String.valueOf(usbDevice)); + } + } + + private static void printSoundCards(SoundCard[] cards) { + oshi.add("Sound Cards:"); + for (SoundCard card : cards) { + oshi.add(" " + String.valueOf(card)); + } + } } diff --git a/javalib-io-binary/pom.xml b/javalib-io-binary/pom.xml index ec43b90d..8cbf26e2 100644 --- a/javalib-io-binary/pom.xml +++ b/javalib-io-binary/pom.xml @@ -1,71 +1,71 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javalib-io-binary - 1.0.0 + io.github.dunwu + javalib-io-binary + 1.0.0 - javalib-io-binary - Java binary serialize Lib Examples + javalib-io-binary + Java binary serialize Lib Examples - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 0.4.8 - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 0.4.8 + - - - - de.ruedigermoeller - fst - 2.56 - - - com.esotericsoftware - kryo - 5.0.0-RC4 - + + + + de.ruedigermoeller + fst + 2.56 + + + com.esotericsoftware + kryo + 5.0.0-RC4 + - - io.github.dunwu - dunwu-common - + + io.github.dunwu + dunwu-common + - - - junit - junit - test - - - org.assertj - assertj-core - test - - - - - - io.github.dunwu - dunwu-dependencies - ${dunwu.version} - pom - import - - - + + + junit + junit + test + + + org.assertj + assertj-core + test + + + + + + io.github.dunwu + dunwu-dependencies + ${dunwu.version} + pom + import + + + - - - - src/main/resources - - - + + + + src/main/resources + + + diff --git a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/FstDemo.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/FstDemo.java index 9f3362e9..54b9428e 100644 --- a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/FstDemo.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/FstDemo.java @@ -15,59 +15,59 @@ */ public class FstDemo { - private static FSTConfiguration DEFAULT_CONFIG = FSTConfiguration.createDefaultConfiguration(); + private static FSTConfiguration DEFAULT_CONFIG = FSTConfiguration.createDefaultConfiguration(); - /** - * 将对象序列化为 byte 数组 - * - * @param obj 任意对象 - * @param 对象的类型 - * @return 序列化后的 byte 数组 - */ - public static byte[] writeToBytes(T obj) { - return DEFAULT_CONFIG.asByteArray(obj); - } + /** + * 将对象序列化为 byte 数组后,再使用 Base64 编码 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的字符串 + */ + public static String writeToString(T obj) { + byte[] bytes = writeToBytes(obj); + return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); + } - /** - * 将对象序列化为 byte 数组后,再使用 Base64 编码 - * - * @param obj 任意对象 - * @param 对象的类型 - * @return 序列化后的字符串 - */ - public static String writeToString(T obj) { - byte[] bytes = writeToBytes(obj); - return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); - } + /** + * 将对象序列化为 byte 数组 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的 byte 数组 + */ + public static byte[] writeToBytes(T obj) { + return DEFAULT_CONFIG.asByteArray(obj); + } - /** - * 将 byte 数组反序列化为原对象 - * - * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 - * @param clazz 原对象的类型 - * @param 原对象的类型 - * @return 原对象 - */ - public static T readFromBytes(byte[] bytes, Class clazz) throws IOException { - Object obj = DEFAULT_CONFIG.asObject(bytes); - if (clazz.isInstance(obj)) { - return (T) obj; - } else { - throw new IOException("derialize failed"); - } - } + /** + * 将字符串反序列化为原对象,先使用 Base64 解码 + * + * @param str {@link #writeToString} 方法序列化后的字符串 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromString(String str, Class clazz) throws IOException { + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + return readFromBytes(Base64.getDecoder().decode(bytes), clazz); + } - /** - * 将字符串反序列化为原对象,先使用 Base64 解码 - * - * @param str {@link #writeToString} 方法序列化后的字符串 - * @param clazz 原对象的类型 - * @param 原对象的类型 - * @return 原对象 - */ - public static T readFromString(String str, Class clazz) throws IOException { - byte[] bytes = str.getBytes(StandardCharsets.UTF_8); - return readFromBytes(Base64.getDecoder().decode(bytes), clazz); - } + /** + * 将 byte 数组反序列化为原对象 + * + * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromBytes(byte[] bytes, Class clazz) throws IOException { + Object obj = DEFAULT_CONFIG.asObject(bytes); + if (clazz.isInstance(obj)) { + return (T) obj; + } else { + throw new IOException("derialize failed"); + } + } } diff --git a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/JdkSerializeDemo.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/JdkSerializeDemo.java index 7f834572..758b315c 100644 --- a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/JdkSerializeDemo.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/JdkSerializeDemo.java @@ -12,67 +12,67 @@ */ public class JdkSerializeDemo { - /** - * 将对象序列化为 byte 数组 - * - * @param obj 任意对象 - * @param 对象的类型 - * @return 序列化后的 byte 数组 - */ - public static byte[] writeToBytes(T obj) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(obj); - byte[] bytes = baos.toByteArray(); - baos.close(); - oos.close(); - return bytes; - } + /** + * 将对象序列化为 byte 数组后,再使用 Base64 编码 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的字符串 + */ + public static String writeToString(T obj) throws IOException { + byte[] bytes = writeToBytes(obj); + return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); + } - /** - * 将对象序列化为 byte 数组后,再使用 Base64 编码 - * - * @param obj 任意对象 - * @param 对象的类型 - * @return 序列化后的字符串 - */ - public static String writeToString(T obj) throws IOException { - byte[] bytes = writeToBytes(obj); - return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); - } + /** + * 将对象序列化为 byte 数组 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的 byte 数组 + */ + public static byte[] writeToBytes(T obj) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + byte[] bytes = baos.toByteArray(); + baos.close(); + oos.close(); + return bytes; + } - /** - * 将 byte 数组反序列化为原对象 - * - * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 - * @param clazz 原对象的类型 - * @param 原对象的类型 - * @return 原对象 - */ - public static T readFromBytes(byte[] bytes, Class clazz) throws IOException, ClassNotFoundException { - ByteArrayInputStream bais = new ByteArrayInputStream(bytes); - ObjectInputStream ois = new ObjectInputStream(bais); - Object obj = ois.readObject(); - bais.close(); - ois.close(); - if (clazz.isInstance(obj)) { - return (T) obj; - } else { - throw new IOException("derialize failed"); - } - } + /** + * 将字符串反序列化为原对象,先使用 Base64 解码 + * + * @param str {@link #writeToString} 方法序列化后的字符串 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromString(String str, Class clazz) throws IOException, ClassNotFoundException { + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + return readFromBytes(Base64.getDecoder().decode(bytes), clazz); + } - /** - * 将字符串反序列化为原对象,先使用 Base64 解码 - * - * @param str {@link #writeToString} 方法序列化后的字符串 - * @param clazz 原对象的类型 - * @param 原对象的类型 - * @return 原对象 - */ - public static T readFromString(String str, Class clazz) throws IOException, ClassNotFoundException { - byte[] bytes = str.getBytes(StandardCharsets.UTF_8); - return readFromBytes(Base64.getDecoder().decode(bytes), clazz); - } + /** + * 将 byte 数组反序列化为原对象 + * + * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromBytes(byte[] bytes, Class clazz) throws IOException, ClassNotFoundException { + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais); + Object obj = ois.readObject(); + bais.close(); + ois.close(); + if (clazz.isInstance(obj)) { + return (T) obj; + } else { + throw new IOException("derialize failed"); + } + } } diff --git a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java index 683be9b5..624fd48a 100644 --- a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/KryoDemo.java @@ -20,95 +20,95 @@ */ public class KryoDemo { - // 每个线程的 Kryo 实例 - private static final ThreadLocal kryoLocal = ThreadLocal.withInitial(() -> { - Kryo kryo = new Kryo(); - - /** - * 不要轻易改变这里的配置!更改之后,序列化的格式就会发生变化, - * 上线的同时就必须清除 Redis 里的所有缓存, - * 否则那些缓存再回来反序列化的时候,就会报错 - */ - //支持对象循环引用(否则会栈溢出) - kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置 - - //不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册) - kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置 - - //Fix the NPE bug when deserializing Collections. - ((DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy()) - .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy()); - - return kryo; - }); - - /** - * 获得当前线程的 Kryo 实例 - * - * @return 当前线程的 Kryo 实例 - */ - public static Kryo getInstance() { - return kryoLocal.get(); - } - - /** - * 将对象序列化为 byte 数组 - * - * @param obj 任意对象 - * @param 对象的类型 - * @return 序列化后的 byte 数组 - */ - public static byte[] writeToBytes(T obj) { - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - Output output = new Output(byteArrayOutputStream); - - Kryo kryo = getInstance(); - kryo.writeObject(output, obj); - output.flush(); - - return byteArrayOutputStream.toByteArray(); - } - - /** - * 将对象序列化为 byte 数组后,再使用 Base64 编码 - * - * @param obj 任意对象 - * @param 对象的类型 - * @return 序列化后的字符串 - */ - public static String writeToString(T obj) { - byte[] bytes = writeToBytes(obj); - return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); - } - - /** - * 将 byte 数组反序列化为原对象 - * - * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 - * @param clazz 原对象的类型 - * @param 原对象的类型 - * @return 原对象 - */ - @SuppressWarnings("unchecked") - public static T readFromBytes(byte[] bytes, Class clazz) { - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); - Input input = new Input(byteArrayInputStream); - - Kryo kryo = getInstance(); - return (T) kryo.readObject(input, clazz); - } - - /** - * 将字符串反序列化为原对象,先使用 Base64 解码 - * - * @param str {@link #writeToString} 方法序列化后的字符串 - * @param clazz 原对象的类型 - * @param 原对象的类型 - * @return 原对象 - */ - public static T readFromString(String str, Class clazz) { - byte[] bytes = str.getBytes(StandardCharsets.UTF_8); - return readFromBytes(Base64.getDecoder().decode(bytes), clazz); - } + // 每个线程的 Kryo 实例 + private static final ThreadLocal kryoLocal = ThreadLocal.withInitial(() -> { + Kryo kryo = new Kryo(); + + /** + * 不要轻易改变这里的配置!更改之后,序列化的格式就会发生变化, + * 上线的同时就必须清除 Redis 里的所有缓存, + * 否则那些缓存再回来反序列化的时候,就会报错 + */ + //支持对象循环引用(否则会栈溢出) + kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置 + + //不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册) + kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置 + + //Fix the NPE bug when deserializing Collections. + ((DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy()) + .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy()); + + return kryo; + }); + + /** + * 将对象序列化为 byte 数组后,再使用 Base64 编码 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的字符串 + */ + public static String writeToString(T obj) { + byte[] bytes = writeToBytes(obj); + return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); + } + + /** + * 将对象序列化为 byte 数组 + * + * @param obj 任意对象 + * @param 对象的类型 + * @return 序列化后的 byte 数组 + */ + public static byte[] writeToBytes(T obj) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Output output = new Output(byteArrayOutputStream); + + Kryo kryo = getInstance(); + kryo.writeObject(output, obj); + output.flush(); + + return byteArrayOutputStream.toByteArray(); + } + + /** + * 获得当前线程的 Kryo 实例 + * + * @return 当前线程的 Kryo 实例 + */ + public static Kryo getInstance() { + return kryoLocal.get(); + } + + /** + * 将字符串反序列化为原对象,先使用 Base64 解码 + * + * @param str {@link #writeToString} 方法序列化后的字符串 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + public static T readFromString(String str, Class clazz) { + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + return readFromBytes(Base64.getDecoder().decode(bytes), clazz); + } + + /** + * 将 byte 数组反序列化为原对象 + * + * @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组 + * @param clazz 原对象的类型 + * @param 原对象的类型 + * @return 原对象 + */ + @SuppressWarnings("unchecked") + public static T readFromBytes(byte[] bytes, Class clazz) { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + Input input = new Input(byteArrayInputStream); + + Kryo kryo = getInstance(); + return (T) kryo.readObject(input, clazz); + } } diff --git a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/BeanUtils.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/BeanUtils.java index 58534b10..f9c5ab76 100644 --- a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/BeanUtils.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/BeanUtils.java @@ -12,22 +12,22 @@ */ public class BeanUtils { - public static TestBean initJdk8Bean() { - String[] strArray = { "a", "b", "c" }; - Integer[] intArray = { 1, 2, 3, 4, 5 }; - List intList = new ArrayList<>(Arrays.asList(intArray)); - Map map = new HashMap<>(); - map.put("name", "jack"); - map.put("age", 18); - map.put("length", 175.3f); - TestBean bean = new TestBean(); - LocalDateTime localDateTime = LocalDateTime.of(2000, 1, 1, 12, 0, 0); - Date date = DateExtUtils.localDateTime2Date(localDateTime); - LocalDate localDate = LocalDate.of(1949, 10, 1); - bean.setI1(10).setI2(1024).setF1(0.5f).setD1(100.0) - .setDate1(date).setDate2(localDateTime).setDate3(localDate) - .setColor(TestBean.Color.BLUE).setStrArray(strArray).setIntList(intList).setMap(map); - return bean; - } + public static TestBean initJdk8Bean() { + String[] strArray = { "a", "b", "c" }; + Integer[] intArray = { 1, 2, 3, 4, 5 }; + List intList = new ArrayList<>(Arrays.asList(intArray)); + Map map = new HashMap<>(); + map.put("name", "jack"); + map.put("age", 18); + map.put("length", 175.3f); + TestBean bean = new TestBean(); + LocalDateTime localDateTime = LocalDateTime.of(2000, 1, 1, 12, 0, 0); + Date date = DateExtUtils.localDateTime2Date(localDateTime); + LocalDate localDate = LocalDate.of(1949, 10, 1); + bean.setI1(10).setI2(1024).setF1(0.5f).setD1(100.0) + .setDate1(date).setDate2(localDateTime).setDate3(localDate) + .setColor(TestBean.Color.BLUE).setStrArray(strArray).setIntList(intList).setMap(map); + return bean; + } } diff --git a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/TestBean.java b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/TestBean.java index b2cfa3f6..539e3ebd 100644 --- a/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/TestBean.java +++ b/javalib-io-binary/src/main/java/io/github/dunwu/javalib/io/bean/TestBean.java @@ -26,34 +26,34 @@ @AllArgsConstructor public class TestBean implements Serializable { - private static final long serialVersionUID = -6473181683996762084L; + private static final long serialVersionUID = -6473181683996762084L; - private int i1; + private int i1; - private Integer i2; + private Integer i2; - private float f1; + private float f1; - private Double d1; + private Double d1; - private Date date1; + private Date date1; - private LocalDateTime date2; + private LocalDateTime date2; - private LocalDate date3; + private LocalDate date3; - private Color color; + private Color color; - private String[] strArray; + private String[] strArray; - private List intList; + private List intList; - private Map map; + private Map map; - public enum Color { - RED, - YELLOW, - BLUE - } + public enum Color { + RED, + YELLOW, + BLUE + } } diff --git a/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializePerformanceTest.java b/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializePerformanceTest.java index ff9a4462..ac50556f 100644 --- a/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializePerformanceTest.java +++ b/javalib-io-binary/src/test/java/io/github/dunwu/javalib/io/SerializePerformanceTest.java @@ -16,48 +16,48 @@ */ public class SerializePerformanceTest { - private static final int BATCH_SIZE = 100000; - - @Test - public void testJdkSerialize() throws IOException, ClassNotFoundException { - long begin = System.currentTimeMillis(); - for (int i = 0; i < BATCH_SIZE; i++) { - TestBean oldBean = BeanUtils.initJdk8Bean(); - byte[] bytes = JdkSerializeDemo.writeToBytes(oldBean); - assertThat(bytes).isNotEmpty(); - TestBean newBean = JdkSerializeDemo.readFromBytes(bytes, TestBean.class); - assertThat(newBean).isNotNull(); - } - long end = System.currentTimeMillis(); - System.out.printf("JDK 默认序列化/反序列化耗时:%s", (end - begin)); - } - - @Test - public void testFst() throws IOException { - long begin = System.currentTimeMillis(); - for (int i = 0; i < BATCH_SIZE; i++) { - TestBean oldBean = BeanUtils.initJdk8Bean(); - byte[] bytes = FstDemo.writeToBytes(oldBean); - assertThat(bytes).isNotEmpty(); - TestBean newBean = FstDemo.readFromBytes(bytes, TestBean.class); - assertThat(newBean).isNotNull(); - } - long end = System.currentTimeMillis(); - System.out.printf("FST 序列化/反序列化耗时:%s", (end - begin)); - } - - @Test - public void testKryo() throws IOException { - long begin = System.currentTimeMillis(); - for (int i = 0; i < BATCH_SIZE; i++) { - TestBean oldBean = BeanUtils.initJdk8Bean(); - byte[] bytes = KryoDemo.writeToBytes(oldBean); - assertThat(bytes).isNotEmpty(); - TestBean newBean = KryoDemo.readFromBytes(bytes, TestBean.class); - assertThat(newBean).isNotNull(); - } - long end = System.currentTimeMillis(); - System.out.printf("Kryo 序列化/反序列化耗时:%s", (end - begin)); - } + private static final int BATCH_SIZE = 100000; + + @Test + public void testJdkSerialize() throws IOException, ClassNotFoundException { + long begin = System.currentTimeMillis(); + for (int i = 0; i < BATCH_SIZE; i++) { + TestBean oldBean = BeanUtils.initJdk8Bean(); + byte[] bytes = JdkSerializeDemo.writeToBytes(oldBean); + assertThat(bytes).isNotEmpty(); + TestBean newBean = JdkSerializeDemo.readFromBytes(bytes, TestBean.class); + assertThat(newBean).isNotNull(); + } + long end = System.currentTimeMillis(); + System.out.printf("JDK 默认序列化/反序列化耗时:%s", (end - begin)); + } + + @Test + public void testFst() throws IOException { + long begin = System.currentTimeMillis(); + for (int i = 0; i < BATCH_SIZE; i++) { + TestBean oldBean = BeanUtils.initJdk8Bean(); + byte[] bytes = FstDemo.writeToBytes(oldBean); + assertThat(bytes).isNotEmpty(); + TestBean newBean = FstDemo.readFromBytes(bytes, TestBean.class); + assertThat(newBean).isNotNull(); + } + long end = System.currentTimeMillis(); + System.out.printf("FST 序列化/反序列化耗时:%s", (end - begin)); + } + + @Test + public void testKryo() throws IOException { + long begin = System.currentTimeMillis(); + for (int i = 0; i < BATCH_SIZE; i++) { + TestBean oldBean = BeanUtils.initJdk8Bean(); + byte[] bytes = KryoDemo.writeToBytes(oldBean); + assertThat(bytes).isNotEmpty(); + TestBean newBean = KryoDemo.readFromBytes(bytes, TestBean.class); + assertThat(newBean).isNotNull(); + } + long end = System.currentTimeMillis(); + System.out.printf("Kryo 序列化/反序列化耗时:%s", (end - begin)); + } } diff --git a/javalib-io-json/pom.xml b/javalib-io-json/pom.xml index dce1f139..a9710004 100644 --- a/javalib-io-json/pom.xml +++ b/javalib-io-json/pom.xml @@ -1,84 +1,84 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javalib-io-json - 1.0.0 + io.github.dunwu + javalib-io-json + 1.0.0 - javalib-io-json - Java Json Lib Examples + javalib-io-json + Java Json Lib Examples - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 0.4.8 - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 0.4.8 + - - - - de.ruedigermoeller - fst - 2.56 - + + + + de.ruedigermoeller + fst + 2.56 + - - com.alibaba - fastjson - - - com.fasterxml.jackson.core - jackson-databind - - - com.google.code.gson - gson - 2.8.6 - + + com.alibaba + fastjson + + + com.fasterxml.jackson.core + jackson-databind + + + com.google.code.gson + gson + 2.8.6 + - - org.projectlombok - lombok - - - io.github.dunwu - dunwu-common - + + org.projectlombok + lombok + + + io.github.dunwu + dunwu-common + - - - junit - junit - test - - - org.assertj - assertj-core - test - - - - - - io.github.dunwu - dunwu-dependencies - ${dunwu.version} - pom - import - - - + + + junit + junit + test + + + org.assertj + assertj-core + test + + + + + + io.github.dunwu + dunwu-dependencies + ${dunwu.version} + pom + import + + + - - - - src/main/resources - - - + + + + src/main/resources + + + diff --git a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/bean/Group.java b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/bean/Group.java index fcdc2180..5ccb4bd9 100644 --- a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/bean/Group.java +++ b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/bean/Group.java @@ -5,38 +5,38 @@ public class Group { - private Long id; + private Long id; - private String name; + private String name; - private List users = new ArrayList(); + private List users = new ArrayList(); - public Long getId() { - return id; - } + public Long getId() { + return id; + } - public void setId(Long id) { - this.id = id; - } + public void setId(Long id) { + this.id = id; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public List getUsers() { - return users; - } + public List getUsers() { + return users; + } - public void setUsers(List users) { - this.users = users; - } + public void setUsers(List users) { + this.users = users; + } - public void addUser(User user) { - users.add(user); - } + public void addUser(User user) { + users.add(user); + } } diff --git a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/bean/User.java b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/bean/User.java index b99a76a5..d52c02d4 100644 --- a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/bean/User.java +++ b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/bean/User.java @@ -2,24 +2,24 @@ public class User { - private Long id; + private Long id; - private String name; + private String name; - public Long getId() { - return id; - } + public Long getId() { + return id; + } - public void setId(Long id) { - this.id = id; - } + public void setId(Long id) { + this.id = id; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } } diff --git a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/fastjson/FastjsonFeildBean.java b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/fastjson/FastjsonFeildBean.java index 8c9cf17b..abae3fd9 100644 --- a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/fastjson/FastjsonFeildBean.java +++ b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/fastjson/FastjsonFeildBean.java @@ -20,121 +20,121 @@ */ public class FastjsonFeildBean { - private int id; - - // 配置date序列化和反序列使用yyyyMMdd日期格式 - @JSONField(format = "yyyyMMdd") - private Date date1; - - // 不序列化 - @JSONField(serialize = false, format = "yyyy-MM-dd hh:mm:ss") - private LocalDate date2; - - // 不反序列化 - @JSONField(deserialize = false, format = "yyyy-MM-dd") - private LocalDateTime date3; - - @JSONField(ordinal = 1) - private Double d1; - - // 按ordinal排序 - @JSONField(ordinal = 2) - private float f1; - - public FastjsonFeildBean() { - } - - public FastjsonFeildBean(int id, Date date1, LocalDate date2, LocalDateTime date3, float f1, Double d1) { - this.id = id; - this.date1 = date1; - this.date2 = date2; - this.date3 = date3; - this.f1 = f1; - this.d1 = d1; - } - - @Override - public String toString() { - return "FastjsonAnnotationBean{" + - "id=" + id + - ", date1=" + date1 + - ", date2=" + date2 + - ", date3=" + date3 + - ", f1=" + f1 + - ", f2=" + d1 + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof FastjsonFeildBean)) { - return false; - } - FastjsonFeildBean that = (FastjsonFeildBean) o; - return id == that.id && - Float.compare(that.f1, f1) == 0 && - Objects.equals(date1, that.date1) && - Objects.equals(date2, that.date2) && - Objects.equals(date3, that.date3) && - Objects.equals(d1, that.d1); - } - - @Override - public int hashCode() { - return Objects.hash(id, date1, date2, date3, f1, d1); - } - - @JSONField(name = "ID") - public int getId() { - return id; - } - - @JSONField(name = "ID") - public void setId(int id) { - this.id = id; - } - - public Date getDate1() { - return date1; - } - - public void setDate1(Date date1) { - this.date1 = date1; - } - - public LocalDate getDate2() { - return date2; - } - - public void setDate2(LocalDate date2) { - this.date2 = date2; - } - - public LocalDateTime getDate3() { - return date3; - } - - public void setDate3(LocalDateTime date3) { - this.date3 = date3; - } - - public float getF1() { - return f1; - } - - public void setF1(float f1) { - this.f1 = f1; - } - - public Double getD1() { - return d1; - } - - public void setD1(Double d1) { - this.d1 = d1; - } + private int id; + + // 配置date序列化和反序列使用yyyyMMdd日期格式 + @JSONField(format = "yyyyMMdd") + private Date date1; + + // 不序列化 + @JSONField(serialize = false, format = "yyyy-MM-dd hh:mm:ss") + private LocalDate date2; + + // 不反序列化 + @JSONField(deserialize = false, format = "yyyy-MM-dd") + private LocalDateTime date3; + + @JSONField(ordinal = 1) + private Double d1; + + // 按ordinal排序 + @JSONField(ordinal = 2) + private float f1; + + public FastjsonFeildBean() { + } + + public FastjsonFeildBean(int id, Date date1, LocalDate date2, LocalDateTime date3, float f1, Double d1) { + this.id = id; + this.date1 = date1; + this.date2 = date2; + this.date3 = date3; + this.f1 = f1; + this.d1 = d1; + } + + @Override + public int hashCode() { + return Objects.hash(id, date1, date2, date3, f1, d1); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof FastjsonFeildBean)) { + return false; + } + FastjsonFeildBean that = (FastjsonFeildBean) o; + return id == that.id && + Float.compare(that.f1, f1) == 0 && + Objects.equals(date1, that.date1) && + Objects.equals(date2, that.date2) && + Objects.equals(date3, that.date3) && + Objects.equals(d1, that.d1); + } + + @Override + public String toString() { + return "FastjsonAnnotationBean{" + + "id=" + id + + ", date1=" + date1 + + ", date2=" + date2 + + ", date3=" + date3 + + ", f1=" + f1 + + ", f2=" + d1 + + '}'; + } + + @JSONField(name = "ID") + public int getId() { + return id; + } + + @JSONField(name = "ID") + public void setId(int id) { + this.id = id; + } + + public Date getDate1() { + return date1; + } + + public void setDate1(Date date1) { + this.date1 = date1; + } + + public LocalDate getDate2() { + return date2; + } + + public void setDate2(LocalDate date2) { + this.date2 = date2; + } + + public LocalDateTime getDate3() { + return date3; + } + + public void setDate3(LocalDateTime date3) { + this.date3 = date3; + } + + public float getF1() { + return f1; + } + + public void setF1(float f1) { + this.f1 = f1; + } + + public Double getD1() { + return d1; + } + + public void setD1(Double d1) { + this.d1 = d1; + } } diff --git a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/fastjson/JacksonAnnotationBean.java b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/fastjson/JacksonAnnotationBean.java index f8cca30b..b5b5ee22 100644 --- a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/fastjson/JacksonAnnotationBean.java +++ b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/fastjson/JacksonAnnotationBean.java @@ -11,45 +11,45 @@ @JsonPropertyOrder(alphabetic = true) public class JacksonAnnotationBean { - private String Name; + private String Name; - private int Age; + private int Age; - @JsonIgnore - private String Sex; + @JsonIgnore + private String Sex; - public JacksonAnnotationBean() { - } + public JacksonAnnotationBean() { + } - public JacksonAnnotationBean(String name, int age, String sex) { - Name = name; - Age = age; - Sex = sex; - } + public JacksonAnnotationBean(String name, int age, String sex) { + Name = name; + Age = age; + Sex = sex; + } - @JsonProperty("username") - public String getName() { - return Name; - } + @JsonProperty("username") + public String getName() { + return Name; + } - public void setName(String name) { - Name = name; - } + public void setName(String name) { + Name = name; + } - public int getAge() { - return Age; - } + public int getAge() { + return Age; + } - public void setAge(int age) { - Age = age; - } + public void setAge(int age) { + Age = age; + } - public String getSex() { - return Sex; - } + public String getSex() { + return Sex; + } - public void setSex(String sex) { - Sex = sex; - } + public void setSex(String sex) { + Sex = sex; + } } diff --git a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/gson/GsonAnnotationBean.java b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/gson/GsonAnnotationBean.java index 82288414..93626b07 100644 --- a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/gson/GsonAnnotationBean.java +++ b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/gson/GsonAnnotationBean.java @@ -10,43 +10,43 @@ */ public class GsonAnnotationBean { - @SerializedName("custom_naming") - private String someField; - - private String someOtherField; - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof GsonAnnotationBean)) { - return false; - } - GsonAnnotationBean that = (GsonAnnotationBean) o; - return Objects.equals(someField, that.someField) && - Objects.equals(someOtherField, that.someOtherField); - } - - @Override - public int hashCode() { - return Objects.hash(someField, someOtherField); - } - - public String getSomeField() { - return someField; - } - - public void setSomeField(String someField) { - this.someField = someField; - } - - public String getSomeOtherField() { - return someOtherField; - } - - public void setSomeOtherField(String someOtherField) { - this.someOtherField = someOtherField; - } + @SerializedName("custom_naming") + private String someField; + + private String someOtherField; + + @Override + public int hashCode() { + return Objects.hash(someField, someOtherField); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof GsonAnnotationBean)) { + return false; + } + GsonAnnotationBean that = (GsonAnnotationBean) o; + return Objects.equals(someField, that.someField) && + Objects.equals(someOtherField, that.someOtherField); + } + + public String getSomeField() { + return someField; + } + + public void setSomeField(String someField) { + this.someField = someField; + } + + public String getSomeOtherField() { + return someOtherField; + } + + public void setSomeOtherField(String someOtherField) { + this.someOtherField = someOtherField; + } } diff --git a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/gson/VersionedClass.java b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/gson/VersionedClass.java index eb3ea432..f435e783 100644 --- a/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/gson/VersionedClass.java +++ b/javalib-io-json/src/main/java/io/github/dunwu/javalib/json/gson/VersionedClass.java @@ -4,18 +4,18 @@ public class VersionedClass { - @Since(1.1) - private final String newerField; + @Since(1.1) + private final String newerField; - @Since(1.0) - private final String newField; + @Since(1.0) + private final String newField; - private final String field; + private final String field; - public VersionedClass() { - this.newerField = "newer"; - this.newField = "new"; - this.field = "old"; - } + public VersionedClass() { + this.newerField = "newer"; + this.newField = "new"; + this.field = "old"; + } } diff --git a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/TestBean.java b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/TestBean.java index 47ac796b..567c0a95 100644 --- a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/TestBean.java +++ b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/TestBean.java @@ -25,32 +25,32 @@ @AllArgsConstructor public class TestBean { - private int i1; + private int i1; - private Integer i2; + private Integer i2; - private float f1; + private float f1; - private Double d1; + private Double d1; - private Date date1; + private Date date1; - private LocalDateTime date2; + private LocalDateTime date2; - private LocalDate date3; + private LocalDate date3; - private Color color; + private Color color; - private String[] strArray; + private String[] strArray; - private List intList; + private List intList; - private Map map; + private Map map; - public static enum Color { - RED, - YELLOW, - BLUE - } + public static enum Color { + RED, + YELLOW, + BLUE + } } diff --git a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/TestBean2.java b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/TestBean2.java index 2b0522ee..0c989ed3 100644 --- a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/TestBean2.java +++ b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/TestBean2.java @@ -23,28 +23,28 @@ @AllArgsConstructor public class TestBean2 { - private int i1; + private int i1; - private Integer i2; + private Integer i2; - private float f1; + private float f1; - private Double d1; + private Double d1; - private Date date1; + private Date date1; - private Color color; + private Color color; - private String[] strArray; + private String[] strArray; - private List intList; + private List intList; - private Map map; + private Map map; - public static enum Color { - RED, - YELLOW, - BLUE - } + public static enum Color { + RED, + YELLOW, + BLUE + } } diff --git a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonCaseTests.java b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonCaseTests.java index cad14d96..d1004e12 100644 --- a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonCaseTests.java +++ b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonCaseTests.java @@ -18,39 +18,39 @@ */ public class FastjsonCaseTests { - @Test - public void test() { - Group oldGroup = BeanUtils.initGroupBean(); - String jsonString = JSON.toJSONString(oldGroup); - System.out.println(jsonString); - Group newGroup = JSON.parseObject(jsonString, Group.class); - assertThat(newGroup).isNotNull(); - } - - /** - * 序列化测试 - */ - @Test - public void serialize() { - LocalDate localDate = LocalDate.of(1949, 10, 1); - LocalDateTime localDateTime = LocalDateTime.of(2000, 1, 1, 12, 0, 0); - Date date = DateExtUtils.localDateTime2Date(localDateTime); - FastjsonFeildBean bean = new FastjsonFeildBean(1, date, localDate, localDateTime, 0.5f, 100.0); - String json = JSON.toJSONString(bean); - assertThat(json).isEqualTo( - "{\"ID\":1,\"date1\":\"20000101\",\"date3\":\"2000-01-01\",\"d1\":100.0,\"f1\":0.5}"); - System.out.println("json = [" + json + "]"); - } - - /** - * 反序列化测试 - */ - @Test - public void deserialize() { - final String json = "{\"ID\":1,\"date1\":\"20000101\",\"date3\":\"2000-01-01\",\"d1\":100.0,\"f1\":0.5}"; - FastjsonFeildBean actualBean = JSON.parseObject(json, FastjsonFeildBean.class); - System.out.printf("deserialize result: %s", actualBean.toString()); - assertThat(actualBean).isNotNull(); - } + @Test + public void test() { + Group oldGroup = BeanUtils.initGroupBean(); + String jsonString = JSON.toJSONString(oldGroup); + System.out.println(jsonString); + Group newGroup = JSON.parseObject(jsonString, Group.class); + assertThat(newGroup).isNotNull(); + } + + /** + * 序列化测试 + */ + @Test + public void serialize() { + LocalDate localDate = LocalDate.of(1949, 10, 1); + LocalDateTime localDateTime = LocalDateTime.of(2000, 1, 1, 12, 0, 0); + Date date = DateExtUtils.localDateTime2Date(localDateTime); + FastjsonFeildBean bean = new FastjsonFeildBean(1, date, localDate, localDateTime, 0.5f, 100.0); + String json = JSON.toJSONString(bean); + assertThat(json).isEqualTo( + "{\"ID\":1,\"date1\":\"20000101\",\"date3\":\"2000-01-01\",\"d1\":100.0,\"f1\":0.5}"); + System.out.println("json = [" + json + "]"); + } + + /** + * 反序列化测试 + */ + @Test + public void deserialize() { + final String json = "{\"ID\":1,\"date1\":\"20000101\",\"date3\":\"2000-01-01\",\"d1\":100.0,\"f1\":0.5}"; + FastjsonFeildBean actualBean = JSON.parseObject(json, FastjsonFeildBean.class); + System.out.printf("deserialize result: %s", actualBean.toString()); + assertThat(actualBean).isNotNull(); + } } diff --git a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonPerformanceTests.java b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonPerformanceTests.java index 070e7dbe..a696d86a 100644 --- a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonPerformanceTests.java +++ b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonPerformanceTests.java @@ -17,34 +17,34 @@ */ public class FastjsonPerformanceTests { - private static final int BATCH_SIZE = 100000; - - /** - * 循环序列化、反序列 {@link #BATCH_SIZE} 条数据,测试性能 - */ - private long donSerializeAndDeserialize() { - TestBean2 bean = BeanUtils.initNotJdk8Bean(); - long begin = System.nanoTime(); - for (int i = 0; i < BATCH_SIZE; i++) { - String json = JSON.toJSONString(bean); - assertThat(json).isNotBlank(); - TestBean2 newBean = JSON.parseObject(json, TestBean2.class); - assertThat(newBean).isNotNull(); - } - long end = System.nanoTime(); - return (end - begin); - } - - /** - * 测试十次,每次序列化、反序列化 100000 条数据,平均耗时约 380 ms - */ - @Test - public void testPerformance() { - long time = 0L; - for (int i = 0; i < 10; i++) { - time += donSerializeAndDeserialize(); - } - System.out.println(String.format("time: %d ms", TimeUnit.NANOSECONDS.toMillis(time / 10))); - } + private static final int BATCH_SIZE = 100000; + + /** + * 测试十次,每次序列化、反序列化 100000 条数据,平均耗时约 380 ms + */ + @Test + public void testPerformance() { + long time = 0L; + for (int i = 0; i < 10; i++) { + time += donSerializeAndDeserialize(); + } + System.out.println(String.format("time: %d ms", TimeUnit.NANOSECONDS.toMillis(time / 10))); + } + + /** + * 循环序列化、反序列 {@link #BATCH_SIZE} 条数据,测试性能 + */ + private long donSerializeAndDeserialize() { + TestBean2 bean = BeanUtils.initNotJdk8Bean(); + long begin = System.nanoTime(); + for (int i = 0; i < BATCH_SIZE; i++) { + String json = JSON.toJSONString(bean); + assertThat(json).isNotBlank(); + TestBean2 newBean = JSON.parseObject(json, TestBean2.class); + assertThat(newBean).isNotNull(); + } + long end = System.nanoTime(); + return (end - begin); + } } diff --git a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/gson/GsonCaseTests.java b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/gson/GsonCaseTests.java index ad5a6a79..ba016f49 100644 --- a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/gson/GsonCaseTests.java +++ b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/gson/GsonCaseTests.java @@ -14,67 +14,67 @@ */ public class GsonCaseTests { - private Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); + private Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); - private Gson gson2 = new GsonBuilder() - .setVersion(1.0) - .setPrettyPrinting() - .setDateFormat("yyyy-MM-dd HH:mm:ss") - .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE) - .create(); + private Gson gson2 = new GsonBuilder() + .setVersion(1.0) + .setPrettyPrinting() + .setDateFormat("yyyy-MM-dd HH:mm:ss") + .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE) + .create(); - @Test - public void test() { - // Serialization - Gson gson = new Gson(); - gson.toJson(1); // ==> 1 - gson.toJson("abcd"); // ==> "abcd" - gson.toJson(10L); // ==> 10 - int[] values = { 1 }; - gson.toJson(values); // ==> [1] + @Test + public void test() { + // Serialization + Gson gson = new Gson(); + gson.toJson(1); // ==> 1 + gson.toJson("abcd"); // ==> "abcd" + gson.toJson(10L); // ==> 10 + int[] values = { 1 }; + gson.toJson(values); // ==> [1] - // Deserialization - int i1 = gson.fromJson("1", int.class); - Integer i2 = gson.fromJson("1", Integer.class); - Long l1 = gson.fromJson("1", Long.class); - Boolean b1 = gson.fromJson("false", Boolean.class); - String str = gson.fromJson("\"abc\"", String.class); - String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class); + // Deserialization + int i1 = gson.fromJson("1", int.class); + Integer i2 = gson.fromJson("1", Integer.class); + Long l1 = gson.fromJson("1", Long.class); + Boolean b1 = gson.fromJson("false", Boolean.class); + String str = gson.fromJson("\"abc\"", String.class); + String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class); - assertThat(i1).isEqualTo(1); - assertThat(i2).isEqualTo(1); - assertThat(l1).isEqualTo(1L); - assertThat(b1).isFalse(); - assertThat(str).isEqualTo("abc"); - } + assertThat(i1).isEqualTo(1); + assertThat(i2).isEqualTo(1); + assertThat(l1).isEqualTo(1L); + assertThat(b1).isFalse(); + assertThat(str).isEqualTo("abc"); + } - @Test - public void testAnnotation() { - GsonAnnotationBean oldBean = new GsonAnnotationBean(); - oldBean.setSomeField("hello"); - oldBean.setSomeOtherField("world"); + @Test + public void testAnnotation() { + GsonAnnotationBean oldBean = new GsonAnnotationBean(); + oldBean.setSomeField("hello"); + oldBean.setSomeOtherField("world"); - String expectStr = "{\"custom_naming\":\"hello\",\"someOtherField\":\"world\"}"; - String json = gson.toJson(oldBean); - assertThat(json).isEqualTo(expectStr); + String expectStr = "{\"custom_naming\":\"hello\",\"someOtherField\":\"world\"}"; + String json = gson.toJson(oldBean); + assertThat(json).isEqualTo(expectStr); - GsonAnnotationBean newBean = gson.fromJson(expectStr, GsonAnnotationBean.class); - assertThat(newBean).isEqualTo(oldBean); - } + GsonAnnotationBean newBean = gson.fromJson(expectStr, GsonAnnotationBean.class); + assertThat(newBean).isEqualTo(oldBean); + } - @Test - public void testVersionedClass() { - VersionedClass versionedObject = new VersionedClass(); - String jsonOutput = gson2.toJson(versionedObject); - System.out.println(jsonOutput); - assertThat(jsonOutput).isEqualTo("{\n" - + " \"newField\": \"new\",\n" - + " \"field\": \"old\"\n" - + "}"); + @Test + public void testVersionedClass() { + VersionedClass versionedObject = new VersionedClass(); + String jsonOutput = gson2.toJson(versionedObject); + System.out.println(jsonOutput); + assertThat(jsonOutput).isEqualTo("{\n" + + " \"newField\": \"new\",\n" + + " \"field\": \"old\"\n" + + "}"); - jsonOutput = gson.toJson(versionedObject); - System.out.println(jsonOutput); - assertThat(jsonOutput).isEqualTo("{\"newerField\":\"newer\",\"newField\":\"new\",\"field\":\"old\"}"); - } + jsonOutput = gson.toJson(versionedObject); + System.out.println(jsonOutput); + assertThat(jsonOutput).isEqualTo("{\"newerField\":\"newer\",\"newField\":\"new\",\"field\":\"old\"}"); + } } diff --git a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/gson/GsonPerformanceTests.java b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/gson/GsonPerformanceTests.java index 4e1185f4..be79f438 100644 --- a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/gson/GsonPerformanceTests.java +++ b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/gson/GsonPerformanceTests.java @@ -18,36 +18,36 @@ */ public class GsonPerformanceTests { - private static final int BATCH_SIZE = 100000; - - private Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); - - /** - * 循环序列化、反序列 {@link #BATCH_SIZE} 条数据,测试性能 - */ - private long donSerializeAndDeserialize() { - TestBean2 bean = BeanUtils.initNotJdk8Bean(); - long begin = System.nanoTime(); - for (int i = 0; i < BATCH_SIZE; i++) { - String json = gson.toJson(bean); - assertThat(json).isNotBlank(); - TestBean2 newBean = gson.fromJson(json, TestBean2.class); - assertThat(newBean).isNotNull(); - } - long end = System.nanoTime(); - return (end - begin); - } - - /** - * 测试十次,每次序列化、反序列化 100000 条数据,平均耗时约 704 ms - */ - @Test - public void testPerformance() { - long time = 0L; - for (int i = 0; i < 10; i++) { - time += donSerializeAndDeserialize(); - } - System.out.println(String.format("time: %d ms", TimeUnit.NANOSECONDS.toMillis(time / 10))); - } + private static final int BATCH_SIZE = 100000; + + private Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); + + /** + * 测试十次,每次序列化、反序列化 100000 条数据,平均耗时约 704 ms + */ + @Test + public void testPerformance() { + long time = 0L; + for (int i = 0; i < 10; i++) { + time += donSerializeAndDeserialize(); + } + System.out.println(String.format("time: %d ms", TimeUnit.NANOSECONDS.toMillis(time / 10))); + } + + /** + * 循环序列化、反序列 {@link #BATCH_SIZE} 条数据,测试性能 + */ + private long donSerializeAndDeserialize() { + TestBean2 bean = BeanUtils.initNotJdk8Bean(); + long begin = System.nanoTime(); + for (int i = 0; i < BATCH_SIZE; i++) { + String json = gson.toJson(bean); + assertThat(json).isNotBlank(); + TestBean2 newBean = gson.fromJson(json, TestBean2.class); + assertThat(newBean).isNotNull(); + } + long end = System.nanoTime(); + return (end - begin); + } } diff --git a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonPerformanceTests.java b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonPerformanceTests.java index cce3363d..5e960c89 100644 --- a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonPerformanceTests.java +++ b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonPerformanceTests.java @@ -18,36 +18,36 @@ */ public class JacksonPerformanceTests { - private final ObjectMapper mapper = new ObjectMapper(); - - private static final int BATCH_SIZE = 100000; - - /** - * 循环序列化、反序列 {@link #BATCH_SIZE} 条数据,测试性能 - */ - private long donSerializeAndDeserialize() throws IOException { - TestBean2 bean = BeanUtils.initNotJdk8Bean(); - long begin = System.nanoTime(); - for (int i = 0; i < BATCH_SIZE; i++) { - String json = mapper.writeValueAsString(bean); - assertThat(json).isNotBlank(); - TestBean2 newBean = mapper.readValue(json, TestBean2.class); - assertThat(newBean).isNotNull(); - } - long end = System.nanoTime(); - return (end - begin); - } - - /** - * 测试十次,每次序列化、反序列化 100000 条数据,平均耗时约 334 ms - */ - @Test - public void testPerformance() throws IOException { - long time = 0L; - for (int i = 0; i < 10; i++) { - time += donSerializeAndDeserialize(); - } - System.out.println(String.format("time: %d ms", TimeUnit.NANOSECONDS.toMillis(time / 10))); - } + private static final int BATCH_SIZE = 100000; + + private final ObjectMapper mapper = new ObjectMapper(); + + /** + * 测试十次,每次序列化、反序列化 100000 条数据,平均耗时约 334 ms + */ + @Test + public void testPerformance() throws IOException { + long time = 0L; + for (int i = 0; i < 10; i++) { + time += donSerializeAndDeserialize(); + } + System.out.println(String.format("time: %d ms", TimeUnit.NANOSECONDS.toMillis(time / 10))); + } + + /** + * 循环序列化、反序列 {@link #BATCH_SIZE} 条数据,测试性能 + */ + private long donSerializeAndDeserialize() throws IOException { + TestBean2 bean = BeanUtils.initNotJdk8Bean(); + long begin = System.nanoTime(); + for (int i = 0; i < BATCH_SIZE; i++) { + String json = mapper.writeValueAsString(bean); + assertThat(json).isNotBlank(); + TestBean2 newBean = mapper.readValue(json, TestBean2.class); + assertThat(newBean).isNotNull(); + } + long end = System.nanoTime(); + return (end - begin); + } } diff --git a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/util/BeanUtils.java b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/util/BeanUtils.java index 98a29622..6cf32ad4 100644 --- a/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/util/BeanUtils.java +++ b/javalib-io-json/src/test/java/io/github/dunwu/javalib/json/util/BeanUtils.java @@ -16,59 +16,59 @@ */ public class BeanUtils { - public static Group initGroupBean() { - Group group = new Group(); - group.setId(0L); - group.setName("admin"); + public static Group initGroupBean() { + Group group = new Group(); + group.setId(0L); + group.setName("admin"); - User guestUser = new User(); - guestUser.setId(2L); - guestUser.setName("guest"); + User guestUser = new User(); + guestUser.setId(2L); + guestUser.setName("guest"); - User rootUser = new User(); - rootUser.setId(3L); - rootUser.setName("root"); + User rootUser = new User(); + rootUser.setId(3L); + rootUser.setName("root"); - group.addUser(guestUser); - group.addUser(rootUser); + group.addUser(guestUser); + group.addUser(rootUser); - return group; - } + return group; + } - public static TestBean initJdk8Bean() { - String[] strArray = { "a", "b", "c" }; - Integer[] intArray = { 1, 2, 3, 4, 5 }; - List intList = new ArrayList<>(); - intList.addAll(Arrays.asList(intArray)); - Map map = new HashMap<>(); - map.put("name", "jack"); - map.put("age", 18); - map.put("length", 175.3f); - TestBean bean = new TestBean(); - Date date = Date.valueOf("2019-11-22"); - LocalDateTime localDateTime = LocalDateTime.of(2000, 1, 1, 12, 0, 0); - LocalDate localDate = LocalDate.of(1949, 10, 1); - bean.setI1(10).setI2(1024).setF1(0.5f).setD1(100.0) - .setDate1(date).setDate2(localDateTime).setDate3(localDate) - .setColor(TestBean.Color.BLUE).setStrArray(strArray).setIntList(intList).setMap(map); - return bean; - } + public static TestBean initJdk8Bean() { + String[] strArray = { "a", "b", "c" }; + Integer[] intArray = { 1, 2, 3, 4, 5 }; + List intList = new ArrayList<>(); + intList.addAll(Arrays.asList(intArray)); + Map map = new HashMap<>(); + map.put("name", "jack"); + map.put("age", 18); + map.put("length", 175.3f); + TestBean bean = new TestBean(); + Date date = Date.valueOf("2019-11-22"); + LocalDateTime localDateTime = LocalDateTime.of(2000, 1, 1, 12, 0, 0); + LocalDate localDate = LocalDate.of(1949, 10, 1); + bean.setI1(10).setI2(1024).setF1(0.5f).setD1(100.0) + .setDate1(date).setDate2(localDateTime).setDate3(localDate) + .setColor(TestBean.Color.BLUE).setStrArray(strArray).setIntList(intList).setMap(map); + return bean; + } - public static TestBean2 initNotJdk8Bean() { - String[] strArray = { "a", "b", "c" }; - Integer[] intArray = { 1, 2, 3, 4, 5 }; - List intList = new ArrayList<>(); - intList.addAll(Arrays.asList(intArray)); - Map map = new HashMap<>(); - map.put("name", "jack"); - map.put("age", 18); - map.put("length", 175.3f); - TestBean2 bean = new TestBean2(); - Date date = Date.valueOf("2019-11-22"); - bean.setI1(10).setI2(1024).setF1(0.5f).setD1(100.0) - .setDate1(date) - .setColor(TestBean2.Color.BLUE).setStrArray(strArray).setIntList(intList).setMap(map); - return bean; - } + public static TestBean2 initNotJdk8Bean() { + String[] strArray = { "a", "b", "c" }; + Integer[] intArray = { 1, 2, 3, 4, 5 }; + List intList = new ArrayList<>(); + intList.addAll(Arrays.asList(intArray)); + Map map = new HashMap<>(); + map.put("name", "jack"); + map.put("age", 18); + map.put("length", 175.3f); + TestBean2 bean = new TestBean2(); + Date date = Date.valueOf("2019-11-22"); + bean.setI1(10).setI2(1024).setF1(0.5f).setD1(100.0) + .setDate1(date) + .setColor(TestBean2.Color.BLUE).setStrArray(strArray).setIntList(intList).setMap(map); + return bean; + } } diff --git a/javalib-io/pom.xml b/javalib-io/pom.xml index 75077cb2..f7668693 100644 --- a/javalib-io/pom.xml +++ b/javalib-io/pom.xml @@ -1,71 +1,71 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javalib-io - 1.0.0 + io.github.dunwu + javalib-io + 1.0.0 - javalib-io - IO Lib Examples + javalib-io + IO Lib Examples - - UTF-8 - 1.8 - ${java.version} - ${java.version} - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + - - - - de.ruedigermoeller - fst - 2.56 - + + + + de.ruedigermoeller + fst + 2.56 + - - com.alibaba - fastjson - 1.2.56 - - - com.fasterxml.jackson.core - jackson-databind - 2.9.10.1 - + + com.alibaba + fastjson + 1.2.56 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.10.1 + - - - io.github.dunwu - dunwu-common - 0.4.8 - + + + io.github.dunwu + dunwu-common + 0.4.8 + - - - junit - junit - 4.12 - test - - - org.assertj - assertj-core - 3.14.0 - test - - + + + junit + junit + 4.12 + test + + + org.assertj + assertj-core + 3.14.0 + test + + - - - - true - src/main/resources - - - + + + + true + src/main/resources + + + diff --git a/javalib-io/src/main/java/io/github/dunwu/javalib/serialize/FstDemo.java b/javalib-io/src/main/java/io/github/dunwu/javalib/serialize/FstDemo.java index 77480adf..0d0efbf3 100644 --- a/javalib-io/src/main/java/io/github/dunwu/javalib/serialize/FstDemo.java +++ b/javalib-io/src/main/java/io/github/dunwu/javalib/serialize/FstDemo.java @@ -10,19 +10,19 @@ */ public class FstDemo { - private static FSTConfiguration DEFAULT_CONFIG = FSTConfiguration.createDefaultConfiguration(); + private static FSTConfiguration DEFAULT_CONFIG = FSTConfiguration.createDefaultConfiguration(); - public static byte[] serializ(T obj) { - return DEFAULT_CONFIG.asByteArray(obj); - } + public static byte[] serializ(T obj) { + return DEFAULT_CONFIG.asByteArray(obj); + } - public static T derialize(byte[] bytes, Class clazz) throws IOException { - Object obj = DEFAULT_CONFIG.asObject(bytes); - if (clazz.isInstance(obj)) { - return (T) obj; - } else { - throw new IOException("derialize failed"); - } - } + public static T derialize(byte[] bytes, Class clazz) throws IOException { + Object obj = DEFAULT_CONFIG.asObject(bytes); + if (clazz.isInstance(obj)) { + return (T) obj; + } else { + throw new IOException("derialize failed"); + } + } } diff --git a/javalib-io/src/main/java/io/github/dunwu/javalib/serialize/JdkSerializeDemo.java b/javalib-io/src/main/java/io/github/dunwu/javalib/serialize/JdkSerializeDemo.java index 1fc8321f..89bd8821 100644 --- a/javalib-io/src/main/java/io/github/dunwu/javalib/serialize/JdkSerializeDemo.java +++ b/javalib-io/src/main/java/io/github/dunwu/javalib/serialize/JdkSerializeDemo.java @@ -10,27 +10,27 @@ */ public class JdkSerializeDemo { - public static byte[] serializ(T obj) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(obj); - byte[] bytes = baos.toByteArray(); - baos.close(); - oos.close(); - return bytes; - } + public static byte[] serializ(T obj) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + byte[] bytes = baos.toByteArray(); + baos.close(); + oos.close(); + return bytes; + } - public static T derialize(byte[] bytes, Class clazz) throws IOException, ClassNotFoundException { - ByteArrayInputStream bais = new ByteArrayInputStream(bytes); - ObjectInputStream ois = new ObjectInputStream(bais); - Object obj = ois.readObject(); - bais.close(); - ois.close(); - if (clazz.isInstance(obj)) { - return (T) obj; - } else { - throw new IOException("derialize failed"); - } - } + public static T derialize(byte[] bytes, Class clazz) throws IOException, ClassNotFoundException { + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais); + Object obj = ois.readObject(); + bais.close(); + ois.close(); + if (clazz.isInstance(obj)) { + return (T) obj; + } else { + throw new IOException("derialize failed"); + } + } } diff --git a/javalib-io/src/test/java/io/github/dunwu/javalib/bean/Person.java b/javalib-io/src/test/java/io/github/dunwu/javalib/bean/Person.java index 23273f09..69ee8a28 100644 --- a/javalib-io/src/test/java/io/github/dunwu/javalib/bean/Person.java +++ b/javalib-io/src/test/java/io/github/dunwu/javalib/bean/Person.java @@ -4,39 +4,39 @@ public class Person implements Serializable { - private static final long serialVersionUID = -210388541252854256L; + private static final long serialVersionUID = -210388541252854256L; - private String name; + private String name; - private int age; + private int age; - public Person() { - } + public Person() { + } - public Person(String name, int age) { - this.name = name; - this.age = age; - } + public Person(String name, int age) { + this.name = name; + this.age = age; + } - public String getName() { - return name; - } + @Override + public String toString() { + return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; + } - public void setName(String name) { - this.name = name; - } + public String getName() { + return name; + } - public int getAge() { - return age; - } + public void setName(String name) { + this.name = name; + } - public void setAge(int age) { - this.age = age; - } + public int getAge() { + return age; + } - @Override - public String toString() { - return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; - } + public void setAge(int age) { + this.age = age; + } } diff --git a/javalib-io/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonAnnotationBean.java b/javalib-io/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonAnnotationBean.java index 7fd43460..ec94ddaa 100644 --- a/javalib-io/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonAnnotationBean.java +++ b/javalib-io/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonAnnotationBean.java @@ -10,86 +10,86 @@ */ public class FastjsonAnnotationBean { - private int id; - - // 配置date序列化和反序列使用yyyyMMdd日期格式 - @JSONField(format = "yyyy-MM-dd") - private Date date1; - - // 不序列化 - @JSONField(serialize = false) - private Date date2; - - // 不反序列化 - @JSONField(deserialize = false) - private Date date3; - - // 按ordinal排序 - @JSONField(ordinal = 2) - private int f1; - - @JSONField(ordinal = 1) - private int f2; - - public FastjsonAnnotationBean() { - } - - public FastjsonAnnotationBean(int id, Date date1, Date date2, Date date3, int f1, int f2) { - this.id = id; - this.date1 = date1; - this.date2 = date2; - this.date3 = date3; - this.f1 = f1; - this.f2 = f2; - } - - @JSONField(name = "ID") - private int getId() { - return id; - } - - private void setId(int id) { - this.id = id; - } - - private Date getDate1() { - return date1; - } - - private void setDate1(Date date1) { - this.date1 = date1; - } - - private Date getDate2() { - return date2; - } - - private void setDate2(Date date2) { - this.date2 = date2; - } - - private Date getDate3() { - return date3; - } - - private void setDate3(Date date3) { - this.date3 = date3; - } - - private int getF1() { - return f1; - } - - private void setF1(int f1) { - this.f1 = f1; - } - - private int getF2() { - return f2; - } - - private void setF2(int f2) { - this.f2 = f2; - } + private int id; + + // 配置date序列化和反序列使用yyyyMMdd日期格式 + @JSONField(format = "yyyy-MM-dd") + private Date date1; + + // 不序列化 + @JSONField(serialize = false) + private Date date2; + + // 不反序列化 + @JSONField(deserialize = false) + private Date date3; + + // 按ordinal排序 + @JSONField(ordinal = 2) + private int f1; + + @JSONField(ordinal = 1) + private int f2; + + public FastjsonAnnotationBean() { + } + + public FastjsonAnnotationBean(int id, Date date1, Date date2, Date date3, int f1, int f2) { + this.id = id; + this.date1 = date1; + this.date2 = date2; + this.date3 = date3; + this.f1 = f1; + this.f2 = f2; + } + + @JSONField(name = "ID") + private int getId() { + return id; + } + + private void setId(int id) { + this.id = id; + } + + private Date getDate1() { + return date1; + } + + private void setDate1(Date date1) { + this.date1 = date1; + } + + private Date getDate2() { + return date2; + } + + private void setDate2(Date date2) { + this.date2 = date2; + } + + private Date getDate3() { + return date3; + } + + private void setDate3(Date date3) { + this.date3 = date3; + } + + private int getF1() { + return f1; + } + + private void setF1(int f1) { + this.f1 = f1; + } + + private int getF2() { + return f2; + } + + private void setF2(int f2) { + this.f2 = f2; + } } diff --git a/javalib-io/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonTests.java b/javalib-io/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonTests.java index bf6b4e68..128b6ad9 100644 --- a/javalib-io/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonTests.java +++ b/javalib-io/src/test/java/io/github/dunwu/javalib/json/fastjson/FastjsonTests.java @@ -15,37 +15,37 @@ */ public class FastjsonTests { - /** - * 序列化测试 - */ - @Test - public void serialize() { - Person p = new Person("Tom", 20); - String json = JSON.toJSONString(p); - Assert.assertNotNull(json); - System.out.println("json = [" + json + "]"); - } + /** + * 序列化测试 + */ + @Test + public void serialize() { + Person p = new Person("Tom", 20); + String json = JSON.toJSONString(p); + Assert.assertNotNull(json); + System.out.println("json = [" + json + "]"); + } - /** - * 反序列化测试 - */ - @Test - public void deserialize() { - final String json = "{\"age\":20,\"name\":\"Tom\"}"; - Person p = JSON.parseObject(json, Person.class); - Assert.assertNotNull(p); - System.out.println("p = [" + p + "]"); - } + /** + * 反序列化测试 + */ + @Test + public void deserialize() { + final String json = "{\"age\":20,\"name\":\"Tom\"}"; + Person p = JSON.parseObject(json, Person.class); + Assert.assertNotNull(p); + System.out.println("p = [" + p + "]"); + } - /** - * 序列化测试 - */ - @Test - public void serializeAnnotation() { - FastjsonAnnotationBean bean = new FastjsonAnnotationBean(1, new Date(), new Date(), new Date(), 10, 20); - String json = JSON.toJSONString(bean); - Assert.assertNotNull(json); - System.out.println("json = [" + json + "]"); - } + /** + * 序列化测试 + */ + @Test + public void serializeAnnotation() { + FastjsonAnnotationBean bean = new FastjsonAnnotationBean(1, new Date(), new Date(), new Date(), 10, 20); + String json = JSON.toJSONString(bean); + Assert.assertNotNull(json); + System.out.println("json = [" + json + "]"); + } } diff --git a/javalib-io/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonAnnotationBean.java b/javalib-io/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonAnnotationBean.java index 63f89634..91c72899 100644 --- a/javalib-io/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonAnnotationBean.java +++ b/javalib-io/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonAnnotationBean.java @@ -11,45 +11,45 @@ @JsonPropertyOrder(alphabetic = true) public class JacksonAnnotationBean { - private String Name; + private String Name; - private int Age; + private int Age; - @JsonIgnore - private String Sex; + @JsonIgnore + private String Sex; - public JacksonAnnotationBean() { - } + public JacksonAnnotationBean() { + } - public JacksonAnnotationBean(String name, int age, String sex) { - Name = name; - Age = age; - Sex = sex; - } + public JacksonAnnotationBean(String name, int age, String sex) { + Name = name; + Age = age; + Sex = sex; + } - @JsonProperty("username") - public String getName() { - return Name; - } + @JsonProperty("username") + public String getName() { + return Name; + } - public void setName(String name) { - Name = name; - } + public void setName(String name) { + Name = name; + } - public int getAge() { - return Age; - } + public int getAge() { + return Age; + } - public void setAge(int age) { - Age = age; - } + public void setAge(int age) { + Age = age; + } - public String getSex() { - return Sex; - } + public String getSex() { + return Sex; + } - public void setSex(String sex) { - Sex = sex; - } + public void setSex(String sex) { + Sex = sex; + } } diff --git a/javalib-io/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonTests.java b/javalib-io/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonTests.java index 46546a90..aa75f894 100644 --- a/javalib-io/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonTests.java +++ b/javalib-io/src/test/java/io/github/dunwu/javalib/json/jackson/JacksonTests.java @@ -20,82 +20,82 @@ */ public class JacksonTests { - final ObjectMapper mapper = new ObjectMapper(); + final ObjectMapper mapper = new ObjectMapper(); - /** - * 序列化测试 - */ - @Test - public void serialize() { - Person p = new Person("Tom", 20); - String json = null; - try { - json = mapper.writeValueAsString(p); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - Assert.assertNotNull(json); - System.out.println("json = [" + json + "]"); - } + /** + * 序列化测试 + */ + @Test + public void serialize() { + Person p = new Person("Tom", 20); + String json = null; + try { + json = mapper.writeValueAsString(p); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + Assert.assertNotNull(json); + System.out.println("json = [" + json + "]"); + } - /** - * 反序列化测试 - */ - @Test - public void deserialize() { - final String json = "{\"age\":20,\"name\":\"Tom\"}"; - Person p = null; - try { - p = mapper.readValue(json, Person.class); - } catch (IOException e) { - e.printStackTrace(); - } - Assert.assertNotNull(p); - System.out.println("p = [" + p + "]"); - } + /** + * 反序列化测试 + */ + @Test + public void deserialize() { + final String json = "{\"age\":20,\"name\":\"Tom\"}"; + Person p = null; + try { + p = mapper.readValue(json, Person.class); + } catch (IOException e) { + e.printStackTrace(); + } + Assert.assertNotNull(p); + System.out.println("p = [" + p + "]"); + } - /** - * 序列化测试 - */ - @Test - public void serialize2() { - Person p = new Person("Tom", 20); - Person p2 = new Person("Jack", 22); - Person p3 = new Person("Mary", 18); + /** + * 序列化测试 + */ + @Test + public void serialize2() { + Person p = new Person("Tom", 20); + Person p2 = new Person("Jack", 22); + Person p3 = new Person("Mary", 18); - List persons = new LinkedList<>(); - persons.add(p); - persons.add(p2); - persons.add(p3); + List persons = new LinkedList<>(); + persons.add(p); + persons.add(p2); + persons.add(p3); - Map map = new HashMap<>(); - map.put("persons", persons); + Map map = new HashMap<>(); + map.put("persons", persons); - String json = null; - try { - json = mapper.writeValueAsString(map); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } + String json = null; + try { + json = mapper.writeValueAsString(map); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } - Assert.assertNotNull(json); - System.out.println("json = [" + json + "]"); - } + Assert.assertNotNull(json); + System.out.println("json = [" + json + "]"); + } - /** - * 序列化测试 - */ - @Test - public void serialize3() { - JacksonAnnotationBean jacksonAnnotationBean = new JacksonAnnotationBean("jack", 19, "男"); - String json = null; - try { - json = mapper.writeValueAsString(jacksonAnnotationBean); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - Assert.assertNotNull(json); - System.out.println("json = [" + json + "]"); - } + /** + * 序列化测试 + */ + @Test + public void serialize3() { + JacksonAnnotationBean jacksonAnnotationBean = new JacksonAnnotationBean("jack", 19, "男"); + String json = null; + try { + json = mapper.writeValueAsString(jacksonAnnotationBean); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + Assert.assertNotNull(json); + System.out.println("json = [" + json + "]"); + } } diff --git a/javalib-io/src/test/java/io/github/dunwu/javalib/serialize/SerializeTest.java b/javalib-io/src/test/java/io/github/dunwu/javalib/serialize/SerializeTest.java index 2ac862ff..d02e19c5 100644 --- a/javalib-io/src/test/java/io/github/dunwu/javalib/serialize/SerializeTest.java +++ b/javalib-io/src/test/java/io/github/dunwu/javalib/serialize/SerializeTest.java @@ -15,34 +15,34 @@ */ public class SerializeTest { - public static final int BATCH_SIZE = 100000; - - @Test - public void testJdkSerialize() throws IOException, ClassNotFoundException { - long begin = System.currentTimeMillis(); - for (int i = 0; i < BATCH_SIZE; i++) { - Person oldPerson = new Person(RandomExtUtils.randomChineseName(), RandomUtils.nextInt(0, 100)); - byte[] bytes = JdkSerializeDemo.serializ(oldPerson); - assertThat(bytes).isNotEmpty(); - Person newPerson = JdkSerializeDemo.derialize(bytes, Person.class); - assertThat(newPerson).isNotNull(); - } - long end = System.currentTimeMillis(); - System.out.printf("耗时:%s", (end - begin)); - } - - @Test - public void testFst() throws IOException { - long begin = System.currentTimeMillis(); - for (int i = 0; i < BATCH_SIZE; i++) { - Person oldPerson = new Person(RandomExtUtils.randomChineseName(), RandomUtils.nextInt(0, 100)); - byte[] bytes = FstDemo.serializ(oldPerson); - assertThat(bytes).isNotEmpty(); - Person newPerson = FstDemo.derialize(bytes, Person.class); - assertThat(newPerson).isNotNull(); - } - long end = System.currentTimeMillis(); - System.out.printf("耗时:%s", (end - begin)); - } + public static final int BATCH_SIZE = 100000; + + @Test + public void testJdkSerialize() throws IOException, ClassNotFoundException { + long begin = System.currentTimeMillis(); + for (int i = 0; i < BATCH_SIZE; i++) { + Person oldPerson = new Person(RandomExtUtils.randomChineseName(), RandomUtils.nextInt(0, 100)); + byte[] bytes = JdkSerializeDemo.serializ(oldPerson); + assertThat(bytes).isNotEmpty(); + Person newPerson = JdkSerializeDemo.derialize(bytes, Person.class); + assertThat(newPerson).isNotNull(); + } + long end = System.currentTimeMillis(); + System.out.printf("耗时:%s", (end - begin)); + } + + @Test + public void testFst() throws IOException { + long begin = System.currentTimeMillis(); + for (int i = 0; i < BATCH_SIZE; i++) { + Person oldPerson = new Person(RandomExtUtils.randomChineseName(), RandomUtils.nextInt(0, 100)); + byte[] bytes = FstDemo.serializ(oldPerson); + assertThat(bytes).isNotEmpty(); + Person newPerson = FstDemo.derialize(bytes, Person.class); + assertThat(newPerson).isNotNull(); + } + long end = System.currentTimeMillis(); + System.out.printf("耗时:%s", (end - begin)); + } } diff --git a/javalib-log/javalib-log-log4j/pom.xml b/javalib-log/javalib-log-log4j/pom.xml index 12dcdfc7..7c9b1805 100644 --- a/javalib-log/javalib-log-log4j/pom.xml +++ b/javalib-log/javalib-log-log4j/pom.xml @@ -1,64 +1,64 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javalib-log-log4j - 1.0.0 - log4j2 示例 + io.github.dunwu + javalib-log-log4j + 1.0.0 + log4j2 示例 - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 0.4.8 - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 0.4.8 + - - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 - - - log4j - log4j - 1.2.17 - - + + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + 1.2.17 + + - - junit - junit - test - - - - - - io.github.dunwu - dunwu-dependencies - ${dunwu.version} - pom - import - - - + + junit + junit + test + + + + + + io.github.dunwu + dunwu-dependencies + ${dunwu.version} + pom + import + + + - - - - true - src/main/resources - - - + + + + true + src/main/resources + + + diff --git a/javalib-log/javalib-log-log4j/src/main/java/io/github/dunwu/javalib/log/Log4jDemo.java b/javalib-log/javalib-log-log4j/src/main/java/io/github/dunwu/javalib/log/Log4jDemo.java index 47aed11d..c9606900 100644 --- a/javalib-log/javalib-log-log4j/src/main/java/io/github/dunwu/javalib/log/Log4jDemo.java +++ b/javalib-log/javalib-log-log4j/src/main/java/io/github/dunwu/javalib/log/Log4jDemo.java @@ -12,16 +12,16 @@ */ public class Log4jDemo { - private static final Logger logger = LoggerFactory.getLogger(Log4jDemo.class); + private static final Logger logger = LoggerFactory.getLogger(Log4jDemo.class); - public static void main(String[] args) { - for (int i = 0; i < 10; i++) { - logger.trace("NO.{} 这是一条 {} 日志记录", i, "trace"); - logger.debug("NO.{} 这是一条 {} 日志记录", i, "debug"); - logger.info("NO.{} 这是一条 {} 日志记录", i, "info"); - logger.warn("NO.{} 这是一条 {} 日志记录", i, "warn"); - logger.error("NO.{} 这是一条 {} 日志记录", i, "error"); - } - } + public static void main(String[] args) { + for (int i = 0; i < 10; i++) { + logger.trace("NO.{} 这是一条 {} 日志记录", i, "trace"); + logger.debug("NO.{} 这是一条 {} 日志记录", i, "debug"); + logger.info("NO.{} 这是一条 {} 日志记录", i, "info"); + logger.warn("NO.{} 这是一条 {} 日志记录", i, "warn"); + logger.error("NO.{} 这是一条 {} 日志记录", i, "error"); + } + } } diff --git a/javalib-log/javalib-log-log4j/src/main/resources/log4j.xml b/javalib-log/javalib-log-log4j/src/main/resources/log4j.xml index f26b41ac..d8e86c72 100644 --- a/javalib-log/javalib-log-log4j/src/main/resources/log4j.xml +++ b/javalib-log/javalib-log-log4j/src/main/resources/log4j.xml @@ -1,44 +1,44 @@ + "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd"> - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + diff --git a/javalib-log/javalib-log-log4j2/pom.xml b/javalib-log/javalib-log-log4j2/pom.xml index 0c1189af..9ab009b4 100644 --- a/javalib-log/javalib-log-log4j2/pom.xml +++ b/javalib-log/javalib-log-log4j2/pom.xml @@ -1,63 +1,63 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javalib-log-log4j2 - 1.0.0 - log4j2 示例 + io.github.dunwu + javalib-log-log4j2 + 1.0.0 + log4j2 示例 - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 0.4.8 - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 0.4.8 + - - - - org.slf4j - slf4j-api - - - org.apache.logging.log4j - log4j-slf4j-impl - - - org.apache.logging.log4j - log4j-core - - + + + + org.slf4j + slf4j-api + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + - - junit - junit - test - - - - - - io.github.dunwu - dunwu-dependencies - ${dunwu.version} - pom - import - - - + + junit + junit + test + + + + + + io.github.dunwu + dunwu-dependencies + ${dunwu.version} + pom + import + + + - - - - true - src/main/resources - - - + + + + true + src/main/resources + + + diff --git a/javalib-log/javalib-log-log4j2/src/main/java/io/github/dunwu/javalib/log/Log4j2Demo.java b/javalib-log/javalib-log-log4j2/src/main/java/io/github/dunwu/javalib/log/Log4j2Demo.java index a03556a2..2d0ea236 100644 --- a/javalib-log/javalib-log-log4j2/src/main/java/io/github/dunwu/javalib/log/Log4j2Demo.java +++ b/javalib-log/javalib-log-log4j2/src/main/java/io/github/dunwu/javalib/log/Log4j2Demo.java @@ -12,16 +12,16 @@ */ public class Log4j2Demo { - private static final Logger logger = LoggerFactory.getLogger(Log4j2Demo.class); + private static final Logger logger = LoggerFactory.getLogger(Log4j2Demo.class); - public static void main(String[] args) { - for (int i = 0; i < 10; i++) { - logger.trace("NO.{} 这是一条 {} 日志记录", i, "trace"); - logger.debug("NO.{} 这是一条 {} 日志记录", i, "debug"); - logger.info("NO.{} 这是一条 {} 日志记录", i, "info"); - logger.warn("NO.{} 这是一条 {} 日志记录", i, "warn"); - logger.error("NO.{} 这是一条 {} 日志记录", i, "error"); - } - } + public static void main(String[] args) { + for (int i = 0; i < 10; i++) { + logger.trace("NO.{} 这是一条 {} 日志记录", i, "trace"); + logger.debug("NO.{} 这是一条 {} 日志记录", i, "debug"); + logger.info("NO.{} 这是一条 {} 日志记录", i, "info"); + logger.warn("NO.{} 这是一条 {} 日志记录", i, "warn"); + logger.error("NO.{} 这是一条 {} 日志记录", i, "error"); + } + } } diff --git a/javalib-log/javalib-log-log4j2/src/main/resources/log4j2.xml b/javalib-log/javalib-log-log4j2/src/main/resources/log4j2.xml index 8e75851e..2a0fa1d9 100644 --- a/javalib-log/javalib-log-log4j2/src/main/resources/log4j2.xml +++ b/javalib-log/javalib-log-log4j2/src/main/resources/log4j2.xml @@ -1,45 +1,45 @@ - - ???? - - - + ???? + + + - - - - - - + + + + + + - - - - - ${PATTERN} - - - - - - - - - - - - - - - - - - - - - + + + + + ${PATTERN} + + + + + + + + + + + + + + + + + + + + + diff --git a/javalib-log/javalib-log-logback/pom.xml b/javalib-log/javalib-log-logback/pom.xml index 8b2e01cd..fbe141a3 100644 --- a/javalib-log/javalib-log-logback/pom.xml +++ b/javalib-log/javalib-log-logback/pom.xml @@ -1,51 +1,51 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javalib-log-logback - 1.0.0 - log4j2 示例 + io.github.dunwu + javalib-log-logback + 1.0.0 + log4j2 示例 - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 1.2.3 - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 1.2.3 + - - - - ch.qos.logback - logback-classic - ${logback.version} - - - ch.qos.logback - logback-access - ${logback.version} - - + + + + ch.qos.logback + logback-classic + ${logback.version} + + + ch.qos.logback + logback-access + ${logback.version} + + - - junit - junit - 4.12 - test - - + + junit + junit + 4.12 + test + + - - - - true - src/main/resources - - - + + + + true + src/main/resources + + + diff --git a/javalib-log/javalib-log-logback/src/main/java/io/github/dunwu/javalib/log/LogbackDemo.java b/javalib-log/javalib-log-logback/src/main/java/io/github/dunwu/javalib/log/LogbackDemo.java index de635725..fcfe8f9e 100644 --- a/javalib-log/javalib-log-logback/src/main/java/io/github/dunwu/javalib/log/LogbackDemo.java +++ b/javalib-log/javalib-log-logback/src/main/java/io/github/dunwu/javalib/log/LogbackDemo.java @@ -12,16 +12,16 @@ */ public class LogbackDemo { - private static final Logger logger = LoggerFactory.getLogger(LogbackDemo.class); + private static final Logger logger = LoggerFactory.getLogger(LogbackDemo.class); - public static void main(String[] args) { - for (int i = 0; i < 10; i++) { - logger.trace("NO.{} 这是一条 {} 日志记录", i, "trace"); - logger.debug("NO.{} 这是一条 {} 日志记录", i, "debug"); - logger.info("NO.{} 这是一条 {} 日志记录", i, "info"); - logger.warn("NO.{} 这是一条 {} 日志记录", i, "warn"); - logger.error("NO.{} 这是一条 {} 日志记录", i, "error"); - } - } + public static void main(String[] args) { + for (int i = 0; i < 10; i++) { + logger.trace("NO.{} 这是一条 {} 日志记录", i, "trace"); + logger.debug("NO.{} 这是一条 {} 日志记录", i, "debug"); + logger.info("NO.{} 这是一条 {} 日志记录", i, "info"); + logger.warn("NO.{} 这是一条 {} 日志记录", i, "warn"); + logger.error("NO.{} 这是一条 {} 日志记录", i, "error"); + } + } } diff --git a/javalib-log/javalib-log-logback/src/main/resources/logback.xml b/javalib-log/javalib-log-logback/src/main/resources/logback.xml index 7aae84c0..4dd6492d 100644 --- a/javalib-log/javalib-log-logback/src/main/resources/logback.xml +++ b/javalib-log/javalib-log-logback/src/main/resources/logback.xml @@ -3,42 +3,42 @@ - - - - + + + + - - - - - ${PATTERN} - - + + + + + ${PATTERN} + + - - - - - logs/${DIR_NAME}/%d{yyyy-MM,aux}/${FILE_NAME}.%d{yyyy-MM-dd}.%i.log - - 30 - - 100MB - - - - ${PATTERN} - - + + + + + logs/${DIR_NAME}/%d{yyyy-MM,aux}/${FILE_NAME}.%d{yyyy-MM-dd}.%i.log + + 30 + + 100MB + + + + ${PATTERN} + + - - - - + + + + - - - + + + diff --git a/javalib-log/pom.xml b/javalib-log/pom.xml index d51d580e..1a5e6b86 100644 --- a/javalib-log/pom.xml +++ b/javalib-log/pom.xml @@ -1,18 +1,18 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javalib-log - 1.0.0 - pom - javalib 之日志库示例集锦 + io.github.dunwu + javalib-log + 1.0.0 + pom + javalib 之日志库示例集锦 - - javalib-log-log4j - javalib-log-log4j2 - javalib-log-logback - + + javalib-log-log4j + javalib-log-log4j2 + javalib-log-logback + diff --git a/javalib-mvel/pom.xml b/javalib-mvel/pom.xml index e2339c5f..16f22c08 100644 --- a/javalib-mvel/pom.xml +++ b/javalib-mvel/pom.xml @@ -1,46 +1,46 @@ - 4.0.0 + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - io.github.dunwu - javalib-mvel - 1.0.0 + io.github.dunwu + javalib-mvel + 1.0.0 - - - org.mvel - mvel2 - 2.4.2.Final - - - com.alibaba - fastjson - 1.2.31 - - - commons-io - commons-io - 2.5 - - - ch.qos.logback - logback-classic - 1.1.3 - - - junit - junit - 4.12 - test - - + + + org.mvel + mvel2 + 2.4.2.Final + + + com.alibaba + fastjson + 1.2.31 + + + commons-io + commons-io + 2.5 + + + ch.qos.logback + logback-classic + 1.1.3 + + + junit + junit + 4.12 + test + + - - UTF-8 - 1.8 - ${java.version} - ${java.version} - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + diff --git a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/BasicRule.java b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/BasicRule.java index da36f14c..ebaccf9a 100644 --- a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/BasicRule.java +++ b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/BasicRule.java @@ -2,127 +2,127 @@ public class BasicRule implements Rule, Comparable { - protected String name; - - private String description; - - private int priority; - - private String condition; - - private String action; - - public BasicRule() { - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + (description != null ? description.hashCode() : 0); - result = 31 * result + priority; - return result; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - BasicRule basicRule = (BasicRule) o; - - if (priority != basicRule.priority) { - return false; - } - if (!name.equals(basicRule.name)) { - return false; - } - return !(description != null ? !description.equals(basicRule.description) : basicRule.description != null); - } - - @Override - public String toString() { - return name; - } - - @Override - public int compareTo(Rule rule) { - if (priority < rule.getPriority()) { - return -1; - } else if (priority > rule.getPriority()) { - return 1; - } else { - return getName().compareTo(rule.getName()); - } - } - - @Override - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - @Override - public int getPriority() { - return priority; - } - - public void setPriority(int priority) { - this.priority = priority; - } - - @Override - public String getCondition() { - return condition; - } - - public void setCondition(String condition) { - this.condition = condition; - } - - @Override - public String getAction() { - return action; - } - - @Override - public boolean validate() { - if (condition == null || condition.length() == 0) { - throw new IllegalArgumentException("The rule condition must not be null or empty"); - } - if (action == null || action.length() == 0) { - throw new IllegalArgumentException("The rule action must not be null or empty"); - } - return true; - } - - @Override - public boolean evaluate(RuleContext ruleContext) { - return false; - } - - @Override - public void execute(RuleContext ruleContext) { - // do nothing - } - - public void setAction(String action) { - this.action = action; - } + protected String name; + + private String description; + + private int priority; + + private String condition; + + private String action; + + public BasicRule() { + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + (description != null ? description.hashCode() : 0); + result = 31 * result + priority; + return result; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BasicRule basicRule = (BasicRule) o; + + if (priority != basicRule.priority) { + return false; + } + if (!name.equals(basicRule.name)) { + return false; + } + return !(description != null ? !description.equals(basicRule.description) : basicRule.description != null); + } + + @Override + public String toString() { + return name; + } + + @Override + public int compareTo(Rule rule) { + if (priority < rule.getPriority()) { + return -1; + } else if (priority > rule.getPriority()) { + return 1; + } else { + return getName().compareTo(rule.getName()); + } + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + this.priority = priority; + } + + @Override + public String getCondition() { + return condition; + } + + public void setCondition(String condition) { + this.condition = condition; + } + + @Override + public String getAction() { + return action; + } + + @Override + public boolean validate() { + if (condition == null || condition.length() == 0) { + throw new IllegalArgumentException("The rule condition must not be null or empty"); + } + if (action == null || action.length() == 0) { + throw new IllegalArgumentException("The rule action must not be null or empty"); + } + return true; + } + + @Override + public boolean evaluate(RuleContext ruleContext) { + return false; + } + + @Override + public void execute(RuleContext ruleContext) { + // do nothing + } + + public void setAction(String action) { + this.action = action; + } } diff --git a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/DefaultRuleEngine.java b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/DefaultRuleEngine.java index a0b2eb95..7c1ba12a 100644 --- a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/DefaultRuleEngine.java +++ b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/DefaultRuleEngine.java @@ -11,153 +11,153 @@ public class DefaultRuleEngine implements RuleEngine { - protected Logger logger = LoggerFactory.getLogger(this.getClass()); - - /** - * The rules set. - */ - protected Set rules; - - /** - * The engine parameters - */ - protected RuleEngineParams params; - - /** - * The rule fact - */ - protected RuleContext fact; - - /** - * ruleSet Map - */ - private Map> ruleSetMap = new ConcurrentHashMap<>(); - - public DefaultRuleEngine(RuleEngineParams params) { - this.params = params; - this.rules = new TreeSet<>(); - if (params.isSilentMode()) { - // cancle log - } - } - - @Override - public RuleEngineParams getParams() { - return params; - } - - @Override - public void registerRule(Rule rule) { - // 检查规则 - if (rule.validate()) { - rules.add(rule); - } - } - - @Override - public void registerRule(MvelRuleSet ruleSet) { - ruleSet.getRules().forEach(rule -> registerRule(rule)); - logRegisteredRules(); - } - - private void logRegisteredRules() { - logger.info("Registered rules:"); - for (Rule rule : rules) { - logger.info("Rule { name = {}, description = {}, priority = {}}", rule.getName(), rule.getDescription(), - rule.getPriority()); - } - } - - @Override - public void unregisterRule(Rule rule) { - rules.remove(rule); - } - - @Override - public void clearRules() { - ruleSetMap.clear(); - } - - @Override - public Set getRules() { - return rules; - } - - @Override - public Map checkRules() { - logger.info("Checking rules"); - sortRules(); - Map result = new HashMap<>(); - for (Rule rule : rules) { - result.put(rule, rule.evaluate(fact)); - } - return result; - } - - @Override - public void launch(RuleContext fact) { - if (rules.isEmpty()) { - logger.warn("No rules registered! Nothing to apply"); - return; - } - - logEngineParams(); - sortRules(); - applyRules(fact); - } - - private void logEngineParams() { - logger.info("----- Params -----"); - logger.info("Engine name: {}", params.getName()); - logger.info("Rule priority threshold: {}", params.getPriorityThreshold()); - logger.info("Skip on first applied rule: {}", params.isSkipOnFirstAppliedRule()); - logger.info("Skip on first unapplied rule: {}", params.isSkipOnFirstUnAppliedRule()); - logger.info("Skip on first failed rule: {}", params.isSkipOnFirstFailedRule()); - } - - private void sortRules() { - rules = new TreeSet<>(rules); - } - - private void applyRules(RuleContext fact) { - logger.info("Rules evaluation started"); - - for (Rule rule : rules) { - final String name = rule.getName(); - final int priority = rule.getPriority(); - - if (priority > params.getPriorityThreshold()) { - logger.info( - "Rule priority threshold ({}) exceeded at rule {} with priority={}, next rules will be skipped", - new Object[] {params.getPriorityThreshold(), name, priority}); - break; - } - - if (rule.evaluate(fact)) { - logger.info("Rule [{}] triggered", name); - try { - rule.execute(fact); - logger.info("Rule {} performed successfully", name); - if (params.isSkipOnFirstAppliedRule()) { - logger.info("Next rules will be skipped since parameter skipOnFirstAppliedRule is set"); - break; - } - if (params.isSkipOnFirstUnAppliedRule()) { - logger.info("Next rules will be skipped since parameter skipOnFirstUnAppliedRule is set"); - break; - } - } catch (Exception exception) { - logger.error("Rule [{}] performed with error {}", name, exception); - - if (params.isSkipOnFirstFailedRule()) { - logger.info("Next rules will be skipped since parameter skipOnFirstFailedRule is set"); - break; - } - } - } else { - logger.info("Rule [{}] has been evaluated to false, it has not been executed", name); - } - } - } + protected Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * The rules set. + */ + protected Set rules; + + /** + * The engine parameters + */ + protected RuleEngineParams params; + + /** + * The rule fact + */ + protected RuleContext fact; + + /** + * ruleSet Map + */ + private Map> ruleSetMap = new ConcurrentHashMap<>(); + + public DefaultRuleEngine(RuleEngineParams params) { + this.params = params; + this.rules = new TreeSet<>(); + if (params.isSilentMode()) { + // cancle log + } + } + + @Override + public RuleEngineParams getParams() { + return params; + } + + @Override + public void registerRule(Rule rule) { + // 检查规则 + if (rule.validate()) { + rules.add(rule); + } + } + + @Override + public void registerRule(MvelRuleSet ruleSet) { + ruleSet.getRules().forEach(rule -> registerRule(rule)); + logRegisteredRules(); + } + + private void logRegisteredRules() { + logger.info("Registered rules:"); + for (Rule rule : rules) { + logger.info("Rule { name = {}, description = {}, priority = {}}", rule.getName(), rule.getDescription(), + rule.getPriority()); + } + } + + @Override + public void unregisterRule(Rule rule) { + rules.remove(rule); + } + + @Override + public void clearRules() { + ruleSetMap.clear(); + } + + @Override + public Set getRules() { + return rules; + } + + @Override + public Map checkRules() { + logger.info("Checking rules"); + sortRules(); + Map result = new HashMap<>(); + for (Rule rule : rules) { + result.put(rule, rule.evaluate(fact)); + } + return result; + } + + @Override + public void launch(RuleContext fact) { + if (rules.isEmpty()) { + logger.warn("No rules registered! Nothing to apply"); + return; + } + + logEngineParams(); + sortRules(); + applyRules(fact); + } + + private void logEngineParams() { + logger.info("----- Params -----"); + logger.info("Engine name: {}", params.getName()); + logger.info("Rule priority threshold: {}", params.getPriorityThreshold()); + logger.info("Skip on first applied rule: {}", params.isSkipOnFirstAppliedRule()); + logger.info("Skip on first unapplied rule: {}", params.isSkipOnFirstUnAppliedRule()); + logger.info("Skip on first failed rule: {}", params.isSkipOnFirstFailedRule()); + } + + private void applyRules(RuleContext fact) { + logger.info("Rules evaluation started"); + + for (Rule rule : rules) { + final String name = rule.getName(); + final int priority = rule.getPriority(); + + if (priority > params.getPriorityThreshold()) { + logger.info( + "Rule priority threshold ({}) exceeded at rule {} with priority={}, next rules will be skipped", + new Object[] { params.getPriorityThreshold(), name, priority }); + break; + } + + if (rule.evaluate(fact)) { + logger.info("Rule [{}] triggered", name); + try { + rule.execute(fact); + logger.info("Rule {} performed successfully", name); + if (params.isSkipOnFirstAppliedRule()) { + logger.info("Next rules will be skipped since parameter skipOnFirstAppliedRule is set"); + break; + } + if (params.isSkipOnFirstUnAppliedRule()) { + logger.info("Next rules will be skipped since parameter skipOnFirstUnAppliedRule is set"); + break; + } + } catch (Exception exception) { + logger.error("Rule [{}] performed with error {}", name, exception); + + if (params.isSkipOnFirstFailedRule()) { + logger.info("Next rules will be skipped since parameter skipOnFirstFailedRule is set"); + break; + } + } + } else { + logger.info("Rule [{}] has been evaluated to false, it has not been executed", name); + } + } + } + + private void sortRules() { + rules = new TreeSet<>(rules); + } } diff --git a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/MvelRule.java b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/MvelRule.java index c6b4a691..ed3a3073 100644 --- a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/MvelRule.java +++ b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/MvelRule.java @@ -6,29 +6,29 @@ public class MvelRule extends BasicRule { - /** - * 判断条件是否匹配 - */ - @Override - public boolean evaluate(RuleContext ruleContext) { - try { - return (Boolean) MVEL.eval(getCondition(), ruleContext); - } catch (Exception e) { - throw new RuntimeException(String.format("条件[%s]匹配发生异常:", getCondition()), e); - } - } + /** + * 判断条件是否匹配 + */ + @Override + public boolean evaluate(RuleContext ruleContext) { + try { + return (Boolean) MVEL.eval(getCondition(), ruleContext); + } catch (Exception e) { + throw new RuntimeException(String.format("条件[%s]匹配发生异常:", getCondition()), e); + } + } - /** - * 执行条件匹配后的操作 - */ - @Override - public void execute(RuleContext ruleContext) { - try { - Serializable exp = MVEL.compileExpression(getAction(), ruleContext); - MVEL.executeExpression(exp, ruleContext); - } catch (Exception e) { - throw new RuntimeException(String.format("后续操作[%s]执行发生异常:", getAction()), e); - } - } + /** + * 执行条件匹配后的操作 + */ + @Override + public void execute(RuleContext ruleContext) { + try { + Serializable exp = MVEL.compileExpression(getAction(), ruleContext); + MVEL.executeExpression(exp, ruleContext); + } catch (Exception e) { + throw new RuntimeException(String.format("后续操作[%s]执行发生异常:", getAction()), e); + } + } } diff --git a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/MvelRuleSet.java b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/MvelRuleSet.java index 1bf096e2..f8269acc 100644 --- a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/MvelRuleSet.java +++ b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/MvelRuleSet.java @@ -5,30 +5,30 @@ public class MvelRuleSet { - private String name; - - private TreeSet rules; - - public String getName() { - return name; - } - - public void setName(String name) { - if (name == null || name.length() == 0) { - name = RuleConstant.DEFAULT_RULE_NAME; - } - this.name = name; - } - - public Set getRules() { - if (rules == null) { - rules = new TreeSet<>(); - } - return rules; - } - - public void setRules(TreeSet rules) { - this.rules = rules; - } + private String name; + + private TreeSet rules; + + public String getName() { + return name; + } + + public void setName(String name) { + if (name == null || name.length() == 0) { + name = RuleConstant.DEFAULT_RULE_NAME; + } + this.name = name; + } + + public Set getRules() { + if (rules == null) { + rules = new TreeSet<>(); + } + return rules; + } + + public void setRules(TreeSet rules) { + this.rules = rules; + } } diff --git a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/Rule.java b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/Rule.java index f35c7599..19a71e56 100644 --- a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/Rule.java +++ b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/Rule.java @@ -2,60 +2,60 @@ public interface Rule { - /** - * Getter for rule name. - * - * @return the rule name - */ - String getName(); - - /** - * Getter for rule description. - * - * @return rule description - */ - String getDescription(); - - /** - * Getter for rule priority. - * - * @return rule priority - */ - int getPriority(); - - /** - * Getter for the rule condition - * - * @return rule condition - */ - String getCondition(); - - /** - * Getter for the rule action - * - * @return rule action - */ - String getAction(); - - /** - * validate - * - * @return boolean - */ - boolean validate(); - - /** - * Rule conditions abstraction : this method encapsulates the rule's conditions. - * - * @return true if the rule should be applied, false else - */ - boolean evaluate(RuleContext ruleContext); - - /** - * Rule actions abstraction : this method encapsulates the rule's actions. - * - * @throws Exception thrown if an exception occurs during actions performing - */ - void execute(RuleContext ruleContext) throws Exception; + /** + * Getter for rule name. + * + * @return the rule name + */ + String getName(); + + /** + * Getter for rule description. + * + * @return rule description + */ + String getDescription(); + + /** + * Getter for rule priority. + * + * @return rule priority + */ + int getPriority(); + + /** + * Getter for the rule condition + * + * @return rule condition + */ + String getCondition(); + + /** + * Getter for the rule action + * + * @return rule action + */ + String getAction(); + + /** + * validate + * + * @return boolean + */ + boolean validate(); + + /** + * Rule conditions abstraction : this method encapsulates the rule's conditions. + * + * @return true if the rule should be applied, false else + */ + boolean evaluate(RuleContext ruleContext); + + /** + * Rule actions abstraction : this method encapsulates the rule's actions. + * + * @throws Exception thrown if an exception occurs during actions performing + */ + void execute(RuleContext ruleContext) throws Exception; } diff --git a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleConstant.java b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleConstant.java index 505da994..0ded47bd 100644 --- a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleConstant.java +++ b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleConstant.java @@ -5,29 +5,29 @@ */ public final class RuleConstant { - /** - * Default rule name. - */ - public static final String DEFAULT_RULE_NAME = "rule"; + /** + * Default rule name. + */ + public static final String DEFAULT_RULE_NAME = "rule"; - /** - * Default engine name. - */ - public static final String DEFAULT_ENGINE_NAME = "engine"; + /** + * Default engine name. + */ + public static final String DEFAULT_ENGINE_NAME = "engine"; - /** - * Default rule description. - */ - public static final String DEFAULT_RULE_DESCRIPTION = "description"; + /** + * Default rule description. + */ + public static final String DEFAULT_RULE_DESCRIPTION = "description"; - /** - * Default rule priority. - */ - public static final int DEFAULT_RULE_PRIORITY = Integer.MAX_VALUE - 1; + /** + * Default rule priority. + */ + public static final int DEFAULT_RULE_PRIORITY = Integer.MAX_VALUE - 1; - /** - * Default rule priority threshold. - */ - public static final int DEFAULT_RULE_PRIORITY_THRESHOLD = Integer.MAX_VALUE; + /** + * Default rule priority threshold. + */ + public static final int DEFAULT_RULE_PRIORITY_THRESHOLD = Integer.MAX_VALUE; } diff --git a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngine.java b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngine.java index 606acb90..3cb19902 100644 --- a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngine.java +++ b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngine.java @@ -5,50 +5,50 @@ public interface RuleEngine { - /** - * 规则引擎 设置参数 - * - * @return The rules engine parameters - */ - RuleEngineParams getParams(); - - /** - * 注册rule - */ - void registerRule(Rule rule); - - /** - * 注册ruleSet - */ - void registerRule(MvelRuleSet ruleSet); - - /** - * 取消注册rule - */ - void unregisterRule(Rule rule); - - /** - * 清空规则列表 - */ - void clearRules(); - - /** - * Return the set of registered rules. - * - * @return the set of registered rules - */ - Set getRules(); - - /** - * Check rules without firing them. - * - * @return a map with the result of evaluation of each rule - */ - Map checkRules(); - - /** - * Launch all registered rules. - */ - void launch(RuleContext ruleContext); + /** + * 规则引擎 设置参数 + * + * @return The rules engine parameters + */ + RuleEngineParams getParams(); + + /** + * 注册rule + */ + void registerRule(Rule rule); + + /** + * 注册ruleSet + */ + void registerRule(MvelRuleSet ruleSet); + + /** + * 取消注册rule + */ + void unregisterRule(Rule rule); + + /** + * 清空规则列表 + */ + void clearRules(); + + /** + * Return the set of registered rules. + * + * @return the set of registered rules + */ + Set getRules(); + + /** + * Check rules without firing them. + * + * @return a map with the result of evaluation of each rule + */ + Map checkRules(); + + /** + * Launch all registered rules. + */ + void launch(RuleContext ruleContext); } diff --git a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngineBuilder.java b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngineBuilder.java index 94ac20dd..47364478 100644 --- a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngineBuilder.java +++ b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngineBuilder.java @@ -2,44 +2,44 @@ public class RuleEngineBuilder { - private RuleEngineParams params; - - private RuleEngineBuilder() { - params = new RuleEngineParams(RuleConstant.DEFAULT_ENGINE_NAME, false, false, false, - RuleConstant.DEFAULT_RULE_PRIORITY_THRESHOLD, false); - } - - public static RuleEngineBuilder newRuleEngine() { - return new RuleEngineBuilder(); - } - - public RuleEngine build() { - return new DefaultRuleEngine(params); - } - - public RuleEngineBuilder named(final String name) { - params.setName(name); - return this; - } - - public RuleEngineBuilder withSkipOnFirstAppliedRule(final boolean skipOnFirstAppliedRule) { - params.setSkipOnFirstAppliedRule(skipOnFirstAppliedRule); - return this; - } - - public RuleEngineBuilder withSkipOnFirstFailedRule(final boolean skipOnFirstFailedRule) { - params.setSkipOnFirstFailedRule(skipOnFirstFailedRule); - return this; - } - - public RuleEngineBuilder withRulePriorityThreshold(final int priorityThreshold) { - params.setPriorityThreshold(priorityThreshold); - return this; - } - - public RuleEngineBuilder withSilentMode(final boolean silentMode) { - params.setSilentMode(silentMode); - return this; - } + private RuleEngineParams params; + + private RuleEngineBuilder() { + params = new RuleEngineParams(RuleConstant.DEFAULT_ENGINE_NAME, false, false, false, + RuleConstant.DEFAULT_RULE_PRIORITY_THRESHOLD, false); + } + + public static RuleEngineBuilder newRuleEngine() { + return new RuleEngineBuilder(); + } + + public RuleEngine build() { + return new DefaultRuleEngine(params); + } + + public RuleEngineBuilder named(final String name) { + params.setName(name); + return this; + } + + public RuleEngineBuilder withSkipOnFirstAppliedRule(final boolean skipOnFirstAppliedRule) { + params.setSkipOnFirstAppliedRule(skipOnFirstAppliedRule); + return this; + } + + public RuleEngineBuilder withSkipOnFirstFailedRule(final boolean skipOnFirstFailedRule) { + params.setSkipOnFirstFailedRule(skipOnFirstFailedRule); + return this; + } + + public RuleEngineBuilder withRulePriorityThreshold(final int priorityThreshold) { + params.setPriorityThreshold(priorityThreshold); + return this; + } + + public RuleEngineBuilder withSilentMode(final boolean silentMode) { + params.setSilentMode(silentMode); + return this; + } } diff --git a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngineParams.java b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngineParams.java index 858e7b6b..b9498311 100644 --- a/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngineParams.java +++ b/javalib-mvel/src/main/java/io/github/dunwu/javalib/mvel/RuleEngineParams.java @@ -2,92 +2,92 @@ public class RuleEngineParams { - /** - * The engine name. - */ - protected String name; - - /** - * 满足任意条件(即遇到第一个匹配规则时停止) Parameter to skip next applicable rules when a rule is applied. - */ - private boolean skipOnFirstAppliedRule; - - /** - * 满足所有条件(即遇到第第一个未匹配规则时停止) Parameter to skip next applicable rules when a rule has failed. - */ - private boolean skipOnFirstUnAppliedRule; - - /** - * Parameter to skip next applicable rules when a rule has failed. - */ - private boolean skipOnFirstFailedRule; - - /** - * Parameter to skip next rules if priority exceeds a user defined threshold. - */ - private int priorityThreshold; - - /** - * Parameter to mute loggers. - */ - private boolean silentMode; - - public RuleEngineParams(String name, boolean skipOnFirstAppliedRule, boolean skipOnFirstUnAppliedRule, - boolean skipOnFirstFailedRule, int priorityThreshold, boolean silentMode) { - this.name = name; - this.skipOnFirstAppliedRule = skipOnFirstAppliedRule; - this.skipOnFirstUnAppliedRule = skipOnFirstUnAppliedRule; - this.skipOnFirstFailedRule = skipOnFirstFailedRule; - this.priorityThreshold = priorityThreshold; - this.silentMode = silentMode; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getPriorityThreshold() { - return priorityThreshold; - } - - public void setPriorityThreshold(int priorityThreshold) { - this.priorityThreshold = priorityThreshold; - } - - public boolean isSilentMode() { - return silentMode; - } - - public void setSilentMode(boolean silentMode) { - this.silentMode = silentMode; - } - - public boolean isSkipOnFirstAppliedRule() { - return skipOnFirstAppliedRule; - } - - public void setSkipOnFirstAppliedRule(boolean skipOnFirstAppliedRule) { - this.skipOnFirstAppliedRule = skipOnFirstAppliedRule; - } - - public boolean isSkipOnFirstFailedRule() { - return skipOnFirstFailedRule; - } - - public void setSkipOnFirstFailedRule(boolean skipOnFirstFailedRule) { - this.skipOnFirstFailedRule = skipOnFirstFailedRule; - } - - public boolean isSkipOnFirstUnAppliedRule() { - return skipOnFirstUnAppliedRule; - } - - public void setSkipOnFirstUnAppliedRule(boolean skipOnFirstUnAppliedRule) { - this.skipOnFirstUnAppliedRule = skipOnFirstUnAppliedRule; - } + /** + * The engine name. + */ + protected String name; + + /** + * 满足任意条件(即遇到第一个匹配规则时停止) Parameter to skip next applicable rules when a rule is applied. + */ + private boolean skipOnFirstAppliedRule; + + /** + * 满足所有条件(即遇到第第一个未匹配规则时停止) Parameter to skip next applicable rules when a rule has failed. + */ + private boolean skipOnFirstUnAppliedRule; + + /** + * Parameter to skip next applicable rules when a rule has failed. + */ + private boolean skipOnFirstFailedRule; + + /** + * Parameter to skip next rules if priority exceeds a user defined threshold. + */ + private int priorityThreshold; + + /** + * Parameter to mute loggers. + */ + private boolean silentMode; + + public RuleEngineParams(String name, boolean skipOnFirstAppliedRule, boolean skipOnFirstUnAppliedRule, + boolean skipOnFirstFailedRule, int priorityThreshold, boolean silentMode) { + this.name = name; + this.skipOnFirstAppliedRule = skipOnFirstAppliedRule; + this.skipOnFirstUnAppliedRule = skipOnFirstUnAppliedRule; + this.skipOnFirstFailedRule = skipOnFirstFailedRule; + this.priorityThreshold = priorityThreshold; + this.silentMode = silentMode; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getPriorityThreshold() { + return priorityThreshold; + } + + public void setPriorityThreshold(int priorityThreshold) { + this.priorityThreshold = priorityThreshold; + } + + public boolean isSilentMode() { + return silentMode; + } + + public void setSilentMode(boolean silentMode) { + this.silentMode = silentMode; + } + + public boolean isSkipOnFirstAppliedRule() { + return skipOnFirstAppliedRule; + } + + public void setSkipOnFirstAppliedRule(boolean skipOnFirstAppliedRule) { + this.skipOnFirstAppliedRule = skipOnFirstAppliedRule; + } + + public boolean isSkipOnFirstFailedRule() { + return skipOnFirstFailedRule; + } + + public void setSkipOnFirstFailedRule(boolean skipOnFirstFailedRule) { + this.skipOnFirstFailedRule = skipOnFirstFailedRule; + } + + public boolean isSkipOnFirstUnAppliedRule() { + return skipOnFirstUnAppliedRule; + } + + public void setSkipOnFirstUnAppliedRule(boolean skipOnFirstUnAppliedRule) { + this.skipOnFirstUnAppliedRule = skipOnFirstUnAppliedRule; + } } diff --git a/javalib-mvel/src/main/resources/logback.xml b/javalib-mvel/src/main/resources/logback.xml index 8436af44..0163bdd0 100644 --- a/javalib-mvel/src/main/resources/logback.xml +++ b/javalib-mvel/src/main/resources/logback.xml @@ -3,141 +3,141 @@ - - - - - - 10 - 100 - - - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n - - - - - - - ${LOG_PATH}/logs/${FILE_NAME}-error.%d{yyyy-MM-dd}.log - 30 - - - - - 10MB - - - - ERROR - ACCEPT - DENY - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n - - - - - - ${LOG_PATH}/logs/${FILE_NAME}-warn.%d{yyyy-MM-dd}.log - 30 - - - - - 10MB - - - - WARN - ACCEPT - DENY - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n - - - - - - ${LOG_PATH}/logs/${FILE_NAME}-info.%d{yyyy-MM-dd}.log - 30 - - - - - 10MB - - - - INFO - ACCEPT - DENY - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n - - - - - - ${LOG_PATH}/logs/${FILE_NAME}-debug.%d{yyyy-MM-dd}.log - 30 - - - - - 10MB - - - - DEBUG - ACCEPT - DENY - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n - - - - - - ${LOG_PATH}/logs/${FILE_NAME}-trace.%d{yyyy-MM-dd}.log - 30 - - - - - 10MB - - - - TRACE - ACCEPT - DENY - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n - - - - - - - - - - - - - + + + + + + 10 + 100 + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n + + + + + + + ${LOG_PATH}/logs/${FILE_NAME}-error.%d{yyyy-MM-dd}.log + 30 + + + + + 10MB + + + + ERROR + ACCEPT + DENY + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n + + + + + + ${LOG_PATH}/logs/${FILE_NAME}-warn.%d{yyyy-MM-dd}.log + 30 + + + + + 10MB + + + + WARN + ACCEPT + DENY + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n + + + + + + ${LOG_PATH}/logs/${FILE_NAME}-info.%d{yyyy-MM-dd}.log + 30 + + + + + 10MB + + + + INFO + ACCEPT + DENY + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n + + + + + + ${LOG_PATH}/logs/${FILE_NAME}-debug.%d{yyyy-MM-dd}.log + 30 + + + + + 10MB + + + + DEBUG + ACCEPT + DENY + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n + + + + + + ${LOG_PATH}/logs/${FILE_NAME}-trace.%d{yyyy-MM-dd}.log + 30 + + + + + 10MB + + + + TRACE + ACCEPT + DENY + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [javalib-mvel] [%thread] [%p] %c{36}#%M - %m%n + + + + + + + + + + + + + diff --git a/javalib-mvel/src/test/java/io/github/dunwu/javalib/mvel/ClassTests.java b/javalib-mvel/src/test/java/io/github/dunwu/javalib/mvel/ClassTests.java index 9d00a5d3..27ec240d 100644 --- a/javalib-mvel/src/test/java/io/github/dunwu/javalib/mvel/ClassTests.java +++ b/javalib-mvel/src/test/java/io/github/dunwu/javalib/mvel/ClassTests.java @@ -15,30 +15,30 @@ */ public class ClassTests { - private final String dir = "src/test/java/" + getClass().getPackage().getName().replaceAll("\\.", "/"); - - @Test - public void testScript() throws IOException { - final Object o = MVEL.evalFile(new File(dir + "/demo.mvel"), new HashMap()); - } - - @Test - public void testEval() { - String expression = "foobar > 99"; - Map vars = new HashMap(); - vars.put("foobar", new Integer(100)); - Boolean result = (Boolean) MVEL.eval(expression, vars); - Assert.assertEquals(true, result); - } - - @Test - public void testCompileExpression() { - String expression = "foobar > 99"; - Serializable compiled = MVEL.compileExpression(expression); - Map vars = new HashMap(); - vars.put("foobar", new Integer(100)); - Boolean result = (Boolean) MVEL.executeExpression(compiled, vars); - Assert.assertEquals(true, result); - } + private final String dir = "src/test/java/" + getClass().getPackage().getName().replaceAll("\\.", "/"); + + @Test + public void testScript() throws IOException { + final Object o = MVEL.evalFile(new File(dir + "/demo.mvel"), new HashMap()); + } + + @Test + public void testEval() { + String expression = "foobar > 99"; + Map vars = new HashMap(); + vars.put("foobar", new Integer(100)); + Boolean result = (Boolean) MVEL.eval(expression, vars); + Assert.assertEquals(true, result); + } + + @Test + public void testCompileExpression() { + String expression = "foobar > 99"; + Serializable compiled = MVEL.compileExpression(expression); + Map vars = new HashMap(); + vars.put("foobar", new Integer(100)); + Boolean result = (Boolean) MVEL.executeExpression(compiled, vars); + Assert.assertEquals(true, result); + } } diff --git a/javalib-mvel/src/test/java/io/github/dunwu/javalib/mvel/SalaryRuleTest.java b/javalib-mvel/src/test/java/io/github/dunwu/javalib/mvel/SalaryRuleTest.java index 6f20ca10..2a07528d 100644 --- a/javalib-mvel/src/test/java/io/github/dunwu/javalib/mvel/SalaryRuleTest.java +++ b/javalib-mvel/src/test/java/io/github/dunwu/javalib/mvel/SalaryRuleTest.java @@ -15,65 +15,65 @@ public class SalaryRuleTest { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - private final String SALARY_RULE_PATH = System.getProperty("user.dir") + "\\src\\test\\resources\\SalaryRule.json"; - - private RuleEngine ruleEngine; - - @Before - public void before() throws IOException { - logger.info("Begin"); - RuleEngineParams params = new RuleEngineParams("SalaryEngine", true, false, true, - RuleConstant.DEFAULT_RULE_PRIORITY_THRESHOLD, false); - ruleEngine = new DefaultRuleEngine(params); - - String json = FileUtils.readFileToString(new File(SALARY_RULE_PATH), "utf-8"); - MvelRuleSet ruleSet = JSON.parseObject(json, MvelRuleSet.class); - ruleEngine.registerRule(ruleSet); - } - - @Test - public void test_salaryRule() { - RuleContext ruleContext = new RuleContext(); - ruleContext.put("fee", 0.0); - - ruleContext.put("salary", 1000); - ruleEngine.launch(ruleContext); - assertEquals(0, ruleContext.get("fee")); - - ruleContext.put("salary", 4000); - ruleEngine.launch(ruleContext); - assertEquals(15.0, ruleContext.get("fee")); - - ruleContext.put("salary", 7000); - ruleEngine.launch(ruleContext); - assertEquals(245.0, ruleContext.get("fee")); - - ruleContext.put("salary", 10000); - ruleEngine.launch(ruleContext); - assertEquals(745.0, ruleContext.get("fee")); - - ruleContext.put("salary", 18000); - ruleEngine.launch(ruleContext); - assertEquals(2620.0, ruleContext.get("fee")); - - ruleContext.put("salary", 40005); - ruleEngine.launch(ruleContext); - assertEquals(8196.50, ruleContext.get("fee")); - - ruleContext.put("salary", 70005); - ruleEngine.launch(ruleContext); - assertEquals(17771.75, ruleContext.get("fee")); - - ruleContext.put("salary", 100000); - ruleEngine.launch(ruleContext); - assertEquals(29920.00, ruleContext.get("fee")); - } - - @After - public void after() { - logger.info("End"); - } + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final String SALARY_RULE_PATH = System.getProperty("user.dir") + "\\src\\test\\resources\\SalaryRule.json"; + + private RuleEngine ruleEngine; + + @Before + public void before() throws IOException { + logger.info("Begin"); + RuleEngineParams params = new RuleEngineParams("SalaryEngine", true, false, true, + RuleConstant.DEFAULT_RULE_PRIORITY_THRESHOLD, false); + ruleEngine = new DefaultRuleEngine(params); + + String json = FileUtils.readFileToString(new File(SALARY_RULE_PATH), "utf-8"); + MvelRuleSet ruleSet = JSON.parseObject(json, MvelRuleSet.class); + ruleEngine.registerRule(ruleSet); + } + + @Test + public void test_salaryRule() { + RuleContext ruleContext = new RuleContext(); + ruleContext.put("fee", 0.0); + + ruleContext.put("salary", 1000); + ruleEngine.launch(ruleContext); + assertEquals(0, ruleContext.get("fee")); + + ruleContext.put("salary", 4000); + ruleEngine.launch(ruleContext); + assertEquals(15.0, ruleContext.get("fee")); + + ruleContext.put("salary", 7000); + ruleEngine.launch(ruleContext); + assertEquals(245.0, ruleContext.get("fee")); + + ruleContext.put("salary", 10000); + ruleEngine.launch(ruleContext); + assertEquals(745.0, ruleContext.get("fee")); + + ruleContext.put("salary", 18000); + ruleEngine.launch(ruleContext); + assertEquals(2620.0, ruleContext.get("fee")); + + ruleContext.put("salary", 40005); + ruleEngine.launch(ruleContext); + assertEquals(8196.50, ruleContext.get("fee")); + + ruleContext.put("salary", 70005); + ruleEngine.launch(ruleContext); + assertEquals(17771.75, ruleContext.get("fee")); + + ruleContext.put("salary", 100000); + ruleEngine.launch(ruleContext); + assertEquals(29920.00, ruleContext.get("fee")); + } + + @After + public void after() { + logger.info("End"); + } } diff --git a/javalib-mvel/src/test/resources/SalaryRule.json b/javalib-mvel/src/test/resources/SalaryRule.json index 7ccd0dd6..6a43150e 100644 --- a/javalib-mvel/src/test/resources/SalaryRule.json +++ b/javalib-mvel/src/test/resources/SalaryRule.json @@ -1,51 +1,51 @@ { - "name": "salaryRule", - "rules": [ - { - "name": "step1", - "action": "fee=0", - "condition": "salary<=3500" - }, - { - "name": "step2", - "action": "fee=(salary-3500)*0.03", - "condition": "salary>3500 && salary<=5000" - }, - { - "name": "step3", - "action": "fee=(salary-3500)*0.1-105", - "condition": "salary>5000 && salary<=8000", - "priority": 3 - }, - { - "name": "step4", - "action": "fee=(salary-3500)*0.2-555", - "condition": "salary>8000 && salary<=12500", - "priority": 4 - }, - { - "name": "step5", - "action": "fee=(salary-3500)*0.25-1005", - "condition": "salary>12500 && salary<=38500", - "priority": 5 - }, - { - "name": "step6", - "action": "fee=(salary-3500)*0.3-2755", - "condition": "salary>38500 && salary<=58500", - "priority": 6 - }, - { - "name": "step7", - "action": "fee=(salary-3500)*0.35-5505", - "condition": "salary>58500 && salary<=83500", - "priority": 7 - }, - { - "name": "step8", - "action": "fee=(salary-3500)*0.45-13505", - "condition": "salary>83500", - "priority": 8 - } - ] + "name": "salaryRule", + "rules": [ + { + "name": "step1", + "action": "fee=0", + "condition": "salary<=3500" + }, + { + "name": "step2", + "action": "fee=(salary-3500)*0.03", + "condition": "salary>3500 && salary<=5000" + }, + { + "name": "step3", + "action": "fee=(salary-3500)*0.1-105", + "condition": "salary>5000 && salary<=8000", + "priority": 3 + }, + { + "name": "step4", + "action": "fee=(salary-3500)*0.2-555", + "condition": "salary>8000 && salary<=12500", + "priority": 4 + }, + { + "name": "step5", + "action": "fee=(salary-3500)*0.25-1005", + "condition": "salary>12500 && salary<=38500", + "priority": 5 + }, + { + "name": "step6", + "action": "fee=(salary-3500)*0.3-2755", + "condition": "salary>38500 && salary<=58500", + "priority": 6 + }, + { + "name": "step7", + "action": "fee=(salary-3500)*0.35-5505", + "condition": "salary>58500 && salary<=83500", + "priority": 7 + }, + { + "name": "step8", + "action": "fee=(salary-3500)*0.45-13505", + "condition": "salary>83500", + "priority": 8 + } + ] } diff --git a/javalib-office/pom.xml b/javalib-office/pom.xml deleted file mode 100644 index 181b562e..00000000 --- a/javalib-office/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - 4.0.0 - - io.github.dunwu - javalib-office - 1.0.0 - pom - javalib-office - - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 4.0.0 - - - - - - org.apache.poi - poi - ${poi.version} - - - org.apache.poi - poi-ooxml - ${poi.version} - - - org.apache.poi - poi-scratchpad - ${poi.version} - - - - - junit - junit - 4.12 - test - - - diff --git a/javalib-office/src/main/java/io/github/dunwu/javalib/office/WordUtil.java b/javalib-office/src/main/java/io/github/dunwu/javalib/office/WordUtil.java deleted file mode 100644 index 27ccde00..00000000 --- a/javalib-office/src/main/java/io/github/dunwu/javalib/office/WordUtil.java +++ /dev/null @@ -1,263 +0,0 @@ -package io.github.dunwu.javalib.office; - -import org.apache.poi.hpsf.DocumentSummaryInformation; -import org.apache.poi.hpsf.SummaryInformation; -import org.apache.poi.hwpf.HWPFDocument; -import org.apache.poi.ooxml.POIXMLProperties; -import org.apache.poi.xwpf.extractor.XWPFWordExtractor; -import org.apache.poi.xwpf.usermodel.*; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; - -/** - * @author Zhang Peng - * @see https://poi.apache.org/ - * @see https://www.w3cschool.cn/apache_poi_word/apache_poi_word_overview.html - * @since 2018-11-08 - */ -public class WordUtil { - - /** - * 创建空白文档 - * - * @param filename - * @throws IOException - */ - public static void create(String filename) throws IOException { - // Blank Document - XWPFDocument document = new XWPFDocument(); - // Write the Document in file system - FileOutputStream out = new FileOutputStream(new File(filename)); - document.write(out); - out.close(); - System.out.printf("create %s written successully\n", filename); - } - - /** - * 创建 *.docx 文档,包含 content 内容 - * - * @param filename - * @throws IOException - */ - public static void create(String filename, String content) throws IOException { - // Blank Document - XWPFDocument document = new XWPFDocument(); - // Write the Document in file system - FileOutputStream out = new FileOutputStream(new File(filename)); - - // create Paragraph - XWPFParagraph paragraph = document.createParagraph(); - XWPFRun run = paragraph.createRun(); - run.setText(content); - document.write(out); - out.close(); - System.out.printf("create %s written successully\n", filename); - } - - /** - * 创建 *.docx 文档,包含 content 内容,content 内容置于边框中 - * - * @param filename - * @throws IOException - */ - public static void createWithBorders(String filename, String content) throws IOException { - // Blank Document - XWPFDocument document = new XWPFDocument(); - - // Write the Document in file system - FileOutputStream out = new FileOutputStream(new File(filename)); - - // create paragraph - XWPFParagraph paragraph = document.createParagraph(); - - // Set bottom border to paragraph - paragraph.setBorderBottom(Borders.BASIC_BLACK_DASHES); - - // Set left border to paragraph - paragraph.setBorderLeft(Borders.BASIC_BLACK_DASHES); - - // Set right border to paragraph - paragraph.setBorderRight(Borders.BASIC_BLACK_DASHES); - - // Set top border to paragraph - paragraph.setBorderTop(Borders.BASIC_BLACK_DASHES); - - XWPFRun run = paragraph.createRun(); - run.setText(content); - - document.write(out); - out.close(); - System.out.printf("create %s written successully\n", filename); - } - - /** - * 表格 - * - * @param filename - * @throws IOException - */ - public static void createWithTable(String filename) throws IOException { - // Blank Document - XWPFDocument document = new XWPFDocument(); - - // Write the Document in file system - FileOutputStream out = new FileOutputStream(new File(filename)); - - // create table - XWPFTable table = document.createTable(); - // create first row - XWPFTableRow tableRowOne = table.getRow(0); - tableRowOne.getCell(0).setText("col one, row one"); - tableRowOne.addNewTableCell().setText("col two, row one"); - tableRowOne.addNewTableCell().setText("col three, row one"); - // create second row - XWPFTableRow tableRowTwo = table.createRow(); - tableRowTwo.getCell(0).setText("col one, row two"); - tableRowTwo.getCell(1).setText("col two, row two"); - tableRowTwo.getCell(2).setText("col three, row two"); - // create third row - XWPFTableRow tableRowThree = table.createRow(); - tableRowThree.getCell(0).setText("col one, row three"); - tableRowThree.getCell(1).setText("col two, row three"); - tableRowThree.getCell(2).setText("col three, row three"); - - document.write(out); - out.close(); - System.out.printf("create %s written successully\n", filename); - } - - /** - * 字体样式 - * - * @param filename - * @throws IOException - */ - public static void createWithFontStyle(String filename) throws IOException { - // Blank Document - XWPFDocument document = new XWPFDocument(); - - // Write the Document in file system - FileOutputStream out = new FileOutputStream(new File(filename)); - - // create paragraph - XWPFParagraph paragraph = document.createParagraph(); - - // Set Bold an Italic - XWPFRun paragraphOneRunOne = paragraph.createRun(); - paragraphOneRunOne.setBold(true); - paragraphOneRunOne.setItalic(true); - paragraphOneRunOne.setText("Font Style"); - paragraphOneRunOne.addBreak(); - - // Set text Position - XWPFRun paragraphOneRunTwo = paragraph.createRun(); - paragraphOneRunTwo.setText("Font Style two"); - paragraphOneRunTwo.setTextPosition(100); - - // Set Strike through and Font Size and Subscript - XWPFRun paragraphOneRunThree = paragraph.createRun(); - paragraphOneRunThree.setStrike(true); - paragraphOneRunThree.setFontSize(20); - paragraphOneRunThree.setSubscript(VerticalAlign.SUBSCRIPT); - paragraphOneRunThree.setText(" Different Font Styles"); - - document.write(out); - out.close(); - System.out.printf("create %s written successully\n", filename); - } - - /** - * 对齐方式 - * - * @param filename - * @throws IOException - */ - public static void createWithAlign(String filename) throws IOException { - // Blank Document - XWPFDocument document = new XWPFDocument(); - - // Write the Document in file system - FileOutputStream out = new FileOutputStream(new File(filename)); - - // create paragraph - XWPFParagraph paragraph = document.createParagraph(); - - // Set alignment paragraph to RIGHT - paragraph.setAlignment(ParagraphAlignment.RIGHT); - XWPFRun run = paragraph.createRun(); - run.setText("At tutorialspoint.com, we strive hard to " + "provide quality tutorials for self-learning " - + "purpose in the domains of Academics, Information " - + "Technology, Management and Computer Programming " + "Languages."); - - // Create Another paragraph - paragraph = document.createParagraph(); - - // Set alignment paragraph to CENTER - paragraph.setAlignment(ParagraphAlignment.CENTER); - run = paragraph.createRun(); - run.setText("The endeavour started by Mohtashim, an AMU " - + "alumni, who is the founder and the managing director " - + "of Tutorials Point (I) Pvt. Ltd. He came up with the " - + "website tutorialspoint.com in year 2006 with the help" - + "of handpicked freelancers, with an array of tutorials" + " for computer programming languages. "); - document.write(out); - out.close(); - System.out.printf("create %s written successully\n", filename); - } - - /** - * 文本提取 - * - * @param filename - * @throws IOException - */ - public static void extractor(String filename) throws IOException { - XWPFDocument docx = new XWPFDocument(new FileInputStream(filename)); - // using XWPFWordExtractor Class - XWPFWordExtractor we = new XWPFWordExtractor(docx); - System.out.println(we.getText()); - } - - public static void setDocxProperties(String filename) throws IOException { - FileInputStream fis = new FileInputStream(new File(filename)); - XWPFDocument doc = new XWPFDocument(fis); - - POIXMLProperties properties = doc.getProperties(); - POIXMLProperties.CoreProperties coreProperties = properties.getCoreProperties(); - coreProperties.setCreator("星环信息科技有限公司"); - coreProperties.setLastModifiedByUser("星环信息科技有限公司"); - POIXMLProperties.ExtendedProperties extendedProperties = properties.getExtendedProperties(); - extendedProperties.getUnderlyingProperties().setCompany("星环信息科技有限公司"); - - FileOutputStream fos = new FileOutputStream(new File(filename)); - doc.write(fos); - - fos.close(); - doc.close(); - fis.close(); - } - - public static void setDocProperties(String filename) throws IOException { - System.out.println("filename = [" + filename + "]"); - FileInputStream fis = new FileInputStream(new File(filename)); - HWPFDocument doc = new HWPFDocument(fis); - - SummaryInformation summaryInformation = doc.getSummaryInformation(); - summaryInformation.setAuthor("张鹏"); - summaryInformation.setLastAuthor("张鹏"); - DocumentSummaryInformation documentSummaryInformation = doc.getDocumentSummaryInformation(); - documentSummaryInformation.setCompany("张鹏"); - documentSummaryInformation.setDocumentVersion("1"); - - FileOutputStream fos = new FileOutputStream(new File(filename)); - doc.write(fos); - - fos.close(); - doc.close(); - fis.close(); - } - -} diff --git a/javalib-office/src/test/java/io/github/dunwu/javalib/office/WordUtilTest.java b/javalib-office/src/test/java/io/github/dunwu/javalib/office/WordUtilTest.java deleted file mode 100644 index 01d8e69f..00000000 --- a/javalib-office/src/test/java/io/github/dunwu/javalib/office/WordUtilTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package io.github.dunwu.javalib.office; - -import org.junit.Test; - -import java.io.IOException; - -/** - * @author Zhang Peng - * @since 2018-11-08 - */ -public class WordUtilTest { - - @Test - public void testCreateDocx() { - try { - WordUtil.create("d://temp.docx"); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Test - public void testCreateDocxWithContent() { - StringBuilder sb = new StringBuilder(); - sb.append("At tutorialspoint.com, we strive hard to "); - sb.append("provide quality tutorials for self-learning "); - sb.append("purpose in the domains of Academics, Information "); - sb.append("Technology, Management and Computer Programming Languages."); - - try { - WordUtil.create("d://temp2.docx", sb.toString()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Test - public void testCreateDocxWithBorder() { - StringBuilder sb = new StringBuilder(); - sb.append("At tutorialspoint.com, we strive hard to "); - sb.append("provide quality tutorials for self-learning "); - sb.append("purpose in the domains of Academics, Information "); - sb.append("Technology, Management and Computer Programming Languages."); - - try { - WordUtil.createWithBorders("d://temp3.docx", sb.toString()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Test - public void testCreateDocxWithTable() { - try { - WordUtil.createWithTable("d://temp4.docx"); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Test - public void testCreateDocxWithFontStyle() { - try { - WordUtil.createWithFontStyle("d://temp5.docx"); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Test - public void testCreateDocxWithAlign() { - try { - WordUtil.createWithAlign("d://temp6.docx"); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Test - public void testExtractor() { - try { - WordUtil.extractor("d://temp6.docx"); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Test - public void test() { - try { - WordUtil.setDocxProperties("d://temp6.docx"); - } catch (IOException e) { - e.printStackTrace(); - } - } - - // @Test - // public void test2() { - // File dir = new File("D:\\Docs\\ZP\\notes\\软件工程\\软件工程文档标准模板"); - // File[] files = dir.listFiles(); - // for (File file : files) { - // if (!file.isDirectory()) { - // try { - // WordUtil.setDocProperties(file.getAbsolutePath()); - // } catch (IOException e) { - // e.printStackTrace(); - // } - // } - // } - // } - -} diff --git a/javalib-test/pom.xml b/javalib-test/pom.xml index eb7a20fe..48733b93 100644 --- a/javalib-test/pom.xml +++ b/javalib-test/pom.xml @@ -1,72 +1,85 @@ - 4.0.0 + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - io.github.dunwu - javalib-test - 1.0.0 + io.github.dunwu + javalib-test + 1.0.0 - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 0.4.8 - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 0.4.8 + 1.22 + - - - ch.qos.logback - logback-classic - + + + ch.qos.logback + logback-classic + - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-params - test - - - org.junit.jupiter - junit-jupiter-engine - test - + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + - - - junit - junit - test - + + + junit + junit + test + - - - org.assertj - assertj-core - test - - - org.mockito - mockito-core - test - - - - - - io.github.dunwu - dunwu-dependencies - ${dunwu.version} - pom - import - - - + + + org.assertj + assertj-core + test + + + org.mockito + mockito-core + test + + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + + + + io.github.dunwu + dunwu-dependencies + ${dunwu.version} + pom + import + + + diff --git a/javalib-test/src/main/java/io/github/dunwu/javalib/jmh/QuickStart.java b/javalib-test/src/main/java/io/github/dunwu/javalib/jmh/QuickStart.java new file mode 100644 index 00000000..bf130aae --- /dev/null +++ b/javalib-test/src/main/java/io/github/dunwu/javalib/jmh/QuickStart.java @@ -0,0 +1,43 @@ +package io.github.dunwu.javalib.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 3) +@Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class QuickStart { + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(QuickStart.class.getSimpleName()) + .forks(1) + .build(); + new Runner(opt).run(); + } + + @Benchmark + public String testStringAdd() { + String a = ""; + for (int i = 0; i < 10; i++) { + a += i; + } + return a; + } + + @Benchmark + public String testStringBuilderAdd() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + return sb.toString(); + } + +} diff --git a/javalib-test/src/main/java/io/github/dunwu/javalib/jmh/StringBuilderBenchmark.java b/javalib-test/src/main/java/io/github/dunwu/javalib/jmh/StringBuilderBenchmark.java new file mode 100644 index 00000000..8596d3c3 --- /dev/null +++ b/javalib-test/src/main/java/io/github/dunwu/javalib/jmh/StringBuilderBenchmark.java @@ -0,0 +1,45 @@ +package io.github.dunwu.javalib.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 3) +@Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS) +@Threads(8) +@Fork(2) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class StringBuilderBenchmark { + + public static void main(String[] args) throws RunnerException { + Options options = new OptionsBuilder() + .include(StringBuilderBenchmark.class.getSimpleName()) + .output("d:/Benchmark.log") + .build(); + new Runner(options).run(); + } + + @Benchmark + public void testStringAdd() { + String a = ""; + for (int i = 0; i < 10; i++) { + a += i; + } + // System.out.println(a); + } + + @Benchmark + public void testStringBuilderAdd() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb.append(i); + } + // System.out.println(sb.toString()); + } + +} diff --git a/javalib-test/src/main/java/io/github/dunwu/javalib/lombok/Calculator.java b/javalib-test/src/main/java/io/github/dunwu/javalib/lombok/Calculator.java index 50ca521c..10612632 100644 --- a/javalib-test/src/main/java/io/github/dunwu/javalib/lombok/Calculator.java +++ b/javalib-test/src/main/java/io/github/dunwu/javalib/lombok/Calculator.java @@ -2,8 +2,8 @@ public class Calculator { - public int add(int a, int b) { - return a + b; - } + public int add(int a, int b) { + return a + b; + } } diff --git a/javalib-test/src/main/java/io/github/dunwu/javalib/lombok/Person.java b/javalib-test/src/main/java/io/github/dunwu/javalib/lombok/Person.java index 5db581ab..d41f6059 100644 --- a/javalib-test/src/main/java/io/github/dunwu/javalib/lombok/Person.java +++ b/javalib-test/src/main/java/io/github/dunwu/javalib/lombok/Person.java @@ -6,29 +6,29 @@ */ public class Person { - private String firstName; + private String firstName; - private String lastName; + private String lastName; - public Person(String firstName, String lastName) { - this.firstName = firstName; - this.lastName = lastName; - } + public Person(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } - public String getFirstName() { - return firstName; - } + public String getFirstName() { + return firstName; + } - public void setFirstName(String firstName) { - this.firstName = firstName; - } + public void setFirstName(String firstName) { + this.firstName = firstName; + } - public String getLastName() { - return lastName; - } + public String getLastName() { + return lastName; + } - public void setLastName(String lastName) { - this.lastName = lastName; - } + public void setLastName(String lastName) { + this.lastName = lastName; + } } diff --git a/javalib-test/src/main/resources/logback.xml b/javalib-test/src/main/resources/logback.xml index dae97aa2..240ee4c6 100644 --- a/javalib-test/src/main/resources/logback.xml +++ b/javalib-test/src/main/resources/logback.xml @@ -1,15 +1,15 @@ - - - %d{HH:mm:ss.SSS} [%boldYellow(%thread)] [%highlight(%-5level)] %boldGreen(%c{36}.%M) - %boldBlue(%m%n) - - - + + + %d{HH:mm:ss.SSS} [%boldYellow(%thread)] [%highlight(%-5level)] %boldGreen(%c{36}.%M) - %boldBlue(%m%n) + + + - + - - - + + + diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/junit4/JUnitTest.java b/javalib-test/src/test/java/io/github/dunwu/javalib/junit4/JUnitTest.java index d6093f01..6370a692 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/junit4/JUnitTest.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/junit4/JUnitTest.java @@ -11,61 +11,61 @@ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class JUnitTest { - /** - * @BeforeClass 注解指出这是附着在静态方法必须执行一次并在类的所有测试之前。 一般用于共享配置方法(如连接到数据库)。 - */ - @BeforeClass - public static void beforeClass() { - System.out.println("call @BeforeClass"); - } + /** + * @BeforeClass 注解指出这是附着在静态方法必须执行一次并在类的所有测试之前。 一般用于共享配置方法(如连接到数据库)。 + */ + @BeforeClass + public static void beforeClass() { + System.out.println("call @BeforeClass"); + } - /** - * 当需要执行所有的测试在JUnit测试用例类后执行,@AfterClass 注解可以使用以清理建立方法,(从数据库如断开连接)。 注意:附有此批注(类似于BeforeClass)的方法必须定义为静态。 - */ - @AfterClass - public static void afterClass() { - System.out.println("call @AfterClass"); - } + /** + * 当需要执行所有的测试在JUnit测试用例类后执行,@AfterClass 注解可以使用以清理建立方法,(从数据库如断开连接)。 注意:附有此批注(类似于BeforeClass)的方法必须定义为静态。 + */ + @AfterClass + public static void afterClass() { + System.out.println("call @AfterClass"); + } - @Test - public void testA() { - System.out.println("call @Test testA"); - int sum = 1 + 2 + 3; - Assert.assertEquals(6, sum); - } + @Test + public void testA() { + System.out.println("call @Test testA"); + int sum = 1 + 2 + 3; + Assert.assertEquals(6, sum); + } - @Test - public void testC() { - System.out.println("call @Test testC"); - } + @Test + public void testC() { + System.out.println("call @Test testC"); + } - @Test - public void testB() { - System.out.println("call @Test testB"); - } + @Test + public void testB() { + System.out.println("call @Test testB"); + } - /** - * @Before 注解修饰的方法必须在类中的每个测试之前执行,以便执行测试某些必要的先决条件。 - */ - @Before - public void before() { - System.out.println("call @Before"); - } + /** + * @Before 注解修饰的方法必须在类中的每个测试之前执行,以便执行测试某些必要的先决条件。 + */ + @Before + public void before() { + System.out.println("call @Before"); + } - /** - * @After 注解修饰的方法在执行每项测试后执行(如执行每一个测试后重置某些变量 , 删除临时变量等) - */ - @After - public void after() { - System.out.println("call @After"); - } + /** + * @After 注解修饰的方法在执行每项测试后执行(如执行每一个测试后重置某些变量 , 删除临时变量等) + */ + @After + public void after() { + System.out.println("call @After"); + } - /** - * 当想暂时禁用特定的测试执行可以使用忽略注释。每个被注解为 @Ignore 的方法将不被执行。 - */ - @Ignore - public void ignore() { - System.out.println("call @Ignore"); - } + /** + * 当想暂时禁用特定的测试执行可以使用忽略注释。每个被注解为 @Ignore 的方法将不被执行。 + */ + @Ignore + public void ignore() { + System.out.println("call @Ignore"); + } } diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/AssertionsTests.java b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/AssertionsTests.java index d3d6e654..ddadf729 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/AssertionsTests.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/AssertionsTests.java @@ -1,120 +1,151 @@ package io.github.dunwu.javalib.junit5; -import io.github.dunwu.javalib.lombok.Person; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import static java.time.Duration.ofMillis; import static java.time.Duration.ofMinutes; import static org.junit.jupiter.api.Assertions.*; /** * Junit5 断言示例 * - * @author Zhang Peng + * @author Zhang Peng * @since 2018-11-29 */ +@Disabled class AssertionsTests { - private static Person person; - - @BeforeAll - public static void beforeAll() { - person = new Person("John", "Doe"); - } - - @Test - void standardAssertions() { - assertEquals(2, 2); - assertEquals(4, 4, "The optional assertion message is now the last parameter."); - assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- " - + "to avoid constructing complex messages unnecessarily."); - } - - @Test - void groupedAssertions() { - // In a grouped assertion all assertions are executed, and any - // failures will be reported together. - assertAll("person", () -> assertEquals("John", person.getFirstName()), - () -> assertEquals("Doe", person.getLastName())); - } - - @Test - void dependentAssertions() { - // Within a code block, if an assertion fails the - // subsequent code in the same block will be skipped. - assertAll("properties", () -> { - String firstName = person.getFirstName(); - assertNotNull(firstName); - - // Executed only if the previous assertion is valid. - assertAll("first name", () -> assertTrue(firstName.startsWith("J")), - () -> assertTrue(firstName.endsWith("n"))); - }, () -> { - // Grouped assertion, so processed independently - // of results of first name assertions. - String lastName = person.getLastName(); - assertNotNull(lastName); - - // Executed only if the previous assertion is valid. - assertAll("last name", () -> assertTrue(lastName.startsWith("D")), - () -> assertTrue(lastName.endsWith("e"))); - }); - } - - @Test - void exceptionTesting() { - Throwable exception = assertThrows(IllegalArgumentException.class, () -> { - throw new IllegalArgumentException("a message"); - }); - assertEquals("a message", exception.getMessage()); - } - - @Test - void timeoutNotExceeded() { - // The following assertion succeeds. - assertTimeout(ofMinutes(2), () -> { - // Perform task that takes less than 2 minutes. - }); - } - - @Test - void timeoutNotExceededWithResult() { - // The following assertion succeeds, and returns the supplied object. - String actualResult = assertTimeout(ofMinutes(2), () -> { - return "a result"; - }); - assertEquals("a result", actualResult); - } - - @Test - void timeoutNotExceededWithMethod() { - // The following assertion invokes a method reference and returns an object. - String actualGreeting = assertTimeout(ofMinutes(2), AssertionsTests::greeting); - assertEquals("Hello, World!", actualGreeting); - } - - private static String greeting() { - return "Hello, World!"; - } - - // @Test - // void timeoutExceeded() { - // // The following assertion fails with an error message similar to: - // // execution exceeded timeout of 10 ms by 91 ms - // assertTimeout(ofMillis(10), () -> { - // // Simulate task that takes more than 10 ms. - // Thread.sleep(5); - // }); - // } - // - // @Test - // void timeoutExceededWithPreemptiveTermination() { - // // The following assertion fails with an error message similar to: - // // execution timed out after 10 ms - // assertTimeoutPreemptively(ofMillis(10), () -> { - // // Simulate task that takes more than 10 ms. - // Thread.sleep(20); - // }); - // } + private static Person person; + + @BeforeAll + static void beforeAll() { + person = new Person("John", "Doe"); + } + + @Test + void dependentAssertions() { + // Within a code block, if an assertion fails the + // subsequent code in the same block will be skipped. + assertAll("properties", () -> { + String firstName = person.getFirstName(); + assertNotNull(firstName); + + // Executed only if the previous assertion is valid. + assertAll("first name", () -> assertTrue(firstName.startsWith("J")), + () -> assertTrue(firstName.endsWith("n"))); + }, () -> { + // Grouped assertion, so processed independently + // of results of first name assertions. + String lastName = person.getLastName(); + assertNotNull(lastName); + + // Executed only if the previous assertion is valid. + assertAll("last name", () -> assertTrue(lastName.startsWith("D")), + () -> assertTrue(lastName.endsWith("e"))); + }); + } + + @Test + void exceptionTesting() { + Throwable exception = assertThrows(IllegalArgumentException.class, () -> { + throw new IllegalArgumentException("a message"); + }); + assertEquals("a message", exception.getMessage()); + } + + @Test + void groupedAssertions() { + // In a grouped assertion all assertions are executed, and any + // failures will be reported together. + assertAll("person", () -> assertEquals("John", person.getFirstName()), + () -> assertEquals("Doe", person.getLastName())); + } + + @Test + void standardAssertions() { + assertEquals(2, 2); + assertEquals(4, 4, "The optional assertion message is now the last parameter."); + assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- " + + "to avoid constructing complex messages unnecessarily."); + } + + @Test + void timeoutExceeded() { + // The following assertion fails with an error message similar to: + // execution exceeded timeout of 10 ms by 91 ms + assertTimeout(ofMillis(10), () -> { + // Simulate task that takes more than 10 ms. + Thread.sleep(100); + }); + } + + @Test + void timeoutExceededWithPreemptiveTermination() { + // The following assertion fails with an error message similar to: + // execution timed out after 10 ms + assertTimeoutPreemptively(ofMillis(10), () -> { + // Simulate task that takes more than 10 ms. + Thread.sleep(100); + }); + } + + @Test + void timeoutNotExceeded() { + // The following assertion succeeds. + assertTimeout(ofMinutes(2), () -> { + // Perform task that takes less than 2 minutes. + }); + } + + @Test + void timeoutNotExceededWithMethod() { + // The following assertion invokes a method reference and returns an object. + String actualGreeting = assertTimeout(ofMinutes(2), AssertionsTests::greeting); + assertEquals("Hello, World!", actualGreeting); + } + + private static String greeting() { + return "Hello, World!"; + } + + @Test + void timeoutNotExceededWithResult() { + // The following assertion succeeds, and returns the supplied object. + String actualResult = assertTimeout(ofMinutes(2), () -> { + return "a result"; + }); + assertEquals("a result", actualResult); + } + + static class Person { + + private String firstName; + + private String lastName; + + Person(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + String getFirstName() { + return firstName; + } + + void setFirstName(String firstName) { + this.firstName = firstName; + } + + String getLastName() { + return lastName; + } + + void setLastName(String lastName) { + this.lastName = lastName; + } + + } } diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/AssumptionsTests.java b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/AssumptionsTests.java index f7d9d6e9..09ae1875 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/AssumptionsTests.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/AssumptionsTests.java @@ -1,5 +1,6 @@ package io.github.dunwu.javalib.junit5; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -9,32 +10,34 @@ /** * Junit5 断言示例 * - * @author Zhang Peng + * @author Zhang Peng * @since 2018-11-29 */ +@Disabled class AssumptionsTests { - @Test - void testOnlyOnCiServer() { - assumeTrue("CI".equals(System.getenv("ENV"))); - // remainder of test - } - - @Test - void testOnlyOnDeveloperWorkstation() { - assumeTrue("DEV".equals(System.getenv("ENV")), () -> "Aborting test: not on developer workstation"); - // remainder of test - } - - @Test - void testInAllEnvironments() { - assumingThat("CI".equals(System.getenv("ENV")), () -> { - // perform these assertions only on the CI server - assertEquals(2, 2); - }); - - // perform these assertions in all environments - assertEquals("a string", "a string"); - } + @Test + void testInAllEnvironments() { + assumingThat("CI".equals(System.getenv("ENV")), () -> { + // perform these assertions only on the CI server + assertEquals(2, 2); + }); + + // perform these assertions in all environments + assertEquals("a string", "a string"); + } + + @Test + void testOnlyOnCiServer() { + assumeTrue("CI".equals(System.getenv("ENV"))); + // remainder of test + } + + @Test + void testOnlyOnDeveloperWorkstation() { + assumeTrue("DEV".equals(System.getenv("ENV")), + () -> "Aborting test: not on developer workstation"); + // remainder of test + } } diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/DisplayNameTests.java b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/DisplayNameTests.java index 5a788be7..20ee7959 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/DisplayNameTests.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/DisplayNameTests.java @@ -1,30 +1,32 @@ package io.github.dunwu.javalib.junit5; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; /** * Junit5 定制测试类和方法的显示名称 * - * @author Zhang Peng + * @author Zhang Peng * @since 2018-11-29 */ +@Disabled @DisplayName("A special test case") class DisplayNameTests { - @Test - @DisplayName("Custom test name containing spaces") - void testWithDisplayNameContainingSpaces() { - } + @Test + @DisplayName("😱") + void testWithDisplayNameContainingEmoji() { + } - @Test - @DisplayName("╯°□°)╯") - void testWithDisplayNameContainingSpecialCharacters() { - } + @Test + @DisplayName("Custom test name containing spaces") + void testWithDisplayNameContainingSpaces() { + } - @Test - @DisplayName("😱") - void testWithDisplayNameContainingEmoji() { - } + @Test + @DisplayName("╯°□°)╯") + void testWithDisplayNameContainingSpecialCharacters() { + } } diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/DynamicTests.java b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/DynamicTests.java index ad27b045..7ce4813e 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/DynamicTests.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/DynamicTests.java @@ -1,14 +1,12 @@ package io.github.dunwu.javalib.junit5; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DynamicNode; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.function.ThrowingConsumer; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.Random; +import java.util.*; import java.util.function.Function; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -17,93 +15,103 @@ import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; import static org.junit.jupiter.api.DynamicTest.dynamicTest; +@Disabled class DynamicTests { - // This will result in a JUnitException! - // @TestFactory - // List dynamicTestsWithInvalidReturnType() { - // return Arrays.asList("Hello"); - // } - - @TestFactory - Collection dynamicTestsFromCollection() { - return Arrays.asList(dynamicTest("1st dynamic test", () -> assertTrue(true)), - dynamicTest("2nd dynamic test", () -> assertEquals(4, 2 * 2))); - } - - @TestFactory - Iterable dynamicTestsFromIterable() { - return Arrays.asList(dynamicTest("3rd dynamic test", () -> assertTrue(true)), - dynamicTest("4th dynamic test", () -> assertEquals(4, 2 * 2))); - } - - @TestFactory - Iterator dynamicTestsFromIterator() { - return Arrays.asList(dynamicTest("5th dynamic test", () -> assertTrue(true)), - dynamicTest("6th dynamic test", () -> assertEquals(4, 2 * 2))).iterator(); - } - - @TestFactory - DynamicTest[] dynamicTestsFromArray() { - return new DynamicTest[] { dynamicTest("7th dynamic test", () -> assertTrue(true)), - dynamicTest("8th dynamic test", () -> assertEquals(4, 2 * 2)) }; - } - - @TestFactory - Stream dynamicTestsFromStream() { - return Stream.of("A", "B", "C").map(str -> dynamicTest("test" + str, () -> { - /* ... */ - })); - } - - @TestFactory - Stream dynamicTestsFromIntStream() { - // Generates tests for the first 10 even integers. - return IntStream.iterate(0, n -> n + 2).limit(10) - .mapToObj(n -> dynamicTest("test" + n, () -> assertTrue(n % 2 == 0))); - } - - @TestFactory - Stream generateRandomNumberOfTests() { - - // Generates random positive integers between 0 and 100 until - // a number evenly divisible by 7 is encountered. - Iterator inputGenerator = new Iterator() { - - Random random = new Random(); - - int current; - - @Override - public boolean hasNext() { - current = random.nextInt(100); - return current % 7 != 0; - } - - @Override - public Integer next() { - return current; - } - }; - - // Generates display names like: input:5, input:37, input:85, etc. - Function displayNameGenerator = (input) -> "input:" + input; - - // Executes tests based on the current input value. - ThrowingConsumer testExecutor = (input) -> assertTrue(input % 7 != 0); - - // Returns a stream of dynamic tests. - return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor); - } - - @TestFactory - Stream dynamicTestsWithContainers() { - return Stream.of("A", "B", "C") - .map(input -> dynamicContainer("Container " + input, - Stream.of(dynamicTest("not null", () -> assertNotNull(input)), - dynamicContainer("properties", - Stream.of(dynamicTest("length > 0", () -> assertTrue(input.length() > 0)), - dynamicTest("not empty", () -> assertFalse(input.isEmpty()))))))); - } + @TestFactory + DynamicTest[] dynamicTestsFromArray() { + return new DynamicTest[] { + dynamicTest("7th dynamic test", () -> assertTrue(true)), + dynamicTest("8th dynamic test", () -> assertEquals(4, 2 * 2)) }; + } + + @TestFactory + Collection dynamicTestsFromCollection() { + return Arrays.asList(dynamicTest("1st dynamic test", () -> assertTrue(true)), + dynamicTest("2nd dynamic test", () -> assertEquals(4, 2 * 2))); + } + + @TestFactory + Stream dynamicTestsFromIntStream() { + // Generates tests for the first 10 even integers. + return IntStream.iterate(0, n -> n + 2).limit(10) + .mapToObj(n -> dynamicTest("test" + n, () -> assertTrue(n % 2 == 0))); + } + + @TestFactory + Iterable dynamicTestsFromIterable() { + return Arrays.asList(dynamicTest("3rd dynamic test", () -> assertTrue(true)), + dynamicTest("4th dynamic test", () -> assertEquals(4, 2 * 2))); + } + + @TestFactory + Iterator dynamicTestsFromIterator() { + return Arrays + .asList(dynamicTest("5th dynamic test", () -> assertTrue(true)), + dynamicTest("6th dynamic test", () -> assertEquals(4, 2 * 2))) + .iterator(); + } + + @TestFactory + Stream dynamicTestsFromStream() { + return Stream.of("A", "B", "C").map(str -> dynamicTest("test" + str, () -> { + /* ... */ + })); + } + + @TestFactory + Stream dynamicTestsWithContainers() { + return Stream + .of("A", "B", "C").map( + input -> dynamicContainer("Container " + input, Stream + .of(dynamicTest("not null", () -> assertNotNull(input)), + dynamicContainer("properties", + Stream.of( + dynamicTest("length > 0", + () -> assertTrue(input + .length() > 0)), + dynamicTest("not empty", + () -> assertFalse(input + .isEmpty()))))))); + } + + // This will result in a JUnitException! + @TestFactory + List dynamicTestsWithInvalidReturnType() { + return Arrays.asList("Hello"); + } + + @TestFactory + Stream generateRandomNumberOfTests() { + + // Generates random positive integers between 0 and 100 until + // a number evenly divisible by 7 is encountered. + Iterator inputGenerator = new Iterator() { + + Random random = new Random(); + + int current; + + @Override + public boolean hasNext() { + current = random.nextInt(100); + return current % 7 != 0; + } + + @Override + public Integer next() { + return current; + } + }; + + // Generates display names like: input:5, input:37, input:85, etc. + Function displayNameGenerator = (input) -> "input:" + input; + + // Executes tests based on the current input value. + ThrowingConsumer testExecutor = (input) -> assertTrue(input % 7 != 0); + + // Returns a stream of dynamic tests. + return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor); + } } diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/NestedTests.java b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/NestedTests.java index c3e65611..140f2382 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/NestedTests.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/NestedTests.java @@ -1,86 +1,84 @@ package io.github.dunwu.javalib.junit5; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import java.util.EmptyStackException; import java.util.Stack; import static org.junit.jupiter.api.Assertions.*; +@Disabled @DisplayName("A stack") class NestedTests { - Stack stack; - - @Test - @DisplayName("is instantiated with new Stack()") - void isInstantiatedWithNew() { - new Stack<>(); - } - - @Nested - @DisplayName("when new") - class WhenNew { - - @BeforeEach - void createNewStack() { - stack = new Stack<>(); - } - - @Test - @DisplayName("is empty") - void isEmpty() { - assertTrue(stack.isEmpty()); - } - - @Test - @DisplayName("throws EmptyStackException when popped") - void throwsExceptionWhenPopped() { - assertThrows(EmptyStackException.class, () -> stack.pop()); - } - - @Test - @DisplayName("throws EmptyStackException when peeked") - void throwsExceptionWhenPeeked() { - assertThrows(EmptyStackException.class, () -> stack.peek()); - } - - @Nested - @DisplayName("after pushing an element") - class AfterPushing { - - String anElement = "an element"; - - @BeforeEach - void pushAnElement() { - stack.push(anElement); - } - - @Test - @DisplayName("it is no longer empty") - void isNotEmpty() { - assertFalse(stack.isEmpty()); - } - - @Test - @DisplayName("returns the element when popped and is empty") - void returnElementWhenPopped() { - assertEquals(anElement, stack.pop()); - assertTrue(stack.isEmpty()); - } - - @Test - @DisplayName("returns the element when peeked but remains not empty") - void returnElementWhenPeeked() { - assertEquals(anElement, stack.peek()); - assertFalse(stack.isEmpty()); - } - - } - - } + Stack stack; + + @Test + @DisplayName("is instantiated with new Stack()") + void isInstantiatedWithNew() { + new Stack<>(); + } + + @Nested + @DisplayName("when new") + class WhenNew { + + @BeforeEach + void createNewStack() { + stack = new Stack<>(); + } + + @Test + @DisplayName("is empty") + void isEmpty() { + assertTrue(stack.isEmpty()); + } + + @Test + @DisplayName("throws EmptyStackException when peeked") + void throwsExceptionWhenPeeked() { + assertThrows(EmptyStackException.class, () -> stack.peek()); + } + + @Test + @DisplayName("throws EmptyStackException when popped") + void throwsExceptionWhenPopped() { + assertThrows(EmptyStackException.class, () -> stack.pop()); + } + + @Nested + @DisplayName("after pushing an element") + class AfterPushing { + + String anElement = "an element"; + + @Test + @DisplayName("it is no longer empty") + void isNotEmpty() { + assertFalse(stack.isEmpty()); + } + + @BeforeEach + void pushAnElement() { + stack.push(anElement); + } + + @Test + @DisplayName("returns the element when peeked but remains not empty") + void returnElementWhenPeeked() { + assertEquals(anElement, stack.peek()); + assertFalse(stack.isEmpty()); + } + + @Test + @DisplayName("returns the element when popped and is empty") + void returnElementWhenPopped() { + assertEquals(anElement, stack.pop()); + assertTrue(stack.isEmpty()); + } + + } + + } } diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/ParameterizedTests.java b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/ParameterizedTests.java index ec8158c1..72d7e19c 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/ParameterizedTests.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/ParameterizedTests.java @@ -1,23 +1,32 @@ package io.github.dunwu.javalib.junit5; -import io.github.dunwu.javalib.lombok.Calculator; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; /** - * @author Zhang Peng + * @author Zhang Peng * @since 2018-11-29 */ -public class ParameterizedTests { - - @ParameterizedTest(name = "{0} + {1} = {2}") - @CsvSource({"0, 1, 1", "1, 2, 3", "49, 51, 100", "1, 100, 101"}) - void add(int first, int second, int expectedResult) { - Calculator calculator = new Calculator(); - assertEquals(expectedResult, calculator.add(first, second), - () -> first + " + " + second + " should equal " + expectedResult); - } +@Disabled +class ParameterizedTests { + + @ParameterizedTest(name = "{0} + {1} = {2}") + @CsvSource({ "0, 1, 1", "1, 2, 3", "49, 51, 100", "1, 100, 101" }) + void add(int first, int second, int expectedResult) { + Calculator calculator = new Calculator(); + assertEquals(expectedResult, calculator.add(first, second), + () -> first + " + " + second + " should equal " + expectedResult); + } + + class Calculator { + + public int add(int a, int b) { + return a + b; + } + + } } diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/RepeatedTests.java b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/RepeatedTests.java index 24b017f8..21014f7c 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/RepeatedTests.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/RepeatedTests.java @@ -1,49 +1,46 @@ package io.github.dunwu.javalib.junit5; import org.junit.jupiter.api.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; +@Disabled class RepeatedTests { - private static final Logger LOGGER = LoggerFactory.getLogger(RepeatedTests.class); - - @BeforeEach - void beforeEach(TestInfo testInfo, RepetitionInfo repetitionInfo) { - int currentRepetition = repetitionInfo.getCurrentRepetition(); - int totalRepetitions = repetitionInfo.getTotalRepetitions(); - String methodName = testInfo.getTestMethod().get().getName(); - LOGGER.info(String.format("About to execute repetition %d of %d for %s", // - currentRepetition, totalRepetitions, methodName)); - } - - @RepeatedTest(10) - void repeatedTest() { - // ... - } - - @RepeatedTest(5) - void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) { - assertEquals(5, repetitionInfo.getTotalRepetitions()); - } - - @RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}") - @DisplayName("Repeat!") - void customDisplayName(TestInfo testInfo) { - assertEquals(testInfo.getDisplayName(), "Repeat! 1/1"); - } - - @RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME) - @DisplayName("Details...") - void customDisplayNameWithLongPattern(TestInfo testInfo) { - assertEquals(testInfo.getDisplayName(), "Details... :: repetition 1 of 1"); - } - - @RepeatedTest(value = 5, name = "Wiederholung {currentRepetition} von {totalRepetitions}") - void repeatedTestInGerman() { - // ... - } + @BeforeEach + void beforeEach(TestInfo testInfo, RepetitionInfo repetitionInfo) { + int currentRepetition = repetitionInfo.getCurrentRepetition(); + int totalRepetitions = repetitionInfo.getTotalRepetitions(); + String methodName = testInfo.getTestMethod().get().getName(); + System.out.println(String.format("About to execute repetition %d of %d for %s", // + currentRepetition, totalRepetitions, methodName)); + } + + @RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}") + @DisplayName("Repeat!") + void customDisplayName(TestInfo testInfo) { + assertEquals(testInfo.getDisplayName(), "Repeat! 1/1"); + } + + @RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME) + @DisplayName("Details...") + void customDisplayNameWithLongPattern(TestInfo testInfo) { + assertEquals(testInfo.getDisplayName(), "Details... :: repetition 1 of 1"); + } + + @RepeatedTest(10) + void repeatedTest() { + // ... + } + + @RepeatedTest(value = 5, name = "Wiederholung {currentRepetition} von {totalRepetitions}") + void repeatedTestInGerman() { + // ... + } + + @RepeatedTest(5) + void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) { + assertEquals(5, repetitionInfo.getTotalRepetitions()); + } } diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/StandardTests.java b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/StandardTests.java index 10f62b44..009e9f60 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/StandardTests.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/junit5/StandardTests.java @@ -1,55 +1,52 @@ package io.github.dunwu.javalib.junit5; import org.junit.jupiter.api.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Junit5 标准测试 * - * @author Zhang Peng + * @author Zhang Peng * @since 2018-11-29 */ +@Disabled class StandardTests { - private static final Logger LOGGER = LoggerFactory.getLogger(StandardTests.class); - - @BeforeAll - static void beforeAll() { - LOGGER.info("call beforeAll()"); - } - - @AfterAll - static void afterAll() { - LOGGER.info("call afterAll()"); - } - - @BeforeEach - void beforeEach() { - LOGGER.info("call beforeEach()"); - } - - @Test - void succeedingTest() { - LOGGER.info("call succeedingTest()"); - } - - @Test - void failingTest() { - LOGGER.info("call failingTest()"); - // fail("a failing test"); - } - - @Test - @Disabled("for demonstration purposes") - void skippedTest() { - LOGGER.info("call skippedTest()"); - // not executed - } - - @AfterEach - void afterEach() { - LOGGER.info("call afterEach()"); - } + @AfterAll + static void afterAll() { + System.out.println("call afterAll()"); + } + + @BeforeAll + static void beforeAll() { + System.out.println("call beforeAll()"); + } + + @AfterEach + void afterEach() { + System.out.println("call afterEach()"); + } + + @BeforeEach + void beforeEach() { + System.out.println("call beforeEach()"); + } + + @Test + void failingTest() { + System.out.println("call failingTest()"); + // fail("a failing test"); + } + + @Test + @Disabled("for demonstration purposes") + void skippedTest() { + System.out.println("call skippedTest()"); + // not executed + } + + @Test + void succeedingTest() { + System.out.println("call succeedingTest()"); + } } diff --git a/javalib-test/src/test/java/io/github/dunwu/javalib/mockito/MockitoTest.java b/javalib-test/src/test/java/io/github/dunwu/javalib/mockito/MockitoTest.java index 984eaf00..9cb7f841 100644 --- a/javalib-test/src/test/java/io/github/dunwu/javalib/mockito/MockitoTest.java +++ b/javalib-test/src/test/java/io/github/dunwu/javalib/mockito/MockitoTest.java @@ -12,215 +12,214 @@ class MockitoTest { - // 模拟 LinkedList 的一个对象 - LinkedList mockedList = mock(LinkedList.class); - - @BeforeEach - void beforeEach() { - mockedList.clear(); - } - - @Test - void test() { - // using mock object - it does not throw any "unexpected interaction" exception - mockedList.add("one"); - // selective, explicit, highly readable verification - verify(mockedList).add("one"); - } - - /** - * 模拟对象 - */ - @Test - void test01() { - // 此时调用get方法,会返回null,因为还没有对方法调用的返回值做模拟 - System.out.println(mockedList.get(0)); - } - - /** - * 模拟方法调用的返回值 - */ - @Test - void test02() { - // 模拟获取第一个元素时,返回字符串first。给特定的方法调用返回固定值在官方说法中称为stub。 - when(mockedList.get(0)).thenReturn("first"); - // 此时打印输出first - System.out.println(mockedList.get(0)); - } - - /** - * 模拟方法调用抛出异常 - */ - @Test - void test03() { - // 模拟获取第二个元素时,抛出RuntimeException - when(mockedList.get(1)).thenThrow(new RuntimeException()); - try { - // 此时将会抛出RuntimeException - System.out.println(mockedList.get(1)); - } catch (RuntimeException e) { - System.err.println(e.getMessage()); - assertThat(e.getMessage()).isEqualTo(null); - } - } - - /** - * 模拟方法调用抛出异常2 - */ - @Test - void test04() { - doThrow(new RuntimeException("clear exception")).when(mockedList).clear(); - try { - mockedList.clear(); - } catch (RuntimeException e) { - System.err.println(e.getMessage()); - assertThat(e.getMessage()).contains("clear exception"); - } - - } - - /** - * 模拟调用方法时的参数匹配 - */ - @Test - void test05() { - // anyInt()匹配任何int参数,这意味着参数为任意值,其返回值均是element - when(mockedList.get(anyInt())).thenReturn("element"); - // 此时打印是element - System.out.println(mockedList.get(999)); - } - - /** - * 模拟方法调用次数 - */ - @Test - void test06() { - // 调用add一次 - mockedList.add("once"); - // 下面两个写法验证效果一样,均验证add方法是否被调用了一次 - verify(mockedList).add("once"); - verify(mockedList, times(1)).add("once"); - } - - /** - * 校验行为 - */ - @Test - void test07() { - // using mock object - mockedList.add("one"); - // verification - verify(mockedList).add("one"); - verify(mockedList).clear(); - } - - /** - * 模拟方法调用(Stubbing) - */ - @Test - void test08() { - // stubbing - when(mockedList.get(0)).thenReturn("first"); - when(mockedList.get(1)).thenThrow(new RuntimeException()); - // following prints "first" - System.out.println(mockedList.get(0)); - try { - // following throws runtime exception - System.out.println(mockedList.get(1)); - } catch (RuntimeException e) { - System.out.println(e.getMessage()); - } - // following prints "null" because get(999) was not stubbed - System.out.println(mockedList.get(999)); - - verify(mockedList).get(0); - } - - /** - * 校验方法调用次数 - */ - @Test - void test09() { - // using mock - mockedList.add("once"); - - mockedList.add("twice"); - mockedList.add("twice"); - - mockedList.add("three"); - mockedList.add("three"); - mockedList.add("three"); - // following two verifications work exactly the same - times(1) is used by default - verify(mockedList).add("once"); - verify(mockedList, times(1)).add("once"); - // exact number of invocations verification - verify(mockedList, times(2)).add("twice"); - verify(mockedList, times(3)).add("three"); - // verification using never(). never() is an alias to times(0) - verify(mockedList, never()).add("never happened"); - // verification using atLeast()/atMost() - verify(mockedList, atLeastOnce()).add("three"); - verify(mockedList, atLeast(2)).add("twice"); - verify(mockedList, atMost(5)).add("three"); - } - - /** - * 校验方法调用顺序 - */ - @Test - void test10() { - // A. Single mock whose methods must be invoked in a particular order - List singleMock = mock(List.class); - // using a single mock - singleMock.add("was added first"); - singleMock.add("was added second"); - // create an inOrder verifier for a single mock - InOrder inOrder = inOrder(singleMock); - // following will make sure that add is first called with "was added first, then - // with "was added second" - inOrder.verify(singleMock).add("was added first"); - inOrder.verify(singleMock).add("was added second"); - - // B. Multiple mocks that must be used in a particular order - List firstMock = mock(List.class); - List secondMock = mock(List.class); - // using mocks - firstMock.add("was called first"); - secondMock.add("was called second"); - // create inOrder object passing any mocks that need to be verified in order - inOrder = inOrder(firstMock, secondMock); - // following will make sure that firstMock was called before secondMock - inOrder.verify(firstMock).add("was called first"); - inOrder.verify(secondMock).add("was called second"); - // Oh, and A + B can be mixed together at will - } - - /** - * 校验方法是否从未调用 - */ - @Test - void test11() { - List mockOne = mock(List.class); - List mockTwo = mock(List.class); - List mockThree = mock(List.class); - // using mocks - only mockOne is interacted - mockOne.add("one"); - // ordinary verification - verify(mockOne).add("one"); - // verify that method was never called on a mock - verify(mockOne, never()).add("two"); - // verify that other mocks were not interacted - verifyZeroInteractions(mockTwo, mockThree); - } - - /** - * 重置Mock - */ - void test12() { - List mock = mock(List.class); - when(mock.size()).thenReturn(10); - mock.add(1); - reset(mock); - } + // 模拟 LinkedList 的一个对象 + LinkedList mockedList = mock(LinkedList.class); + + @BeforeEach + void beforeEach() { + mockedList.clear(); + } + + @Test + void test() { + // using mock object - it does not throw any "unexpected interaction" exception + mockedList.add("one"); + // selective, explicit, highly readable verification + verify(mockedList).add("one"); + } + + /** + * 模拟对象 + */ + @Test + void test01() { + // 此时调用get方法,会返回null,因为还没有对方法调用的返回值做模拟 + System.out.println(mockedList.get(0)); + } + + /** + * 模拟方法调用的返回值 + */ + @Test + void test02() { + // 模拟获取第一个元素时,返回字符串first。给特定的方法调用返回固定值在官方说法中称为stub。 + when(mockedList.get(0)).thenReturn("first"); + // 此时打印输出first + System.out.println(mockedList.get(0)); + } + + /** + * 模拟方法调用抛出异常 + */ + @Test + void test03() { + // 模拟获取第二个元素时,抛出RuntimeException + when(mockedList.get(1)).thenThrow(new RuntimeException()); + try { + // 此时将会抛出RuntimeException + System.out.println(mockedList.get(1)); + } catch (RuntimeException e) { + System.err.println(e.getMessage()); + assertThat(e.getMessage()).isEqualTo(null); + } + } + + /** + * 模拟方法调用抛出异常2 + */ + @Test + void test04() { + doThrow(new RuntimeException("clear exception")).when(mockedList).clear(); + try { + mockedList.clear(); + } catch (RuntimeException e) { + System.err.println(e.getMessage()); + assertThat(e.getMessage()).contains("clear exception"); + } + } + + /** + * 模拟调用方法时的参数匹配 + */ + @Test + void test05() { + // anyInt()匹配任何int参数,这意味着参数为任意值,其返回值均是element + when(mockedList.get(anyInt())).thenReturn("element"); + // 此时打印是element + System.out.println(mockedList.get(999)); + } + + /** + * 模拟方法调用次数 + */ + @Test + void test06() { + // 调用add一次 + mockedList.add("once"); + // 下面两个写法验证效果一样,均验证add方法是否被调用了一次 + verify(mockedList).add("once"); + verify(mockedList, times(1)).add("once"); + } + + /** + * 校验行为 + */ + @Test + void test07() { + // using mock object + mockedList.add("one"); + // verification + verify(mockedList).add("one"); + verify(mockedList).clear(); + } + + /** + * 模拟方法调用(Stubbing) + */ + @Test + void test08() { + // stubbing + when(mockedList.get(0)).thenReturn("first"); + when(mockedList.get(1)).thenThrow(new RuntimeException()); + // following prints "first" + System.out.println(mockedList.get(0)); + try { + // following throws runtime exception + System.out.println(mockedList.get(1)); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + // following prints "null" because get(999) was not stubbed + System.out.println(mockedList.get(999)); + + verify(mockedList).get(0); + } + + /** + * 校验方法调用次数 + */ + @Test + void test09() { + // using mock + mockedList.add("once"); + + mockedList.add("twice"); + mockedList.add("twice"); + + mockedList.add("three"); + mockedList.add("three"); + mockedList.add("three"); + // following two verifications work exactly the same - times(1) is used by default + verify(mockedList).add("once"); + verify(mockedList, times(1)).add("once"); + // exact number of invocations verification + verify(mockedList, times(2)).add("twice"); + verify(mockedList, times(3)).add("three"); + // verification using never(). never() is an alias to times(0) + verify(mockedList, never()).add("never happened"); + // verification using atLeast()/atMost() + verify(mockedList, atLeastOnce()).add("three"); + verify(mockedList, atLeast(2)).add("twice"); + verify(mockedList, atMost(5)).add("three"); + } + + /** + * 校验方法调用顺序 + */ + @Test + void test10() { + // A. Single mock whose methods must be invoked in a particular order + List singleMock = mock(List.class); + // using a single mock + singleMock.add("was added first"); + singleMock.add("was added second"); + // create an inOrder verifier for a single mock + InOrder inOrder = inOrder(singleMock); + // following will make sure that add is first called with "was added first, then + // with "was added second" + inOrder.verify(singleMock).add("was added first"); + inOrder.verify(singleMock).add("was added second"); + + // B. Multiple mocks that must be used in a particular order + List firstMock = mock(List.class); + List secondMock = mock(List.class); + // using mocks + firstMock.add("was called first"); + secondMock.add("was called second"); + // create inOrder object passing any mocks that need to be verified in order + inOrder = inOrder(firstMock, secondMock); + // following will make sure that firstMock was called before secondMock + inOrder.verify(firstMock).add("was called first"); + inOrder.verify(secondMock).add("was called second"); + // Oh, and A + B can be mixed together at will + } + + /** + * 校验方法是否从未调用 + */ + @Test + void test11() { + List mockOne = mock(List.class); + List mockTwo = mock(List.class); + List mockThree = mock(List.class); + // using mocks - only mockOne is interacted + mockOne.add("one"); + // ordinary verification + verify(mockOne).add("one"); + // verify that method was never called on a mock + verify(mockOne, never()).add("two"); + // verify that other mocks were not interacted + verifyZeroInteractions(mockTwo, mockThree); + } + + /** + * 重置Mock + */ + void test12() { + List mock = mock(List.class); + when(mock.size()).thenReturn(10); + mock.add(1); + reset(mock); + } } diff --git a/javalib-text/pom.xml b/javalib-text/pom.xml new file mode 100644 index 00000000..aa36b436 --- /dev/null +++ b/javalib-text/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + io.github.dunwu + javalib-text + 1.0.0 + pom + javalib-text + + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 4.0.0 + + + + + + org.apache.poi + poi + ${poi.version} + + + org.apache.poi + poi-ooxml + ${poi.version} + + + org.apache.poi + poi-scratchpad + ${poi.version} + + + + + io.github.biezhi + TinyPinyin + 2.0.3.RELEASE + + + + junit + junit + 4.12 + test + + + diff --git a/javalib-text/src/main/java/io/github/dunwu/javalib/office/PinyinDemo.java b/javalib-text/src/main/java/io/github/dunwu/javalib/office/PinyinDemo.java new file mode 100644 index 00000000..545529aa --- /dev/null +++ b/javalib-text/src/main/java/io/github/dunwu/javalib/office/PinyinDemo.java @@ -0,0 +1,15 @@ +package io.github.dunwu.javalib.office; + +import com.github.promeg.pinyinhelper.Pinyin; + +/** + * @author Zhang Peng + * @since 2019-12-09 + */ +public class PinyinDemo { + + public static void main(String[] args) { + System.out.println("args = " + Pinyin.toPinyin('中')); + } + +} diff --git a/javalib-text/src/main/java/io/github/dunwu/javalib/office/WordUtil.java b/javalib-text/src/main/java/io/github/dunwu/javalib/office/WordUtil.java new file mode 100644 index 00000000..57ccbf89 --- /dev/null +++ b/javalib-text/src/main/java/io/github/dunwu/javalib/office/WordUtil.java @@ -0,0 +1,263 @@ +package io.github.dunwu.javalib.office; + +import org.apache.poi.hpsf.DocumentSummaryInformation; +import org.apache.poi.hpsf.SummaryInformation; +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.ooxml.POIXMLProperties; +import org.apache.poi.xwpf.extractor.XWPFWordExtractor; +import org.apache.poi.xwpf.usermodel.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * @author Zhang Peng + * @see https://poi.apache.org/ + * @see https://www.w3cschool.cn/apache_poi_word/apache_poi_word_overview.html + * @since 2018-11-08 + */ +public class WordUtil { + + /** + * 创建空白文档 + * + * @param filename + * @throws IOException + */ + public static void create(String filename) throws IOException { + // Blank Document + XWPFDocument document = new XWPFDocument(); + // Write the Document in file system + FileOutputStream out = new FileOutputStream(new File(filename)); + document.write(out); + out.close(); + System.out.printf("create %s written successully\n", filename); + } + + /** + * 创建 *.docx 文档,包含 content 内容 + * + * @param filename + * @throws IOException + */ + public static void create(String filename, String content) throws IOException { + // Blank Document + XWPFDocument document = new XWPFDocument(); + // Write the Document in file system + FileOutputStream out = new FileOutputStream(new File(filename)); + + // create Paragraph + XWPFParagraph paragraph = document.createParagraph(); + XWPFRun run = paragraph.createRun(); + run.setText(content); + document.write(out); + out.close(); + System.out.printf("create %s written successully\n", filename); + } + + /** + * 创建 *.docx 文档,包含 content 内容,content 内容置于边框中 + * + * @param filename + * @throws IOException + */ + public static void createWithBorders(String filename, String content) throws IOException { + // Blank Document + XWPFDocument document = new XWPFDocument(); + + // Write the Document in file system + FileOutputStream out = new FileOutputStream(new File(filename)); + + // create paragraph + XWPFParagraph paragraph = document.createParagraph(); + + // Set bottom border to paragraph + paragraph.setBorderBottom(Borders.BASIC_BLACK_DASHES); + + // Set left border to paragraph + paragraph.setBorderLeft(Borders.BASIC_BLACK_DASHES); + + // Set right border to paragraph + paragraph.setBorderRight(Borders.BASIC_BLACK_DASHES); + + // Set top border to paragraph + paragraph.setBorderTop(Borders.BASIC_BLACK_DASHES); + + XWPFRun run = paragraph.createRun(); + run.setText(content); + + document.write(out); + out.close(); + System.out.printf("create %s written successully\n", filename); + } + + /** + * 表格 + * + * @param filename + * @throws IOException + */ + public static void createWithTable(String filename) throws IOException { + // Blank Document + XWPFDocument document = new XWPFDocument(); + + // Write the Document in file system + FileOutputStream out = new FileOutputStream(new File(filename)); + + // create table + XWPFTable table = document.createTable(); + // create first row + XWPFTableRow tableRowOne = table.getRow(0); + tableRowOne.getCell(0).setText("col one, row one"); + tableRowOne.addNewTableCell().setText("col two, row one"); + tableRowOne.addNewTableCell().setText("col three, row one"); + // create second row + XWPFTableRow tableRowTwo = table.createRow(); + tableRowTwo.getCell(0).setText("col one, row two"); + tableRowTwo.getCell(1).setText("col two, row two"); + tableRowTwo.getCell(2).setText("col three, row two"); + // create third row + XWPFTableRow tableRowThree = table.createRow(); + tableRowThree.getCell(0).setText("col one, row three"); + tableRowThree.getCell(1).setText("col two, row three"); + tableRowThree.getCell(2).setText("col three, row three"); + + document.write(out); + out.close(); + System.out.printf("create %s written successully\n", filename); + } + + /** + * 字体样式 + * + * @param filename + * @throws IOException + */ + public static void createWithFontStyle(String filename) throws IOException { + // Blank Document + XWPFDocument document = new XWPFDocument(); + + // Write the Document in file system + FileOutputStream out = new FileOutputStream(new File(filename)); + + // create paragraph + XWPFParagraph paragraph = document.createParagraph(); + + // Set Bold an Italic + XWPFRun paragraphOneRunOne = paragraph.createRun(); + paragraphOneRunOne.setBold(true); + paragraphOneRunOne.setItalic(true); + paragraphOneRunOne.setText("Font Style"); + paragraphOneRunOne.addBreak(); + + // Set text Position + XWPFRun paragraphOneRunTwo = paragraph.createRun(); + paragraphOneRunTwo.setText("Font Style two"); + paragraphOneRunTwo.setTextPosition(100); + + // Set Strike through and Font Size and Subscript + XWPFRun paragraphOneRunThree = paragraph.createRun(); + paragraphOneRunThree.setStrike(true); + paragraphOneRunThree.setFontSize(20); + paragraphOneRunThree.setSubscript(VerticalAlign.SUBSCRIPT); + paragraphOneRunThree.setText(" Different Font Styles"); + + document.write(out); + out.close(); + System.out.printf("create %s written successully\n", filename); + } + + /** + * 对齐方式 + * + * @param filename + * @throws IOException + */ + public static void createWithAlign(String filename) throws IOException { + // Blank Document + XWPFDocument document = new XWPFDocument(); + + // Write the Document in file system + FileOutputStream out = new FileOutputStream(new File(filename)); + + // create paragraph + XWPFParagraph paragraph = document.createParagraph(); + + // Set alignment paragraph to RIGHT + paragraph.setAlignment(ParagraphAlignment.RIGHT); + XWPFRun run = paragraph.createRun(); + run.setText("At tutorialspoint.com, we strive hard to " + "provide quality tutorials for self-learning " + + "purpose in the domains of Academics, Information " + + "Technology, Management and Computer Programming " + "Languages."); + + // Create Another paragraph + paragraph = document.createParagraph(); + + // Set alignment paragraph to CENTER + paragraph.setAlignment(ParagraphAlignment.CENTER); + run = paragraph.createRun(); + run.setText("The endeavour started by Mohtashim, an AMU " + + "alumni, who is the founder and the managing director " + + "of Tutorials Point (I) Pvt. Ltd. He came up with the " + + "website tutorialspoint.com in year 2006 with the help" + + "of handpicked freelancers, with an array of tutorials" + " for computer programming languages. "); + document.write(out); + out.close(); + System.out.printf("create %s written successully\n", filename); + } + + /** + * 文本提取 + * + * @param filename + * @throws IOException + */ + public static void extractor(String filename) throws IOException { + XWPFDocument docx = new XWPFDocument(new FileInputStream(filename)); + // using XWPFWordExtractor Class + XWPFWordExtractor we = new XWPFWordExtractor(docx); + System.out.println(we.getText()); + } + + public static void setDocxProperties(String filename) throws IOException { + FileInputStream fis = new FileInputStream(new File(filename)); + XWPFDocument doc = new XWPFDocument(fis); + + POIXMLProperties properties = doc.getProperties(); + POIXMLProperties.CoreProperties coreProperties = properties.getCoreProperties(); + coreProperties.setCreator("星环信息科技有限公司"); + coreProperties.setLastModifiedByUser("星环信息科技有限公司"); + POIXMLProperties.ExtendedProperties extendedProperties = properties.getExtendedProperties(); + extendedProperties.getUnderlyingProperties().setCompany("星环信息科技有限公司"); + + FileOutputStream fos = new FileOutputStream(new File(filename)); + doc.write(fos); + + fos.close(); + doc.close(); + fis.close(); + } + + public static void setDocProperties(String filename) throws IOException { + System.out.println("filename = [" + filename + "]"); + FileInputStream fis = new FileInputStream(new File(filename)); + HWPFDocument doc = new HWPFDocument(fis); + + SummaryInformation summaryInformation = doc.getSummaryInformation(); + summaryInformation.setAuthor("张鹏"); + summaryInformation.setLastAuthor("张鹏"); + DocumentSummaryInformation documentSummaryInformation = doc.getDocumentSummaryInformation(); + documentSummaryInformation.setCompany("张鹏"); + documentSummaryInformation.setDocumentVersion("1"); + + FileOutputStream fos = new FileOutputStream(new File(filename)); + doc.write(fos); + + fos.close(); + doc.close(); + fis.close(); + } + +} diff --git a/javalib-text/src/test/java/io/github/dunwu/javalib/office/WordUtilTest.java b/javalib-text/src/test/java/io/github/dunwu/javalib/office/WordUtilTest.java new file mode 100644 index 00000000..a8cb21d4 --- /dev/null +++ b/javalib-text/src/test/java/io/github/dunwu/javalib/office/WordUtilTest.java @@ -0,0 +1,111 @@ +package io.github.dunwu.javalib.office; + +import org.junit.Test; + +import java.io.IOException; + +/** + * @author Zhang Peng + * @since 2018-11-08 + */ +public class WordUtilTest { + + @Test + public void testCreateDocx() { + try { + WordUtil.create("d://temp.docx"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testCreateDocxWithContent() { + StringBuilder sb = new StringBuilder(); + sb.append("At tutorialspoint.com, we strive hard to "); + sb.append("provide quality tutorials for self-learning "); + sb.append("purpose in the domains of Academics, Information "); + sb.append("Technology, Management and Computer Programming Languages."); + + try { + WordUtil.create("d://temp2.docx", sb.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testCreateDocxWithBorder() { + StringBuilder sb = new StringBuilder(); + sb.append("At tutorialspoint.com, we strive hard to "); + sb.append("provide quality tutorials for self-learning "); + sb.append("purpose in the domains of Academics, Information "); + sb.append("Technology, Management and Computer Programming Languages."); + + try { + WordUtil.createWithBorders("d://temp3.docx", sb.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testCreateDocxWithTable() { + try { + WordUtil.createWithTable("d://temp4.docx"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testCreateDocxWithFontStyle() { + try { + WordUtil.createWithFontStyle("d://temp5.docx"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testCreateDocxWithAlign() { + try { + WordUtil.createWithAlign("d://temp6.docx"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testExtractor() { + try { + WordUtil.extractor("d://temp6.docx"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void test() { + try { + WordUtil.setDocxProperties("d://temp6.docx"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // @Test + // public void test2() { + // File dir = new File("D:\\Docs\\ZP\\notes\\软件工程\\软件工程文档标准模板"); + // File[] files = dir.listFiles(); + // for (File file : files) { + // if (!file.isDirectory()) { + // try { + // WordUtil.setDocProperties(file.getAbsolutePath()); + // } catch (IOException e) { + // e.printStackTrace(); + // } + // } + // } + // } +} diff --git a/javatool-embedded-server/pom.xml b/javatool-embedded-server/pom.xml index 5c140f77..e322c7a2 100644 --- a/javatool-embedded-server/pom.xml +++ b/javatool-embedded-server/pom.xml @@ -1,93 +1,93 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javatool-embedded-server - 1.0.0 - war - Java工具-服务器 示例 + io.github.dunwu + javatool-embedded-server + 1.0.0 + war + Java工具-服务器 示例 - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 0.4.8 - [8.5.40,) - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 0.4.8 + [8.5.40,) + - - - - ch.qos.logback - logback-classic - - - org.logback-extensions - logback-ext-spring - 0.1.2 - - - org.slf4j - jcl-over-slf4j - - + + + + ch.qos.logback + logback-classic + + + org.logback-extensions + logback-ext-spring + 0.1.2 + + + org.slf4j + jcl-over-slf4j + + - - - javax.servlet - javax.servlet-api - provided - - - javax.servlet.jsp - jsp-api - 2.2 - provided - - + + + javax.servlet + javax.servlet-api + provided + + + javax.servlet.jsp + jsp-api + 2.2 + provided + + - - - org.apache.tomcat.embed - tomcat-embed-core - ${tomcat.version} - - - org.apache.tomcat.embed - tomcat-embed-el - ${tomcat.version} - - - org.apache.tomcat.embed - tomcat-embed-jasper - ${tomcat.version} - - + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} + + + org.apache.tomcat.embed + tomcat-embed-el + ${tomcat.version} + + + org.apache.tomcat.embed + tomcat-embed-jasper + ${tomcat.version} + + - - - org.springframework - spring-context-support - - - org.springframework - spring-webmvc - - - - - - - io.github.dunwu - dunwu-dependencies - ${dunwu.version} - pom - import - - - + + + org.springframework + spring-context-support + + + org.springframework + spring-webmvc + + + + + + + io.github.dunwu + dunwu-dependencies + ${dunwu.version} + pom + import + + + diff --git a/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/controller/HelloController.java b/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/controller/HelloController.java index 7719ed36..8b7cce7e 100644 --- a/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/controller/HelloController.java +++ b/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/controller/HelloController.java @@ -19,38 +19,38 @@ @RequestMapping(value = "/hello") public class HelloController { - private final Logger log = LoggerFactory.getLogger(this.getClass()); + private final Logger log = LoggerFactory.getLogger(this.getClass()); - /** - *

    - * 在本例中,Spring将会将数据传给 hello.jsp - *

    - * 访问形式:http://localhost:8080/hello?name=张三 - */ - @RequestMapping(value = "/name", method = RequestMethod.GET) - public ModelAndView hello(@RequestParam("name") String name) { - ModelAndView mav = new ModelAndView(); - mav.addObject("message", "你好," + name); - mav.setViewName("hello"); - return mav; - } + /** + *

    + * 在本例中,Spring将会将数据传给 hello.jsp + *

    + * 访问形式:http://localhost:8080/hello?name=张三 + */ + @RequestMapping(value = "/name", method = RequestMethod.GET) + public ModelAndView hello(@RequestParam("name") String name) { + ModelAndView mav = new ModelAndView(); + mav.addObject("message", "你好," + name); + mav.setViewName("hello"); + return mav; + } - /** - *

    - * 测试 logback 分级日志。配置项见src/main/resouces/logback.xml - *

    - * 访问形式:http://localhost:8080/log - */ - @ResponseBody - @RequestMapping(value = "/log", method = RequestMethod.GET) - public String log() { - String msg = "print log, current level: {}"; - log.trace(msg, "trace"); - log.debug(msg, "debug"); - log.info(msg, "info"); - log.warn(msg, "warn"); - log.error(msg, "error"); - return msg; - } + /** + *

    + * 测试 logback 分级日志。配置项见src/main/resouces/logback.xml + *

    + * 访问形式:http://localhost:8080/log + */ + @ResponseBody + @RequestMapping(value = "/log", method = RequestMethod.GET) + public String log() { + String msg = "print log, current level: {}"; + log.trace(msg, "trace"); + log.debug(msg, "debug"); + log.info(msg, "info"); + log.warn(msg, "warn"); + log.error(msg, "error"); + return msg; + } } diff --git a/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/controller/IndexController.java b/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/controller/IndexController.java index b2939a51..e2a2a090 100644 --- a/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/controller/IndexController.java +++ b/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/controller/IndexController.java @@ -15,17 +15,17 @@ @Controller public class IndexController { - /** - *

    - * 返回 ModelAndView 对象到视图层。在本例中,视图解析器解析视图名为 index,会自动关联到 index.jsp。 - *

    - * 访问形式:http://localhost:8080/ - */ - @RequestMapping(value = "/", method = RequestMethod.GET) - public ModelAndView index() { - ModelAndView mav = new ModelAndView(); - mav.setViewName("index"); - return mav; - } + /** + *

    + * 返回 ModelAndView 对象到视图层。在本例中,视图解析器解析视图名为 index,会自动关联到 index.jsp。 + *

    + * 访问形式:http://localhost:8080/ + */ + @RequestMapping(value = "/", method = RequestMethod.GET) + public ModelAndView index() { + ModelAndView mav = new ModelAndView(); + mav.setViewName("index"); + return mav; + } } diff --git a/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/server/SimpleTomcatServer.java b/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/server/SimpleTomcatServer.java index dbf54cae..577dcc70 100644 --- a/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/server/SimpleTomcatServer.java +++ b/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/server/SimpleTomcatServer.java @@ -11,31 +11,31 @@ */ public class SimpleTomcatServer { - private static final int PORT = 8080; - - private static final String CONTEXT_PATH = "/javatool-server"; - - public static void main(String[] args) throws Exception { - // 设定 profile - Optional profile = Optional.ofNullable(System.getProperty("spring.profiles.active")); - System.setProperty("spring.profiles.active", profile.orElse("develop")); - - Tomcat tomcat = new Tomcat(); - tomcat.setPort(PORT); - tomcat.getHost().setAppBase("."); - tomcat.addWebapp(CONTEXT_PATH, getAbsolutePath() + "src/main/webapp"); - tomcat.start(); - tomcat.getServer().await(); - } - - private static String getAbsolutePath() { - String path = null; - String folderPath = SimpleTomcatServer.class.getProtectionDomain().getCodeSource().getLocation().getPath() - .substring(1); - if (folderPath.indexOf("target") > 0) { - path = folderPath.substring(0, folderPath.indexOf("target")); - } - return path; - } + private static final int PORT = 8080; + + private static final String CONTEXT_PATH = "/javatool-server"; + + public static void main(String[] args) throws Exception { + // 设定 profile + Optional profile = Optional.ofNullable(System.getProperty("spring.profiles.active")); + System.setProperty("spring.profiles.active", profile.orElse("develop")); + + Tomcat tomcat = new Tomcat(); + tomcat.setPort(PORT); + tomcat.getHost().setAppBase("."); + tomcat.addWebapp(CONTEXT_PATH, getAbsolutePath() + "src/main/webapp"); + tomcat.start(); + tomcat.getServer().await(); + } + + private static String getAbsolutePath() { + String path = null; + String folderPath = SimpleTomcatServer.class.getProtectionDomain().getCodeSource().getLocation().getPath() + .substring(1); + if (folderPath.indexOf("target") > 0) { + path = folderPath.substring(0, folderPath.indexOf("target")); + } + return path; + } } diff --git a/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/server/TomcatServer.java b/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/server/TomcatServer.java index 3431032a..cb7773cb 100644 --- a/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/server/TomcatServer.java +++ b/javatool-embedded-server/src/main/java/io/github/dunwu/javatool/server/TomcatServer.java @@ -13,121 +13,121 @@ public class TomcatServer { - private static final Logger log = LoggerFactory.getLogger(TomcatServer.class); - - private static final String CONNECTOR_PORT = "8080"; - - private static final String RELATIVE_DEV_DUBBO_RESOVE_FILE = - "src/main/resources/properties/dubbo-resolve.properties"; - - private static final String RELATIVE_DUBBO_RESOVE_FILE = "WEB-INF/classes/properties/dubbo-resolve.properties"; - - // 以下设置轻易不要改动 - private static final String RELATIVE_DEV_BASE_DIR = "src/main/resources/tomcat/"; - - private static final String RELATIVE_BASE_DIR = "WEB-INF/classes/tomcat/"; - - private static final String RELATIVE_DEV_DOCBASE_DIR = "src/main/webapp"; - - private static final String RELATIVE_DOCBASE_DIR = "./"; - - public static void main(String[] args) throws Exception { - // 设定Spring的profile - if (StringUtils.isEmpty(System.getProperty("spring.profiles.active"))) { - System.setProperty("spring.profiles.active", "develop"); - } - - System.setProperty("tomcat.host.appBase", getAbsolutePath()); - File checkFile = new File(System.getProperty("tomcat.host.appBase") + "/WEB-INF"); - if (!checkFile.exists()) { - System.setProperty("catalina.base", getAbsolutePath() + RELATIVE_DEV_BASE_DIR); - System.setProperty("tomcat.context.docBase", RELATIVE_DEV_DOCBASE_DIR); - System.setProperty("dubbo.resolve.file", getAbsolutePath() + RELATIVE_DEV_DUBBO_RESOVE_FILE); - } else { - System.setProperty("catalina.base", getAbsolutePath() + RELATIVE_BASE_DIR); - System.setProperty("tomcat.context.docBase", RELATIVE_DOCBASE_DIR); - if ("develop".equalsIgnoreCase(System.getProperty("spring.profiles.active")) - || "test".equalsIgnoreCase("spring.profiles.active")) { - System.setProperty("dubbo.resolve.file", getAbsolutePath() + RELATIVE_DUBBO_RESOVE_FILE); - } - } - - if (StringUtils.isEmpty(System.getProperty("tomcat.connector.port"))) { - System.setProperty("tomcat.connector.port", CONNECTOR_PORT); - } - if (StringUtils.isEmpty(System.getProperty("tomcat.server.shutdownPort"))) { - System.setProperty("tomcat.server.shutdownPort", - String.valueOf(Integer.valueOf(System.getProperty("tomcat.connector.port")) + 10000)); - } - - log.info("====================ENV setting===================="); - log.info("spring.profiles.active:" + System.getProperty("spring.profiles.active")); - log.info("dubbo.resolve.file:" + System.getProperty("dubbo.resolve.file")); - log.info("catalina.base:" + System.getProperty("catalina.base")); - log.info("tomcat.host.appBase:" + System.getProperty("tomcat.host.appBase")); - log.info("tomcat.context.docBase:" + System.getProperty("tomcat.context.docBase")); - log.info("tomcat.connector.port:" + System.getProperty("tomcat.connector.port")); - log.info("tomcat.server.shutdownPort:" + System.getProperty("tomcat.server.shutdownPort")); - - ExtendedTomcat tomcat = new ExtendedTomcat(); - tomcat.start(); - tomcat.getServer().await(); - } - - private static String getAbsolutePath() { - String path = null; - String folderPath = TomcatServer.class.getProtectionDomain().getCodeSource().getLocation().getPath(); - if (folderPath.indexOf("WEB-INF") > 0) { - path = folderPath.substring(0, folderPath.indexOf("WEB-INF")); - } else if (folderPath.indexOf("target") > 0) { - path = folderPath.substring(0, folderPath.indexOf("target")); - } - return path; - } + private static final Logger log = LoggerFactory.getLogger(TomcatServer.class); + + private static final String CONNECTOR_PORT = "8080"; + + private static final String RELATIVE_DEV_DUBBO_RESOVE_FILE = + "src/main/resources/properties/dubbo-resolve.properties"; + + private static final String RELATIVE_DUBBO_RESOVE_FILE = "WEB-INF/classes/properties/dubbo-resolve.properties"; + + // 以下设置轻易不要改动 + private static final String RELATIVE_DEV_BASE_DIR = "src/main/resources/tomcat/"; + + private static final String RELATIVE_BASE_DIR = "WEB-INF/classes/tomcat/"; + + private static final String RELATIVE_DEV_DOCBASE_DIR = "src/main/webapp"; + + private static final String RELATIVE_DOCBASE_DIR = "./"; + + public static void main(String[] args) throws Exception { + // 设定Spring的profile + if (StringUtils.isEmpty(System.getProperty("spring.profiles.active"))) { + System.setProperty("spring.profiles.active", "develop"); + } + + System.setProperty("tomcat.host.appBase", getAbsolutePath()); + File checkFile = new File(System.getProperty("tomcat.host.appBase") + "/WEB-INF"); + if (!checkFile.exists()) { + System.setProperty("catalina.base", getAbsolutePath() + RELATIVE_DEV_BASE_DIR); + System.setProperty("tomcat.context.docBase", RELATIVE_DEV_DOCBASE_DIR); + System.setProperty("dubbo.resolve.file", getAbsolutePath() + RELATIVE_DEV_DUBBO_RESOVE_FILE); + } else { + System.setProperty("catalina.base", getAbsolutePath() + RELATIVE_BASE_DIR); + System.setProperty("tomcat.context.docBase", RELATIVE_DOCBASE_DIR); + if ("develop".equalsIgnoreCase(System.getProperty("spring.profiles.active")) + || "test".equalsIgnoreCase("spring.profiles.active")) { + System.setProperty("dubbo.resolve.file", getAbsolutePath() + RELATIVE_DUBBO_RESOVE_FILE); + } + } + + if (StringUtils.isEmpty(System.getProperty("tomcat.connector.port"))) { + System.setProperty("tomcat.connector.port", CONNECTOR_PORT); + } + if (StringUtils.isEmpty(System.getProperty("tomcat.server.shutdownPort"))) { + System.setProperty("tomcat.server.shutdownPort", + String.valueOf(Integer.valueOf(System.getProperty("tomcat.connector.port")) + 10000)); + } + + log.info("====================ENV setting===================="); + log.info("spring.profiles.active:" + System.getProperty("spring.profiles.active")); + log.info("dubbo.resolve.file:" + System.getProperty("dubbo.resolve.file")); + log.info("catalina.base:" + System.getProperty("catalina.base")); + log.info("tomcat.host.appBase:" + System.getProperty("tomcat.host.appBase")); + log.info("tomcat.context.docBase:" + System.getProperty("tomcat.context.docBase")); + log.info("tomcat.connector.port:" + System.getProperty("tomcat.connector.port")); + log.info("tomcat.server.shutdownPort:" + System.getProperty("tomcat.server.shutdownPort")); + + ExtendedTomcat tomcat = new ExtendedTomcat(); + tomcat.start(); + tomcat.getServer().await(); + } + + private static String getAbsolutePath() { + String path = null; + String folderPath = TomcatServer.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + if (folderPath.indexOf("WEB-INF") > 0) { + path = folderPath.substring(0, folderPath.indexOf("WEB-INF")); + } else if (folderPath.indexOf("target") > 0) { + path = folderPath.substring(0, folderPath.indexOf("target")); + } + return path; + } } class ExtendedTomcat extends Tomcat { - private static final String RELATIVE_SERVERXML_PATH = "/conf/server.xml"; - - private Logger log = LoggerFactory.getLogger(this.getClass()); - - @Override - public Server getServer() { - if (server != null) { - return server; - } - // 默认不开启JNDI. 开启时, 注意maven必须添加tomcat-dbcp依赖 - System.setProperty("catalina.useNaming", "false"); - ExtendedCatalina extendedCatalina = new ExtendedCatalina(); - - // 覆盖默认的skip和scan jar包配置 - System.setProperty(Constants.SKIP_JARS_PROPERTY, ""); - System.setProperty(Constants.SCAN_JARS_PROPERTY, ""); - - Digester digester = extendedCatalina.createStartDigester(); - digester.push(extendedCatalina); - try { - server = ((ExtendedCatalina) digester - .parse(new File(System.getProperty("catalina.base") + RELATIVE_SERVERXML_PATH))).getServer(); - // 设置catalina.base和catalna.home - this.initBaseDir(); - return server; - } catch (Exception e) { - log.error("Error while parsing server.xml", e); - throw new RuntimeException("server未创建,请检查server.xml(路径:" + System.getProperty("catalina.base") - + RELATIVE_SERVERXML_PATH + ")配置是否正确"); - } - } - - private static class ExtendedCatalina extends Catalina { - - @Override - public Digester createStartDigester() { - return super.createStartDigester(); - } - - } + private static final String RELATIVE_SERVERXML_PATH = "/conf/server.xml"; + + private Logger log = LoggerFactory.getLogger(this.getClass()); + + @Override + public Server getServer() { + if (server != null) { + return server; + } + // 默认不开启JNDI. 开启时, 注意maven必须添加tomcat-dbcp依赖 + System.setProperty("catalina.useNaming", "false"); + ExtendedCatalina extendedCatalina = new ExtendedCatalina(); + + // 覆盖默认的skip和scan jar包配置 + System.setProperty(Constants.SKIP_JARS_PROPERTY, ""); + System.setProperty(Constants.SCAN_JARS_PROPERTY, ""); + + Digester digester = extendedCatalina.createStartDigester(); + digester.push(extendedCatalina); + try { + server = ((ExtendedCatalina) digester + .parse(new File(System.getProperty("catalina.base") + RELATIVE_SERVERXML_PATH))).getServer(); + // 设置catalina.base和catalna.home + this.initBaseDir(); + return server; + } catch (Exception e) { + log.error("Error while parsing server.xml", e); + throw new RuntimeException("server未创建,请检查server.xml(路径:" + System.getProperty("catalina.base") + + RELATIVE_SERVERXML_PATH + ")配置是否正确"); + } + } + + private static class ExtendedCatalina extends Catalina { + + @Override + public Digester createStartDigester() { + return super.createStartDigester(); + } + + } } diff --git a/javatool-embedded-server/src/main/resources/logback.xml b/javatool-embedded-server/src/main/resources/logback.xml index fc50142b..3603a214 100644 --- a/javatool-embedded-server/src/main/resources/logback.xml +++ b/javatool-embedded-server/src/main/resources/logback.xml @@ -3,45 +3,45 @@ - - - - - - %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n - - - - - - - - ${user.dir}/logs/${FILE_NAME}-all.%d{yyyy-MM-dd}.log - 30 - - - - - 30MB - - - - %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n - - - - - - - - - - - - - - - - + + + + + + %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n + + + + + + + + ${user.dir}/logs/${FILE_NAME}-all.%d{yyyy-MM-dd}.log + 30 + + + + + 30MB + + + + %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n + + + + + + + + + + + + + + + + diff --git a/javatool-embedded-server/src/main/resources/spring/spring-servlet.xml b/javatool-embedded-server/src/main/resources/spring/spring-servlet.xml index 00a9b947..6c05fe85 100644 --- a/javatool-embedded-server/src/main/resources/spring/spring-servlet.xml +++ b/javatool-embedded-server/src/main/resources/spring/spring-servlet.xml @@ -1,22 +1,22 @@ - - - + + + - - - - - + + + + + diff --git a/javatool-embedded-server/src/main/resources/tomcat/conf/server.xml b/javatool-embedded-server/src/main/resources/tomcat/conf/server.xml index 735922c0..77316698 100644 --- a/javatool-embedded-server/src/main/resources/tomcat/conf/server.xml +++ b/javatool-embedded-server/src/main/resources/tomcat/conf/server.xml @@ -1,37 +1,37 @@ - - - - + + + + - + - + - + - + - - - - - - - - - - + + + + + + + + + + diff --git a/javatool-embedded-server/src/main/resources/tomcat/conf/web.xml b/javatool-embedded-server/src/main/resources/tomcat/conf/web.xml index 75a7692b..7dc916a5 100644 --- a/javatool-embedded-server/src/main/resources/tomcat/conf/web.xml +++ b/javatool-embedded-server/src/main/resources/tomcat/conf/web.xml @@ -16,290 +16,290 @@ limitations under the License. --> + version="3.1"> - - - - - - - - - + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - default - org.apache.catalina.servlets.DefaultServlet - - debug - 0 - - - listings - false - - 1 - + + default + org.apache.catalina.servlets.DefaultServlet + + debug + 0 + + + listings + false + + 1 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - jsp - org.apache.jasper.servlet.JspServlet - - fork - false - - - xpoweredBy - false - - - keepgenerated - false - - 3 - + + jsp + org.apache.jasper.servlet.JspServlet + + fork + false + + + xpoweredBy + false + + + keepgenerated + false + + 3 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - + + + + - - - default - / - + + + default + / + - - - jsp - *.jsp - *.jspx - + + + jsp + *.jsp + *.jspx + - - + - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + - - + - - + - - + - - - + + + - - 30 - + + 30 + - - - - - - - + + + + + + + - - 123 - application/vnd.lotus-1-2-3 - - - 3dml - text/vnd.in3d.3dml - - - 3ds - image/x-3ds - - - 3g2 - video/3gpp2 - - - 3gp - video/3gpp - - - 7z - application/x-7z-compressed - - - aab - application/x-authorware-bin - - - aac - audio/x-aac - - - aam - application/x-authorware-map - - - aas - application/x-authorware-seg - - - abs - audio/x-mpeg - - - abw - application/x-abiword - - - ac - application/pkix-attr-cert - - - acc - application/vnd.americandynamics.acc - - - ace - application/x-ace-compressed - - - acu - application/vnd.acucobol - - - acutc - application/vnd.acucorp - - - adp - audio/adpcm - - - aep - application/vnd.audiograph - - - afm - application/x-font-type1 - - - afp - application/vnd.ibm.modcap - - - ahead - application/vnd.ahead.space - - - ai - application/postscript - - - aif - audio/x-aiff - - - aifc - audio/x-aiff - - - aiff - audio/x-aiff - - - aim - application/x-aim - - - air - application/vnd.adobe.air-application-installer-package+zip - - - ait - application/vnd.dvb.ait - - - ami - application/vnd.amiga.ami - - - anx - application/annodex - - - apk - application/vnd.android.package-archive - - - appcache - text/cache-manifest - - - application - application/x-ms-application - - - apr - application/vnd.lotus-approach - - - arc - application/x-freearc - - - art - image/x-jg - - - asc - application/pgp-signature - - - asf - video/x-ms-asf - - - asm - text/x-asm - - - aso - application/vnd.accpac.simply.aso - - - asx - video/x-ms-asf - - - atc - application/vnd.acucorp - - - atom - application/atom+xml - - - atomcat - application/atomcat+xml - - - atomsvc - application/atomsvc+xml - - - atx - application/vnd.antix.game-component - - - au - audio/basic - - - avi - video/x-msvideo - - - avx - video/x-rad-screenplay - - - aw - application/applixware - - - axa - audio/annodex - - - axv - video/annodex - - - azf - application/vnd.airzip.filesecure.azf - - - azs - application/vnd.airzip.filesecure.azs - - - azw - application/vnd.amazon.ebook - - - bat - application/x-msdownload - - - bcpio - application/x-bcpio - - - bdf - application/x-font-bdf - - - bdm - application/vnd.syncml.dm+wbxml - - - bed - application/vnd.realvnc.bed - - - bh2 - application/vnd.fujitsu.oasysprs - - - bin - application/octet-stream - - - blb - application/x-blorb - - - blorb - application/x-blorb - - - bmi - application/vnd.bmi - - - bmp - image/bmp - - - body - text/html - - - book - application/vnd.framemaker - - - box - application/vnd.previewsystems.box - - - boz - application/x-bzip2 - - - bpk - application/octet-stream - - - btif - image/prs.btif - - - bz - application/x-bzip - - - bz2 - application/x-bzip2 - - - c - text/x-c - - - c11amc - application/vnd.cluetrust.cartomobile-config - - - c11amz - application/vnd.cluetrust.cartomobile-config-pkg - - - c4d - application/vnd.clonk.c4group - - - c4f - application/vnd.clonk.c4group - - - c4g - application/vnd.clonk.c4group - - - c4p - application/vnd.clonk.c4group - - - c4u - application/vnd.clonk.c4group - - - cab - application/vnd.ms-cab-compressed - - - caf - audio/x-caf - - - cap - application/vnd.tcpdump.pcap - - - car - application/vnd.curl.car - - - cat - application/vnd.ms-pki.seccat - - - cb7 - application/x-cbr - - - cba - application/x-cbr - - - cbr - application/x-cbr - - - cbt - application/x-cbr - - - cbz - application/x-cbr - - - cc - text/x-c - - - cct - application/x-director - - - ccxml - application/ccxml+xml - - - cdbcmsg - application/vnd.contact.cmsg - - - cdf - application/x-cdf - - - cdkey - application/vnd.mediastation.cdkey - - - cdmia - application/cdmi-capability - - - cdmic - application/cdmi-container - - - cdmid - application/cdmi-domain - - - cdmio - application/cdmi-object - - - cdmiq - application/cdmi-queue - - - cdx - chemical/x-cdx - - - cdxml - application/vnd.chemdraw+xml - - - cdy - application/vnd.cinderella - - - cer - application/pkix-cert - - - cfs - application/x-cfs-compressed - - - cgm - image/cgm - - - chat - application/x-chat - - - chm - application/vnd.ms-htmlhelp - - - chrt - application/vnd.kde.kchart - - - cif - chemical/x-cif - - - cii - application/vnd.anser-web-certificate-issue-initiation - - - cil - application/vnd.ms-artgalry - - - cla - application/vnd.claymore - - - class - application/java - - - clkk - application/vnd.crick.clicker.keyboard - - - clkp - application/vnd.crick.clicker.palette - - - clkt - application/vnd.crick.clicker.template - - - clkw - application/vnd.crick.clicker.wordbank - - - clkx - application/vnd.crick.clicker - - - clp - application/x-msclip - - - cmc - application/vnd.cosmocaller - - - cmdf - chemical/x-cmdf - - - cml - chemical/x-cml - - - cmp - application/vnd.yellowriver-custom-menu - - - cmx - image/x-cmx - - - cod - application/vnd.rim.cod - - - com - application/x-msdownload - - - conf - text/plain - - - cpio - application/x-cpio - - - cpp - text/x-c - - - cpt - application/mac-compactpro - - - crd - application/x-mscardfile - - - crl - application/pkix-crl - - - crt - application/x-x509-ca-cert - - - cryptonote - application/vnd.rig.cryptonote - - - csh - application/x-csh - - - csml - chemical/x-csml - - - csp - application/vnd.commonspace - - - css - text/css - - - cst - application/x-director - - - csv - text/csv - - - cu - application/cu-seeme - - - curl - text/vnd.curl - - - cww - application/prs.cww - - - cxt - application/x-director - - - cxx - text/x-c - - - dae - model/vnd.collada+xml - - - daf - application/vnd.mobius.daf - - - dart - application/vnd.dart - - - dataless - application/vnd.fdsn.seed - - - davmount - application/davmount+xml - - - dbk - application/docbook+xml - - - dcr - application/x-director - - - dcurl - text/vnd.curl.dcurl - - - dd2 - application/vnd.oma.dd2+xml - - - ddd - application/vnd.fujixerox.ddd - - - deb - application/x-debian-package - - - def - text/plain - - - deploy - application/octet-stream - - - der - application/x-x509-ca-cert - - - dfac - application/vnd.dreamfactory - - - dgc - application/x-dgc-compressed - - - dib - image/bmp - - - dic - text/x-c - - - dir - application/x-director - - - dis - application/vnd.mobius.dis - - - dist - application/octet-stream - - - distz - application/octet-stream - - - djv - image/vnd.djvu - - - djvu - image/vnd.djvu - - - dll - application/x-msdownload - - - dmg - application/x-apple-diskimage - - - dmp - application/vnd.tcpdump.pcap - - - dms - application/octet-stream - - - dna - application/vnd.dna - - - doc - application/msword - - - docm - application/vnd.ms-word.document.macroenabled.12 - - - docx - application/vnd.openxmlformats-officedocument.wordprocessingml.document - - - dot - application/msword - - - dotm - application/vnd.ms-word.template.macroenabled.12 - - - dotx - application/vnd.openxmlformats-officedocument.wordprocessingml.template - - - dp - application/vnd.osgi.dp - - - dpg - application/vnd.dpgraph - - - dra - audio/vnd.dra - - - dsc - text/prs.lines.tag - - - dssc - application/dssc+der - - - dtb - application/x-dtbook+xml - - - dtd - application/xml-dtd - - - dts - audio/vnd.dts - - - dtshd - audio/vnd.dts.hd - - - dump - application/octet-stream - - - dv - video/x-dv - - - dvb - video/vnd.dvb.file - - - dvi - application/x-dvi - - - dwf - model/vnd.dwf - - - dwg - image/vnd.dwg - - - dxf - image/vnd.dxf - - - dxp - application/vnd.spotfire.dxp - - - dxr - application/x-director - - - ecelp4800 - audio/vnd.nuera.ecelp4800 - - - ecelp7470 - audio/vnd.nuera.ecelp7470 - - - ecelp9600 - audio/vnd.nuera.ecelp9600 - - - ecma - application/ecmascript - - - edm - application/vnd.novadigm.edm - - - edx - application/vnd.novadigm.edx - - - efif - application/vnd.picsel - - - ei6 - application/vnd.pg.osasli - - - elc - application/octet-stream - - - emf - application/x-msmetafile - - - eml - message/rfc822 - - - emma - application/emma+xml - - - emz - application/x-msmetafile - - - eol - audio/vnd.digital-winds - - - eot - application/vnd.ms-fontobject - - - eps - application/postscript - - - epub - application/epub+zip - - - es3 - application/vnd.eszigno3+xml - - - esa - application/vnd.osgi.subsystem - - - esf - application/vnd.epson.esf - - - et3 - application/vnd.eszigno3+xml - - - etx - text/x-setext - - - eva - application/x-eva - - - evy - application/x-envoy - - - exe - application/octet-stream - - - exi - application/exi - - - ext - application/vnd.novadigm.ext - - - ez - application/andrew-inset - - - ez2 - application/vnd.ezpix-album - - - ez3 - application/vnd.ezpix-package - - - f - text/x-fortran - - - f4v - video/x-f4v - - - f77 - text/x-fortran - - - f90 - text/x-fortran - - - fbs - image/vnd.fastbidsheet - - - fcdt - application/vnd.adobe.formscentral.fcdt - - - fcs - application/vnd.isac.fcs - - - fdf - application/vnd.fdf - - - fe_launch - application/vnd.denovo.fcselayout-link - - - fg5 - application/vnd.fujitsu.oasysgp - - - fgd - application/x-director - - - fh - image/x-freehand - - - fh4 - image/x-freehand - - - fh5 - image/x-freehand - - - fh7 - image/x-freehand - - - fhc - image/x-freehand - - - fig - application/x-xfig - - - flac - audio/flac - - - fli - video/x-fli - - - flo - application/vnd.micrografx.flo - - - flv - video/x-flv - - - flw - application/vnd.kde.kivio - - - flx - text/vnd.fmi.flexstor - - - fly - text/vnd.fly - - - fm - application/vnd.framemaker - - - fnc - application/vnd.frogans.fnc - - - for - text/x-fortran - - - fpx - image/vnd.fpx - - - frame - application/vnd.framemaker - - - fsc - application/vnd.fsc.weblaunch - - - fst - image/vnd.fst - - - ftc - application/vnd.fluxtime.clip - - - fti - application/vnd.anser-web-funds-transfer-initiation - - - fvt - video/vnd.fvt - - - fxp - application/vnd.adobe.fxp - - - fxpl - application/vnd.adobe.fxp - - - fzs - application/vnd.fuzzysheet - - - g2w - application/vnd.geoplan - - - g3 - image/g3fax - - - g3w - application/vnd.geospace - - - gac - application/vnd.groove-account - - - gam - application/x-tads - - - gbr - application/rpki-ghostbusters - - - gca - application/x-gca-compressed - - - gdl - model/vnd.gdl - - - geo - application/vnd.dynageo - - - gex - application/vnd.geometry-explorer - - - ggb - application/vnd.geogebra.file - - - ggt - application/vnd.geogebra.tool - - - ghf - application/vnd.groove-help - - - gif - image/gif - - - gim - application/vnd.groove-identity-message - - - gml - application/gml+xml - - - gmx - application/vnd.gmx - - - gnumeric - application/x-gnumeric - - - gph - application/vnd.flographit - - - gpx - application/gpx+xml - - - gqf - application/vnd.grafeq - - - gqs - application/vnd.grafeq - - - gram - application/srgs - - - gramps - application/x-gramps-xml - - - gre - application/vnd.geometry-explorer - - - grv - application/vnd.groove-injector - - - grxml - application/srgs+xml - - - gsf - application/x-font-ghostscript - - - gtar - application/x-gtar - - - gtm - application/vnd.groove-tool-message - - - gtw - model/vnd.gtw - - - gv - text/vnd.graphviz - - - gxf - application/gxf - - - gxt - application/vnd.geonext - - - gz - application/x-gzip - - - h - text/x-c - - - h261 - video/h261 - - - h263 - video/h263 - - - h264 - video/h264 - - - hal - application/vnd.hal+xml - - - hbci - application/vnd.hbci - - - hdf - application/x-hdf - - - hh - text/x-c - - - hlp - application/winhlp - - - hpgl - application/vnd.hp-hpgl - - - hpid - application/vnd.hp-hpid - - - hps - application/vnd.hp-hps - - - hqx - application/mac-binhex40 - - - htc - text/x-component - - - htke - application/vnd.kenameaapp - - - htm - text/html - - - html - text/html - - - hvd - application/vnd.yamaha.hv-dic - - - hvp - application/vnd.yamaha.hv-voice - - - hvs - application/vnd.yamaha.hv-script - - - i2g - application/vnd.intergeo - - - icc - application/vnd.iccprofile - - - ice - x-conference/x-cooltalk - - - icm - application/vnd.iccprofile - - - ico - image/x-icon - - - ics - text/calendar - - - ief - image/ief - - - ifb - text/calendar - - - ifm - application/vnd.shana.informed.formdata - - - iges - model/iges - - - igl - application/vnd.igloader - - - igm - application/vnd.insors.igm - - - igs - model/iges - - - igx - application/vnd.micrografx.igx - - - iif - application/vnd.shana.informed.interchange - - - imp - application/vnd.accpac.simply.imp - - - ims - application/vnd.ms-ims - - - in - text/plain - - - ink - application/inkml+xml - - - inkml - application/inkml+xml - - - install - application/x-install-instructions - - - iota - application/vnd.astraea-software.iota - - - ipfix - application/ipfix - - - ipk - application/vnd.shana.informed.package - - - irm - application/vnd.ibm.rights-management - - - irp - application/vnd.irepository.package+xml - - - iso - application/x-iso9660-image - - - itp - application/vnd.shana.informed.formtemplate - - - ivp - application/vnd.immervision-ivp - - - ivu - application/vnd.immervision-ivu - - - jad - text/vnd.sun.j2me.app-descriptor - - - jam - application/vnd.jam - - - jar - application/java-archive - - - java - text/x-java-source - - - jisp - application/vnd.jisp - - - jlt - application/vnd.hp-jlyt - - - jnlp - application/x-java-jnlp-file - - - joda - application/vnd.joost.joda-archive - - - jpe - image/jpeg - - - jpeg - image/jpeg - - - jpg - image/jpeg - - - jpgm - video/jpm - - - jpgv - video/jpeg - - - jpm - video/jpm - - - js - application/javascript - - - jsf - text/plain - - - json - application/json - - - jsonml - application/jsonml+json - - - jspf - text/plain - - - kar - audio/midi - - - karbon - application/vnd.kde.karbon - - - kfo - application/vnd.kde.kformula - - - kia - application/vnd.kidspiration - - - kml - application/vnd.google-earth.kml+xml - - - kmz - application/vnd.google-earth.kmz - - - kne - application/vnd.kinar - - - knp - application/vnd.kinar - - - kon - application/vnd.kde.kontour - - - kpr - application/vnd.kde.kpresenter - - - kpt - application/vnd.kde.kpresenter - - - kpxx - application/vnd.ds-keypoint - - - ksp - application/vnd.kde.kspread - - - ktr - application/vnd.kahootz - - - ktx - image/ktx - - - ktz - application/vnd.kahootz - - - kwd - application/vnd.kde.kword - - - kwt - application/vnd.kde.kword - - - lasxml - application/vnd.las.las+xml - - - latex - application/x-latex - - - lbd - application/vnd.llamagraphics.life-balance.desktop - - - lbe - application/vnd.llamagraphics.life-balance.exchange+xml - - - les - application/vnd.hhe.lesson-player - - - lha - application/x-lzh-compressed - - - link66 - application/vnd.route66.link66+xml - - - list - text/plain - - - list3820 - application/vnd.ibm.modcap - - - listafp - application/vnd.ibm.modcap - - - lnk - application/x-ms-shortcut - - - log - text/plain - - - lostxml - application/lost+xml - - - lrf - application/octet-stream - - - lrm - application/vnd.ms-lrm - - - ltf - application/vnd.frogans.ltf - - - lvp - audio/vnd.lucent.voice - - - lwp - application/vnd.lotus-wordpro - - - lzh - application/x-lzh-compressed - - - m13 - application/x-msmediaview - - - m14 - application/x-msmediaview - - - m1v - video/mpeg - - - m21 - application/mp21 - - - m2a - audio/mpeg - - - m2v - video/mpeg - - - m3a - audio/mpeg - - - m3u - audio/x-mpegurl - - - m3u8 - application/vnd.apple.mpegurl - - - m4a - audio/mp4 - - - m4b - audio/mp4 - - - m4r - audio/mp4 - - - m4u - video/vnd.mpegurl - - - m4v - video/mp4 - - - ma - application/mathematica - - - mac - image/x-macpaint - - - mads - application/mads+xml - - - mag - application/vnd.ecowin.chart - - - maker - application/vnd.framemaker - - - man - text/troff - - - mar - application/octet-stream - - - mathml - application/mathml+xml - - - mb - application/mathematica - - - mbk - application/vnd.mobius.mbk - - - mbox - application/mbox - - - mc1 - application/vnd.medcalcdata - - - mcd - application/vnd.mcd - - - mcurl - text/vnd.curl.mcurl - - - mdb - application/x-msaccess - - - mdi - image/vnd.ms-modi - - - me - text/troff - - - mesh - model/mesh - - - meta4 - application/metalink4+xml - - - metalink - application/metalink+xml - - - mets - application/mets+xml - - - mfm - application/vnd.mfmp - - - mft - application/rpki-manifest - - - mgp - application/vnd.osgeo.mapguide.package - - - mgz - application/vnd.proteus.magazine - - - mid - audio/midi - - - midi - audio/midi - - - mie - application/x-mie - - - mif - application/x-mif - - - mime - message/rfc822 - - - mj2 - video/mj2 - - - mjp2 - video/mj2 - - - mk3d - video/x-matroska - - - mka - audio/x-matroska - - - mks - video/x-matroska - - - mkv - video/x-matroska - - - mlp - application/vnd.dolby.mlp - - - mmd - application/vnd.chipnuts.karaoke-mmd - - - mmf - application/vnd.smaf - - - mmr - image/vnd.fujixerox.edmics-mmr - - - mng - video/x-mng - - - mny - application/x-msmoney - - - mobi - application/x-mobipocket-ebook - - - mods - application/mods+xml - - - mov - video/quicktime - - - movie - video/x-sgi-movie - - - mp1 - audio/mpeg - - - mp2 - audio/mpeg - - - mp21 - application/mp21 - - - mp2a - audio/mpeg - - - mp3 - audio/mpeg - - - mp4 - video/mp4 - - - mp4a - audio/mp4 - - - mp4s - application/mp4 - - - mp4v - video/mp4 - - - mpa - audio/mpeg - - - mpc - application/vnd.mophun.certificate - - - mpe - video/mpeg - - - mpeg - video/mpeg - - - mpega - audio/x-mpeg - - - mpg - video/mpeg - - - mpg4 - video/mp4 - - - mpga - audio/mpeg - - - mpkg - application/vnd.apple.installer+xml - - - mpm - application/vnd.blueice.multipass - - - mpn - application/vnd.mophun.application - - - mpp - application/vnd.ms-project - - - mpt - application/vnd.ms-project - - - mpv2 - video/mpeg2 - - - mpy - application/vnd.ibm.minipay - - - mqy - application/vnd.mobius.mqy - - - mrc - application/marc - - - mrcx - application/marcxml+xml - - - ms - text/troff - - - mscml - application/mediaservercontrol+xml - - - mseed - application/vnd.fdsn.mseed - - - mseq - application/vnd.mseq - - - msf - application/vnd.epson.msf - - - msh - model/mesh - - - msi - application/x-msdownload - - - msl - application/vnd.mobius.msl - - - msty - application/vnd.muvee.style - - - mts - model/vnd.mts - - - mus - application/vnd.musician - - - musicxml - application/vnd.recordare.musicxml+xml - - - mvb - application/x-msmediaview - - - mwf - application/vnd.mfer - - - mxf - application/mxf - - - mxl - application/vnd.recordare.musicxml - - - mxml - application/xv+xml - - - mxs - application/vnd.triscape.mxs - - - mxu - video/vnd.mpegurl - - - n-gage - application/vnd.nokia.n-gage.symbian.install - - - n3 - text/n3 - - - nb - application/mathematica - - - nbp - application/vnd.wolfram.player - - - nc - application/x-netcdf - - - ncx - application/x-dtbncx+xml - - - nfo - text/x-nfo - - - ngdat - application/vnd.nokia.n-gage.data - - - nitf - application/vnd.nitf - - - nlu - application/vnd.neurolanguage.nlu - - - nml - application/vnd.enliven - - - nnd - application/vnd.noblenet-directory - - - nns - application/vnd.noblenet-sealer - - - nnw - application/vnd.noblenet-web - - - npx - image/vnd.net-fpx - - - nsc - application/x-conference - - - nsf - application/vnd.lotus-notes - - - ntf - application/vnd.nitf - - - nzb - application/x-nzb - - - oa2 - application/vnd.fujitsu.oasys2 - - - oa3 - application/vnd.fujitsu.oasys3 - - - oas - application/vnd.fujitsu.oasys - - - obd - application/x-msbinder - - - obj - application/x-tgif - - - oda - application/oda - - - - odb - application/vnd.oasis.opendocument.database - - - - odc - application/vnd.oasis.opendocument.chart - - - - odf - application/vnd.oasis.opendocument.formula - - - odft - application/vnd.oasis.opendocument.formula-template - - - - odg - application/vnd.oasis.opendocument.graphics - - - - odi - application/vnd.oasis.opendocument.image - - - - odm - application/vnd.oasis.opendocument.text-master - - - - odp - application/vnd.oasis.opendocument.presentation - - - - ods - application/vnd.oasis.opendocument.spreadsheet - - - - odt - application/vnd.oasis.opendocument.text - - - oga - audio/ogg - - - ogg - audio/ogg - - - ogv - video/ogg - - - - ogx - application/ogg - - - omdoc - application/omdoc+xml - - - onepkg - application/onenote - - - onetmp - application/onenote - - - onetoc - application/onenote - - - onetoc2 - application/onenote - - - opf - application/oebps-package+xml - - - opml - text/x-opml - - - oprc - application/vnd.palm - - - org - application/vnd.lotus-organizer - - - osf - application/vnd.yamaha.openscoreformat - - - osfpvg - application/vnd.yamaha.openscoreformat.osfpvg+xml - - - otc - application/vnd.oasis.opendocument.chart-template - - - otf - font/otf - - - - otg - application/vnd.oasis.opendocument.graphics-template - - - - oth - application/vnd.oasis.opendocument.text-web - - - oti - application/vnd.oasis.opendocument.image-template - - - - otp - application/vnd.oasis.opendocument.presentation-template - - - - ots - application/vnd.oasis.opendocument.spreadsheet-template - - - - ott - application/vnd.oasis.opendocument.text-template - - - oxps - application/oxps - - - oxt - application/vnd.openofficeorg.extension - - - p - text/x-pascal - - - p10 - application/pkcs10 - - - p12 - application/x-pkcs12 - - - p7b - application/x-pkcs7-certificates - - - p7c - application/pkcs7-mime - - - p7m - application/pkcs7-mime - - - p7r - application/x-pkcs7-certreqresp - - - p7s - application/pkcs7-signature - - - p8 - application/pkcs8 - - - pas - text/x-pascal - - - paw - application/vnd.pawaafile - - - pbd - application/vnd.powerbuilder6 - - - pbm - image/x-portable-bitmap - - - pcap - application/vnd.tcpdump.pcap - - - pcf - application/x-font-pcf - - - pcl - application/vnd.hp-pcl - - - pclxl - application/vnd.hp-pclxl - - - pct - image/pict - - - pcurl - application/vnd.curl.pcurl - - - pcx - image/x-pcx - - - pdb - application/vnd.palm - - - pdf - application/pdf - - - pfa - application/x-font-type1 - - - pfb - application/x-font-type1 - - - pfm - application/x-font-type1 - - - pfr - application/font-tdpfr - - - pfx - application/x-pkcs12 - - - pgm - image/x-portable-graymap - - - pgn - application/x-chess-pgn - - - pgp - application/pgp-encrypted - - - pic - image/pict - - - pict - image/pict - - - pkg - application/octet-stream - - - pki - application/pkixcmp - - - pkipath - application/pkix-pkipath - - - plb - application/vnd.3gpp.pic-bw-large - - - plc - application/vnd.mobius.plc - - - plf - application/vnd.pocketlearn - - - pls - audio/x-scpls - - - pml - application/vnd.ctc-posml - - - png - image/png - - - pnm - image/x-portable-anymap - - - pnt - image/x-macpaint - - - portpkg - application/vnd.macports.portpkg - - - pot - application/vnd.ms-powerpoint - - - potm - application/vnd.ms-powerpoint.template.macroenabled.12 - - - potx - application/vnd.openxmlformats-officedocument.presentationml.template - - - ppam - application/vnd.ms-powerpoint.addin.macroenabled.12 - - - ppd - application/vnd.cups-ppd - - - ppm - image/x-portable-pixmap - - - pps - application/vnd.ms-powerpoint - - - ppsm - application/vnd.ms-powerpoint.slideshow.macroenabled.12 - - - ppsx - application/vnd.openxmlformats-officedocument.presentationml.slideshow - - - ppt - application/vnd.ms-powerpoint - - - pptm - application/vnd.ms-powerpoint.presentation.macroenabled.12 - - - pptx - application/vnd.openxmlformats-officedocument.presentationml.presentation - - - pqa - application/vnd.palm - - - prc - application/x-mobipocket-ebook - - - pre - application/vnd.lotus-freelance - - - prf - application/pics-rules - - - ps - application/postscript - - - psb - application/vnd.3gpp.pic-bw-small - - - psd - image/vnd.adobe.photoshop - - - psf - application/x-font-linux-psf - - - pskcxml - application/pskc+xml - - - ptid - application/vnd.pvi.ptid1 - - - pub - application/x-mspublisher - - - pvb - application/vnd.3gpp.pic-bw-var - - - pwn - application/vnd.3m.post-it-notes - - - pya - audio/vnd.ms-playready.media.pya - - - pyv - video/vnd.ms-playready.media.pyv - - - qam - application/vnd.epson.quickanime - - - qbo - application/vnd.intu.qbo - - - qfx - application/vnd.intu.qfx - - - qps - application/vnd.publishare-delta-tree - - - qt - video/quicktime - - - qti - image/x-quicktime - - - qtif - image/x-quicktime - - - qwd - application/vnd.quark.quarkxpress - - - qwt - application/vnd.quark.quarkxpress - - - qxb - application/vnd.quark.quarkxpress - - - qxd - application/vnd.quark.quarkxpress - - - qxl - application/vnd.quark.quarkxpress - - - qxt - application/vnd.quark.quarkxpress - - - ra - audio/x-pn-realaudio - - - ram - audio/x-pn-realaudio - - - rar - application/x-rar-compressed - - - ras - image/x-cmu-raster - - - rcprofile - application/vnd.ipunplugged.rcprofile - - - rdf - application/rdf+xml - - - rdz - application/vnd.data-vision.rdz - - - rep - application/vnd.businessobjects - - - res - application/x-dtbresource+xml - - - rgb - image/x-rgb - - - rif - application/reginfo+xml - - - rip - audio/vnd.rip - - - ris - application/x-research-info-systems - - - rl - application/resource-lists+xml - - - rlc - image/vnd.fujixerox.edmics-rlc - - - rld - application/resource-lists-diff+xml - - - rm - application/vnd.rn-realmedia - - - rmi - audio/midi - - - rmp - audio/x-pn-realaudio-plugin - - - rms - application/vnd.jcp.javame.midlet-rms - - - rmvb - application/vnd.rn-realmedia-vbr - - - rnc - application/relax-ng-compact-syntax - - - roa - application/rpki-roa - - - roff - text/troff - - - rp9 - application/vnd.cloanto.rp9 - - - rpss - application/vnd.nokia.radio-presets - - - rpst - application/vnd.nokia.radio-preset - - - rq - application/sparql-query - - - rs - application/rls-services+xml - - - rsd - application/rsd+xml - - - rss - application/rss+xml - - - rtf - application/rtf - - - rtx - text/richtext - - - s - text/x-asm - - - s3m - audio/s3m - - - saf - application/vnd.yamaha.smaf-audio - - - sbml - application/sbml+xml - - - sc - application/vnd.ibm.secure-container - - - scd - application/x-msschedule - - - scm - application/vnd.lotus-screencam - - - scq - application/scvp-cv-request - - - scs - application/scvp-cv-response - - - scurl - text/vnd.curl.scurl - - - sda - application/vnd.stardivision.draw - - - sdc - application/vnd.stardivision.calc - - - sdd - application/vnd.stardivision.impress - - - sdkd - application/vnd.solent.sdkm+xml - - - sdkm - application/vnd.solent.sdkm+xml - - - sdp - application/sdp - - - sdw - application/vnd.stardivision.writer - - - see - application/vnd.seemail - - - seed - application/vnd.fdsn.seed - - - sema - application/vnd.sema - - - semd - application/vnd.semd - - - semf - application/vnd.semf - - - ser - application/java-serialized-object - - - setpay - application/set-payment-initiation - - - setreg - application/set-registration-initiation - - - sfd-hdstx - application/vnd.hydrostatix.sof-data - - - sfs - application/vnd.spotfire.sfs - - - sfv - text/x-sfv - - - sgi - image/sgi - - - sgl - application/vnd.stardivision.writer-global - - - sgm - text/sgml - - - sgml - text/sgml - - - sh - application/x-sh - - - shar - application/x-shar - - - shf - application/shf+xml - - + odb + application/vnd.oasis.opendocument.database + + + + odc + application/vnd.oasis.opendocument.chart + + + + odf + application/vnd.oasis.opendocument.formula + + + odft + application/vnd.oasis.opendocument.formula-template + + + + odg + application/vnd.oasis.opendocument.graphics + + + + odi + application/vnd.oasis.opendocument.image + + + + odm + application/vnd.oasis.opendocument.text-master + + + + odp + application/vnd.oasis.opendocument.presentation + + + + ods + application/vnd.oasis.opendocument.spreadsheet + + + + odt + application/vnd.oasis.opendocument.text + + + oga + audio/ogg + + + ogg + audio/ogg + + + ogv + video/ogg + + + + ogx + application/ogg + + + omdoc + application/omdoc+xml + + + onepkg + application/onenote + + + onetmp + application/onenote + + + onetoc + application/onenote + + + onetoc2 + application/onenote + + + opf + application/oebps-package+xml + + + opml + text/x-opml + + + oprc + application/vnd.palm + + + org + application/vnd.lotus-organizer + + + osf + application/vnd.yamaha.openscoreformat + + + osfpvg + application/vnd.yamaha.openscoreformat.osfpvg+xml + + + otc + application/vnd.oasis.opendocument.chart-template + + + otf + font/otf + + + + otg + application/vnd.oasis.opendocument.graphics-template + + + + oth + application/vnd.oasis.opendocument.text-web + + + oti + application/vnd.oasis.opendocument.image-template + + + + otp + application/vnd.oasis.opendocument.presentation-template + + + + ots + application/vnd.oasis.opendocument.spreadsheet-template + + + + ott + application/vnd.oasis.opendocument.text-template + + + oxps + application/oxps + + + oxt + application/vnd.openofficeorg.extension + + + p + text/x-pascal + + + p10 + application/pkcs10 + + + p12 + application/x-pkcs12 + + + p7b + application/x-pkcs7-certificates + + + p7c + application/pkcs7-mime + + + p7m + application/pkcs7-mime + + + p7r + application/x-pkcs7-certreqresp + + + p7s + application/pkcs7-signature + + + p8 + application/pkcs8 + + + pas + text/x-pascal + + + paw + application/vnd.pawaafile + + + pbd + application/vnd.powerbuilder6 + + + pbm + image/x-portable-bitmap + + + pcap + application/vnd.tcpdump.pcap + + + pcf + application/x-font-pcf + + + pcl + application/vnd.hp-pcl + + + pclxl + application/vnd.hp-pclxl + + + pct + image/pict + + + pcurl + application/vnd.curl.pcurl + + + pcx + image/x-pcx + + + pdb + application/vnd.palm + + + pdf + application/pdf + + + pfa + application/x-font-type1 + + + pfb + application/x-font-type1 + + + pfm + application/x-font-type1 + + + pfr + application/font-tdpfr + + + pfx + application/x-pkcs12 + + + pgm + image/x-portable-graymap + + + pgn + application/x-chess-pgn + + + pgp + application/pgp-encrypted + + + pic + image/pict + + + pict + image/pict + + + pkg + application/octet-stream + + + pki + application/pkixcmp + + + pkipath + application/pkix-pkipath + + + plb + application/vnd.3gpp.pic-bw-large + + + plc + application/vnd.mobius.plc + + + plf + application/vnd.pocketlearn + + + pls + audio/x-scpls + + + pml + application/vnd.ctc-posml + + + png + image/png + + + pnm + image/x-portable-anymap + + + pnt + image/x-macpaint + + + portpkg + application/vnd.macports.portpkg + + + pot + application/vnd.ms-powerpoint + + + potm + application/vnd.ms-powerpoint.template.macroenabled.12 + + + potx + application/vnd.openxmlformats-officedocument.presentationml.template + + + ppam + application/vnd.ms-powerpoint.addin.macroenabled.12 + + + ppd + application/vnd.cups-ppd + + + ppm + image/x-portable-pixmap + + + pps + application/vnd.ms-powerpoint + + + ppsm + application/vnd.ms-powerpoint.slideshow.macroenabled.12 + + + ppsx + application/vnd.openxmlformats-officedocument.presentationml.slideshow + + + ppt + application/vnd.ms-powerpoint + + + pptm + application/vnd.ms-powerpoint.presentation.macroenabled.12 + + + pptx + application/vnd.openxmlformats-officedocument.presentationml.presentation + + + pqa + application/vnd.palm + + + prc + application/x-mobipocket-ebook + + + pre + application/vnd.lotus-freelance + + + prf + application/pics-rules + + + ps + application/postscript + + + psb + application/vnd.3gpp.pic-bw-small + + + psd + image/vnd.adobe.photoshop + + + psf + application/x-font-linux-psf + + + pskcxml + application/pskc+xml + + + ptid + application/vnd.pvi.ptid1 + + + pub + application/x-mspublisher + + + pvb + application/vnd.3gpp.pic-bw-var + + + pwn + application/vnd.3m.post-it-notes + + + pya + audio/vnd.ms-playready.media.pya + + + pyv + video/vnd.ms-playready.media.pyv + + + qam + application/vnd.epson.quickanime + + + qbo + application/vnd.intu.qbo + + + qfx + application/vnd.intu.qfx + + + qps + application/vnd.publishare-delta-tree + + + qt + video/quicktime + + + qti + image/x-quicktime + + + qtif + image/x-quicktime + + + qwd + application/vnd.quark.quarkxpress + + + qwt + application/vnd.quark.quarkxpress + + + qxb + application/vnd.quark.quarkxpress + + + qxd + application/vnd.quark.quarkxpress + + + qxl + application/vnd.quark.quarkxpress + + + qxt + application/vnd.quark.quarkxpress + + + ra + audio/x-pn-realaudio + + + ram + audio/x-pn-realaudio + + + rar + application/x-rar-compressed + + + ras + image/x-cmu-raster + + + rcprofile + application/vnd.ipunplugged.rcprofile + + + rdf + application/rdf+xml + + + rdz + application/vnd.data-vision.rdz + + + rep + application/vnd.businessobjects + + + res + application/x-dtbresource+xml + + + rgb + image/x-rgb + + + rif + application/reginfo+xml + + + rip + audio/vnd.rip + + + ris + application/x-research-info-systems + + + rl + application/resource-lists+xml + + + rlc + image/vnd.fujixerox.edmics-rlc + + + rld + application/resource-lists-diff+xml + + + rm + application/vnd.rn-realmedia + + + rmi + audio/midi + + + rmp + audio/x-pn-realaudio-plugin + + + rms + application/vnd.jcp.javame.midlet-rms + + + rmvb + application/vnd.rn-realmedia-vbr + + + rnc + application/relax-ng-compact-syntax + + + roa + application/rpki-roa + + + roff + text/troff + + + rp9 + application/vnd.cloanto.rp9 + + + rpss + application/vnd.nokia.radio-presets + + + rpst + application/vnd.nokia.radio-preset + + + rq + application/sparql-query + + + rs + application/rls-services+xml + + + rsd + application/rsd+xml + + + rss + application/rss+xml + + + rtf + application/rtf + + + rtx + text/richtext + + + s + text/x-asm + + + s3m + audio/s3m + + + saf + application/vnd.yamaha.smaf-audio + + + sbml + application/sbml+xml + + + sc + application/vnd.ibm.secure-container + + + scd + application/x-msschedule + + + scm + application/vnd.lotus-screencam + + + scq + application/scvp-cv-request + + + scs + application/scvp-cv-response + + + scurl + text/vnd.curl.scurl + + + sda + application/vnd.stardivision.draw + + + sdc + application/vnd.stardivision.calc + + + sdd + application/vnd.stardivision.impress + + + sdkd + application/vnd.solent.sdkm+xml + + + sdkm + application/vnd.solent.sdkm+xml + + + sdp + application/sdp + + + sdw + application/vnd.stardivision.writer + + + see + application/vnd.seemail + + + seed + application/vnd.fdsn.seed + + + sema + application/vnd.sema + + + semd + application/vnd.semd + + + semf + application/vnd.semf + + + ser + application/java-serialized-object + + + setpay + application/set-payment-initiation + + + setreg + application/set-registration-initiation + + + sfd-hdstx + application/vnd.hydrostatix.sof-data + + + sfs + application/vnd.spotfire.sfs + + + sfv + text/x-sfv + + + sgi + image/sgi + + + sgl + application/vnd.stardivision.writer-global + + + sgm + text/sgml + + + sgml + text/sgml + + + sh + application/x-sh + + + shar + application/x-shar + + + shf + application/shf+xml + + - - sid - image/x-mrsid-image - - - sig - application/pgp-signature - - - sil - audio/silk - - - silo - model/mesh - - - sis - application/vnd.symbian.install - - - sisx - application/vnd.symbian.install - - - sit - application/x-stuffit - - - sitx - application/x-stuffitx - - - skd - application/vnd.koan - - - skm - application/vnd.koan - - - skp - application/vnd.koan - - - skt - application/vnd.koan - - - sldm - application/vnd.ms-powerpoint.slide.macroenabled.12 - - - sldx - application/vnd.openxmlformats-officedocument.presentationml.slide - - - slt - application/vnd.epson.salt - - - sm - application/vnd.stepmania.stepchart - - - smf - application/vnd.stardivision.math - - - smi - application/smil+xml - - - smil - application/smil+xml - - - smv - video/x-smv - - - smzip - application/vnd.stepmania.package - - - snd - audio/basic - - - snf - application/x-font-snf - - - so - application/octet-stream - - - spc - application/x-pkcs7-certificates - - - spf - application/vnd.yamaha.smaf-phrase - - - spl - application/x-futuresplash - - - spot - text/vnd.in3d.spot - - - spp - application/scvp-vp-response - - - spq - application/scvp-vp-request - - - spx - audio/ogg - - - sql - application/x-sql - - - src - application/x-wais-source - - - srt - application/x-subrip - - - sru - application/sru+xml - - - srx - application/sparql-results+xml - - - ssdl - application/ssdl+xml - - - sse - application/vnd.kodak-descriptor - - - ssf - application/vnd.epson.ssf - - - ssml - application/ssml+xml - - - st - application/vnd.sailingtracker.track - - - stc - application/vnd.sun.xml.calc.template - - - std - application/vnd.sun.xml.draw.template - - - stf - application/vnd.wt.stf - - - sti - application/vnd.sun.xml.impress.template - - - stk - application/hyperstudio - - - stl - application/vnd.ms-pki.stl - - - str - application/vnd.pg.format - - - stw - application/vnd.sun.xml.writer.template - - - sub - text/vnd.dvb.subtitle - - - sus - application/vnd.sus-calendar - - - susp - application/vnd.sus-calendar - - - sv4cpio - application/x-sv4cpio - - - sv4crc - application/x-sv4crc - - - svc - application/vnd.dvb.service - - - svd - application/vnd.svd - - - svg - image/svg+xml - - - svgz - image/svg+xml - - - swa - application/x-director - - - swf - application/x-shockwave-flash - - - swi - application/vnd.aristanetworks.swi - - - sxc - application/vnd.sun.xml.calc - - - sxd - application/vnd.sun.xml.draw - - - sxg - application/vnd.sun.xml.writer.global - - - sxi - application/vnd.sun.xml.impress - - - sxm - application/vnd.sun.xml.math - - - sxw - application/vnd.sun.xml.writer - - - t - text/troff - - - t3 - application/x-t3vm-image - - - taglet - application/vnd.mynfc - - - tao - application/vnd.tao.intent-module-archive - - - tar - application/x-tar - - - tcap - application/vnd.3gpp2.tcap - - - tcl - application/x-tcl - - - teacher - application/vnd.smart.teacher - - - tei - application/tei+xml - - - teicorpus - application/tei+xml - - - tex - application/x-tex - - - texi - application/x-texinfo - - - texinfo - application/x-texinfo - - - text - text/plain - - - tfi - application/thraud+xml - - - tfm - application/x-tex-tfm - - - tga - image/x-tga - - - thmx - application/vnd.ms-officetheme - - - tif - image/tiff - - - tiff - image/tiff - - - tmo - application/vnd.tmobile-livetv - - - torrent - application/x-bittorrent - - - tpl - application/vnd.groove-tool-template - - - tpt - application/vnd.trid.tpt - - - tr - text/troff - - - tra - application/vnd.trueapp - - - trm - application/x-msterminal - - - tsd - application/timestamped-data - - - tsv - text/tab-separated-values - - - ttc - font/collection - - - ttf - font/ttf - - - ttl - text/turtle - - - twd - application/vnd.simtech-mindmapper - - - twds - application/vnd.simtech-mindmapper - - - txd - application/vnd.genomatix.tuxedo - - - txf - application/vnd.mobius.txf - - - txt - text/plain - - - u32 - application/x-authorware-bin - - - udeb - application/x-debian-package - - - ufd - application/vnd.ufdl - - - ufdl - application/vnd.ufdl - - - ulw - audio/basic - - - ulx - application/x-glulx - - - umj - application/vnd.umajin - - - unityweb - application/vnd.unity - - - uoml - application/vnd.uoml+xml - - - uri - text/uri-list - - - uris - text/uri-list - - - urls - text/uri-list - - - ustar - application/x-ustar - - - utz - application/vnd.uiq.theme - - - uu - text/x-uuencode - - - uva - audio/vnd.dece.audio - - - uvd - application/vnd.dece.data - - - uvf - application/vnd.dece.data - - - uvg - image/vnd.dece.graphic - - - uvh - video/vnd.dece.hd - - - uvi - image/vnd.dece.graphic - - - uvm - video/vnd.dece.mobile - - - uvp - video/vnd.dece.pd - - - uvs - video/vnd.dece.sd - - - uvt - application/vnd.dece.ttml+xml - - - uvu - video/vnd.uvvu.mp4 - - - uvv - video/vnd.dece.video - - - uvva - audio/vnd.dece.audio - - - uvvd - application/vnd.dece.data - - - uvvf - application/vnd.dece.data - - - uvvg - image/vnd.dece.graphic - - - uvvh - video/vnd.dece.hd - - - uvvi - image/vnd.dece.graphic - - - uvvm - video/vnd.dece.mobile - - - uvvp - video/vnd.dece.pd - - - uvvs - video/vnd.dece.sd - - - uvvt - application/vnd.dece.ttml+xml - - - uvvu - video/vnd.uvvu.mp4 - - - uvvv - video/vnd.dece.video - - - uvvx - application/vnd.dece.unspecified - - - uvvz - application/vnd.dece.zip - - - uvx - application/vnd.dece.unspecified - - - uvz - application/vnd.dece.zip - - - vcard - text/vcard - - - vcd - application/x-cdlink - - - vcf - text/x-vcard - - - vcg - application/vnd.groove-vcard - - - vcs - text/x-vcalendar - - - vcx - application/vnd.vcx - - - vis - application/vnd.visionary - - - viv - video/vnd.vivo - - - vob - video/x-ms-vob - - - vor - application/vnd.stardivision.writer - - - vox - application/x-authorware-bin - - - vrml - model/vrml - - - vsd - application/vnd.visio - - - vsf - application/vnd.vsf - - - vss - application/vnd.visio - - - vst - application/vnd.visio - - - vsw - application/vnd.visio - - - vtu - model/vnd.vtu - - - vxml - application/voicexml+xml - - - w3d - application/x-director - - - wad - application/x-doom - - - wav - audio/x-wav - - - wax - audio/x-ms-wax - - - - wbmp - image/vnd.wap.wbmp - - - wbs - application/vnd.criticaltools.wbs+xml - - - wbxml - application/vnd.wap.wbxml - - - wcm - application/vnd.ms-works - - - wdb - application/vnd.ms-works - - - wdp - image/vnd.ms-photo - - - weba - audio/webm - - - webm - video/webm - - - webp - image/webp - - - wg - application/vnd.pmi.widget - - - wgt - application/widget - - - wks - application/vnd.ms-works - - - wm - video/x-ms-wm - - - wma - audio/x-ms-wma - - - wmd - application/x-ms-wmd - - - wmf - application/x-msmetafile - - - - wml - text/vnd.wap.wml - - - - wmlc - application/vnd.wap.wmlc - - - - wmls - text/vnd.wap.wmlscript - - - - wmlsc - application/vnd.wap.wmlscriptc - - - wmv - video/x-ms-wmv - - - wmx - video/x-ms-wmx - - - wmz - application/x-msmetafile - - - woff - font/woff - - - woff2 - font/woff2 - - - wpd - application/vnd.wordperfect - - - wpl - application/vnd.ms-wpl - - - wps - application/vnd.ms-works - - - wqd - application/vnd.wqd - - - wri - application/x-mswrite - - - wrl - model/vrml - - - wsdl - application/wsdl+xml - - - wspolicy - application/wspolicy+xml - - - wtb - application/vnd.webturbo - - - wvx - video/x-ms-wvx - - - x32 - application/x-authorware-bin - - - x3d - model/x3d+xml - - - x3db - model/x3d+binary - - - x3dbz - model/x3d+binary - - - x3dv - model/x3d+vrml - - - x3dvz - model/x3d+vrml - - - x3dz - model/x3d+xml - - - xaml - application/xaml+xml - - - xap - application/x-silverlight-app - - - xar - application/vnd.xara - - - xbap - application/x-ms-xbap - - - xbd - application/vnd.fujixerox.docuworks.binder - - - xbm - image/x-xbitmap - - - xdf - application/xcap-diff+xml - - - xdm - application/vnd.syncml.dm+xml - - - xdp - application/vnd.adobe.xdp+xml - - - xdssc - application/dssc+xml - - - xdw - application/vnd.fujixerox.docuworks - - - xenc - application/xenc+xml - - - xer - application/patch-ops-error+xml - - - xfdf - application/vnd.adobe.xfdf - - - xfdl - application/vnd.xfdl - - - xht - application/xhtml+xml - - - xhtml - application/xhtml+xml - - - xhvml - application/xv+xml - - - xif - image/vnd.xiff - - - xla - application/vnd.ms-excel - - - xlam - application/vnd.ms-excel.addin.macroenabled.12 - - - xlc - application/vnd.ms-excel - - - xlf - application/x-xliff+xml - - - xlm - application/vnd.ms-excel - - - xls - application/vnd.ms-excel - - - xlsb - application/vnd.ms-excel.sheet.binary.macroenabled.12 - - - xlsm - application/vnd.ms-excel.sheet.macroenabled.12 - - - xlsx - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - - - xlt - application/vnd.ms-excel - - - xltm - application/vnd.ms-excel.template.macroenabled.12 - - - xltx - application/vnd.openxmlformats-officedocument.spreadsheetml.template - - - xlw - application/vnd.ms-excel - - - xm - audio/xm - - - xml - application/xml - - - xo - application/vnd.olpc-sugar - - - xop - application/xop+xml - - - xpi - application/x-xpinstall - - - xpl - application/xproc+xml - - - xpm - image/x-xpixmap - - - xpr - application/vnd.is-xpr - - - xps - application/vnd.ms-xpsdocument - - - xpw - application/vnd.intercon.formnet - - - xpx - application/vnd.intercon.formnet - - - xsl - application/xml - - - xslt - application/xslt+xml - - - xsm - application/vnd.syncml+xml - - - xspf - application/xspf+xml - - - xul - application/vnd.mozilla.xul+xml - - - xvm - application/xv+xml - - - xvml - application/xv+xml - - - xwd - image/x-xwindowdump - - - xyz - chemical/x-xyz - - - xz - application/x-xz - - - yang - application/yang - - - yin - application/yin+xml - - - z - application/x-compress - - - Z - application/x-compress - - - z1 - application/x-zmachine - - - z2 - application/x-zmachine - - - z3 - application/x-zmachine - - - z4 - application/x-zmachine - - - z5 - application/x-zmachine - - - z6 - application/x-zmachine - - - z7 - application/x-zmachine - - - z8 - application/x-zmachine - - - zaz - application/vnd.zzazz.deck+xml - - - zip - application/zip - - - zir - application/vnd.zul - - - zirz - application/vnd.zul - - - zmm - application/vnd.handheld-entertainment+xml - + + sid + image/x-mrsid-image + + + sig + application/pgp-signature + + + sil + audio/silk + + + silo + model/mesh + + + sis + application/vnd.symbian.install + + + sisx + application/vnd.symbian.install + + + sit + application/x-stuffit + + + sitx + application/x-stuffitx + + + skd + application/vnd.koan + + + skm + application/vnd.koan + + + skp + application/vnd.koan + + + skt + application/vnd.koan + + + sldm + application/vnd.ms-powerpoint.slide.macroenabled.12 + + + sldx + application/vnd.openxmlformats-officedocument.presentationml.slide + + + slt + application/vnd.epson.salt + + + sm + application/vnd.stepmania.stepchart + + + smf + application/vnd.stardivision.math + + + smi + application/smil+xml + + + smil + application/smil+xml + + + smv + video/x-smv + + + smzip + application/vnd.stepmania.package + + + snd + audio/basic + + + snf + application/x-font-snf + + + so + application/octet-stream + + + spc + application/x-pkcs7-certificates + + + spf + application/vnd.yamaha.smaf-phrase + + + spl + application/x-futuresplash + + + spot + text/vnd.in3d.spot + + + spp + application/scvp-vp-response + + + spq + application/scvp-vp-request + + + spx + audio/ogg + + + sql + application/x-sql + + + src + application/x-wais-source + + + srt + application/x-subrip + + + sru + application/sru+xml + + + srx + application/sparql-results+xml + + + ssdl + application/ssdl+xml + + + sse + application/vnd.kodak-descriptor + + + ssf + application/vnd.epson.ssf + + + ssml + application/ssml+xml + + + st + application/vnd.sailingtracker.track + + + stc + application/vnd.sun.xml.calc.template + + + std + application/vnd.sun.xml.draw.template + + + stf + application/vnd.wt.stf + + + sti + application/vnd.sun.xml.impress.template + + + stk + application/hyperstudio + + + stl + application/vnd.ms-pki.stl + + + str + application/vnd.pg.format + + + stw + application/vnd.sun.xml.writer.template + + + sub + text/vnd.dvb.subtitle + + + sus + application/vnd.sus-calendar + + + susp + application/vnd.sus-calendar + + + sv4cpio + application/x-sv4cpio + + + sv4crc + application/x-sv4crc + + + svc + application/vnd.dvb.service + + + svd + application/vnd.svd + + + svg + image/svg+xml + + + svgz + image/svg+xml + + + swa + application/x-director + + + swf + application/x-shockwave-flash + + + swi + application/vnd.aristanetworks.swi + + + sxc + application/vnd.sun.xml.calc + + + sxd + application/vnd.sun.xml.draw + + + sxg + application/vnd.sun.xml.writer.global + + + sxi + application/vnd.sun.xml.impress + + + sxm + application/vnd.sun.xml.math + + + sxw + application/vnd.sun.xml.writer + + + t + text/troff + + + t3 + application/x-t3vm-image + + + taglet + application/vnd.mynfc + + + tao + application/vnd.tao.intent-module-archive + + + tar + application/x-tar + + + tcap + application/vnd.3gpp2.tcap + + + tcl + application/x-tcl + + + teacher + application/vnd.smart.teacher + + + tei + application/tei+xml + + + teicorpus + application/tei+xml + + + tex + application/x-tex + + + texi + application/x-texinfo + + + texinfo + application/x-texinfo + + + text + text/plain + + + tfi + application/thraud+xml + + + tfm + application/x-tex-tfm + + + tga + image/x-tga + + + thmx + application/vnd.ms-officetheme + + + tif + image/tiff + + + tiff + image/tiff + + + tmo + application/vnd.tmobile-livetv + + + torrent + application/x-bittorrent + + + tpl + application/vnd.groove-tool-template + + + tpt + application/vnd.trid.tpt + + + tr + text/troff + + + tra + application/vnd.trueapp + + + trm + application/x-msterminal + + + tsd + application/timestamped-data + + + tsv + text/tab-separated-values + + + ttc + font/collection + + + ttf + font/ttf + + + ttl + text/turtle + + + twd + application/vnd.simtech-mindmapper + + + twds + application/vnd.simtech-mindmapper + + + txd + application/vnd.genomatix.tuxedo + + + txf + application/vnd.mobius.txf + + + txt + text/plain + + + u32 + application/x-authorware-bin + + + udeb + application/x-debian-package + + + ufd + application/vnd.ufdl + + + ufdl + application/vnd.ufdl + + + ulw + audio/basic + + + ulx + application/x-glulx + + + umj + application/vnd.umajin + + + unityweb + application/vnd.unity + + + uoml + application/vnd.uoml+xml + + + uri + text/uri-list + + + uris + text/uri-list + + + urls + text/uri-list + + + ustar + application/x-ustar + + + utz + application/vnd.uiq.theme + + + uu + text/x-uuencode + + + uva + audio/vnd.dece.audio + + + uvd + application/vnd.dece.data + + + uvf + application/vnd.dece.data + + + uvg + image/vnd.dece.graphic + + + uvh + video/vnd.dece.hd + + + uvi + image/vnd.dece.graphic + + + uvm + video/vnd.dece.mobile + + + uvp + video/vnd.dece.pd + + + uvs + video/vnd.dece.sd + + + uvt + application/vnd.dece.ttml+xml + + + uvu + video/vnd.uvvu.mp4 + + + uvv + video/vnd.dece.video + + + uvva + audio/vnd.dece.audio + + + uvvd + application/vnd.dece.data + + + uvvf + application/vnd.dece.data + + + uvvg + image/vnd.dece.graphic + + + uvvh + video/vnd.dece.hd + + + uvvi + image/vnd.dece.graphic + + + uvvm + video/vnd.dece.mobile + + + uvvp + video/vnd.dece.pd + + + uvvs + video/vnd.dece.sd + + + uvvt + application/vnd.dece.ttml+xml + + + uvvu + video/vnd.uvvu.mp4 + + + uvvv + video/vnd.dece.video + + + uvvx + application/vnd.dece.unspecified + + + uvvz + application/vnd.dece.zip + + + uvx + application/vnd.dece.unspecified + + + uvz + application/vnd.dece.zip + + + vcard + text/vcard + + + vcd + application/x-cdlink + + + vcf + text/x-vcard + + + vcg + application/vnd.groove-vcard + + + vcs + text/x-vcalendar + + + vcx + application/vnd.vcx + + + vis + application/vnd.visionary + + + viv + video/vnd.vivo + + + vob + video/x-ms-vob + + + vor + application/vnd.stardivision.writer + + + vox + application/x-authorware-bin + + + vrml + model/vrml + + + vsd + application/vnd.visio + + + vsf + application/vnd.vsf + + + vss + application/vnd.visio + + + vst + application/vnd.visio + + + vsw + application/vnd.visio + + + vtu + model/vnd.vtu + + + vxml + application/voicexml+xml + + + w3d + application/x-director + + + wad + application/x-doom + + + wav + audio/x-wav + + + wax + audio/x-ms-wax + + + + wbmp + image/vnd.wap.wbmp + + + wbs + application/vnd.criticaltools.wbs+xml + + + wbxml + application/vnd.wap.wbxml + + + wcm + application/vnd.ms-works + + + wdb + application/vnd.ms-works + + + wdp + image/vnd.ms-photo + + + weba + audio/webm + + + webm + video/webm + + + webp + image/webp + + + wg + application/vnd.pmi.widget + + + wgt + application/widget + + + wks + application/vnd.ms-works + + + wm + video/x-ms-wm + + + wma + audio/x-ms-wma + + + wmd + application/x-ms-wmd + + + wmf + application/x-msmetafile + + + + wml + text/vnd.wap.wml + + + + wmlc + application/vnd.wap.wmlc + + + + wmls + text/vnd.wap.wmlscript + + + + wmlsc + application/vnd.wap.wmlscriptc + + + wmv + video/x-ms-wmv + + + wmx + video/x-ms-wmx + + + wmz + application/x-msmetafile + + + woff + font/woff + + + woff2 + font/woff2 + + + wpd + application/vnd.wordperfect + + + wpl + application/vnd.ms-wpl + + + wps + application/vnd.ms-works + + + wqd + application/vnd.wqd + + + wri + application/x-mswrite + + + wrl + model/vrml + + + wsdl + application/wsdl+xml + + + wspolicy + application/wspolicy+xml + + + wtb + application/vnd.webturbo + + + wvx + video/x-ms-wvx + + + x32 + application/x-authorware-bin + + + x3d + model/x3d+xml + + + x3db + model/x3d+binary + + + x3dbz + model/x3d+binary + + + x3dv + model/x3d+vrml + + + x3dvz + model/x3d+vrml + + + x3dz + model/x3d+xml + + + xaml + application/xaml+xml + + + xap + application/x-silverlight-app + + + xar + application/vnd.xara + + + xbap + application/x-ms-xbap + + + xbd + application/vnd.fujixerox.docuworks.binder + + + xbm + image/x-xbitmap + + + xdf + application/xcap-diff+xml + + + xdm + application/vnd.syncml.dm+xml + + + xdp + application/vnd.adobe.xdp+xml + + + xdssc + application/dssc+xml + + + xdw + application/vnd.fujixerox.docuworks + + + xenc + application/xenc+xml + + + xer + application/patch-ops-error+xml + + + xfdf + application/vnd.adobe.xfdf + + + xfdl + application/vnd.xfdl + + + xht + application/xhtml+xml + + + xhtml + application/xhtml+xml + + + xhvml + application/xv+xml + + + xif + image/vnd.xiff + + + xla + application/vnd.ms-excel + + + xlam + application/vnd.ms-excel.addin.macroenabled.12 + + + xlc + application/vnd.ms-excel + + + xlf + application/x-xliff+xml + + + xlm + application/vnd.ms-excel + + + xls + application/vnd.ms-excel + + + xlsb + application/vnd.ms-excel.sheet.binary.macroenabled.12 + + + xlsm + application/vnd.ms-excel.sheet.macroenabled.12 + + + xlsx + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + + + xlt + application/vnd.ms-excel + + + xltm + application/vnd.ms-excel.template.macroenabled.12 + + + xltx + application/vnd.openxmlformats-officedocument.spreadsheetml.template + + + xlw + application/vnd.ms-excel + + + xm + audio/xm + + + xml + application/xml + + + xo + application/vnd.olpc-sugar + + + xop + application/xop+xml + + + xpi + application/x-xpinstall + + + xpl + application/xproc+xml + + + xpm + image/x-xpixmap + + + xpr + application/vnd.is-xpr + + + xps + application/vnd.ms-xpsdocument + + + xpw + application/vnd.intercon.formnet + + + xpx + application/vnd.intercon.formnet + + + xsl + application/xml + + + xslt + application/xslt+xml + + + xsm + application/vnd.syncml+xml + + + xspf + application/xspf+xml + + + xul + application/vnd.mozilla.xul+xml + + + xvm + application/xv+xml + + + xvml + application/xv+xml + + + xwd + image/x-xwindowdump + + + xyz + chemical/x-xyz + + + xz + application/x-xz + + + yang + application/yang + + + yin + application/yin+xml + + + z + application/x-compress + + + Z + application/x-compress + + + z1 + application/x-zmachine + + + z2 + application/x-zmachine + + + z3 + application/x-zmachine + + + z4 + application/x-zmachine + + + z5 + application/x-zmachine + + + z6 + application/x-zmachine + + + z7 + application/x-zmachine + + + z8 + application/x-zmachine + + + zaz + application/vnd.zzazz.deck+xml + + + zip + application/zip + + + zir + application/vnd.zul + + + zirz + application/vnd.zul + + + zmm + application/vnd.handheld-entertainment+xml + - - - - - - - - - - - - - + + + + + + + + + + + + + - - index.html - index.htm - index.jsp - + + index.html + index.htm + index.jsp + diff --git a/javatool-embedded-server/src/main/webapp/WEB-INF/web.xml b/javatool-embedded-server/src/main/webapp/WEB-INF/web.xml index 6adf32a5..974a088b 100644 --- a/javatool-embedded-server/src/main/webapp/WEB-INF/web.xml +++ b/javatool-embedded-server/src/main/webapp/WEB-INF/web.xml @@ -1,47 +1,47 @@ - javatool + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> + javatool - - - spring-servlet - org.springframework.web.servlet.DispatcherServlet - 1 - - contextConfigLocation - classpath:spring/spring-servlet.xml - - - - spring-servlet - / - - + + + spring-servlet + org.springframework.web.servlet.DispatcherServlet + 1 + + contextConfigLocation + classpath:spring/spring-servlet.xml + + + + spring-servlet + / + + - - - - encodingFilter - org.springframework.web.filter.CharacterEncodingFilter - - encoding - UTF-8 - - - forceEncoding - true - - - - encodingFilter - /* - REQUEST - FORWARD - - + + + + encodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + UTF-8 + + + forceEncoding + true + + + + encodingFilter + /* + REQUEST + FORWARD + + - - /views/jsp/index.jsp - + + /views/jsp/index.jsp + diff --git a/javatool-embedded-server/src/main/webapp/views/jsp/hello.jsp b/javatool-embedded-server/src/main/webapp/views/jsp/hello.jsp index 04f4cc37..774fc949 100644 --- a/javatool-embedded-server/src/main/webapp/views/jsp/hello.jsp +++ b/javatool-embedded-server/src/main/webapp/views/jsp/hello.jsp @@ -1,13 +1,13 @@ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <% - String path = request.getContextPath(); - String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; + String path = request.getContextPath(); + String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> - HelloController + HelloController

    ${message}

    diff --git a/javatool-embedded-server/src/main/webapp/views/jsp/index.jsp b/javatool-embedded-server/src/main/webapp/views/jsp/index.jsp index 1be779b8..763fee86 100644 --- a/javatool-embedded-server/src/main/webapp/views/jsp/index.jsp +++ b/javatool-embedded-server/src/main/webapp/views/jsp/index.jsp @@ -1,28 +1,28 @@ <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <% - String path = request.getContextPath(); - String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; + String path = request.getContextPath(); + String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> - javatool + javatool

    javatool

    <%out.print("Server Ip:" + basePath);%>

    -

    示例列表

    - +

    示例列表

    +
    diff --git a/javatool-examples/pom.xml b/javatool-examples/pom.xml index 3e2e06ed..fcca7c05 100644 --- a/javatool-examples/pom.xml +++ b/javatool-examples/pom.xml @@ -1,60 +1,60 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - javatool-examples - 1.0.0 - Java 工具使用示例 + io.github.dunwu + javatool-examples + 1.0.0 + Java 工具使用示例 - - UTF-8 - 1.8 - ${java.version} - ${java.version} - 0.4.8 - 1.2.3 - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 0.4.8 + 1.2.3 + - - - log4j - log4j - 1.2.17 - - - ch.qos.logback - logback-classic - ${logback.version} - - + + + log4j + log4j + 1.2.17 + + + ch.qos.logback + logback-classic + ${logback.version} + + - - - - io.github.dunwu - dunwu-dependencies - ${dunwu.version} - pom - import - - - + + + + io.github.dunwu + dunwu-dependencies + ${dunwu.version} + pom + import + + + - - ${artifactId} - - - true - src/main/resources - - logback.xml - log4j.xml - - - - + + ${artifactId} + + + true + src/main/resources + + logback.xml + log4j.xml + + + + diff --git a/javatool-examples/src/main/java/io/github/dunwu/javatool/ElasticDemo.java b/javatool-examples/src/main/java/io/github/dunwu/javatool/ElasticDemo.java index 5d5751fc..c72bf5da 100644 --- a/javatool-examples/src/main/java/io/github/dunwu/javatool/ElasticDemo.java +++ b/javatool-examples/src/main/java/io/github/dunwu/javatool/ElasticDemo.java @@ -15,33 +15,33 @@ */ public class ElasticDemo { - private static final Logger logger = LoggerFactory.getLogger(ElasticDemo.class); - - private static final org.apache.log4j.Logger log4jLog = org.apache.log4j.Logger.getLogger(ElasticDemo.class); - - private static volatile int index = 0; - - private static void sendLog4jLog() { - for (int i = 0; i < 100; i++) { - log4jLog.info(String.format("这是第 %d 条日志", ++index)); - } - } - - public static void main(String[] args) { - // sendLog4jLog(); - sendLogbackLog(); - } - - private static void sendLogbackLog() { - ExecutorService executorService = Executors.newFixedThreadPool(100); - for (int i = 0; i < 10000; i++) { - executorService.submit(new Runnable() { - @Override - public void run() { - logger.info("这是第 {} 条日志", ++index); - } - }); - } - } + private static final Logger logger = LoggerFactory.getLogger(ElasticDemo.class); + + private static final org.apache.log4j.Logger log4jLog = org.apache.log4j.Logger.getLogger(ElasticDemo.class); + + private static volatile int index = 0; + + public static void main(String[] args) { + // sendLog4jLog(); + sendLogbackLog(); + } + + private static void sendLogbackLog() { + ExecutorService executorService = Executors.newFixedThreadPool(100); + for (int i = 0; i < 10000; i++) { + executorService.submit(new Runnable() { + @Override + public void run() { + logger.info("这是第 {} 条日志", ++index); + } + }); + } + } + + private static void sendLog4jLog() { + for (int i = 0; i < 100; i++) { + log4jLog.info(String.format("这是第 %d 条日志", ++index)); + } + } } diff --git a/javatool-examples/src/main/resources/log4j.xml b/javatool-examples/src/main/resources/log4j.xml index 1bd36221..e60d4b99 100644 --- a/javatool-examples/src/main/resources/log4j.xml +++ b/javatool-examples/src/main/resources/log4j.xml @@ -3,53 +3,53 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javatool-examples/src/main/resources/logback.xml b/javatool-examples/src/main/resources/logback.xml index ad778724..2492bfb1 100644 --- a/javatool-examples/src/main/resources/logback.xml +++ b/javatool-examples/src/main/resources/logback.xml @@ -3,64 +3,64 @@ - + - - - - %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n - - + + + + %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n + + - - - - - ${user.dir}/logs/${FILE_NAME}-all.%d{yyyy-MM-dd}.log - 30 - + + + + + ${user.dir}/logs/${FILE_NAME}-all.%d{yyyy-MM-dd}.log + 30 + - - - 30MB - + + + 30MB + - - %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n - - - - 192.168.28.32 - 9250 - - - - 192.168.28.32:9251 - 5 minutes - 6 second - 16384 - - {"appname":"metis"} - {"subappname":"metis-vdisk"} - - + + %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n + + + + 192.168.28.32 + 9250 + + + + 192.168.28.32:9251 + 5 minutes + 6 second + 16384 + + {"appname":"metis"} + {"subappname":"metis-vdisk"} + + - + - - - - - - - + + + + + + + - - - - + + + + diff --git a/pom.xml b/pom.xml index d99d1233..991d7bb7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,27 +1,27 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0"> + 4.0.0 - io.github.dunwu - java-tutorial - 1.0.0 - pom + io.github.dunwu + java-tutorial + 1.0.0 + pom - java-tutorial - Java 常用 Lib / Tool 使用示例 + java-tutorial + Java 常用 Lib / Tool 使用示例 - - javalib-bean - javalib-cli - javalib-io - javalib-io-binary - javalib-io-json - javalib-log - javalib-mvel - javalib-office - javalib-test - javatool-embedded-server - + + javalib-bean + javalib-cli + javalib-io + javalib-io-binary + javalib-io-json + javalib-log + javalib-mvel + javalib-text + javalib-test + javatool-embedded-server + diff --git a/prettier.config.js b/prettier.config.js index eb6bb1f5..a6594e50 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -3,5 +3,5 @@ * @see https://prettier.io/docs/en/configuration.html */ module.exports = { - tabWidth: 2, semi: false, singleQuote: true + tabWidth: 2, semi: false, singleQuote: true } diff --git a/settings/codestyle/google-codestyle.xml b/settings/codestyle/google-codestyle.xml index acb627b5..f3a6743e 100644 --- a/settings/codestyle/google-codestyle.xml +++ b/settings/codestyle/google-codestyle.xml @@ -1,598 +1,598 @@ - - - - + + + diff --git a/settings/codestyle/square-codestyle.xml b/settings/codestyle/square-codestyle.xml index f91a85d6..f7becd5d 100644 --- a/settings/codestyle/square-codestyle.xml +++ b/settings/codestyle/square-codestyle.xml @@ -1,318 +1,318 @@ - - - - + + +