Skip to content

Commit 396ca48

Browse files
committed
support plugin 3rd party controller annotation system
1 parent b40ef03 commit 396ca48

9 files changed

Lines changed: 309 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# ActFramework Change Log
22

3+
**1.7.0**
4+
* Support plugin JAX-RS #448
5+
36
**1.6.2 TBD**
47
* Some view engine caused browser always loading when running in prod mode #447
58
* Param binding - it shall not try to get provider for simple types. #449

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<groupId>org.actframework</groupId>
2222
<artifactId>act</artifactId>
2323
<packaging>jar</packaging>
24-
<version>1.6.2-SNAPSHOT</version>
24+
<version>1.7.0-SNAPSHOT</version>
2525

2626
<name>ACT Framework</name>
2727
<description>The ACT full stack MVC framework</description>

src/main/java/act/controller/bytecode/ControllerByteCodeScanner.java

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import act.controller.Controller;
3333
import act.controller.annotation.Port;
3434
import act.controller.annotation.TemplateContext;
35-
import act.controller.annotation.UrlContext;
3635
import act.controller.meta.*;
3736
import act.handler.builtin.controller.RequestHandlerProxy;
3837
import act.route.RouteSource;
@@ -129,21 +128,22 @@ public void visit(int version, int access, String name, String signature, String
129128
@Override
130129
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
131130
AnnotationVisitor av = super.visitAnnotation(desc, visible);
132-
if (Type.getType(Controller.class).getDescriptor().equals(desc)) {
131+
Class<? extends Annotation> c = AsmType.classForDesc(desc);
132+
if (Controller.class == c) {
133133
classInfo.isController(true);
134134
return new ControllerAnnotationVisitor(av);
135-
} else if (Type.getType(UrlContext.class).getDescriptor().equals(desc)) {
135+
} else if (ControllerClassMetaInfo.isUrlContextAnnotation(c)) {
136136
classInfo.isController(true);
137-
return new UrlContextAnnotationVisitor(av);
138-
} else if (Type.getType(TemplateContext.class).getDescriptor().equals(desc)) {
137+
return new ClassUrlContextAnnotationVisitor(av, ControllerClassMetaInfo.isUrlContextAnnotationSupportInheritance(c));
138+
} else if (TemplateContext.class == c) {
139139
classInfo.isController(true);
140140
return new TemplateContextAnnotationVisitor(av);
141-
} else if (Type.getType(Port.class).getDescriptor().equals(desc)) {
141+
} else if (Port.class == c) {
142142
return new PortAnnotationVisitor(av);
143-
} else if (Type.getType(With.class).getDescriptor().equals(desc)) {
143+
} else if (With.class == c) {
144144
classInfo.isController(true);
145145
return new ClassWithAnnotationVisitor(av);
146-
} else if (Type.getType(WsEndpoint.class).getDescriptor().equals(desc)) {
146+
} else if (WsEndpoint.class == c) {
147147
classInfo.isController(true);
148148
return new WsEndpointAnnotationVisitor(av);
149149
}
@@ -262,15 +262,21 @@ public void visitEnd() {
262262
}
263263
}
264264

265-
private class UrlContextAnnotationVisitor extends AnnotationVisitor {
266-
UrlContextAnnotationVisitor(AnnotationVisitor av) {
265+
private class ClassUrlContextAnnotationVisitor extends AnnotationVisitor {
266+
private final boolean supportInheritance;
267+
ClassUrlContextAnnotationVisitor(AnnotationVisitor av, boolean supportInheritance) {
267268
super(ASM5, av);
269+
this.supportInheritance = supportInheritance;
268270
}
269271

270272
@Override
271273
public void visit(String name, Object value) {
272274
if ("value".equals(name)) {
273-
classInfo.urlContext(value.toString());
275+
String pathComponent = value.toString();
276+
if (!supportInheritance && !pathComponent.startsWith("/")) {
277+
pathComponent = "/" + pathComponent;
278+
}
279+
classInfo.urlContext(pathComponent);
274280
}
275281
super.visit(name, value);
276282
}
@@ -324,6 +330,7 @@ private class ActionMethodVisitor extends MethodVisitor implements Opcodes {
324330
private boolean disableJsonCircularRefDetect;
325331
private HandlerMethodMetaInfo methodInfo;
326332
private PropertySpec.MetaInfo propSpec;
333+
List<String> paths = C.newList();
327334
private Map<Integer, List<ParamAnnoInfoTrait>> paramAnnoInfoList = C.newMap();
328335
private Map<Integer, List<GeneralAnnoInfo>> genericParamAnnoInfoList = C.newMap();
329336
private BitSet contextInfo = new BitSet();
@@ -351,8 +358,7 @@ private class ActionMethodVisitor extends MethodVisitor implements Opcodes {
351358
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
352359
AnnotationVisitor av = super.visitAnnotation(desc, visible);
353360
Type type = Type.getType(desc);
354-
String className = type.getClassName();
355-
Class<? extends Annotation> c = $.classForName(className);
361+
Class<? extends Annotation> c = AsmType.classForType(type);
356362
if (Virtual.class.getName().equals(c.getName())) {
357363
isVirtual.set(true);
358364
return av;
@@ -375,7 +381,9 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
375381
methodInfo.propertySpec(propSpec);
376382
}
377383
methodInfo.disableJsonCircularRefDetect(disableJsonCircularRefDetect);
378-
return new ActionAnnotationVisitor(av, ControllerClassMetaInfo.lookupHttpMethod(c), ControllerClassMetaInfo.isActionUtilAnnotation(c), isStatic);
384+
return new ActionAnnotationVisitor(av, ControllerClassMetaInfo.lookupHttpMethod(c), ControllerClassMetaInfo.isActionUtilAnnotation(c), isStatic, ControllerClassMetaInfo.noDefPath(c));
385+
} else if (ControllerClassMetaInfo.isUrlContextAnnotation(c)) {
386+
return new MethodUrlContextAnnotationVisitor(av, ControllerClassMetaInfo.isUrlContextAnnotationSupportAbsolutePath(c));
379387
} else if (ControllerClassMetaInfo.isInterceptorAnnotation(c)) {
380388
checkMethodName(methodName);
381389
markRequireScan();
@@ -682,20 +690,42 @@ public void visitEnd() {
682690
}
683691
}
684692

693+
private class MethodUrlContextAnnotationVisitor extends AnnotationVisitor {
694+
private final boolean supportAbsolutePath;
695+
MethodUrlContextAnnotationVisitor(AnnotationVisitor av, boolean supportAbsolutePath) {
696+
super(ASM5, av);
697+
this.supportAbsolutePath = supportAbsolutePath;
698+
}
699+
700+
@Override
701+
public void visit(String name, Object value) {
702+
if ("value".equals(name)) {
703+
String pathComponent = value.toString();
704+
if (!supportAbsolutePath && pathComponent.startsWith("/")) {
705+
pathComponent = pathComponent.substring(1);
706+
}
707+
paths.add(pathComponent);
708+
}
709+
super.visit(name, value);
710+
}
711+
}
712+
713+
685714
private class ActionAnnotationVisitor extends AnnotationVisitor implements Opcodes {
686715

687716
List<H.Method> httpMethods = C.newList();
688-
List<String> paths = C.newList();
689717
boolean isUtil;
690718
boolean isStatic;
719+
boolean noDefPath;
691720

692-
public ActionAnnotationVisitor(AnnotationVisitor av, H.Method method, boolean isUtil, boolean staticMethod) {
721+
public ActionAnnotationVisitor(AnnotationVisitor av, H.Method method, boolean isUtil, boolean staticMethod, boolean noDefPath) {
693722
super(ASM5, av);
694723
if (null != method) {
695724
httpMethods.add(method);
696725
}
697726
this.isUtil = isUtil;
698727
this.isStatic = staticMethod;
728+
this.noDefPath = noDefPath;
699729
}
700730

701731
@Override
@@ -737,7 +767,7 @@ public void visitEnd() {
737767
httpMethods.addAll(H.Method.actionMethods());
738768
}
739769
final List<Router> routers = routers();
740-
if (paths.isEmpty()) {
770+
if (!noDefPath && paths.isEmpty()) {
741771
paths.add("");
742772
}
743773

src/main/java/act/controller/meta/ControllerClassMetaInfo.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
import act.app.App;
2525
import act.app.AppClassLoader;
2626
import act.asm.Type;
27+
import act.controller.annotation.UrlContext;
2728
import act.handler.builtin.controller.ControllerAction;
2829
import act.handler.builtin.controller.Handler;
30+
import act.plugin.ControllerPlugin;
2931
import act.util.ClassInfoRepository;
3032
import act.util.ClassNode;
3133
import act.util.DestroyableBase;
@@ -36,9 +38,7 @@
3638

3739
import javax.enterprise.context.ApplicationScoped;
3840
import java.lang.annotation.Annotation;
39-
import java.util.List;
40-
import java.util.Map;
41-
import java.util.Set;
41+
import java.util.*;
4242

4343
import static act.Destroyable.Util.destroyAll;
4444

@@ -479,12 +479,21 @@ private void buildHandlerLookup() {
479479
handlerLookup = lookup;
480480
}
481481

482+
public static void registerMethodLookups(Map<Class<? extends Annotation>, H.Method> annotationMethodLookup, boolean noDefaultPath) {
483+
METHOD_LOOKUP.putAll(annotationMethodLookup);
484+
if (noDefaultPath) {
485+
NO_DEF_PATH_ACTIONS.addAll(annotationMethodLookup.keySet());
486+
}
487+
}
488+
489+
public static void registerUrlContextAnnotation(ControllerPlugin.PathAnnotationSpec pathAnnotationInfo) {
490+
URL_CONTEXT_ANNOTATIONS.put(pathAnnotationInfo.annoType(), pathAnnotationInfo);
491+
}
492+
482493
private static final C.Set<Class<? extends Annotation>> INTERCEPTOR_ANNOTATION_TYPES = C.set(
483494
Before.class, After.class, Catch.class, Finally.class);
484495

485-
public static final C.Set<H.Method> ACTION_METHODS = C.set(H.Method.GET, H.Method.POST, H.Method.PUT, H.Method.DELETE);
486-
487-
private static final Map<Class<? extends Action>, H.Method> METHOD_LOOKUP = C.newMap(
496+
private static final Map<Class<? extends Annotation>, H.Method> METHOD_LOOKUP = C.newMap(
488497
GetAction.class, H.Method.GET,
489498
PostAction.class, H.Method.POST,
490499
PutAction.class, H.Method.PUT,
@@ -493,15 +502,35 @@ private void buildHandlerLookup() {
493502
WsAction.class, H.Method.GET
494503
);
495504

505+
private static final Set<Class<? extends Annotation>> NO_DEF_PATH_ACTIONS = new HashSet<>();
506+
507+
public static boolean noDefPath(Class<? extends Annotation> actionAnno) {
508+
return NO_DEF_PATH_ACTIONS.contains(actionAnno);
509+
}
510+
511+
// map annotype to support_absolute_path
512+
private static final Map<Class<? extends Annotation>, ControllerPlugin.PathAnnotationSpec> URL_CONTEXT_ANNOTATIONS = new HashMap<>();
513+
496514
public static boolean isActionAnnotation(Class<? extends Annotation> type) {
497515
return METHOD_LOOKUP.containsKey(type) || Action.class == type;
498516
}
499517

518+
public static boolean isUrlContextAnnotation(Class<? extends Annotation> anno) {
519+
return UrlContext.class == anno || URL_CONTEXT_ANNOTATIONS.containsKey(anno);
520+
}
521+
522+
public static boolean isUrlContextAnnotationSupportAbsolutePath(Class<? extends Annotation> anno) {
523+
return UrlContext.class == anno || URL_CONTEXT_ANNOTATIONS.get(anno).supportAbsolutePath();
524+
}
525+
526+
public static boolean isUrlContextAnnotationSupportInheritance(Class<? extends Annotation> anno) {
527+
return UrlContext.class == anno || URL_CONTEXT_ANNOTATIONS.get(anno).supportInheritance();
528+
}
529+
500530
public static H.Method lookupHttpMethod(Class annotationClass) {
501531
return METHOD_LOOKUP.get(annotationClass);
502532
}
503533

504-
505534
public static boolean isActionUtilAnnotation(Class<? extends Annotation> type) {
506535
return ActionUtil.class == type;
507536
}

src/main/java/act/handler/builtin/controller/impl/ReflectedHandlerInvoker.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import act.inject.param.ParamValueLoaderService;
4444
import act.job.AppJobManager;
4545
import act.job.TrackableWorker;
46+
import act.plugin.ControllerPlugin;
4647
import act.security.CORS;
4748
import act.security.CSRF;
4849
import act.security.CSP;
@@ -133,6 +134,8 @@ public void visit(H.Format format) throws Osgl.Break {
133134
private boolean forceDataBinding;
134135
private Class<? extends SerializeFilter> filters[];
135136
private SerializerFeature features[];
137+
private $.Function<ActionContext, Result> pluginBeforeHandler;
138+
private $.Func2<Result, ActionContext, Result> pluginAfterHandler;
136139

137140
private ReflectedHandlerInvoker(M handlerMetaInfo, App app) {
138141
this.app = app;
@@ -150,6 +153,8 @@ private ReflectedHandlerInvoker(M handlerMetaInfo, App app) {
150153
} catch (NoSuchMethodException e) {
151154
throw E.unexpected(e);
152155
}
156+
this.pluginBeforeHandler = ControllerPlugin.Manager.INST.beforeHandler(controllerClass, method);
157+
this.pluginAfterHandler = ControllerPlugin.Manager.INST.afterHandler(controllerClass, method);
153158
this.disabled = this.disabled || !Env.matches(method);
154159
this.forceDataBinding = method.isAnnotationPresent(RequireDataBind.class);
155160
this.async = null != method.getAnnotation(Async.class);
@@ -329,6 +334,11 @@ public Result handle(final ActionContext context) throws Exception {
329334
}
330335
}
331336

337+
Result result = pluginBeforeHandler.apply(context);
338+
if (null != result) {
339+
return result;
340+
}
341+
332342
if (null != filters) {
333343
context.fastjsonFilters(filters);
334344
}
@@ -404,7 +414,7 @@ protected void run(ProgressGauge progressGauge) {
404414
}
405415

406416
try {
407-
return invoke(handler, context, controller, params);
417+
return pluginAfterHandler.apply(invoke(handler, context, controller, params), context);
408418
} finally {
409419
if (hasOutputVar) {
410420
fillOutputVariables(controller, params, context);

0 commit comments

Comments
 (0)