diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d3f5f6309..724db0484 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,14 +1,18 @@ --- -name: Bug report -about: Create a report to help us improve - +name: πŸ› Bug Report +about: Report a general framework issue. help us improve this framework. --- -**Describe the bug** +- System Version (e.g. Mac Os 10.14.3): +- Build tools (e.g. maven/gradle): +- JDK Version (e.g. e.g `2.0.8-R1`): +- Database Version: + +### Describe the bug A clear and concise description of what the bug is. -**To Reproduce** +### To Reproduce Steps to reproduce the behavior: @@ -17,14 +21,14 @@ Steps to reproduce the behavior: 3. JDK version and blade version(e.g `2.0.8-R1`) 4. See error -**Expected behavior** +### Expected behavior: A clear and concise description of what you expected to happen. -**Screenshots** +### Screenshots: If applicable, add screenshots to help explain your problem. -**Additional context** +### Additional context: Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/documentation_issue.md b/.github/ISSUE_TEMPLATE/documentation_issue.md new file mode 100644 index 000000000..2a56a7d30 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_issue.md @@ -0,0 +1,9 @@ +--- +name: πŸ“š Documentation Issue +about: For documentation issues, open a pull request at https://github.com/lets-blade/lets-blade.github.io + +--- + +The blade documentation has its own dedicated repository. Please open a pull request at https://github.com/lets-blade/lets-blade.github.io to correct the issue you have found. + +Thanks! \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 65322c2e0..b2c7140d9 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,21 +1,21 @@ --- -name: Feature request -about: Suggest an idea for this project +name: ✨ Feature Request +about: For ideas or feature requests --- -**Is your feature request related to a problem? Please describe.** +### Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -**Describe the solution you'd like** +### Describe the solution you'd like A clear and concise description of what you want to happen. -**Describe alternatives you've considered** +### Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. -**Additional context** +### Additional context Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/general_question.md b/.github/ISSUE_TEMPLATE/general_question.md index bc4a2621a..df6ef6322 100644 --- a/.github/ISSUE_TEMPLATE/general_question.md +++ b/.github/ISSUE_TEMPLATE/general_question.md @@ -1,5 +1,5 @@ --- -name: General question +name: 🍻 General Question about: Template for asking question --- diff --git a/README.md b/README.md index d82604e88..efcfd2bc8 100644 --- a/README.md +++ b/README.md @@ -65,14 +65,14 @@ Run with `Maven`: com.bladejava blade-mvc - 2.0.15.ALPHA + 2.0.15.BETA ``` or `Gradle`: ```sh -compile 'com.bladejava:blade-mvc:2.0.15.ALPHA' +compile 'com.bladejava:blade-mvc:2.0.15.BETA' ``` Write the `main` method and the `Hello World`: diff --git a/README_CN.md b/README_CN.md index c694aee3e..e90fd8b86 100644 --- a/README_CN.md +++ b/README_CN.md @@ -61,7 +61,7 @@ com.bladejava blade-mvc - 2.0.15.ALPHA + 2.0.15.BETA ``` @@ -70,7 +70,7 @@ ζˆ–θ€… `Gradle`: ```sh -compile 'com.bladejava:blade-mvc:2.0.15.ALPHA' +compile 'com.bladejava:blade-mvc:2.0.15.BETA' ``` 编写 `main` 函数写一δΈͺ `Hello World`: diff --git a/pom.xml b/pom.xml index 989976ccb..9ba7ad0d9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bladejava blade-mvc - 2.0.15.ALPHA + 2.0.15.BETA jar blade diff --git a/src/main/java/com/blade/Blade.java b/src/main/java/com/blade/Blade.java index 11f6ad4e9..1dea071a4 100644 --- a/src/main/java/com/blade/Blade.java +++ b/src/main/java/com/blade/Blade.java @@ -24,6 +24,7 @@ import com.blade.kit.BladeKit; import com.blade.kit.JsonKit; import com.blade.kit.StringKit; +import com.blade.kit.UncheckedFnKit; import com.blade.kit.reload.FileChangeDetector; import com.blade.loader.BladeLoader; import com.blade.mvc.handler.*; @@ -832,8 +833,14 @@ public Blade watchEnvChange(boolean watchEnvChange) { * * @return return blade instance */ - public Blade start() { - return this.start(null, null); + public Blade start(String... args) { + Class caller = Arrays.stream(Thread.currentThread().getStackTrace()) + .filter(st -> "main".equals(st.getMethodName())) + .findFirst() + .map(StackTraceElement::getClassName) + .map(UncheckedFnKit.function(Class::forName)) + .orElse(null); + return this.start(caller, args); } /** diff --git a/src/main/java/com/blade/ioc/annotation/Bean.java b/src/main/java/com/blade/ioc/annotation/Bean.java index 6342e5602..4bbd238db 100644 --- a/src/main/java/com/blade/ioc/annotation/Bean.java +++ b/src/main/java/com/blade/ioc/annotation/Bean.java @@ -8,7 +8,7 @@ * @author biezhi * @since 1.5 */ -@Target(ElementType.TYPE) +@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Bean { diff --git a/src/main/java/com/blade/ioc/annotation/Configuration.java b/src/main/java/com/blade/ioc/annotation/Configuration.java new file mode 100644 index 000000000..e884ff055 --- /dev/null +++ b/src/main/java/com/blade/ioc/annotation/Configuration.java @@ -0,0 +1,15 @@ +package com.blade.ioc.annotation; + +import java.lang.annotation.*; + +/** + * @author zhangx + * @since 2.0.15 + **/ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Configuration { + + String name() default ""; +} diff --git a/src/main/java/com/blade/kit/ReflectKit.java b/src/main/java/com/blade/kit/ReflectKit.java index 4ca731ec3..32c80f6c7 100644 --- a/src/main/java/com/blade/kit/ReflectKit.java +++ b/src/main/java/com/blade/kit/ReflectKit.java @@ -147,6 +147,8 @@ public static Object convert(Type type, String value) { return DateKit.toLocalDate(value, "yyyy-MM-dd"); } else if (type.equals(LocalDateTime.class)) { return DateKit.toLocalDateTime(value, "yyyy-MM-dd HH:mm:ss"); + } else if (type instanceof Class && ((Class) type).isEnum()){ + return Enum.valueOf((Class)type,value); } return value; } diff --git a/src/main/java/com/blade/kit/UncheckedFnKit.java b/src/main/java/com/blade/kit/UncheckedFnKit.java new file mode 100644 index 000000000..4cf1dd079 --- /dev/null +++ b/src/main/java/com/blade/kit/UncheckedFnKit.java @@ -0,0 +1,90 @@ +package com.blade.kit; + +import com.blade.exception.BladeException; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * @author darren + * @date 2019/3/18 11:47 + */ +public class UncheckedFnKit { + + @FunctionalInterface + public interface Consumer_WithExceptions { + void accept(T t) throws E; + } + + @FunctionalInterface + public interface BiConsumer_WithExceptions { + void accept(T t, U u) throws E; + } + + @FunctionalInterface + public interface Function_WithExceptions { + R apply(T t) throws E; + } + + @FunctionalInterface + public interface Supplier_WithExceptions { + T get() throws E; + } + + @FunctionalInterface + public interface Runnable_WithExceptions { + void run() throws E; + } + + public static Consumer consumer(Consumer_WithExceptions consumer) { + return t -> { + try { + consumer.accept(t); + } catch (Throwable throwable) { + throw new BladeException(throwable); + } + }; + } + + public static BiConsumer biConsumer(BiConsumer_WithExceptions biConsumer) { + return (t, u) -> { + try { + biConsumer.accept(t, u); + } catch (Throwable throwable) { + throw new BladeException(throwable); + } + }; + } + + public static Function function(Function_WithExceptions function) { + return t -> { + try { + return function.apply(t); + } catch (Throwable throwable) { + throw new BladeException(throwable); + } + }; + } + + public static Supplier supplier(Supplier_WithExceptions function) { + return () -> { + try { + return function.get(); + } catch (Throwable throwable) { + throw new BladeException(throwable); + } + }; + } + + public static Runnable runnable(Runnable_WithExceptions t) { + return ()-> { + try { + t.run(); + } catch (Throwable throwable) { + throw new BladeException(throwable); + } + }; + } +} diff --git a/src/main/java/com/blade/kit/json/BeanSerializer.java b/src/main/java/com/blade/kit/json/BeanSerializer.java index 4ada657e7..af0419664 100755 --- a/src/main/java/com/blade/kit/json/BeanSerializer.java +++ b/src/main/java/com/blade/kit/json/BeanSerializer.java @@ -25,6 +25,10 @@ public static Object serialize(SerializeMapping serializeMapping, Object bean) t return bean.toString().replaceAll("\"", "\\\\\""); } + if (bean instanceof Enum){ + return bean.toString(); + } + if (ReflectKit.isBasicType(bean.getClass()) || bean instanceof Number || bean instanceof Date || bean instanceof LocalDate || bean instanceof LocalDateTime) { return bean; @@ -42,8 +46,9 @@ public static Object serialize(SerializeMapping serializeMapping, Object bean) t } if (bean instanceof Map) { - Map map = (Map) bean; - map.forEach((Object key, Object value) -> { + Map beanMap = (Map) bean; + Map map = new HashMap(beanMap.size()); + beanMap.forEach((Object key, Object value) -> { try { map.put(key, serialize(serializeMapping, value)); } catch (Exception e) { @@ -337,6 +342,8 @@ public static T deserialize(Class klass, Object object) { } if (ReflectKit.isBasicType(object)) { return (T) ReflectKit.convert(klass, object.toString()); + } else if (klass.isEnum()){ + return (T) Enum.valueOf((Class) klass,object.toString()); } else if (object instanceof Map) { if (Map.class.isAssignableFrom(klass)) { return klass.cast(object); diff --git a/src/main/java/com/blade/mvc/Const.java b/src/main/java/com/blade/mvc/Const.java index df2c118dd..a18ce3f21 100644 --- a/src/main/java/com/blade/mvc/Const.java +++ b/src/main/java/com/blade/mvc/Const.java @@ -31,7 +31,7 @@ public interface Const { int DEFAULT_SERVER_PORT = 9000; String DEFAULT_SERVER_ADDRESS = "0.0.0.0"; String LOCAL_IP_ADDRESS = "127.0.0.1"; - String VERSION = "2.0.15.ALPHA"; + String VERSION = "2.0.15.BETA"; String WEB_JARS = "/webjars/"; String CLASSPATH = BladeKit.getCurrentClassPath(); String CONTENT_TYPE_HTML = "text/html; charset=UTF-8"; diff --git a/src/main/java/com/blade/mvc/RouteContext.java b/src/main/java/com/blade/mvc/RouteContext.java index f4195a682..c2b99f8e8 100644 --- a/src/main/java/com/blade/mvc/RouteContext.java +++ b/src/main/java/com/blade/mvc/RouteContext.java @@ -577,24 +577,6 @@ public boolean isAbort() { public void initRoute(Route route) { this.request.initPathParams(route); this.route = route; -// if (null != route.getTarget() && route.getTargetType().equals(RouteHandler.class)) { -// return; -// } -// boolean singleton = IocKit.isSingleton(route.getTargetType()); -// -// if (singleton) { -// BeanDefine beanDefine = WebContext.blade().ioc().getBeanDefine(route.getTargetType()); -// if (beanDefine.isFieldHasPrototype()) { -// // reset initialize -// IocKit.injection(WebContext.blade().ioc(), beanDefine); -// } else { -// Object target = WebContext.blade().ioc().getBean(route.getTargetType()); -// this.route.setTarget(target); -// } -// } else { -// Object target = WebContext.blade().ioc().createBean(route.getTargetType()); -// this.route.setTarget(target); -// } } public void injectParameters() { diff --git a/src/main/java/com/blade/mvc/handler/RouteActionArguments.java b/src/main/java/com/blade/mvc/handler/RouteActionArguments.java index 60e7ff9e4..10013cf7d 100644 --- a/src/main/java/com/blade/mvc/handler/RouteActionArguments.java +++ b/src/main/java/com/blade/mvc/handler/RouteActionArguments.java @@ -166,7 +166,7 @@ private static Object getQueryParam(ParamStruct paramStruct) { if (ReflectKit.isBasicType(argType) || argType.equals(Date.class) || argType.equals(BigDecimal.class) || argType.equals(LocalDate.class) - || argType.equals(LocalDateTime.class)) { + || argType.equals(LocalDateTime.class) || (argType instanceof Class && ((Class) argType).isEnum())) { String value = request.query(name).orElseGet(() -> getDefaultValue(param.defaultValue(), argType)); diff --git a/src/main/java/com/blade/mvc/route/RouteMatcher.java b/src/main/java/com/blade/mvc/route/RouteMatcher.java index 7eae42945..1ece1476e 100644 --- a/src/main/java/com/blade/mvc/route/RouteMatcher.java +++ b/src/main/java/com/blade/mvc/route/RouteMatcher.java @@ -41,7 +41,7 @@ @Slf4j public class RouteMatcher { - private static final Pattern PATH_VARIABLE_PATTERN = Pattern.compile("/(([^:/]*):([^/]+))|(\\.\\*)"); + private static final Pattern PATH_VARIABLE_PATTERN = Pattern.compile("/(?:([^:/]*):([^/]+))|(\\.\\*)"); private static final String METHOD_NAME = "handle"; // Storage URL and route diff --git a/src/main/java/com/blade/server/netty/NettyServer.java b/src/main/java/com/blade/server/netty/NettyServer.java index 662d9e0cd..aea3736bc 100644 --- a/src/main/java/com/blade/server/netty/NettyServer.java +++ b/src/main/java/com/blade/server/netty/NettyServer.java @@ -23,6 +23,7 @@ import com.blade.ioc.DynamicContext; import com.blade.ioc.Ioc; import com.blade.ioc.annotation.Bean; +import com.blade.ioc.annotation.Configuration; import com.blade.ioc.annotation.Value; import com.blade.ioc.bean.BeanDefine; import com.blade.ioc.bean.ClassInfo; @@ -70,6 +71,7 @@ import java.io.File; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -301,7 +303,18 @@ private void parseAndCreate(Class clazz) { Object controller = blade.getBean(clazz); routeBuilder.addRouter(clazz, controller); } - + if (null != clazz.getAnnotation(Configuration.class) && clazz.getMethods().length > 0) { + Object config = ReflectKit.newInstance(clazz); + Arrays.stream(clazz.getMethods()) + .filter(m -> m.getAnnotation(Bean.class) != null) + .forEach(n -> { + try { + blade.register(n.invoke(config)); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } if (ReflectKit.hasInterface(clazz, WebHook.class) && null != clazz.getAnnotation(Bean.class)) { URLPattern URLPattern = clazz.getAnnotation(URLPattern.class); if (null == URLPattern) { diff --git a/src/main/java/com/blade/task/TaskManager.java b/src/main/java/com/blade/task/TaskManager.java index 14a28719f..08d2e9157 100644 --- a/src/main/java/com/blade/task/TaskManager.java +++ b/src/main/java/com/blade/task/TaskManager.java @@ -19,12 +19,10 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import lombok.var; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import static com.blade.kit.BladeKit.getStartedSymbol; @@ -41,6 +39,9 @@ public final class TaskManager { private final static Map TASK_MAP = new HashMap<>(8); + private final static ReentrantReadWriteLock rrw = new ReentrantReadWriteLock(); + private final static Lock readLock = rrw.readLock(); + private final static Lock writeLock = rrw.writeLock(); private static CronExecutorService cronExecutorService; @@ -57,21 +58,45 @@ public static CronExecutorService getExecutorService() { } public static void addTask(Task task) { - TASK_MAP.put(task.getName(), task); + writeLock.lock(); + try { + TASK_MAP.put(task.getName(), task); + } finally { + writeLock.unlock(); + } log.info("{}Add task [{}]", getStartedSymbol(), task.getName()); } public static List getTasks() { - return new ArrayList<>(TASK_MAP.values()); + Collection values; + readLock.lock(); + try { + values = Optional.ofNullable(TASK_MAP.values()).orElse(Collections.EMPTY_LIST); + } finally { + readLock.unlock(); + } + return new ArrayList<>(values); } public static Task getTask(String name) { - return TASK_MAP.get(name); + readLock.lock(); + try { + return TASK_MAP.get(name); + } finally { + readLock.unlock(); + } + } public static boolean stopTask(String name) { - var task = TASK_MAP.get(name); - return task.stop(); + Task task; + readLock.lock(); + try { + task = TASK_MAP.get(name); + } finally { + readLock.unlock(); + } + return task == null ? Boolean.FALSE : task.stop(); } } diff --git a/src/test/java/com/blade/task/TaskManagerTest.java b/src/test/java/com/blade/task/TaskManagerTest.java new file mode 100644 index 000000000..5fea19d00 --- /dev/null +++ b/src/test/java/com/blade/task/TaskManagerTest.java @@ -0,0 +1,30 @@ +package com.blade.task; + +import java.util.concurrent.CountDownLatch; +import java.util.stream.IntStream; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author PSH + * @date 2019/03/16 + */ +public class TaskManagerTest { + + @Test + public void testAddTaskMultiThreading() throws Exception { + + final int tackCount = 500; + CountDownLatch downLatch = new CountDownLatch(tackCount); + IntStream.range(0, tackCount).forEach(i -> { + Task task = new Task("task-" + i, null, Integer.MAX_VALUE); + new Thread(() -> { + TaskManager.addTask(task); + downLatch.countDown(); + }).start(); + }); + + downLatch.await(); + Assert.assertEquals(tackCount, TaskManager.getTasks().size()); + } +}