diff --git a/src/META-INF/application-template.xml b/src/META-INF/application-template.xml index a9310c33a..2fefa626b 100644 --- a/src/META-INF/application-template.xml +++ b/src/META-INF/application-template.xml @@ -133,7 +133,7 @@ --> diff --git a/src/org/redkale/boot/NodeHttpServer.java b/src/org/redkale/boot/NodeHttpServer.java index 93f2dff86..6e2f8aa98 100644 --- a/src/org/redkale/boot/NodeHttpServer.java +++ b/src/org/redkale/boot/NodeHttpServer.java @@ -172,7 +172,6 @@ public class NodeHttpServer extends NodeServer { final ClassFilter restFilter = ClassFilter.create(restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues); super.interceptorServiceWrappers.forEach((wrapper) -> { - if (!wrapper.getName().isEmpty()) return; //只加载resourceName为空的service final Class stype = wrapper.getType(); RestService rs = (RestService) stype.getAnnotation(RestService.class); if (rs != null && rs.ignore()) return; @@ -183,17 +182,8 @@ public class NodeHttpServer extends NodeServer { if (!autoload && !includeValues.contains(stypename)) return; if (!restFilter.accept(stypename)) return; - RestHttpServlet servlet = Rest.createRestServlet(baseServletClass, wrapper.getName(), stype, sncp); - if (servlet == null) return; + RestHttpServlet servlet = httpServer.addRestServlet(stype, wrapper.getName(), wrapper.getService(), baseServletClass, prefix, (AnyValue) null); if (finest) logger.finest("Create RestServlet = " + servlet); - try { - Field serviceField = servlet.getClass().getDeclaredField("_service"); - serviceField.setAccessible(true); - serviceField.set(servlet, wrapper.getService()); - } catch (Exception e) { - throw new RuntimeException(wrapper.getType() + " generate rest servlet error", e); - } - httpServer.addHttpServlet(servlet, prefix, (AnyValue) null); if (ss != null) { String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); for (int i = 0; i < mappings.length; i++) { diff --git a/src/org/redkale/net/http/HttpPrepareServlet.java b/src/org/redkale/net/http/HttpPrepareServlet.java index 45640f5cd..17c753812 100644 --- a/src/org/redkale/net/http/HttpPrepareServlet.java +++ b/src/org/redkale/net/http/HttpPrepareServlet.java @@ -107,7 +107,7 @@ public final class HttpPrepareServlet extends PrepareServlet getServlets() { + return this.servlets; + } + @Override public void destroy(HttpContext context, AnyValue config) { this.resourceHttpServlet.destroy(context, config); diff --git a/src/org/redkale/net/http/HttpServer.java b/src/org/redkale/net/http/HttpServer.java index 18f83b257..4589a5f2d 100644 --- a/src/org/redkale/net/http/HttpServer.java +++ b/src/org/redkale/net/http/HttpServer.java @@ -5,11 +5,13 @@ */ package org.redkale.net.http; +import java.lang.reflect.Field; import java.net.HttpCookie; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.atomic.AtomicLong; import org.redkale.net.*; +import org.redkale.service.Service; import org.redkale.util.*; import org.redkale.watch.WatchFactory; @@ -39,6 +41,48 @@ public final class HttpServer extends Server RestHttpServlet addRestServlet(Class serviceType, + final String name, final S service, final Class baseServletClass, final String prefix, AnyValue conf) { + RestHttpServlet servlet = null; + for (final HttpServlet item : ((HttpPrepareServlet) this.prepare).getServlets()) { + if (!(item instanceof RestHttpServlet)) continue; + try { + Field field = item.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME); + if (serviceType.equals(field.getType())) { + servlet = (RestHttpServlet) item; + break; + } + } catch (NoSuchFieldException | SecurityException e) { + continue; + } + } + if (servlet == null) servlet = Rest.createRestServlet(baseServletClass, serviceType, false); + try { //若提供动态变更Service服务功能,则改Rest服务无法做出相应更新 + Field field = servlet.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME); + field.setAccessible(true); + + Field mapfield = servlet.getClass().getDeclaredField(Rest.REST_SERVICEMAP_FIELD_NAME); + mapfield.setAccessible(true); + + Service firstService = (Service) field.get(servlet); + if (name.isEmpty()) { + field.set(servlet, service); + firstService = service; + } + Map map = (Map) mapfield.get(servlet); + if (map == null && !name.isEmpty()) map = new HashMap(); + if (map != null) { + map.put(name, service); + if (firstService != null) map.put("", firstService); + } + mapfield.set(servlet, map); + } catch (Exception e) { + throw new RuntimeException(serviceType + " generate rest servlet error", e); + } + this.prepare.addServlet(servlet, prefix, conf); + return servlet; + } + @Override @SuppressWarnings("unchecked") protected HttpContext createContext() { diff --git a/src/org/redkale/net/http/Rest.java b/src/org/redkale/net/http/Rest.java index 2d3e5e9fd..dc4fc3b2c 100644 --- a/src/org/redkale/net/http/Rest.java +++ b/src/org/redkale/net/http/Rest.java @@ -27,6 +27,12 @@ import org.redkale.source.Flipper; */ public final class Rest { + public static final String REST_HEADER_RESOURCE_NAME = "rest-resource-name"; + + static final String REST_SERVICE_FIELD_NAME = "_service"; + + static final String REST_SERVICEMAP_FIELD_NAME = "_servicemap"; //如果只有name=""的Service资源,则实例中_servicemap必须为null + private static final Set EXCLUDERMETHODS = new HashSet<>(); static { @@ -38,14 +44,14 @@ public final class Rest { private Rest() { } - public static String getWebModuleName(Class serviceType) { + static String getWebModuleName(Class serviceType) { final RestService controller = serviceType.getAnnotation(RestService.class); if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); if (controller.ignore()) return null; return (!controller.value().isEmpty()) ? controller.value() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); } - public static T createRestServlet(final Class baseServletClass, final String serviceName, final Class serviceType, final boolean sncp) { + static T createRestServlet(final Class baseServletClass, final Class serviceType, final boolean sncp) { if (baseServletClass == null || serviceType == null) return null; if (!RestHttpServlet.class.isAssignableFrom(baseServletClass)) return null; int mod = baseServletClass.getModifiers(); @@ -66,13 +72,6 @@ public final class Rest { if (controller != null && controller.ignore()) return null; //标记为ignore=true不创建Servlet ClassLoader loader = Sncp.class.getClassLoader(); String newDynName = serviceTypeString.substring(0, serviceTypeString.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet"; - if (!serviceName.isEmpty()) { - boolean normal = true; - for (char ch : serviceName.toCharArray()) {//含特殊字符的使用hash值 - if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) normal = false; - } - newDynName += "_" + (normal ? serviceName : Sncp.hash(serviceName)); - } try { return ((Class) Class.forName(newDynName.replace('/', '.'))).newInstance(); } catch (Exception ex) { @@ -113,18 +112,21 @@ public final class Rest { av0.visitEnd(); classMap.put("type", serviceType.getName()); classMap.put("url", urlpath); - if (!serviceName.isEmpty()) classMap.put("resource", serviceName); classMap.put("moduleid", moduleid); classMap.put("repair", repair); } { //注入 @Resource private XXXService _service; - fv = cw.visitField(ACC_PRIVATE, "_service", serviceDesc, null, null); + fv = cw.visitField(ACC_PRIVATE, REST_SERVICE_FIELD_NAME, serviceDesc, null, null); av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true); - av0.visit("name", serviceName); + av0.visit("name", ""); av0.visitEnd(); fv.visitEnd(); } + { //_servicemap字段 Map + fv = cw.visitField(ACC_PRIVATE, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;", "Ljava/util/Map;", null); + fv.visitEnd(); + } { //构造函数 mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); //mv.setDebug(true); @@ -182,9 +184,34 @@ public final class Rest { av0 = mv.visitAnnotation(authDesc, true); av0.visitEnd(); } + + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;"); + Label lmapif = new Label(); + mv.visitJumpInsn(IFNONNULL, lmapif); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICE_FIELD_NAME, serviceDesc); + Label lserif = new Label(); + mv.visitJumpInsn(GOTO, lserif); + mv.visitLabel(lmapif); + + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;"); + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(REST_HEADER_RESOURCE_NAME); + mv.visitLdcInsn(""); + mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/HttpRequest", "getHeader", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); + mv.visitTypeInsn(CHECKCAST, serviceTypeString); + mv.visitLabel(lserif); + mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{serviceTypeString}); + mv.visitVarInsn(ASTORE, 3); + final int maxStack = 3 + params.length; List varInsns = new ArrayList<>(); - int maxLocals = 3; + int maxLocals = 4; boolean hasVisitWebAction = false; final String jsvar = entry.jsvar.isEmpty() ? null : entry.jsvar; int argIndex = 0; @@ -495,8 +522,9 @@ public final class Rest { actionMap.put("methods", entry.methods); } - mv.visitVarInsn(ALOAD, 0); //调用this - mv.visitFieldInsn(GETFIELD, newDynName, "_service", serviceDesc); + //mv.visitVarInsn(ALOAD, 0); //调用this + //mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICE_FIELD_NAME, serviceDesc); + mv.visitVarInsn(ALOAD, 3); for (int[] ins : varInsns) { mv.visitVarInsn(ins[0], ins[1]); } diff --git a/test/org/redkale/test/rest/HelloService.java b/test/org/redkale/test/rest/HelloService.java index ac3500e3a..e8078a6bd 100644 --- a/test/org/redkale/test/rest/HelloService.java +++ b/test/org/redkale/test/rest/HelloService.java @@ -19,9 +19,18 @@ import org.redkale.util.Sheet; */ public class HelloService implements Service { + private int nodeid; + @Resource private DataSource source; + public HelloService() { + } + + public HelloService(int nodeid) { + this.nodeid = nodeid; + } + //增加记录 public RetResult createHello(UserInfo info, HelloEntity entity) { entity.setCreator(info == null ? 0 : info.getUserid()); //设置当前用户ID @@ -37,7 +46,7 @@ public class HelloService implements Service { //修改记录 public void updateHello(@RestAddress String clientAddr, HelloEntity entity) { //通过 /hello/update?bean={...} 修改对象 - System.out.println("修改记录: clientAddr = " + clientAddr + ", entity =" + entity); + System.out.println("修改记录-" + nodeid + ": clientAddr = " + clientAddr + ", entity =" + entity); if (entity != null) entity.setUpdatetime(System.currentTimeMillis()); if (source != null) source.update(entity); } @@ -66,4 +75,5 @@ public class HelloService implements Service { public HelloEntity findHello(@RestParam("#") int id) { //通过 /hello/find/1234 查询对象 return source.find(HelloEntity.class, id); } + } diff --git a/test/org/redkale/test/rest/_DynHelloRestServlet1.java b/test/org/redkale/test/rest/_DynHelloRestServlet1.java index 2961e1f28..f0bc56c3d 100644 --- a/test/org/redkale/test/rest/_DynHelloRestServlet1.java +++ b/test/org/redkale/test/rest/_DynHelloRestServlet1.java @@ -1,7 +1,6 @@ package org.redkale.test.rest; import java.io.IOException; -import java.lang.reflect.Field; import java.util.*; import javax.annotation.Resource; import org.redkale.net.http.*; @@ -16,15 +15,16 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { @Resource private HelloService _service; + @Resource + private Map _servicemap; + public static void main(String[] args) throws Throwable { final int port = 8888; HelloService service = new HelloService(); HttpServer server = new HttpServer(); - RestHttpServlet servlet = Rest.createRestServlet(SimpleRestServlet.class, "", HelloService.class, false); - Field field = servlet.getClass().getDeclaredField("_service"); - field.setAccessible(true); - field.set(servlet, service); - server.addHttpServlet(servlet, "/pipes", null, "/hello/*"); + + server.addRestServlet(HelloService.class, "", service, SimpleRestServlet.class, "/pipes", null); + server.addRestServlet(HelloService.class, "my-res", new HelloService(3), SimpleRestServlet.class, "/pipes", null); DefaultAnyValue conf = DefaultAnyValue.create("port", "" + port); server.init(conf); @@ -35,6 +35,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { entity.setHelloname("my name"); Map headers = new HashMap<>(); headers.put("hello-res", "my res"); + //headers.put(Rest.REST_HEADER_RESOURCE_NAME, "my-res"); String url = "http://127.0.0.1:" + port + "/pipes/hello/update?entity={}&bean2={}"; System.out.println(Utility.postHttpContent(url, headers, null)); @@ -43,68 +44,76 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { @AuthIgnore @WebAction(url = "/hello/create") public void create(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); UserInfo user = currentUser(req); - RetResult result = _service.createHello(user, bean); + RetResult result = service.createHello(user, bean); resp.finishJson(result); } @AuthIgnore @WebAction(url = "/hello/delete/") public void delete(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath()); - _service.deleteHello(id); + service.deleteHello(id); resp.finishJson(RetResult.success()); } @AuthIgnore @WebAction(url = "/hello/update") public void update(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); String clientaddr = req.getRemoteAddr(); HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); - _service.updateHello(clientaddr, bean); + service.updateHello(clientaddr, bean); resp.finishJson(RetResult.success()); } @AuthIgnore @WebAction(url = "/hello/partupdate") public void partupdate(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); String[] cols = req.getJsonParameter(String[].class, "cols"); - _service.updateHello(bean, cols); + service.updateHello(bean, cols); resp.finishJson(RetResult.success()); } @AuthIgnore @WebAction(url = "/hello/query") public void query(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); Flipper flipper = req.getFlipper(); - Sheet result = _service.queryHello(bean, flipper); + Sheet result = service.queryHello(bean, flipper); resp.finishJson(result); } @AuthIgnore @WebAction(url = "/hello/list") public void list(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); - List result = _service.queryHello(bean); + List result = service.queryHello(bean); resp.finishJson(result); } @AuthIgnore @WebAction(url = "/hello/find/") public void find(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath()); - HelloEntity bean = _service.findHello(id); + HelloEntity bean = service.findHello(id); resp.finishJson(bean); } @AuthIgnore @WebAction(url = "/hello/jsfind/") public void jsfind(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath()); - HelloEntity bean = _service.findHello(id); + HelloEntity bean = service.findHello(id); resp.finishJsResult("varhello", bean); } } diff --git a/test/org/redkale/test/rest/_DynHelloRestServlet2.java b/test/org/redkale/test/rest/_DynHelloRestServlet2.java index 85e87e664..33de5bf98 100644 --- a/test/org/redkale/test/rest/_DynHelloRestServlet2.java +++ b/test/org/redkale/test/rest/_DynHelloRestServlet2.java @@ -6,6 +6,7 @@ package org.redkale.test.rest; import java.io.IOException; +import java.util.Map; import javax.annotation.Resource; import org.redkale.net.http.*; import org.redkale.service.RetResult; @@ -22,45 +23,53 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet { @Resource private HelloService2 _service; + @Resource + private Map _servicemap; + @AuthIgnore @WebAction(url = "/hello/create") public void create(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); UserInfo user = currentUser(req); - RetResult result = _service.createHello(user, bean); + RetResult result = service.createHello(user, bean); resp.finishJson(result); } @AuthIgnore @WebAction(url = "/hello/delete/") public void delete(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath()); - _service.deleteHello(id); + service.deleteHello(id); resp.finishJson(RetResult.success()); } @AuthIgnore @WebAction(url = "/hello/update") public void update(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); - _service.updateHello(bean); + service.updateHello(bean); resp.finishJson(RetResult.success()); } @AuthIgnore @WebAction(url = "/hello/query") public void query(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); Flipper flipper = req.getFlipper(); - Sheet result = _service.queryHello(bean, flipper); + Sheet result = service.queryHello(bean, flipper); resp.finishJson(result); } @AuthIgnore @WebAction(url = "/hello/find/") public void find(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath()); - HelloEntity bean = _service.findHello(id); + HelloEntity bean = service.findHello(id); resp.finishJson(bean); } }