67 Commits

Author SHA1 Message Date
Redkale
b0ab792f72 2017-06-24 22:45:19 +08:00
Redkale
0df9a940c5 2017-06-24 16:24:13 +08:00
Redkale
fc35fc5abc 2017-06-24 15:26:52 +08:00
Redkale
eaa0a99933 2017-06-24 12:33:48 +08:00
Redkale
83bdb97842 2017-06-24 12:31:20 +08:00
Redkale
a71a4d0fed 2017-06-24 10:47:41 +08:00
Redkale
835435c220 2017-06-24 10:38:41 +08:00
Redkale
c524ba1797 2017-06-24 10:32:00 +08:00
Redkale
f254b48693 2017-06-24 10:09:26 +08:00
Redkale
922697eb4d 2017-06-22 14:37:42 +08:00
Redkale
450e3e3ea2 2017-06-22 09:42:24 +08:00
Redkale
b31f75f4f6 2017-06-22 00:54:51 +08:00
Redkale
1d1f18b046 2017-06-21 23:05:40 +08:00
Redkale
73f942746b 2017-06-21 17:35:18 +08:00
Redkale
b6cefe8c2d 2017-06-21 17:34:08 +08:00
Redkale
ebc0e4eb41 2017-06-21 17:32:22 +08:00
Redkale
179a7b22ea 2017-06-21 16:27:18 +08:00
Redkale
0b87d9a261 2017-06-21 16:10:15 +08:00
Redkale
685a686ead 2017-06-20 21:58:20 +08:00
Redkale
568e1cf62d 2017-06-20 21:57:29 +08:00
Redkale
1d121bd2ab 2017-06-20 13:56:45 +08:00
Redkale
2ca4bdaaec 2017-06-19 13:46:32 +08:00
Redkale
878fda30f6 2017-06-19 13:45:16 +08:00
Redkale
abb611382c 2017-06-19 13:41:43 +08:00
Redkale
b53510a26f 2017-06-19 10:07:59 +08:00
Redkale
2aee84d477 2017-06-19 09:53:29 +08:00
Redkale
0ba2e25f2e 2017-06-19 08:20:52 +08:00
Redkale
98e8a7eb05 2017-06-18 22:31:13 +08:00
Redkale
62139efca9 2017-06-18 21:58:28 +08:00
Redkale
2b62cbe455 2017-06-18 21:51:20 +08:00
Redkale
e44602fe3b 2017-06-15 23:27:56 +08:00
Redkale
276cb4da92 2017-06-15 08:08:38 +08:00
Redkale
7081f94afc 2017-06-12 14:43:32 +08:00
Redkale
2d6cefeb43 2017-06-07 09:38:57 +08:00
Redkale
ea6c703ac6 2017-06-07 07:02:16 +08:00
Redkale
d1f14962fd 2017-06-07 06:59:51 +08:00
Redkale
79ca63bf81 2017-06-06 09:44:51 +08:00
Redkale
6421bc2851 2017-06-06 09:43:31 +08:00
Redkale
43f9f50f4c 2017-06-06 09:35:41 +08:00
Redkale
5f140a8ce9 2017-06-06 09:31:27 +08:00
Redkale
c0f8cdf902 2017-06-06 09:30:46 +08:00
Redkale
8d66b1b4a7 2017-06-06 08:35:57 +08:00
Redkale
8c7ee4136c 2017-06-06 08:20:47 +08:00
Redkale
d9498c9a6c 2017-06-05 16:09:06 +08:00
Redkale
08060a8c86 2017-06-05 09:18:05 +08:00
Redkale
2bdf0e4a50 2017-06-05 08:55:37 +08:00
Redkale
eb184df100 2017-06-04 11:30:07 +08:00
Redkale
cf545a731c 2017-06-04 08:03:03 +08:00
Redkale
95e18dfd48 2017-06-04 08:00:13 +08:00
Redkale
023a9abdef 2017-06-03 22:32:47 +08:00
Redkale
7f3776c224 2017-06-03 22:12:29 +08:00
Redkale
7b15ba33e0 2017-06-03 22:05:53 +08:00
Redkale
a6c105d63d 2017-06-03 18:41:08 +08:00
Redkale
10e22b0873 2017-06-03 16:50:40 +08:00
Redkale
53a35e6397 2017-06-03 10:20:36 +08:00
Redkale
b5a3c39f4f 2017-06-03 09:51:53 +08:00
Redkale
99381d4842 2017-06-02 15:24:53 +08:00
Redkale
5f2c2a9f2c 2017-06-02 14:00:14 +08:00
Redkale
dc3f318949 2017-06-02 09:46:58 +08:00
Redkale
cfae61faea 2017-06-01 20:06:46 +08:00
Redkale
dd7626b1a3 2017-06-01 15:21:28 +08:00
Redkale
2171aa1232 2017-06-01 13:23:35 +08:00
Redkale
801ad489d2 2017-06-01 11:38:49 +08:00
Redkale
c88d0b402d 2017-05-31 21:19:51 +08:00
Redkale
542bb4353b 2017-05-31 14:07:21 +08:00
Redkale
fe1e0a845a 2017-05-31 13:41:10 +08:00
Redkale
f320f4c550 2017-05-31 12:17:10 +08:00
84 changed files with 1306 additions and 1245 deletions

View File

@@ -16,6 +16,10 @@
<directory>${project.basedir}/conf</directory>
<outputDirectory>conf</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.basedir}/libs</directory>
<outputDirectory>libs</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.basedir}/logs</directory>
<outputDirectory>logs</outputDirectory>

View File

@@ -5,3 +5,4 @@ SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
java -DAPP_HOME=%APP_HOME% -classpath %APP_HOME%\lib\* org.redkale.boot.Application

View File

@@ -5,19 +5,16 @@
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->
<resources>
<!--
<properties>
<property name="system.property.convert.json.tiny" value="true"/>
</properties>
-->
</resources>
<server protocol="HTTP" host="0.0.0.0" port="6060" root="root">
<services autoload="true"/>
<!-- base指定的自定义HttpServlet子类必须标记@HttpUserType, 不设置base则视为没有当前用户信息设置 -->
<rest path="/pipes" base="org.redkale.net.http.HttpServlet"/>
<filters autoload="true"/>
<rest path="/pipes" /> <!-- base指定的自定义HttpServlet子类必须标记@HttpUserType, 不设置base则视为没有当前用户信息设置 -->
<servlets path="/pipes" autoload="true" />

View File

@@ -1 +1 @@
<EFBFBD><EFBFBD><EFBFBD>н<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD>
<EFBFBD><EFBFBD><EFBFBD>н<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD>

1
libs/readme.txt Normal file
View File

@@ -0,0 +1 @@
<EFBFBD>Լ<EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD>

View File

@@ -96,12 +96,12 @@
</resources>
<!--
protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCHSNCP使用TCP实现;
protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。
name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线
host: 服务所占address 默认: 0.0.0.0
port: required 服务所占端口
root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root
lib: server额外的class目录 默认为
lib: server额外的class目录 默认为${APP_HOME}/libs/*;
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8
backlog: 默认10K
@@ -119,8 +119,7 @@
<!--
加载所有的Service服务;
在同一个进程中同一个name同一类型的Service将共用同一个实例
autoload="true" 默认值. 自动加载以下目录(如果存在的话)下所有的Service类:
server.lib; server.lib/*; server.classes;
autoload="true" 默认值. 自动加载classpath下所有的Service类
autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
@@ -135,6 +134,7 @@
<!--
name: 显式指定name覆盖默认的空字符串值。 注意: name不能包含$符号。
groups: 显式指定groups覆盖<services>节点的groups默认值。
ignore: 是否禁用, 默认为false。
-->
<service value="com.xxx.XXX2Service" name="" groups="xxx;yyy"/>
<!-- 给Service增加配置属性 -->
@@ -154,6 +154,11 @@
-->
<filters autoload="true" includes="" excludes="">
<!--
显著加载指定的Filter类
value=: Filter类名。必须与Server的协议层相同HTTP必须是HttpFilter
ignore: 是否禁用, 默认为false。
-->
<!-- 显著加载指定的Filter类 -->
<filter value="com.xxx.XXX1Filter"/>
@@ -240,14 +245,17 @@
<!--
加载所有的Servlet服务;
path: servlet的ContextPath前缀 默认为空
autoload="true" 默认值. 自动加载以下目录(如果存在的话)下所有的Servlet类:
${APP_HOME}/lib; ${APP_HOME}/root/lib/*; ${APP_HOME}/root/classes;
autoload="true" 默认值. 自动加载classpath下所有的Servlet类
autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
-->
<servlets path="/pipes" autoload="true" includes="" excludes="">
<!-- 显著加载指定的Servlet -->
<!--
显著加载指定的Servlet类
value=: Servlet类名。必须与Server的协议层相同HTTP必须是HttpServlet
ignore: 是否禁用, 默认为false。
-->
<servlet value="com.xxx.XXX1Servlet" />
<servlet value="com.xxx.XXX2Servlet" />
<servlet value="com.xxx.XXX3Servlet" >

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.boot;
import org.redkale.util.RedkaleClassLoader;
import org.redkale.net.TransportGroupInfo;
import java.io.*;
import java.lang.reflect.*;
@@ -19,6 +20,7 @@ import java.util.logging.*;
import javax.annotation.Resource;
import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.Convert;
import org.redkale.convert.bson.BsonFactory;
import org.redkale.convert.json.JsonFactory;
import org.redkale.net.*;
@@ -136,7 +138,10 @@ public final class Application {
private final CountDownLatch serversLatch;
//根ClassLoader
private final NodeClassLoader classLoader;
private final RedkaleClassLoader classLoader;
//Server根ClassLoader
private final RedkaleClassLoader serverClassLoader;
private Application(final AnyValue config) {
this(false, config);
@@ -267,8 +272,9 @@ public final class Application {
}
}
this.transportFactory = new TransportFactory(transportExec, transportPool, transportGroup);
this.classLoader = new NodeClassLoader(Thread.currentThread().getContextClassLoader());
this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
Thread.currentThread().setContextClassLoader(this.classLoader);
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
}
public ResourceFactory getResourceFactory() {
@@ -279,10 +285,14 @@ public final class Application {
return transportFactory;
}
public NodeClassLoader getNodeClassLoader() {
public RedkaleClassLoader getClassLoader() {
return classLoader;
}
public RedkaleClassLoader getServerClassLoader() {
return serverClassLoader;
}
public List<NodeServer> getNodeServers() {
return new ArrayList<>(servers);
}
@@ -295,6 +305,10 @@ public final class Application {
return startTime;
}
public AnyValue getAppConfig() {
return config;
}
public void init() throws Exception {
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4);
System.setProperty("convert.bson.tiny", "true");
@@ -310,7 +324,7 @@ public final class Application {
logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress());
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
Server.loadLib(logger, lib);
Server.loadLib(classLoader, logger, lib);
//------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources");
@@ -354,6 +368,8 @@ public final class Application {
this.resourceFactory.register(JsonFactory.root());
this.resourceFactory.register(BsonFactory.root().getConvert());
this.resourceFactory.register(JsonFactory.root().getConvert());
this.resourceFactory.register("bsonconvert", Convert.class, BsonFactory.root().getConvert());
this.resourceFactory.register("jsonconvert", Convert.class, JsonFactory.root().getConvert());
//只有WatchService才能加载Application、WatchFactory
final Application application = this;
this.resourceFactory.register(new ResourceFactory.ResourceLoader() {
@@ -367,6 +383,8 @@ public final class Application {
Class type = field.getType();
if (type == Application.class) {
field.set(src, application);
} else if (type == ResourceFactory.class) {
field.set(src, res.name().equalsIgnoreCase("server") ? rf : (res.name().isEmpty() ? application.resourceFactory : null));
} else if (type == TransportFactory.class) {
field.set(src, application.transportFactory);
} else if (type == NodeSncpServer.class) {
@@ -440,6 +458,16 @@ public final class Application {
//------------------------------------------------------------------------
}
public void restoreConfig() throws IOException {
synchronized (this) {
File confFile = new File(this.home, "conf/application.xml");
confFile.renameTo(new File(this.home, "conf/application_" + String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", System.currentTimeMillis()) + ".xml"));
final PrintStream ps = new PrintStream(new FileOutputStream(confFile));
ps.append(config.toXML("application"));
ps.close();
}
}
private void startSelfServer() throws Exception {
final Application application = this;
new Thread() {
@@ -553,13 +581,12 @@ public final class Application {
others.add(entry);
}
}
if (watchs.size() > 1) throw new RuntimeException("Found one more WATCH Server");
this.watching = !watchs.isEmpty();
//单向SNCP服务不需要对等group
//if (!sncps.isEmpty() && globalNodes.isEmpty()) throw new RuntimeException("found SNCP Server node but not found <group> node info.");
runServers(timecd, sncps); //必须确保sncp都启动后再启动其他协议
runServers(timecd, sncps); //必须确保SNCP服务都启动后再启动其他服务
runServers(timecd, others);
runServers(timecd, watchs); //必须在所有server都启动后再启动
runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务
timecd.await();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n");
if (!singletonrun) this.serversLatch.await();
@@ -603,7 +630,7 @@ public final class Application {
if (!inited.get()) {
synchronized (nodeClasses) {
if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS
ClassFilter profilter = new ClassFilter(NodeProtocol.class, NodeServer.class, (Class[]) null);
ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null);
ClassFilter.Loader.load(home, serconf.getValue("excludelibs", "").split(";"), profilter);
final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys();
for (FilterEntry<NodeServer> entry : entrys) {

View File

@@ -58,19 +58,22 @@ public final class ClassFilter<T> {
private AnyValue conf; //基本配置信息, 当符合条件时将conf的属性赋值到FilterEntry中去。
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
this(annotationClass, superClass, excludeSuperClasses, null);
private final ClassLoader classLoader;
public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
this(classLoader, annotationClass, superClass, excludeSuperClasses, null);
}
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) {
public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) {
this.annotationClass = annotationClass;
this.superClass = superClass;
this.excludeSuperClasses = excludeSuperClasses;
this.conf = conf;
this.classLoader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
}
public static ClassFilter create(Class[] excludeSuperClasses, String includeregs, String excluderegs, Set<String> includeValues, Set<String> excludeValues) {
ClassFilter filter = new ClassFilter(null, null, excludeSuperClasses);
ClassFilter filter = new ClassFilter(null, null, null, excludeSuperClasses);
filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";"));
filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";"));
filter.setPrivilegeIncludes(includeValues);
@@ -156,7 +159,7 @@ public final class ClassFilter<T> {
}
if (cf == null || clazzname.startsWith("sun.")) return;
try {
Class clazz = Class.forName(clazzname);
Class clazz = classLoader.loadClass(clazzname);
if (!cf.accept(property, clazz, autoscan)) return;
if (cf.conf != null) {
if (property == null) {
@@ -445,12 +448,12 @@ public final class ClassFilter<T> {
* @throws IOException 异常
*/
public static void load(final File excludeFile, final String[] excludeRegs, final ClassFilter... filters) throws IOException {
URLClassLoader loader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
RedkaleClassLoader loader = (RedkaleClassLoader) Thread.currentThread().getContextClassLoader();
List<URL> urlfiles = new ArrayList<>(2);
List<URL> urljares = new ArrayList<>(2);
final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null;
final Pattern[] excludePatterns = toPattern(excludeRegs);
for (URL url : loader.getURLs()) {
for (URL url : loader.getAllURLs()) {
if (exurl != null && exurl.sameFile(url)) continue;
if (excludePatterns != null) {
boolean skip = false;
@@ -482,6 +485,12 @@ public final class ClassFilter<T> {
if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) {
String classname = entryname.substring(0, entryname.length() - 6);
if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
//常见的jar跳过
if (classname.startsWith("com.mysql.")) break;
if (classname.startsWith("org.mariadb.")) break;
if (classname.startsWith("oracle.jdbc.")) break;
if (classname.startsWith("org.postgresql.")) break;
if (classname.startsWith("com.microsoft.sqlserver.")) break;
classes.add(classname);
if (debug) debugstr.append(classname).append("\r\n");
for (final ClassFilter filter : filters) {

View File

@@ -1,28 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot;
import java.net.*;
/**
*
* @author zhangjx
*/
public class NodeClassLoader extends URLClassLoader {
public NodeClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
public Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
}

View File

@@ -109,7 +109,7 @@ public class NodeHttpServer extends NodeServer {
synchronized (regFactory) {
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
if (nodeService == null) {
nodeService = Sncp.createLocalService(resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
regFactory.register(resourceName, WebSocketNode.class, nodeService);
}
resourceFactory.inject(nodeService, self);
@@ -210,8 +210,9 @@ public class NodeHttpServer extends NodeServer {
final boolean autoload = restConf.getBoolValue("autoload", true);
{ //加载RestService
String userTypeStr = restConf.getValue("usertype");
final Class userType = userTypeStr == null ? null : Class.forName(userTypeStr);
final Class baseServletType = Class.forName(restConf.getValue("base", HttpServlet.class.getName()));
final Class userType = userTypeStr == null ? null : this.serverClassLoader.loadClass(userTypeStr);
final Class baseServletType = this.serverClassLoader.loadClass(restConf.getValue("base", HttpServlet.class.getName()));
final Set<String> includeValues = new HashSet<>();
final Set<String> excludeValues = new HashSet<>();
for (AnyValue item : restConf.getAnyValues("service")) {
@@ -238,14 +239,17 @@ public class NodeHttpServer extends NodeServer {
return;
}
restedObjects.add(service); //避免重复创建Rest对象
HttpServlet servlet = httpServer.addRestServlet(service, userType, baseServletType, prefix);
HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix);
if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix + mappings[i];
mappings[i] = prefix2 + mappings[i];
}
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
}
@@ -288,14 +292,17 @@ public class NodeHttpServer extends NodeServer {
return;
}
restedObjects.add(stype); //避免重复创建Rest对象
HttpServlet servlet = httpServer.addRestWebSocketServlet(stype, prefix, en.getProperty());
HttpServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, prefix, en.getProperty());
if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " " + stype.getName() + " create RestWebSocketServlet " + servlet);
if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix + mappings[i];
mappings[i] = prefix2 + mappings[i];
}
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
}

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.boot;
import org.redkale.util.RedkaleClassLoader;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
@@ -54,7 +55,9 @@ public abstract class NodeServer {
protected final Server server;
//ClassLoader
protected final NodeClassLoader classLoader;
protected RedkaleClassLoader serverClassLoader;
protected final Thread serverThread;
//当前Server的SNCP协议的组
protected String sncpGroup = null;
@@ -89,35 +92,9 @@ public abstract class NodeServer {
this.resourceFactory = application.getResourceFactory().createChild();
this.server = server;
this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.classLoader = new NodeClassLoader(Thread.currentThread().getContextClassLoader());
Thread.currentThread().setContextClassLoader(this.classLoader);
}
protected Consumer<Runnable> getExecutor() throws Exception {
if (server == null) return null;
final Field field = Server.class.getDeclaredField("context");
field.setAccessible(true);
return new Consumer<Runnable>() {
private Context context;
@Override
public void accept(Runnable t) {
if (context == null && server != null) {
try {
this.context = (Context) field.get(server);
} catch (Exception e) {
logger.log(Level.SEVERE, "Server (" + server.getSocketAddress() + ") cannot find Context", e);
}
}
if (context == null) {
t.run();
} else {
context.submitAsync(t);
}
}
};
this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader());
Thread.currentThread().setContextClassLoader(this.serverClassLoader);
this.serverThread = Thread.currentThread();
}
public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) {
@@ -156,9 +133,9 @@ public abstract class NodeServer {
resourceFactory.register(Server.RESNAME_SERVER_ROOT, File.class, myroot.getCanonicalFile());
resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath());
final String homepath = myroot.getCanonicalPath();
//加入指定的classpath
Server.loadLib(logger, this.serverConf.getValue("lib", "").replace("${APP_HOME}", homepath) + ";" + homepath + "/lib/*;" + homepath + "/classes");
Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "${APP_HOME}/libs/*").replace("${APP_HOME}", application.getHome().getPath().replace('\\', '/')));
this.serverThread.setContextClassLoader(this.serverClassLoader);
}
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf);
@@ -166,7 +143,7 @@ public abstract class NodeServer {
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", "");
if (!interceptorClass.isEmpty()) {
Class clazz = Class.forName(interceptorClass);
Class clazz = serverClassLoader.loadClass(interceptorClass);
this.interceptor = (NodeInterceptor) clazz.newInstance();
}
@@ -200,7 +177,7 @@ public abstract class NodeServer {
if (resources != null) {
for (AnyValue sourceConf : resources.getAnyValues("source")) {
try {
Class type = Class.forName(sourceConf.getValue("value"));
Class type = serverClassLoader.loadClass(sourceConf.getValue("value"));
if (!Service.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application source resource, but not Service error: " + sourceConf);
} else if (CacheSource.class.isAssignableFrom(type)) {
@@ -256,7 +233,7 @@ public abstract class NodeServer {
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
Service cacheListenerService = Sncp.createLocalService(resourceName, DataCacheListenerService.class, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
Service cacheListenerService = Sncp.createLocalService(serverClassLoader, resourceName, DataCacheListenerService.class, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
localServices.add(cacheListenerService);
sncpServer.consumerAccept(cacheListenerService);
@@ -282,11 +259,11 @@ public abstract class NodeServer {
SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final AnyValue sourceConf = cacheResource.get(resourceName);
final Class sourceType = sourceConf == null ? CacheMemorySource.class : Class.forName(sourceConf.getValue("type"));
final Class sourceType = sourceConf == null ? CacheMemorySource.class : serverClassLoader.loadClass(sourceConf.getValue("type"));
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
final CacheSource source = (CacheSource) Sncp.createLocalService(resourceName, sourceType, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
final CacheSource source = (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[1];
@@ -357,9 +334,9 @@ public abstract class NodeServer {
Service service;
boolean ws = src instanceof WebSocketServlet;
if (ws || localed) { //本地模式
service = Sncp.createLocalService(resourceName, serviceImplClass, appResourceFactory, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, appResourceFactory, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} else {
service = Sncp.createRemoteService(resourceName, serviceImplClass, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
}
if (SncpClient.parseMethod(serviceImplClass).isEmpty()) return; //class没有可用的方法 通常为BaseService
@@ -449,62 +426,6 @@ public abstract class NodeServer {
maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
}
/*
* protected List<Transport> loadTransports(final HashSet<String> groups) {
* if (groups == null) return null;
* final List<Transport> transports = new ArrayList<>();
* for (String group : groups) {
* if (this.sncpGroup == null || !this.sncpGroup.equals(group)) {
* transports.add(loadTransport(group));
* }
* }
* return transports;
* }
* protected Transport loadTransport(final HashSet<String> groups) {
* if (groups == null || groups.isEmpty()) return null;
* final String groupid = new ArrayList<>(groups).stream().sorted().collect(Collectors.joining(";")); //按字母排列顺序
* Transport transport = application.resourceFactory.find(groupid, Transport.class);
* if (transport != null) return transport;
* final List<Transport> transports = new ArrayList<>();
* for (String group : groups) {
* transports.add(loadTransport(group));
* }
* Set<InetSocketAddress> addrs = new HashSet();
* transports.forEach(t -> addrs.addAll(Arrays.asList(t.getRemoteAddresses())));
* Transport first = transports.get(0);
* TransportGroupInfo ginfo = application.transportFactory.findGroupInfo(first.getName());
* Transport newTransport = new Transport(groupid, ginfo.getProtocol(),
* ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
* synchronized (application.resourceFactory) {
* transport = application.resourceFactory.find(groupid, Transport.class);
* if (transport == null) {
* transport = newTransport;
* application.resourceFactory.register(groupid, transport);
* }
* }
* return transport;
* }
*
* protected Transport loadTransport(final String group) {
* if (group == null) return null;
* Transport transport;
* synchronized (application.resourceFactory) {
* transport = application.resourceFactory.find(group, Transport.class);
* if (transport != null) {
* if (this.sncpAddress != null && !this.sncpAddress.equals(transport.getClientAddress())) {
* throw new RuntimeException(transport + "repeat create on newClientAddress = " + this.sncpAddress + ", oldClientAddress = " + transport.getClientAddress());
* }
* return transport;
* }
* TransportGroupInfo ginfo = application.findGroupInfo(group);
* Set<InetSocketAddress> addrs = ginfo.copyAddresses();
* if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> ");
* transport = new Transport(group, ginfo.getProtocol(), ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
* application.resourceFactory.register(group, transport);
* }
* return transport;
* }
*/
protected abstract ClassFilter<Filter> createFilterClassFilter();
protected abstract ClassFilter<Servlet> createServletClassFilter();
@@ -519,7 +440,7 @@ public abstract class NodeServer {
protected ClassFilter createClassFilter(final String localGroup, Class<? extends Annotation> ref,
Class inter, Class[] excludeSuperClasses, Class<? extends Annotation> ref2, String properties, String property) {
ClassFilter cf = new ClassFilter(ref, inter, excludeSuperClasses, null);
ClassFilter cf = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, null);
if (properties == null && properties == null) {
cf.setRefused(true);
return cf;
@@ -546,7 +467,7 @@ public abstract class NodeServer {
prop = new AnyValue.DefaultAnyValue();
prop.addValue("groups", sc);
}
ClassFilter filter = new ClassFilter(ref, inter, excludeSuperClasses, prop);
ClassFilter filter = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, prop);
for (AnyValue av : list.getAnyValues(property)) { // <service>、<filter>、<servlet> 节点
final AnyValue[] items = av.getAnyValues("property");
if (av instanceof DefaultAnyValue && items.length > 0) { //存在 <property>节点
@@ -570,8 +491,10 @@ public abstract class NodeServer {
dav.addValue("properties", ps);
av = dav;
}
if (!av.getBoolValue("ignore", false)) {
filter.filter(av, av.getValue("value"), false);
}
}
if (list.getBoolValue("autoload", true)) {
String includes = list.getValue("includes", "");
String excludes = list.getValue("excludes", "");
@@ -593,12 +516,22 @@ public abstract class NodeServer {
return false;
}
public boolean isWATCH() {
return false;
}
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
public NodeClassLoader getNodeClassLoader() {
return classLoader;
public RedkaleClassLoader getServerClassLoader() {
return serverClassLoader;
}
public void setServerClassLoader(RedkaleClassLoader serverClassLoader) {
Objects.requireNonNull(this.serverClassLoader);
this.serverClassLoader = serverClassLoader;
this.serverThread.setContextClassLoader(serverClassLoader);
}
public InetSocketAddress getSncpAddress() {

View File

@@ -37,4 +37,14 @@ public class NodeWatchServer extends NodeHttpServer {
protected ClassFilter<Servlet> createServletClassFilter() {
return createClassFilter(null, WebServlet.class, WatchServlet.class, null, null, "servlets", "servlet");
}
@Override
protected ClassFilter createOtherClassFilter() {
return null;
}
@Override
public boolean isWATCH() {
return true;
}
}

View File

@@ -0,0 +1,17 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import org.redkale.service.AbstractService;
import org.redkale.watch.WatchService;
/**
*
* @author zhangjx
*/
public abstract class AbstractWatchService extends AbstractService implements WatchService {
}

View File

@@ -0,0 +1,50 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import java.io.IOException;
import java.util.List;
import javax.annotation.Resource;
import org.redkale.boot.*;
import org.redkale.net.http.*;
import org.redkale.service.RetResult;
import org.redkale.util.Comment;
/**
*
* @author zhangjx
*/
@RestService(name = "filter", catalog = "watch", repair = false)
public class FilterWatchService extends AbstractWatchService {
@Comment("Filter类名不存在")
public static final int RET_FILTER_TYPE_NOT_EXISTS = 1601_0002;
@Comment("Filter类名不合法")
public static final int RET_FILTER_TYPE_ILLEGAL = 1601_0003;
@Comment("Filter类名已存在")
public static final int RET_FILTER_EXISTS = 1601_0004;
@Comment("Filter的JAR包不存在")
public static final int RET_FILTER_JAR_ILLEGAL = 1601_0005;
@Resource
private Application application;
@RestMapping(name = "addfilter", auth = false, comment = "动态增加Filter")
public RetResult addFilter(@RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar,
@RestParam(name = "server", comment = "Server节点名") final String serverName,
@RestParam(name = "type", comment = "Filter类名") final String filterType) throws IOException {
if (filterType == null) return new RetResult(RET_FILTER_TYPE_NOT_EXISTS, "Not found Filter Type (" + filterType + ")");
if (jar == null) return new RetResult(RET_FILTER_JAR_ILLEGAL, "Not found jar file");
List<NodeServer> nodes = application.getNodeServers();
for (NodeServer node : nodes) {
if (node.getServer().containsFilter(filterType)) return new RetResult(RET_FILTER_EXISTS, "Filter(" + filterType + ") exists");
}
return RetResult.success();
}
}

View File

@@ -0,0 +1,17 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import org.redkale.net.http.RestService;
/**
*
* @author zhangjx
*/
@RestService(name = "server", catalog = "watch", repair = false)
public class ServerWatchService extends AbstractWatchService {
}

View File

@@ -0,0 +1,39 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.net.TransportFactory;
import org.redkale.net.http.*;
/**
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@RestService(name = "service", catalog = "watch", repair = false)
public class ServiceWatchService extends AbstractWatchService {
@Resource
private Application application;
@Resource
private TransportFactory transportFactory;
// @RestMapping(name = "load", auth = false, comment = "动态增加Service")
// public RetResult loadService(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
// //待开发
// return RetResult.success();
// }
//
// @RestMapping(name = "stop", auth = false, comment = "动态停止Service")
// public RetResult stopService(String name, String type) {
// //待开发
// return RetResult.success();
// }
}

View File

@@ -0,0 +1,39 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.net.TransportFactory;
import org.redkale.net.http.*;
/**
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@RestService(name = "servlet", catalog = "watch", repair = false)
public class ServletWatchService extends AbstractWatchService {
@Resource
private Application application;
@Resource
private TransportFactory transportFactory;
//
// @RestMapping(name = "load", auth = false, comment = "动态增加Servlet")
// public RetResult loadServlet(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
// //待开发
// return RetResult.success();
// }
//
// @RestMapping(name = "stop", auth = false, comment = "动态停止Servlet")
// public RetResult stopServlet(String type) {
// //待开发
// return RetResult.success();
// }
}

View File

@@ -0,0 +1,26 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.net.TransportFactory;
import org.redkale.net.http.RestService;
/**
*
* @author zhangjx
*/
@RestService(name = "source", catalog = "watch", repair = false)
public class SourceWatchService extends AbstractWatchService {
@Resource
private Application application;
@Resource
private TransportFactory transportFactory;
}

View File

@@ -0,0 +1,138 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import java.io.IOException;
import java.net.*;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.net.*;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.*;
import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/**
*
* @author zhangjx
*/
@RestService(name = "transport", catalog = "watch", repair = false)
public class TransportWatchService extends AbstractWatchService {
@Comment("不存在的Group节点")
public static final int RET_TRANSPORT_GROUP_NOT_EXISTS = 1606_0001;
@Comment("非法的Node节点IP地址")
public static final int RET_TRANSPORT_ADDR_ILLEGAL = 1606_0002;
@Comment("Node节点IP地址已存在")
public static final int RET_TRANSPORT_ADDR_EXISTS = 1606_0003;
@Resource
private Application application;
@Resource
private TransportFactory transportFactory;
@RestMapping(name = "listnodes", auth = false, comment = "获取所有Node节点")
public List<TransportGroupInfo> listNodes() {
return transportFactory.getGroupInfos();
}
@RestMapping(name = "addnode", auth = false, comment = "动态增加指定Group的Node节点")
public RetResult addNode(@RestParam(name = "group", comment = "Group节点名") final String group,
@RestParam(name = "addr", comment = "节点IP") final String addr,
@RestParam(name = "port", comment = "节点端口") final int port) throws IOException {
InetSocketAddress address;
try {
address = new InetSocketAddress(addr, port);
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
channel.connect(address).get(2, TimeUnit.SECONDS); //连接超时2秒
channel.close();
} catch (Exception e) {
return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") is illegal or cannot connect");
}
if (transportFactory.findGroupName(address) != null) return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") is exists");
synchronized (this) {
if (transportFactory.findGroupInfo(group) == null) {
return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")");
}
transportFactory.addGroupInfo(group, address);
for (Service service : transportFactory.getServices()) {
if (!Sncp.isSncpDyn(service)) continue;
SncpClient client = Sncp.getSncpClient(service);
if (Sncp.isRemote(service)) {
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().addRemoteAddresses(address);
}
} else {
if (group.equals(client.getSameGroup())) {
client.getSameGroupTransport().addRemoteAddresses(address);
}
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
for (Transport transport : client.getDiffGroupTransports()) {
transport.addRemoteAddresses(address);
}
}
}
}
DefaultAnyValue node = DefaultAnyValue.create("addr", addr).addValue("port", port);
for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) {
if (group.equals(groupconf.getValue("name"))) {
((DefaultAnyValue) groupconf).addValue("node", node);
break;
}
}
application.restoreConfig();
}
return RetResult.success();
}
@RestMapping(name = "removenode", auth = false, comment = "动态删除指定Group的Node节点")
public RetResult removeNode(@RestParam(name = "group", comment = "Group节点名") final String group,
@RestParam(name = "addr", comment = "节点IP") final String addr,
@RestParam(name = "port", comment = "节点端口") final int port) throws IOException {
if (group == null) return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")");
final InetSocketAddress address = new InetSocketAddress(addr, port);
if (!group.equals(transportFactory.findGroupName(address))) return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") not belong to group(" + group + ")");
synchronized (this) {
if (transportFactory.findGroupInfo(group) == null) {
return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")");
}
transportFactory.removeGroupInfo(group, address);
for (Service service : transportFactory.getServices()) {
if (!Sncp.isSncpDyn(service)) continue;
SncpClient client = Sncp.getSncpClient(service);
if (Sncp.isRemote(service)) {
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().removeRemoteAddresses(address);
}
} else {
if (group.equals(client.getSameGroup())) {
client.getSameGroupTransport().removeRemoteAddresses(address);
}
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
for (Transport transport : client.getDiffGroupTransports()) {
transport.removeRemoteAddresses(address);
}
}
}
}
for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) {
if (group.equals(groupconf.getValue("name"))) {
((DefaultAnyValue) groupconf).removeValue("node", DefaultAnyValue.create("addr", addr).addValue("port", port));
break;
}
}
application.restoreConfig();
}
return RetResult.success();
}
}

View File

@@ -5,6 +5,10 @@
*/
package org.redkale.convert;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.function.Supplier;
/**
* 序列化/反序列化操作类
*
@@ -26,4 +30,14 @@ public abstract class Convert<R extends Reader, W extends Writer> {
public ConvertFactory<R, W> getFactory() {
return this.factory;
}
public abstract boolean isBinary();
public abstract <T> T convertFrom(final Type type, final ByteBuffer... buffers);
public abstract <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value);
}

View File

@@ -21,7 +21,7 @@ import static java.lang.annotation.RetentionPolicy.*;
@Documented
@Target({METHOD, FIELD})
@Retention(RUNTIME)
@Repeatable(ConvertColumns.class)
@Repeatable(ConvertColumn.ConvertColumns.class)
public @interface ConvertColumn {
/**
@@ -31,6 +31,13 @@ public @interface ConvertColumn {
*/
String name() default "";
/**
* 给字段取个序号ID值小靠前
*
* @return 字段排序ID
*/
int index() default 0;
/**
* 解析/序列化时是否屏蔽该字段
*
@@ -44,4 +51,21 @@ public @interface ConvertColumn {
* @return JSON or BSON or ALL
*/
ConvertType type() default ConvertType.ALL;
/**
* ConvertColumn 的多用类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public static @interface ConvertColumns {
ConvertColumn[] value();
}
}

View File

@@ -8,11 +8,15 @@ package org.redkale.convert;
/**
* ConvertColumn 对应的实体类
*
* <p> 详情见: https://redkale.org
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public final class ConvertColumnEntry {
private int index;
private String name = "";
private boolean ignore;
@@ -25,6 +29,7 @@ public final class ConvertColumnEntry {
public ConvertColumnEntry(ConvertColumn column) {
if (column == null) return;
this.name = column.name();
this.index = column.index();
this.ignore = column.ignore();
this.convertType = column.type();
}
@@ -45,6 +50,13 @@ public final class ConvertColumnEntry {
this.convertType = convertType;
}
public ConvertColumnEntry(String name, int index, boolean ignore, ConvertType convertType) {
this.name = name;
this.index = index;
this.ignore = ignore;
this.convertType = convertType;
}
public String name() {
return name == null ? "" : name;
}
@@ -69,4 +81,12 @@ public final class ConvertColumnEntry {
this.convertType = convertType;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}

View File

@@ -1,25 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* ConvertColumn 的多用类
*
* <p> 详情见: https://redkale.org
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface ConvertColumns {
ConvertColumn[] value();
}

View File

@@ -129,7 +129,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
public abstract ConvertType getConvertType();
public abstract boolean isReversible();
public abstract boolean isReversible(); //是否可逆的
public abstract ConvertFactory createChild();
@@ -253,7 +253,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
Class clazz = findEntityAlias(name);
try {
return clazz == null ? Class.forName(name) : clazz;
return clazz == null ? Thread.currentThread().getContextClassLoader().loadClass(name) : clazz;
} catch (Exception ex) {
throw new ConvertException("convert entity is " + name, ex);
}

View File

@@ -22,6 +22,8 @@ import org.redkale.util.Attribute;
@SuppressWarnings("unchecked")
public final class DeMember<R extends Reader, T, F> implements Comparable<DeMember<R, T, F>> {
protected int index;
protected final Attribute<T, F> attribute;
protected Decodeable<R, F> decoder;
@@ -68,9 +70,14 @@ public final class DeMember<R extends Reader, T, F> implements Comparable<DeMemb
return this.attribute;
}
public int getIndex() {
return this.index;
}
@Override
public final int compareTo(DeMember<R, T, F> o) {
if (o == null) return 1;
if (o == null) return -1;
if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index);
return this.attribute.field().compareTo(o.attribute.field());
}

View File

@@ -31,6 +31,8 @@ public final class EnMember<W extends Writer, T, F> implements Comparable<EnMemb
//final boolean isnumber;
final boolean isbool;
protected int index;
public EnMember(Attribute<T, F> attribute, Encodeable<W, F> encoder) {
this.attribute = attribute;
this.encoder = encoder;
@@ -61,9 +63,14 @@ public final class EnMember<W extends Writer, T, F> implements Comparable<EnMemb
return attribute.field().equals(name);
}
public int getIndex() {
return this.index;
}
@Override
public final int compareTo(EnMember<W, T, F> o) {
if (o == null) return 1;
if (o == null) return -1;
if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index);
return this.attribute.field().compareTo(o.attribute.field());
}

View File

@@ -78,7 +78,9 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(field.getGenericType(), this.type);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t)));
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
}
final boolean reversible = factory.isReversible();
for (final Method method : clazz.getMethods()) {
@@ -101,7 +103,9 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
ref = factory.findRef(method);
if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(method.getGenericParameterTypes()[0], this.type);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t)));
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
}
if (cps != null) { //可能存在某些构造函数中的字段名不存在setter方法
for (final String constructorField : cps) {

View File

@@ -69,7 +69,9 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(field.getGenericType(), this.type);
list.add(new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t)));
EnMember member = new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
}
for (final Method method : clazz.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue;
@@ -92,7 +94,9 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
ref = factory.findRef(method);
if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(method.getGenericReturnType(), this.type);
list.add(new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t)));
EnMember member = new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
}
this.members = list.toArray(new EnMember[list.size()]);
Arrays.sort(this.members);

View File

@@ -59,6 +59,11 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return BsonFactory.root().getConvert();
}
@Override
public boolean isBinary() {
return true;
}
//------------------------------ reader -----------------------------------------------------------
public BsonReader pollBsonReader(final ByteBuffer... buffers) {
return new BsonByteBufferReader((ConvertMask) null, buffers);
@@ -114,11 +119,13 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return (T) factory.loadDecoder(type).convertFrom(new BsonStreamReader(in));
}
@Override
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers.length < 1) return null;
return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader((ConvertMask) null, buffers));
}
@Override
public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) {
if (type == null || buffers.length < 1) return null;
return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader(mask, buffers));
@@ -169,17 +176,7 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
}
}
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
if (value == null) {
out.writeNull();
} else {
factory.loadEncoder(type).convertTo(out, value);
}
return out.toBuffers();
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
@@ -191,6 +188,18 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return out.toBuffers();
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
if (value == null) {
out.writeNull();
} else {
factory.loadEncoder(type).convertTo(out, value);
}
return out.toBuffers();
}
public void convertTo(final BsonWriter writer, final Object value) {
if (value == null) {
writer.writeNull();

View File

@@ -37,7 +37,7 @@ public class TypeSimpledCoder<R extends Reader, W extends Writer> extends Simple
String str = in.readSmallString();
if (str == null) return null;
try {
return Class.forName(str);
return Thread.currentThread().getContextClassLoader().loadClass(str);
} catch (Throwable e) {
return null;
}

View File

@@ -162,7 +162,7 @@ public class JsonByteBufferReader extends JsonReader {
public final boolean hasNext() {
char ch = nextGoodChar();
if (ch == ',') return true;
if (ch == '}' || ch == ']') return false;
if (ch == '}' || ch == ']' || ch == 0) return false;
backChar(ch); // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取
return true;
}
@@ -253,6 +253,7 @@ public class JsonByteBufferReader extends JsonReader {
throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")");
}
} else if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') { // ch <= ' ' 包含 0
backChar(ch);
break;
} else {
sb.append(ch);

View File

@@ -46,6 +46,11 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return JsonFactory.root().getConvert();
}
@Override
public boolean isBinary() {
return false;
}
//------------------------------ reader -----------------------------------------------------------
public JsonReader pollJsonReader(final ByteBuffer... buffers) {
return new JsonByteBufferReader((ConvertMask) null, buffers);
@@ -109,11 +114,13 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return (T) factory.loadDecoder(type).convertFrom(new JsonStreamReader(in));
}
@Override
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers));
}
@Override
public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader(mask, buffers));
@@ -159,6 +166,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
}
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);
@@ -170,6 +178,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return out.toBuffers();
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);

View File

@@ -404,6 +404,7 @@ public class JsonReader extends Reader {
@Override
public final DeMember readFieldName(final DeMember[] members) {
final String exceptedfield = this.readSmallString();
if(exceptedfield == null) return null;
final int len = members.length;
if (this.fieldIndex >= len) this.fieldIndex = 0;
for (int k = this.fieldIndex; k < len; k++) {

View File

@@ -19,7 +19,7 @@ import org.redkale.util.*;
* @param <R> Request的子类型
* @param <P> Response的子类型
*/
public abstract class Filter<C extends Context, R extends Request<C>, P extends Response<C, R>> implements Comparable, Resourcable {
public abstract class Filter<C extends Context, R extends Request<C>, P extends Response<C, R>> implements Comparable {
AnyValue _conf; //当前Filter的配置
@@ -33,11 +33,6 @@ public abstract class Filter<C extends Context, R extends Request<C>, P extends
public void destroy(C context, AnyValue config) {
}
@Override
public String resourceName() {
return "";
}
/**
* 值越小越靠前执行
*
@@ -48,7 +43,7 @@ public abstract class Filter<C extends Context, R extends Request<C>, P extends
}
@Override
public int compareTo(Object o) {
public final int compareTo(Object o) {
if (!(o instanceof Filter)) return 1;
return this.getIndex() - ((Filter) o).getIndex();
}

View File

@@ -63,6 +63,24 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
}
}
public boolean containsServlet(Class<? extends S> servletClass) {
synchronized (lock1) {
for (S servlet : new HashSet<>(servlets)) {
if (servlet.getClass().equals(servletClass)) return true;
}
return false;
}
}
public boolean containsServlet(String servletClassName) {
synchronized (lock1) {
for (S servlet : new HashSet<>(servlets)) {
if (servlet.getClass().getName().equals(servletClassName)) return true;
}
return false;
}
}
protected void putMapping(K key, S servlet) {
synchronized (lock2) {
Map<K, S> newmappings = new HashMap<>(mappings);
@@ -132,24 +150,39 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
filter._conf = conf;
synchronized (filters) {
this.filters.add(filter);
Collections.sort(this.filters);
}
}
public Filter<C, R, P> removeFilter(Class<? extends Filter<C, R, P>> filterClass) {
public <T extends Filter<C, R, P>> T removeFilter(Class<T> filterClass) {
return removeFilter(f -> filterClass.equals(f.getClass()));
}
public Filter<C, R, P> removeFilter(String filterName) {
return removeFilter(f -> filterName.equals(f.resourceName()));
public boolean containsFilter(Class<? extends Filter> filterClass) {
if (this.headFilter == null || filterClass == null) return false;
Filter filter = this.headFilter;
do {
if (filter.getClass().equals(filterClass)) return true;
} while ((filter = filter._next) != null);
return false;
}
public Filter<C, R, P> removeFilter(Predicate<Filter<C, R, P>> predicate) {
public boolean containsFilter(String filterClassName) {
if (this.headFilter == null || filterClassName == null) return false;
Filter filter = this.headFilter;
do {
if (filter.getClass().getName().equals(filterClassName)) return true;
} while ((filter = filter._next) != null);
return false;
}
public <T extends Filter<C, R, P>> T removeFilter(Predicate<T> predicate) {
if (this.headFilter == null || predicate == null) return null;
synchronized (filters) {
Filter filter = this.headFilter;
Filter prev = null;
do {
if (predicate.test(filter)) break;
if (predicate.test((T) filter)) break;
prev = filter;
} while ((filter = filter._next) != null);
if (filter != null) {
@@ -161,10 +194,14 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
filter._next = null;
this.filters.remove(filter);
}
return filter;
return (T) filter;
}
}
public <T extends Filter<C, R, P>> List<T> getFilters() {
return (List) new ArrayList<>(filters);
}
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings);
public final void prepare(final ByteBuffer buffer, final R request, final P response) throws IOException {
@@ -226,7 +263,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
servlet._conf = conf;
}
public Set<S> getServlets() {
return new LinkedHashSet<>(servlets);
public List<S> getServlets() {
return new ArrayList<>(servlets);
}
}

View File

@@ -170,18 +170,6 @@ public abstract class Response<C extends Context, R extends Request<C>> {
}
}
/**
* 使用 public void recycleListener(BiConsumer recycleListener) 代替
*
* @param recycleListener BiConsumer
*
* @deprecated
*/
@Deprecated
public void setRecycleListener(BiConsumer<R, Response<C, R>> recycleListener) {
this.recycleListener = recycleListener;
}
public void recycleListener(BiConsumer<R, Response<C, R>> recycleListener) {
this.recycleListener = recycleListener;
}

View File

@@ -6,7 +6,6 @@
package org.redkale.net;
import java.io.*;
import java.lang.reflect.Method;
import java.net.*;
import java.nio.charset.Charset;
import java.text.*;
@@ -14,7 +13,7 @@ import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import org.redkale.util.AnyValue;
import org.redkale.util.*;
/**
*
@@ -185,6 +184,52 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
}
/**
* 判断是否存在Filter
*
* @param <T> 泛型
* @param filterClass Filter类
*
* @return boolean
*/
public <T extends Filter> boolean containsFilter(Class<T> filterClass) {
return this.prepare.containsFilter(filterClass);
}
/**
* 判断是否存在Filter
*
* @param <T> 泛型
* @param filterClassName Filter类
*
* @return boolean
*/
public <T extends Filter> boolean containsFilter(String filterClassName) {
return this.prepare.containsFilter(filterClassName);
}
/**
* 判断是否存在Servlet
*
* @param servletClass Servlet类
*
* @return boolean
*/
public boolean containsServlet(Class<? extends S> servletClass) {
return this.prepare.containsServlet(servletClass);
}
/**
* 判断是否存在Servlet
*
* @param servletClassName Servlet类
*
* @return boolean
*/
public boolean containsServlet(String servletClassName) {
return this.prepare.containsServlet(servletClassName);
}
/**
* 销毁Servlet
*
@@ -217,7 +262,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return new DecimalFormat(sf);
}
public static URL[] loadLib(final Logger logger, final String lib) throws Exception {
public static URL[] loadLib(final RedkaleClassLoader classLoader, final Logger logger, final String lib) throws Exception {
if (lib == null || lib.isEmpty()) return new URL[0];
final Set<URL> set = new HashSet<>();
for (String s : lib.split(";")) {
@@ -235,17 +280,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
}
}
if (set.isEmpty()) return new URL[0];
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl instanceof URLClassLoader) {
URLClassLoader loader = (URLClassLoader) cl;
final Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
for (URL url : set) {
method.invoke(loader, url);
//if (logger != null) logger.log(Level.INFO, "Server found ClassPath({0})", url);
}
} else {
Thread.currentThread().setContextClassLoader(new URLClassLoader(set.toArray(new URL[set.size()]), cl));
classLoader.addURL(url);
}
List<URL> list = new ArrayList<>(set);
Collections.sort(list, (URL o1, URL o2) -> o1.getFile().compareTo(o2.getFile()));

View File

@@ -11,8 +11,7 @@ import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.redkale.util.ObjectPool;
import org.redkale.util.*;
/**
* 传输客户端
@@ -76,30 +75,6 @@ public final class Transport {
updateRemoteAddresses(addresses);
}
public Transport(final Collection<Transport> transports) {
Transport first = null;
List<String> tmpgroup = new ArrayList<>();
if (transports != null) {
for (Transport t : transports) {
if (first == null) first = t;
tmpgroup.add(t.name);
}
}
if (first == null) throw new NullPointerException("Collection<Transport> is null or empty");
//必须按字母排列顺序确保相同内容的transport列表组合的name相同而不会因为list的顺序不同产生不同的name
this.name = tmpgroup.stream().sorted().collect(Collectors.joining(";"));
//this.watch = first.watch;
this.subprotocol = first.subprotocol;
this.protocol = first.protocol;
this.tcp = "TCP".equalsIgnoreCase(first.protocol);
this.group = first.group;
this.bufferPool = first.bufferPool;
this.clientAddress = first.clientAddress;
Set<InetSocketAddress> addrs = new HashSet<>();
transports.forEach(t -> addrs.addAll(Arrays.asList(t.getRemoteAddresses())));
updateRemoteAddresses(addrs);
}
public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) {
InetSocketAddress[] oldAddresses = this.remoteAddres;
List<InetSocketAddress> list = new ArrayList<>();
@@ -113,6 +88,30 @@ public final class Transport {
return oldAddresses;
}
public final boolean addRemoteAddresses(final InetSocketAddress addr) {
if (addr == null) return false;
synchronized (this) {
if (this.remoteAddres == null) {
this.remoteAddres = new InetSocketAddress[]{addr};
} else {
for (InetSocketAddress i : this.remoteAddres) {
if (addr.equals(i)) return false;
}
this.remoteAddres = Utility.append(remoteAddres, addr);
}
return true;
}
}
public final boolean removeRemoteAddresses(InetSocketAddress addr) {
if (addr == null) return false;
if (this.remoteAddres == null) return false;
synchronized (this) {
this.remoteAddres = Utility.remove(remoteAddres, addr);
}
return true;
}
public String getName() {
return name;
}

View File

@@ -12,6 +12,7 @@ import java.nio.channels.AsynchronousChannelGroup;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.util.stream.Collectors;
import org.redkale.service.Service;
import org.redkale.util.ObjectPool;
@@ -53,14 +54,24 @@ public class TransportFactory {
return groupAddrs.get(addr);
}
public TransportGroupInfo findGroupInfo2(String group) {
public TransportGroupInfo findGroupInfo(String group) {
if (group == null) return null;
return groupInfos.get(group);
}
public TransportFactory addGroupInfo(String name, InetSocketAddress... addrs) {
addGroupInfo(new TransportGroupInfo(name, addrs));
return this;
public boolean addGroupInfo(String groupName, InetSocketAddress... addrs) {
addGroupInfo(new TransportGroupInfo(groupName, addrs));
return true;
}
public boolean removeGroupInfo(String groupName, InetSocketAddress addr) {
if (groupName == null || groupName.isEmpty() || addr == null) return false;
if (!groupName.equals(groupAddrs.get(addr))) return false;
TransportGroupInfo group = groupInfos.get(groupName);
if (group == null) return false;
group.removeAddress(addr);
groupAddrs.remove(addr);
return true;
}
public TransportFactory addGroupInfo(String name, Set<InetSocketAddress> addrs) {
@@ -116,7 +127,7 @@ public class TransportFactory {
}
if (info == null) return null;
if (sncpAddress != null) addresses.remove(sncpAddress);
return new Transport("remotes", info.protocol, info.subprotocol, this.bufferPool, this.channelGroup, sncpAddress, addresses);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this.bufferPool, this.channelGroup, sncpAddress, addresses);
}
private Transport loadTransport(final String groupName, InetSocketAddress sncpAddress) {
@@ -130,6 +141,10 @@ public class TransportFactory {
return executor;
}
public List<TransportGroupInfo> getGroupInfos() {
return new ArrayList<>(this.groupInfos.values());
}
public void addSncpService(Service service) {
if (service == null) return;
services.add(new WeakReference<>(service));

View File

@@ -90,21 +90,35 @@ public class TransportGroupInfo {
}
public boolean containsAddress(InetSocketAddress addr) {
synchronized (this) {
if (this.addresses == null) return false;
return this.addresses.contains(addr);
}
}
public void removeAddress(InetSocketAddress addr) {
if (addr == null) return;
synchronized (this) {
if (this.addresses == null) return;
this.addresses.remove(addr);
}
}
public void putAddress(InetSocketAddress addr) {
if (addr == null) return;
synchronized (this) {
if (this.addresses == null) this.addresses = new HashSet<>();
this.addresses.add(addr);
}
}
public void putAddress(Set<InetSocketAddress> addrs) {
if (addrs == null) return;
synchronized (this) {
if (this.addresses == null) this.addresses = new HashSet<>();
this.addresses.addAll(addrs);
}
}
@Override
public String toString() {

View File

@@ -1,26 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.IOException;
/**
* 默认Servlet, 没有配置RestServlet实现类则使用该默认类
* <p>
* 详情见: https://redkale.org
*
* @deprecated
* @author zhangjx
*/
@Deprecated
public class DefaultRestServlet extends RestHttpServlet<Object> {
@Override
protected Object currentUser(HttpRequest req) throws IOException {
return new Object();
}
}

View File

@@ -1,538 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.*;
import org.redkale.net.Response;
import org.redkale.net.Request;
import org.redkale.util.AnyValue;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.reflect.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.*;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import org.redkale.service.RetResult;
/**
* 直接只用HttpServlet代替, 将在1.9版本移除
*
* <p>
* 详情见: https://redkale.org
*
* @deprecated
* @author zhangjx
*/
@Deprecated
public abstract class HttpBaseServlet extends HttpServlet {
/**
* 使用 org.redkale.util.AuthIgnore 代替
*
* <p>
* 详情见: https://redkale.org
*
* @deprecated
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, TYPE})
@Retention(RUNTIME)
@Deprecated
protected @interface AuthIgnore {
}
/**
* 配合 &#64;WebParam 使用。
* 用于对&#64;WebParam中参数的来源类型
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
protected enum ParamSourceType {
PARAMETER, HEADER, COOKIE;
}
/**
*
* 使用 org.redkale.net.http.HttpParam 代替
*
* <p>
* 详情见: https://redkale.org
*
* @deprecated
* @author zhangjx
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@Repeatable(WebParams.class)
@Deprecated
protected @interface WebParam {
String name(); //参数名
Class type(); //参数的数据类型
String comment() default ""; //备注描述
ParamSourceType src() default ParamSourceType.PARAMETER; //参数来源类型
int radix() default 10; //转换数字byte/short/int/long时所用的进制数 默认10进制
boolean required() default true; //参数是否必传
}
@Documented
@Target({METHOD})
@Retention(RUNTIME)
protected @interface WebParams {
WebParam[] value();
}
/**
* 使用 org.redkale.net.http.HttpMapping 替代。
* <p>
* 详情见: https://redkale.org
*
* @deprecated
* @author zhangjx
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@Deprecated
protected @interface WebAction {
int actionid() default 0;
String url();
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法
String comment() default ""; //备注描述
boolean inherited() default true; //是否能被继承, 当 HttpBaseServlet 被继承后该方法是否能被子类继承
String result() default "Object"; //输出结果的数据类型
Class[] results() default {}; //输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合
}
/**
* 使用 org.redkale.net.http.HttpMapping 替代。
*
* <p>
* 详情见: https://redkale.org
*
* @deprecated
* @author zhangjx
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@Deprecated
protected @interface WebMapping {
int actionid() default 0;
String url();
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法
String comment() default ""; //备注描述
boolean inherited() default true; //是否能被继承, 当 HttpBaseServlet 被继承后该方法是否能被子类继承
String result() default "Object"; //输出结果的数据类型
Class[] results() default {}; //输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合
}
/**
* 使用 org.redkale.net.http.HttpMapping.cacheseconds 替代。
*
* @deprecated
* @author zhangjx
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@Deprecated
protected @interface HttpCacheable {
/**
* 超时的秒数
*
* @return 超时秒数
*/
int seconds() default 15;
}
private Map.Entry<String, Entry>[] mappings;
private final HttpServlet authSuccessServlet = new HttpServlet() {
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
Entry entry = (Entry) request.getAttribute("_redkale_entry");
if (entry.cacheseconds > 0) {//有缓存设置
CacheEntry ce = entry.cache.get(request.getRequestURI());
if (ce != null && ce.time + entry.cacheseconds > System.currentTimeMillis()) { //缓存有效
response.setStatus(ce.status);
response.setContentType(ce.contentType);
response.finish(ce.getBuffers());
return;
}
response.setBufferHandler(entry.cacheHandler);
}
entry.servlet.execute(request, response);
}
};
private final HttpServlet preSuccessServlet = new HttpServlet() {
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
for (Map.Entry<String, Entry> en : mappings) {
if (request.getRequestURI().startsWith(en.getKey())) {
Entry entry = en.getValue();
if (!entry.checkMethod(request.getMethod())) {
response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error"));
return;
}
request.setAttribute("_redkale_entry", entry);
if (entry.ignore) {
authSuccessServlet.execute(request, response);
} else {
HttpBaseServlet.this.authenticate(entry.moduleid, entry.actionid, request, response, authSuccessServlet);
}
return;
}
}
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
}
};
/**
* <p>
* 预执行方法在execute方法之前运行通常用于常规统计或基础检测例如 : <br>
* <blockquote><pre>
* &#64;Override
* public void preExecute(final HttpRequest request, final HttpResponse response, HttpServlet next) throws IOException {
* if (finer) response.setRecycleListener((req, resp) -&#62; { //记录处理时间比较长的请求
* long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
* if (e &#62; 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
* });
* next.execute(request, response);
* }
* </pre></blockquote>
* <p>
*
* @param request HttpRequest
* @param response HttpResponse
* @param next HttpServlet
*
* @throws IOException IOException
*/
public void preExecute(HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException {
next.execute(request, response);
}
/**
* 使用 public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException 代替
*
* @param moduleid int
* @param actionid int
* @param request HttpRequest
* @param response HttpResponse
*
* @return boolean
* @throws IOException IOException
* @deprecated
*/
@Deprecated
public boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException {
return true;
}
/**
* <p>
* 用户登录或权限验证, 没有注解为&#64;AuthIgnore 的方法会执行authenticate方法, 若验证成功则必须调用next.execute(request, response);进行下一步操作, 例如: <br>
* <blockquote><pre>
* &#64;Override
* public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException {
* UserInfo info = currentUser(request);
* if (info == null) {
* response.finishJson(RET_UNLOGIN);
* return;
* } else if (!info.checkAuth(module, actionid)) {
* response.finishJson(RET_AUTHILLEGAL);
* return;
* }
* next.execute(request, response);
* }
* </pre></blockquote>
* <p>
*
*
* @param moduleid 模块ID来自&#64;WebServlet.moduleid()
* @param actionid 操作ID来自&#64;WebMapping.actionid()
* @param request HttpRequest
* @param response HttpResponse
* @param next HttpServlet
*
* @throws IOException IOException
*/
public abstract void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException;
@Override
public final void execute(HttpRequest request, HttpResponse response) throws IOException {
preExecute(request, response, preSuccessServlet);
}
public final void preInit(HttpContext context, AnyValue config) {
String path = _prefix == null ? "" : _prefix;
WebServlet ws = this.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) path = "";
HashMap<String, Entry> map = load();
this.mappings = new Map.Entry[map.size()];
int i = -1;
for (Map.Entry<String, Entry> en : map.entrySet()) {
mappings[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue());
}
//必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query
Arrays.sort(mappings, (o1, o2) -> o2.getKey().compareTo(o1.getKey()));
}
public final void postDestroy(HttpContext context, AnyValue config) {
}
protected void setHeader(HttpRequest request, String name, Serializable value) {
request.header.setValue(name, String.valueOf(value));
}
protected void addHeader(HttpRequest request, String name, Serializable value) {
request.header.addValue(name, String.valueOf(value));
}
protected String _prefix(HttpServlet servlet) {
return servlet._prefix;
}
private HashMap<String, Entry> load() {
final boolean typeIgnore = this.getClass().getAnnotation(AuthIgnore.class) != null;
WebServlet module = this.getClass().getAnnotation(WebServlet.class);
final int serviceid = module == null ? 0 : module.moduleid();
final HashMap<String, Entry> map = new HashMap<>();
HashMap<String, Class> nameset = new HashMap<>();
final Class selfClz = this.getClass();
Class clz = this.getClass();
do {
if (Modifier.isAbstract(clz.getModifiers())) break;
for (final Method method : clz.getMethods()) {
//-----------------------------------------------
String methodname = method.getName();
if ("service".equals(methodname) || "preExecute".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue;
//-----------------------------------------------
Class[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 2 || paramTypes[0] != HttpRequest.class
|| paramTypes[1] != HttpResponse.class) continue;
//-----------------------------------------------
Class[] exps = method.getExceptionTypes();
if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue;
//-----------------------------------------------
final WebMapping mapping = method.getAnnotation(WebMapping.class);
final WebAction action = method.getAnnotation(WebAction.class);
if (mapping == null && action == null) continue;
final boolean inherited = mapping == null ? action.inherited() : mapping.inherited();
if (!inherited && selfClz != clz) continue; //忽略不被继承的方法
final int actionid = mapping == null ? action.actionid() : mapping.actionid();
final String name = mapping == null ? action.url().trim() : mapping.url().trim();
final String[] methods = mapping == null ? action.methods() : mapping.methods();
if (nameset.containsKey(name)) {
if (nameset.get(name) != clz) continue;
throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + WebMapping.class.getSimpleName() + "(" + name + ")");
}
nameset.put(name, clz);
map.put(name, new Entry(typeIgnore, serviceid, actionid, name, methods, method, createHttpServlet(method)));
}
} while ((clz = clz.getSuperclass()) != HttpBaseServlet.class);
return map;
}
private HttpServlet createHttpServlet(final Method method) {
//------------------------------------------------------------------------------
final String supDynName = HttpServlet.class.getName().replace('.', '/');
final String interName = this.getClass().getName().replace('.', '/');
final String interDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(this.getClass());
final String requestSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Request.class);
final String responseSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Response.class);
final String requestDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpRequest.class);
final String responseDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpResponse.class);
String newDynName = interName + "_Dyn_" + method.getName();
int i = 0;
for (;;) {
try {
Class.forName(newDynName.replace('/', '.'));
newDynName += "_" + (++i);
} catch (Throwable ex) {
break;
}
}
//------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
final String factfield = "_factServlet";
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
{
fv = cw.visitField(ACC_PUBLIC, factfield, interDesc, null, null);
fv.visitEnd();
}
{ //构造函数
mv = (cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = (cw.visitMethod(ACC_PUBLIC, "execute", "(" + requestDesc + responseDesc + ")V", null, new String[]{"java/io/IOException"}));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, factfield, interDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, interName, method.getName(), "(" + requestDesc + responseDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "execute", "(" + requestSupDesc + responseSupDesc + ")V", null, new String[]{"java/io/IOException"});
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, HttpRequest.class.getName().replace('.', '/'));
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, HttpResponse.class.getName().replace('.', '/'));
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "execute", "(" + requestDesc + responseDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
cw.visitEnd();
//------------------------------------------------------------------------------
byte[] bytes = cw.toByteArray();
Class<?> newClazz = new ClassLoader(this.getClass().getClassLoader()) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}.loadClass(newDynName.replace('/', '.'), bytes);
try {
HttpServlet instance = (HttpServlet) newClazz.newInstance();
instance.getClass().getField(factfield).set(instance, this);
return instance;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static final class Entry {
public Entry(boolean typeIgnore, int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
this.moduleid = moduleid;
this.actionid = actionid;
this.name = name;
this.methods = methods;
this.method = method;
this.servlet = servlet;
this.ignore = typeIgnore || method.getAnnotation(AuthIgnore.class) != null;
HttpCacheable hc = method.getAnnotation(HttpCacheable.class);
this.cacheseconds = hc == null ? 0 : hc.seconds() * 1000;
this.cache = cacheseconds > 0 ? new ConcurrentHashMap() : null;
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
int status = response.getStatus();
if (status != 200) return null;
CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers);
cache.put(response.getRequest().getRequestURI(), ce);
return ce.getBuffers();
} : null;
}
public boolean isNeedCheck() {
return this.moduleid != 0 || this.actionid != 0;
}
public boolean checkMethod(final String reqMethod) {
if (methods.length == 0) return true;
for (String m : methods) {
if (reqMethod.equalsIgnoreCase(m)) return true;
}
return false;
}
public final HttpResponse.BufferHandler cacheHandler;
public final ConcurrentHashMap<String, CacheEntry> cache;
public final int cacheseconds;
public final boolean ignore;
public final int moduleid;
public final int actionid;
public final String name;
public final String[] methods;
public final Method method;
public final HttpServlet servlet;
}
private static final class CacheEntry {
public final long time = System.currentTimeMillis();
private final ByteBuffer[] buffers;
private final int status;
private final String contentType;
public CacheEntry(int status, String contentType, ByteBuffer[] bufs) {
this.status = status;
this.contentType = contentType;
final ByteBuffer[] newBuffers = new ByteBuffer[bufs.length];
for (int i = 0; i < newBuffers.length; i++) {
newBuffers[i] = bufs[i].duplicate().asReadOnlyBuffer();
}
this.buffers = newBuffers;
}
public ByteBuffer[] getBuffers() {
final ByteBuffer[] newBuffers = new ByteBuffer[buffers.length];
for (int i = 0; i < newBuffers.length; i++) {
newBuffers[i] = buffers[i].duplicate();
}
return newBuffers;
}
}
}

View File

@@ -222,7 +222,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
}
String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName());
try {
this.resourceHttpServlet = (HttpServlet) Class.forName(resServlet).newInstance();
this.resourceHttpServlet = (HttpServlet) Thread.currentThread().getContextClassLoader().loadClass(resServlet).newInstance();
} catch (Throwable e) {
this.resourceHttpServlet = new HttpResourceServlet();
logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e);

View File

@@ -162,6 +162,24 @@ public class HttpResourceServlet extends HttpServlet {
}
}
public void serRoot(String rootstr) {
if (rootstr == null) return;
try {
this.root = new File(rootstr).getCanonicalFile();
} catch (IOException ioe) {
this.root = new File(rootstr);
}
}
public void serRoot(File file) {
if (file == null) return;
try {
this.root = file.getCanonicalFile();
} catch (IOException ioe) {
this.root = file;
}
}
protected static long parseLenth(String value, long defValue) {
if (value == null) return defValue;
value = value.toUpperCase().replace("B", "");

View File

@@ -47,6 +47,10 @@ public class HttpResult<T> {
return this;
}
public HttpResult<T> cookie(String name, Serializable value) {
return cookie(new HttpCookie(name, String.valueOf(value)));
}
public HttpResult<T> cookie(HttpCookie cookie) {
if (this.cookies == null) this.cookies = new ArrayList<>();
this.cookies.add(cookie);

View File

@@ -38,24 +38,21 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
super.init(config);
}
public List<HttpServlet> getHttpServlets() {
return this.prepare.getServlets();
}
public List<HttpFilter> getHttpFilters() {
return this.prepare.getFilters();
}
/**
* 获取静态资源HttpServlet
*
* @return HttpServlet
*/
public HttpServlet getResourceServlet() {
return ((HttpPrepareServlet) this.prepare).resourceHttpServlet;
}
/**
* 删除HttpFilter
*
* @param filterName HttpFilter名称
*
* @return HttpFilter
*/
public HttpFilter removeFilter(String filterName) {
return (HttpFilter) this.prepare.removeFilter(filterName);
public HttpResourceServlet getResourceServlet() {
return (HttpResourceServlet) ((HttpPrepareServlet) this.prepare).resourceHttpServlet;
}
/**
@@ -111,7 +108,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
*
* @return HttpFilter
*/
public <T extends HttpFilter> T removeFilter(Class<T> filterClass) {
public <T extends HttpFilter> T removeHttpFilter(Class<T> filterClass) {
return (T) this.prepare.removeFilter(filterClass);
}
@@ -175,14 +172,15 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
*
* @param <S> WebSocket
* @param <T> HttpServlet
* @param classLoader ClassLoader
* @param webSocketType WebSocket的类型
* @param prefix url前缀
* @param conf 配置信息
*
* @return RestServlet
*/
public <S extends WebSocket, T extends HttpServlet> T addRestWebSocketServlet(final Class<S> webSocketType, final String prefix, final AnyValue conf) {
T servlet = Rest.createRestWebSocketServlet(webSocketType);
public <S extends WebSocket, T extends HttpServlet> T addRestWebSocketServlet(final ClassLoader classLoader, final Class<S> webSocketType, final String prefix, final AnyValue conf) {
T servlet = Rest.createRestWebSocketServlet(classLoader, webSocketType);
if (servlet != null) this.prepare.addServlet(servlet, prefix, conf);
return servlet;
}
@@ -192,6 +190,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
*
* @param <S> Service
* @param <T> HttpServlet
* @param classLoader ClassLoader
* @param service Service对象
* @param userType 用户数据类型
* @param baseServletType RestServlet基类
@@ -199,8 +198,8 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
*
* @return RestServlet
*/
public <S extends Service, T extends HttpServlet> T addRestServlet(final S service, final Class userType, final Class<T> baseServletType, final String prefix) {
return addRestServlet(null, service, userType, baseServletType, prefix);
public <S extends Service, T extends HttpServlet> T addRestServlet(final ClassLoader classLoader, final S service, final Class userType, final Class<T> baseServletType, final String prefix) {
return addRestServlet(classLoader, null, service, userType, baseServletType, prefix);
}
/**
@@ -208,6 +207,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
*
* @param <S> Service
* @param <T> HttpServlet
* @param classLoader ClassLoader
* @param name 资源名
* @param service Service对象
* @param userType 用户数据类型
@@ -216,7 +216,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
*
* @return RestServlet
*/
public <S extends Service, T extends HttpServlet> T addRestServlet(final String name, final S service, final Class userType, final Class<T> baseServletType, final String prefix) {
public <S extends Service, T extends HttpServlet> T addRestServlet(final ClassLoader classLoader, final String name, final S service, final Class userType, final Class<T> baseServletType, final String prefix) {
T servlet = null;
final boolean sncp = Sncp.isSncpDyn(service);
final String resname = name == null ? (sncp ? Sncp.getResourceName(service) : "") : name;
@@ -236,7 +236,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
}
}
final boolean first = servlet == null;
if (servlet == null) servlet = Rest.createRestServlet(userType, baseServletType, serviceType);
if (servlet == null) servlet = Rest.createRestServlet(classLoader, userType, baseServletType, serviceType);
if (servlet == null) return null; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
try { //若提供动态变更Service服务功能则改Rest服务无法做出相应更新
Field field = servlet.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME);

View File

@@ -27,9 +27,9 @@ import org.redkale.util.*;
*/
public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse> {
public static final int RET_SERVER_ERROR = 1800_0001;
public static final int RET_SERVER_ERROR = 1200_0001;
public static final int RET_METHOD_ERROR = 1800_0002;
public static final int RET_METHOD_ERROR = 1200_0002;
String _prefix = ""; //当前HttpServlet的path前缀
@@ -214,7 +214,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
int i = 0;
for (;;) {
try {
Class.forName(newDynName.replace('/', '.'));
Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.'));
newDynName += "_" + (++i);
} catch (Throwable ex) {
break;

View File

@@ -17,7 +17,7 @@ import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.org.objectweb.asm.Type;
import org.redkale.convert.json.JsonConvert;
import org.redkale.convert.json.*;
import org.redkale.service.*;
import org.redkale.util.*;
import org.redkale.source.Flipper;
@@ -35,6 +35,8 @@ public final class Rest {
static final String REST_SERVICE_FIELD_NAME = "_redkale_service";
static final String REST_JSONCONVERT_FIELD_PREFIX = "_redkale_jsonconvert_";
static final String REST_SERVICEMAP_FIELD_NAME = "_redkale_servicemap"; //如果只有name=""的Service资源则实例中_servicemap必须为null
private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标 第二维度是参数的下标
@@ -109,6 +111,20 @@ public final class Rest {
}
}
static JsonConvert createJsonConvert(RestConvert[] converts) {
if (converts == null || converts.length < 1) return JsonConvert.root();
final JsonFactory childFactory = JsonFactory.root().createChild();
List<Class> types = new ArrayList<>();
for (RestConvert rc : converts) {
if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat");
childFactory.register(rc.type(), false, rc.convertColumns());
childFactory.register(rc.type(), true, rc.ignoreColumns());
childFactory.reloadCoder(rc.type());
types.add(rc.type());
}
return childFactory.getConvert();
}
static String getWebModuleName(Class<? extends Service> serviceType) {
final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
@@ -142,7 +158,7 @@ public final class Rest {
}
}
static <T extends HttpServlet> T createRestWebSocketServlet(final Class<? extends WebSocket> webSocketType) {
static <T extends HttpServlet> T createRestWebSocketServlet(final ClassLoader classLoader, final Class<? extends WebSocket> webSocketType) {
if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet");
if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet");
if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet");
@@ -162,6 +178,7 @@ public final class Rest {
//----------------------------------------------------------------------------------------
final Set<Field> resourcesFieldSet = new LinkedHashSet<>();
final ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
Class clzz = webSocketType;
do {
for (Field field : webSocketType.getDeclaredFields()) {
@@ -216,7 +233,6 @@ public final class Rest {
final String newDynConsumerSimpleName = "_DynRestOnMessageConsumer";
final String newDynConsumerFullName = newDynName + "$" + newDynConsumerSimpleName;
//----------------------------------------------------------------------------------------
ClassLoader loader = Rest.class.getClassLoader();
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
@@ -514,7 +530,7 @@ public final class Rest {
}
}
static <T extends HttpServlet> T createRestServlet(final Class userType0, final Class<T> baseServletType, final Class<? extends Service> serviceType) {
static <T extends HttpServlet> T createRestServlet(final ClassLoader classLoader, final Class userType0, final Class<T> baseServletType, final Class<? extends Service> serviceType) {
if (baseServletType == null || serviceType == null) throw new RuntimeException(" Servlet or Service is null Class on createRestServlet");
if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet");
int mod = baseServletType.getModifiers();
@@ -525,6 +541,7 @@ public final class Rest {
final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String reqDesc = Type.getDescriptor(HttpRequest.class);
final String respDesc = Type.getDescriptor(HttpResponse.class);
final String convertDesc = Type.getDescriptor(JsonConvert.class);
final String retDesc = Type.getDescriptor(RetResult.class);
final String futureDesc = Type.getDescriptor(CompletableFuture.class);
final String flipperDesc = Type.getDescriptor(Flipper.class);
@@ -547,7 +564,7 @@ public final class Rest {
final String supDynName = baseServletType.getName().replace('.', '/');
final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet
ClassLoader loader = Rest.class.getClassLoader();
ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet";
//------------------------------------------------------------------------------
@@ -672,6 +689,7 @@ public final class Rest {
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType);
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
final List<RestConvert[]> restConverts = new ArrayList<>();
for (final MappingEntry entry : entrys) {
RestUploadFile mupload = null;
Class muploadType = null;
@@ -680,6 +698,9 @@ public final class Rest {
final String methodDesc = Type.getMethodDescriptor(method);
final Parameter[] params = method.getParameters();
final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class);
if (rcs != null && rcs.length > 0) restConverts.add(rcs);
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, entry.name, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
//mv.setDebug(true);
mv.debugLine();
@@ -725,6 +746,7 @@ public final class Rest {
RestHeader annhead = param.getAnnotation(RestHeader.class);
if (annhead != null) {
if (ptype != String.class) throw new RuntimeException("@RestHeader must on String Parameter in " + method);
n = annhead.name();
radix = annhead.radix();
comment = annhead.comment();
@@ -801,7 +823,7 @@ public final class Rest {
} else if (ptype == Flipper.class) {
n = "flipper";
} else {
n = ("bean" + i);
throw new RuntimeException("Parameter " + param.getName() + " not found name by @RestParam in " + method);
}
}
if (annhead == null && anncookie == null && annaddr == null && annbody == null && annfile == null
@@ -839,7 +861,7 @@ public final class Rest {
}
}
av0 = mv.visitAnnotation(mappingDesc, true);
String url = "/" + defmodulename.toLowerCase() + "/" + entry.name + (reqpath ? "/" : "");
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename.toLowerCase() + "/" + entry.name + (reqpath ? "/" : "");
av0.visit("url", url);
av0.visit("auth", entry.auth);
av0.visit("cacheseconds", entry.cacheseconds);
@@ -1488,15 +1510,29 @@ public final class Rest {
} else if (RetResult.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + retDesc + ")V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
} else if (HttpResult.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + httprsDesc + ")V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + httprsDesc + ")V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
} else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面
@@ -1510,15 +1546,29 @@ public final class Rest {
} else if (CompletableFuture.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2);//response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + futureDesc + ")V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + futureDesc + ")V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
} else {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + "Ljava/lang/Object;)V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
}
@@ -1537,6 +1587,11 @@ public final class Rest {
fv.visitEnd();
}
for (int i = 1; i <= restConverts.size(); i++) {
fv = cw.visitField(ACC_PRIVATE, REST_JSONCONVERT_FIELD_PREFIX + i, convertDesc, null, null);
fv.visitEnd();
}
//classMap.put("mappings", mappingMaps); //不显示太多信息
{ //toString函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
@@ -1561,6 +1616,11 @@ public final class Rest {
genField.setAccessible(true);
genField.set(obj, en.getValue());
}
for (int i = 0; i < restConverts.size(); i++) {
Field genField = newClazz.getDeclaredField(REST_JSONCONVERT_FIELD_PREFIX + (i + 1));
genField.setAccessible(true);
genField.set(obj, createJsonConvert(restConverts.get(i)));
}
Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME);
typesfield.setAccessible(true);
java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramtypes.size()][];
@@ -1611,10 +1671,10 @@ public final class Rest {
if (mapping == null) mapping = DEFAULT__MAPPING;
this.methodidx = methodidx;
this.ignore = mapping.ignore();
String n = mapping.name().toLowerCase();
String n = mapping.name();
if (n.isEmpty()) {
String t = method.getName().toLowerCase();
int pos = t.indexOf(defmodulename.toLowerCase());
String t = method.getName();
int pos = t.indexOf(defmodulename);
n = pos > 0 ? t.substring(0, pos) : t;
}
this.name = n;

View File

@@ -0,0 +1,41 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@Repeatable(RestConvert.RestConverts.class)
public @interface RestConvert {
Class type();
String[] ignoreColumns() default {};
String[] convertColumns() default {};
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@interface RestConverts {
RestConvert[] value();
}
}

View File

@@ -1,26 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.*;
/**
* 使用 RestServlet 代替
*
* 详情见: https://redkale.org
*
* @author zhangjx
* @deprecated
* @param <T> 当前用户对象类型
*/
@Deprecated
public class RestHttpServlet<T> extends HttpServlet {
protected T currentUser(HttpRequest req) throws IOException {
return null;
}
}

View File

@@ -1,104 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.Serializable;
import java.net.HttpCookie;
import java.util.*;
/**
* 使用 HttpResult 代替
*
* <p>
* 详情见: https://redkale.org
*
* @deprecated
* @author zhangjx
* @param <T> 结果对象的类型
*/
@Deprecated
public class RestOutput<T> {
private Map<String, String> headers;
private List<HttpCookie> cookies;
private String contentType;
private T result;
private int status = 0; //不设置则为 200
private String message;
public RestOutput() {
}
public RestOutput(T result) {
this.result = result;
}
public RestOutput<T> addHeader(String name, Serializable value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(name, String.valueOf(value));
return this;
}
public RestOutput<T> addCookie(HttpCookie cookie) {
if (this.cookies == null) this.cookies = new ArrayList<>();
this.cookies.add(cookie);
return this;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public List<HttpCookie> getCookies() {
return cookies;
}
public void setCookies(List<HttpCookie> cookies) {
this.cookies = cookies;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -37,6 +37,13 @@ public @interface RestWebSocket {
*/
String catalog() default "";
/**
* 是否为二进制消息, 默认为文本消息
*
* @return boolean
*/
boolean binary() default false;
/**
* 是否单用户单连接, 默认单用户单连接
*
@@ -45,11 +52,11 @@ public @interface RestWebSocket {
boolean single() default true;
/**
* WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值:60
* WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值:15
*
* @return int
*/
int liveinterval() default 60;
int liveinterval() default WebSocketServlet.DEFAILT_LIVEINTERVAL;
/**
* 是否屏蔽该类的转换

View File

@@ -8,30 +8,24 @@ package org.redkale.net.http;
import org.redkale.net.http.WebSocketPacket.FrameType;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*;
import org.redkale.convert.Convert;
import org.redkale.util.Comment;
/**
* <blockquote><pre>
* 一个WebSocket连接对应一个WebSocket实体即一个WebSocket会绑定一个TCP连接。
* WebSocket 有两种模式:
* 1) 普通模式: 协议上符合HTML5规范, 其流程顺序如下:
* 协议上符合HTML5规范, 其流程顺序如下:
* 1.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。
* 1.2 createUserid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。
* 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
* 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
* 1.5 onClose WebSocket被关闭后回调此方法。
* 普通模式下 以上方法都应该被重载。
*
* 2) 原始二进制模式: 此模式有别于HTML5规范可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下:
* 2.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。
* 2.2 createWebSocketid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。
* 2.3 onRead WebSocket成功连接后回调此方法 由此方法处理原始的TCP连接 需要业务代码去控制WebSocket的关闭。
* 二进制模式下 以上方法都应该被重载。
* </pre></blockquote>
* <p>
* 详情见: https://redkale.org
@@ -78,7 +72,11 @@ public abstract class WebSocket<G extends Serializable, T> {
String _remoteAddr;//不可能为空
JsonConvert _jsonConvert; //不可能为空
Convert _textConvert; //不可能为空
Convert _binaryConvert; //可能为空
Convert _sendConvert; //不可能为空
java.lang.reflect.Type _messageTextType; //不可能为空
@@ -134,7 +132,7 @@ public abstract class WebSocket<G extends Serializable, T> {
} else if (message instanceof WebSocketPacket) {
return sendPacket((WebSocketPacket) message);
} else {
return sendPacket(new WebSocketPacket(_jsonConvert, json, last));
return sendPacket(new WebSocketPacket(getSendConvert(), json, last));
}
});
}
@@ -143,36 +141,36 @@ public abstract class WebSocket<G extends Serializable, T> {
} else if (message instanceof WebSocketPacket) {
return sendPacket((WebSocketPacket) message);
} else {
return sendPacket(new WebSocketPacket(_jsonConvert, message, last));
return sendPacket(new WebSocketPacket(getSendConvert(), message, last));
}
}
/**
* 给自身发送消息, 消息类型是JavaBean对象
*
* @param convert JsonConvert
* @param convert Convert
* @param message 不可为空, 只能是JSON对象
*
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> send(JsonConvert convert, Object message) {
public final CompletableFuture<Integer> send(Convert convert, Object message) {
return send(convert, message, true);
}
/**
* 给自身发送消息, 消息类型是JavaBean对象
*
* @param convert JsonConvert
* @param convert Convert
* @param message 不可为空, 只能是JavaBean对象
* @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> send(JsonConvert convert, Object message, boolean last) {
public final CompletableFuture<Integer> send(Convert convert, Object message, boolean last) {
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(convert == null ? _jsonConvert : convert, json, last)));
return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, json, last)));
}
return sendPacket(new WebSocketPacket(convert == null ? _jsonConvert : convert, message, last));
return sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, message, last));
}
/**
@@ -184,7 +182,7 @@ public abstract class WebSocket<G extends Serializable, T> {
*/
CompletableFuture<Integer> sendPacket(WebSocketPacket packet) {
CompletableFuture<Integer> rs = this._runner.sendMessage(packet);
if (_engine.finest) _engine.logger.finest("userid:" + userid() + " send websocket message(" + packet + ")" + " on " + this);
if (_engine.finest) _engine.logger.finest("userid:" + getUserid() + " send websocket message(" + packet + ")" + " on " + this);
return rs == null ? CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED) : rs;
}
@@ -216,7 +214,7 @@ public abstract class WebSocket<G extends Serializable, T> {
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.sendMessage(json, last, userids));
}
CompletableFuture<Integer> rs = _engine.node.sendMessage(message, last, userids);
if (_engine.finest) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket message(" + _jsonConvert.convertTo(message) + ")");
if (_engine.finest) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket message(" + message + ")");
return rs;
}
@@ -245,7 +243,7 @@ public abstract class WebSocket<G extends Serializable, T> {
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(json, last));
}
CompletableFuture<Integer> rs = _engine.node.broadcastMessage(message, last);
if (_engine.finest) _engine.logger.finest("broadcast send websocket message(" + _jsonConvert.convertTo(message) + ")");
if (_engine.finest) _engine.logger.finest("broadcast send websocket message(" + message + ")");
return rs;
}
@@ -317,7 +315,7 @@ public abstract class WebSocket<G extends Serializable, T> {
*
* @return userid
*/
public final G userid() {
public final G getUserid() {
return _userid;
}
@@ -348,6 +346,18 @@ public abstract class WebSocket<G extends Serializable, T> {
return _remoteAddr;
}
protected Convert getTextConvert() {
return _textConvert;
}
protected Convert getBinaryConvert() {
return _binaryConvert;
}
protected Convert getSendConvert() {
return _sendConvert;
}
//-------------------------------------------------------------------
/**
* 获取指定userid的WebSocket数组, 没有返回null <br>
@@ -382,6 +392,15 @@ public abstract class WebSocket<G extends Serializable, T> {
return _engine.getLocalWebSockets();
}
/**
* 获取ByteBuffer资源池
*
* @return Supplier
*/
protected Supplier<ByteBuffer> getByteBufferSupplier() {
return this._runner.context.getBufferSupplier();
}
//-------------------------------------------------------------------
/**
* 返回sessionid, null表示连接不合法或异常,默认实现是request.sessionid(true),通常需要重写该方法
@@ -401,14 +420,6 @@ public abstract class WebSocket<G extends Serializable, T> {
*/
protected abstract CompletableFuture<G> createUserid();
/**
* 标记为WebSocketBinary才需要重写此方法
*
* @param channel 请求连接
*/
public void onRead(AsyncConnection channel) {
}
/**
* WebSokcet连接成功后的回调方法
*/
@@ -440,6 +451,15 @@ public abstract class WebSocket<G extends Serializable, T> {
public void onMessage(T message, boolean last) {
}
/**
* 接收到文本消息的回调方法
*
* @param text 消息
* @param last 是否最后一条
*/
public void onMessage(String text, boolean last) {
}
/**
* 接收到二进制消息的回调方法
*
@@ -476,6 +496,6 @@ public abstract class WebSocket<G extends Serializable, T> {
@Override
public String toString() {
return this.userid() + "@" + _remoteAddr;
return this.getUserid() + "@" + _remoteAddr;
}
}

View File

@@ -1,24 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 被标记为 &#64;WebSocketBinary 的WebSocketServlet 将使用原始的TCP传输, 通常用于类似音频/视频传输场景
*
* <p> 详情见: https://redkale.org
* @author zhangjx
*/
@Inherited
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public @interface WebSocketBinary {
}

View File

@@ -12,7 +12,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
import java.util.stream.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.convert.Convert;
import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY;
import org.redkale.util.*;
@@ -40,8 +40,8 @@ public final class WebSocketEngine {
//HttpContext
protected final HttpContext context;
//JsonConvert
protected final JsonConvert convert;
//Convert
protected final Convert sendConvert;
protected final boolean single; //是否单用户单连接
@@ -62,11 +62,11 @@ public final class WebSocketEngine {
private int liveinterval;
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, WebSocketNode node, Logger logger) {
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, WebSocketNode node, Convert sendConvert, Logger logger) {
this.engineid = engineid;
this.single = single;
this.context = context;
this.convert = context.getJsonConvert();
this.sendConvert = sendConvert;
this.node = node;
this.liveinterval = liveinterval;
this.logger = logger;
@@ -84,8 +84,10 @@ public final class WebSocketEngine {
return t;
});
long delay = (interval - System.currentTimeMillis() / 1000 % interval) + index * 5;
final int intervalms = interval * 1000;
scheduler.scheduleWithFixedDelay(() -> {
getLocalWebSockets().forEach(x -> x.sendPing());
long now = System.currentTimeMillis();
getLocalWebSockets().stream().filter(x -> (now - x.getLastSendTime()) > intervalms).forEach(x -> x.sendPing());
}, delay, interval, TimeUnit.SECONDS);
if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + interval + "s) scheduler executor");
}
@@ -133,7 +135,7 @@ public final class WebSocketEngine {
if (more) {
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.convert, message, last));
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
CompletableFuture<Integer> future = null;
if (single) {
@@ -174,7 +176,7 @@ public final class WebSocketEngine {
if (more) {
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.convert, message, last));
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
CompletableFuture<Integer> future = null;
if (single) {

View File

@@ -56,7 +56,7 @@ public abstract class WebSocketNode {
public final void postDestroy(AnyValue conf) {
if (this.localEngine == null) return;
//关掉所有本地本地WebSocket
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.userid()));
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.getUserid()));
if (sncpNodeAddresses != null && localSncpAddress != null) sncpNodeAddresses.removeSetItem("redkale_sncpnodes", localSncpAddress);
}

View File

@@ -10,8 +10,7 @@ import java.io.*;
import java.nio.ByteBuffer;
import java.util.function.Supplier;
import java.util.logging.*;
import org.redkale.convert.ConvertMask;
import org.redkale.convert.json.JsonConvert;
import org.redkale.convert.*;
/**
*
@@ -60,7 +59,7 @@ public final class WebSocketPacket {
protected Object sendJson;
JsonConvert sendConvert;
Convert sendConvert;
ByteBuffer[] sendBuffers;
@@ -93,8 +92,8 @@ public final class WebSocketPacket {
this.last = fin;
}
public WebSocketPacket(JsonConvert convert, Object json, boolean fin) {
this.type = FrameType.TEXT;
public WebSocketPacket(Convert convert, Object json, boolean fin) {
this.type = (convert == null || !convert.isBinary()) ? FrameType.TEXT : FrameType.BINARY;
this.sendConvert = convert;
this.sendJson = json;
this.last = fin;
@@ -160,7 +159,7 @@ public final class WebSocketPacket {
@Override
public String toString() {
return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : "") + "]";
return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : "") + (sendJson != null ? (", json=" + sendJson) : "") + "]";
}
/**

View File

@@ -16,7 +16,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.logging.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.convert.Convert;
/**
* WebSocket的消息接收发送器, 一个WebSocket对应一个WebSocketRunner
@@ -44,23 +44,17 @@ class WebSocketRunner implements Runnable {
private final BlockingQueue<QueueEntry> queue = new ArrayBlockingQueue(1024);
private final boolean wsbinary;
private final BiConsumer<WebSocket, Object> restMessageConsumer; //主要供RestWebSocket使用
protected long lastSendTime;
protected final JsonConvert convert;
WebSocketRunner(Context context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer, AsyncConnection channel, final boolean wsbinary) {
WebSocketRunner(Context context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer, AsyncConnection channel) {
this.context = context;
this.engine = webSocket._engine;
this.webSocket = webSocket;
this.restMessageConsumer = messageConsumer;
this.channel = channel;
this.wsbinary = wsbinary;
this.readBuffer = context.pollBuffer();
this.convert = context.getJsonConvert();
}
@Override
@@ -70,10 +64,6 @@ class WebSocketRunner implements Runnable {
webSocket.onConnected();
channel.setReadTimeoutSecond(300); //读取超时5分钟
if (channel.isOpen()) {
if (wsbinary) {
webSocket.onRead(channel);
return;
}
channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {
private ByteBuffer recentExBuffer;
@@ -118,7 +108,20 @@ class WebSocketRunner implements Runnable {
}
if (packet.type == FrameType.TEXT) {
Object message = convert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
Convert textConvert = webSocket.getTextConvert();
if (textConvert == null) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
webSocket.onMessage(new String(message, "UTF-8"), packet.last);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
}
} else {
Object message = textConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
@@ -132,7 +135,10 @@ class WebSocketRunner implements Runnable {
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
}
} else if (packet.type == FrameType.BINARY) {
Convert binaryConvert = webSocket.getBinaryConvert();
if (binaryConvert == null) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
readBuffer.clear();
@@ -143,6 +149,22 @@ class WebSocketRunner implements Runnable {
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
}
} else {
Object message = binaryConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, message);
} else {
webSocket.onMessage(message, packet.last);
}
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
}
} else if (packet.type == FrameType.PONG) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
@@ -207,7 +229,7 @@ class WebSocketRunner implements Runnable {
if (closed) return CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED);
boolean debug = true;
//System.out.println("推送消息");
if (debug) context.getLogger().log(Level.FINEST, "send web socket message: " + packet);
//if (debug) context.getLogger().log(Level.FINEST, "send web socket message: " + packet);
final CompletableFuture<Integer> futureResult = new CompletableFuture<>();
if (writing.getAndSet(true)) {
queue.add(new QueueEntry(futureResult, packet));
@@ -258,7 +280,7 @@ class WebSocketRunner implements Runnable {
QueueEntry entry = queue.poll();
if (entry != null) {
future = entry.future;
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(context.getBufferSupplier());
ByteBuffer[] buffers = entry.packet.sendBuffers != null ? entry.packet.duplicateSendBuffers() : entry.packet.encode(context.getBufferSupplier());
lastSendTime = System.currentTimeMillis();
channel.write(buffers, buffers, this);
}

View File

@@ -15,7 +15,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.logging.*;
import javax.annotation.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.convert.Convert;
import org.redkale.service.*;
import org.redkale.util.*;
@@ -46,15 +46,12 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
@Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒")
public static final int DEFAILT_LIVEINTERVAL = 60;
public static final int DEFAILT_LIVEINTERVAL = 15;
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
private final MessageDigest digest = getMessageDigest();
@Comment("是否用于二进制流传输")
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
private final BiConsumer<WebSocket, Object> restMessageConsumer = createRestOnMessageConsumer();
protected Type messageTextType; //RestWebSocket时会被修改
@@ -63,11 +60,20 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
protected int liveinterval = DEFAILT_LIVEINTERVAL;
@Resource
protected JsonConvert jsonConvert; //Rest.createRestWebSocketServlet 需要过滤掉已有的@Resource
@Resource(name = "jsonconvert")
protected Convert jsonConvert;
@Resource(name = "$_textconvert")
protected Convert textConvert;
@Resource(name = "$_binaryconvert")
protected Convert binaryConvert;
@Resource(name = "$_sendconvert")
protected Convert sendConvert;
@Resource(name = "$")
protected WebSocketNode node; //Rest.createRestWebSocketServlet 需要过滤掉已有的@Resource
protected WebSocketNode node;
protected WebSocketServlet() {
Type msgtype = String.class;
@@ -90,6 +96,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
@Override
final void preInit(HttpContext context, AnyValue conf) {
if (this.textConvert == null) this.textConvert = jsonConvert;
if (this.binaryConvert == null) this.binaryConvert = jsonConvert;
if (this.sendConvert == null) this.sendConvert = jsonConvert;
InetSocketAddress addr = context.getServerAddress();
if (this.node == null) this.node = createWebSocketNode();
if (this.node == null) { //没有部署SNCP即不是分布式
@@ -97,7 +106,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
}
//存在WebSocketServlet则此WebSocketNode必须是本地模式Service
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.single, context, liveinterval, this.node, logger);
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.single, context, liveinterval, this.node, this.sendConvert, logger);
this.node.init(conf);
this.node.localEngine.init(conf);
}
@@ -131,7 +140,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
final WebSocket webSocket = this.createWebSocket();
webSocket._engine = this.node.localEngine;
webSocket._messageTextType = this.messageTextType;
webSocket._jsonConvert = jsonConvert;
webSocket._textConvert = textConvert;
webSocket._binaryConvert = binaryConvert;
webSocket._sendConvert = sendConvert;
webSocket._remoteAddress = request.getRemoteAddress();
webSocket._remoteAddr = request.getRemoteAddr();
initRestWebSocket(webSocket);
@@ -176,7 +187,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
}
webSocket._userid = userid;
WebSocketServlet.this.node.localEngine.add(webSocket);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel(), wsbinary);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
webSocket._runner = runner;
context.runAsync(runner);
response.finish(true);

View File

@@ -256,13 +256,14 @@ public abstract class Sncp {
* 创建Service的本地模式Class
*
* @param <T> Service子类
* @param classLoader ClassLoader
* @param name 资源名
* @param serviceImplClass Service类
*
* @return Service实例
*/
@SuppressWarnings("unchecked")
protected static <T extends Service> Class<? extends T> createLocalServiceClass(final String name, final Class<T> serviceImplClass) {
protected static <T extends Service> Class<? extends T> createLocalServiceClass(ClassLoader classLoader, final String name, final Class<T> serviceImplClass) {
if (serviceImplClass == null) return null;
if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass;
int mod = serviceImplClass.getModifiers();
@@ -274,7 +275,7 @@ public abstract class Sncp {
final String clientDesc = Type.getDescriptor(SncpClient.class);
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
ClassLoader loader = Sncp.class.getClassLoader();
ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceImplClass.getSimpleName();
if (!name.isEmpty()) {
boolean normal = true;
@@ -285,7 +286,7 @@ public abstract class Sncp {
newDynName += "_" + (normal ? name : hash(name));
}
try {
return (Class<T>) Class.forName(newDynName.replace('/', '.'));
return (Class<T>) loader.loadClass(newDynName.replace('/', '.'));
} catch (Throwable ex) {
}
//------------------------------------------------------------------------------
@@ -738,7 +739,7 @@ public abstract class Sncp {
public static <T extends Service> T createSimpleLocalService(final Class<T> serviceImplClass,
final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) {
return createLocalService("", serviceImplClass, ResourceFactory.root(), transportFactory, clientSncpAddress, Utility.ofSet(groups), null);
return createLocalService(null, "", serviceImplClass, ResourceFactory.root(), transportFactory, clientSncpAddress, Utility.ofSet(groups), null);
}
/**
@@ -746,6 +747,7 @@ public abstract class Sncp {
* 创建本地模式Service实例
*
* @param <T> Service泛型
* @param classLoader ClassLoader
* @param name 资源名
* @param serviceImplClass Service类
* @param resourceFactory ResourceFactory
@@ -758,6 +760,7 @@ public abstract class Sncp {
*/
@SuppressWarnings("unchecked")
public static <T extends Service> T createLocalService(
final ClassLoader classLoader,
final String name,
final Class<T> serviceImplClass,
final ResourceFactory resourceFactory,
@@ -766,7 +769,7 @@ public abstract class Sncp {
final Set<String> groups,
final AnyValue conf) {
try {
final Class newClazz = createLocalServiceClass(name, serviceImplClass);
final Class newClazz = createLocalServiceClass(classLoader, name, serviceImplClass);
T rs = (T) newClazz.newInstance();
//--------------------------------------
Service remoteService = null;
@@ -780,7 +783,7 @@ public abstract class Sncp {
if (!field.getType().isAssignableFrom(newClazz)) continue;
field.setAccessible(true);
if (remoteService == null && clientSncpAddress != null) {
remoteService = createRemoteService(name, serviceImplClass, transportFactory, clientSncpAddress, groups, conf);
remoteService = createRemoteService(classLoader, name, serviceImplClass, transportFactory, clientSncpAddress, groups, conf);
}
if (remoteService != null) field.set(rs, remoteService);
}
@@ -821,7 +824,7 @@ public abstract class Sncp {
public static <T extends Service> T createSimpleRemoteService(final Class<T> serviceImplClass,
final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) {
return createRemoteService("", serviceImplClass, transportFactory, clientSncpAddress, Utility.ofSet(groups), null);
return createRemoteService(null, "", serviceImplClass, transportFactory, clientSncpAddress, Utility.ofSet(groups), null);
}
/**
@@ -865,6 +868,7 @@ public abstract class Sncp {
* 创建远程模式的Service实例
*
* @param <T> Service泛型
* @param classLoader ClassLoader
* @param name 资源名
* @param serviceTypeOrImplClass Service类
* @param transportFactory TransportFactory
@@ -877,6 +881,7 @@ public abstract class Sncp {
@SuppressWarnings("unchecked")
public static <T extends Service> T createRemoteService(
final ClassLoader classLoader,
final String name,
final Class<T> serviceTypeOrImplClass,
final TransportFactory transportFactory,
@@ -893,12 +898,12 @@ public abstract class Sncp {
final String clientDesc = Type.getDescriptor(SncpClient.class);
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
ClassLoader loader = Sncp.class.getClassLoader();
ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName();
try {
Class newClazz = Class.forName(newDynName.replace('/', '.'));
Class newClazz = loader.loadClass(newDynName.replace('/', '.'));
T rs = (T) newClazz.newInstance();
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
client.setRemoteGroups(groups);
client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups));
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client");
@@ -987,7 +992,7 @@ public abstract class Sncp {
mv.visitEnd();
}
int i = -1;
for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass)) {
for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass)) {
final int index = ++i;
final java.lang.reflect.Method method = entry.method;
{
@@ -1091,7 +1096,7 @@ public abstract class Sncp {
}.loadClass(newDynName.replace('/', '.'), bytes);
try {
T rs = (T) newClazz.newInstance();
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
client.setRemoteGroups(groups);
client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups));
{

View File

@@ -310,7 +310,7 @@ public final class SncpDynServlet extends SncpServlet {
+ "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid;
while (true) {
try {
Class.forName(newDynName.replace('/', '.'));
Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.'));
newDynName += "_";
} catch (Throwable ex) {
break;

View File

@@ -9,7 +9,6 @@ import org.redkale.net.PrepareServlet;
import org.redkale.util.AnyValue;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import org.redkale.service.Service;
import org.redkale.util.*;
@@ -55,10 +54,6 @@ public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpR
return rs;
}
public List<SncpServlet> getSncpServlets() {
return new ArrayList<>(getServlets());
}
@Override
public void init(SncpContext context, AnyValue config) {
super.init(context, config); //必须要执行

View File

@@ -37,15 +37,12 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
super.init(config);
}
/**
* 删除SncpFilter
*
* @param filterName SncpFilter名称
*
* @return SncpFilter
*/
public SncpFilter removeFilter(String filterName) {
return (SncpFilter) this.prepare.removeFilter(filterName);
public List<SncpServlet> getSncpServlets() {
return this.prepare.getServlets();
}
public List<SncpFilter> getSncpFilters() {
return this.prepare.getFilters();
}
/**
@@ -56,7 +53,7 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
*
* @return SncpFilter
*/
public <T extends SncpFilter> T removeFilter(Class<T> filterClass) {
public <T extends SncpFilter> T removeSncpFilter(Class<T> filterClass) {
return (T) this.prepare.removeFilter(filterClass);
}
@@ -89,10 +86,6 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
this.prepare.addServlet(sds, null, Sncp.getConf(sncpService));
}
public List<SncpServlet> getSncpServlets() {
return ((SncpPrepareServlet) this.prepare).getSncpServlets();
}
@Override
@SuppressWarnings("unchecked")
protected SncpContext createContext() {

View File

@@ -40,6 +40,10 @@ public abstract class SncpServlet extends Servlet<SncpContext, SncpRequest, Sncp
return serviceName;
}
public Class getServiceType() {
return type;
}
public abstract DLong getServiceid();
protected ExecutorService getExecutor() {

View File

@@ -29,35 +29,47 @@ public @interface RetLabel {
String value();
public static abstract class AbstractRetCode {
public static class RetCode {
protected static final Map<Integer, String> rets = new HashMap();
protected final Map<Integer, String> rets = new HashMap();
protected static void load(Class clazz) {
rets.putAll(RetLoader.load(clazz));
protected final Class clazz;
protected boolean inited;
public RetCode(Class clazz) {
this.clazz = clazz;
}
public static RetResult retResult(int retcode) {
public RetResult retResult(int retcode) {
if (retcode == 0) return RetResult.success();
return new RetResult(retcode, retInfo(retcode));
}
public static RetResult retResult(int retcode, Object... args) {
public RetResult retResult(int retcode, Object... args) {
if (retcode == 0) return RetResult.success();
if (args == null || args.length < 1) return new RetResult(retcode, retInfo(retcode));
String info = MessageFormat.format(retInfo(retcode), args);
return new RetResult(retcode, info);
}
public static RetResult set(RetResult result, int retcode, Object... args) {
public RetResult set(RetResult result, int retcode, Object... args) {
if (retcode == 0) return result.retcode(0).retinfo("");
if (args == null || args.length < 1) return result.retcode(retcode).retinfo(retInfo(retcode));
String info = MessageFormat.format(retInfo(retcode), args);
return result.retcode(retcode).retinfo(info);
}
public static String retInfo(int retcode) {
public String retInfo(int retcode) {
if (retcode == 0) return "成功";
if (!this.inited) {
synchronized (this) {
if (!this.inited) {
rets.putAll(RetLoader.load(clazz));
}
this.inited = true;
}
}
return rets.getOrDefault(retcode, "未知错误");
}
}

View File

@@ -13,7 +13,7 @@ import java.util.function.Consumer;
import java.util.logging.*;
import javax.annotation.Resource;
import org.redkale.convert.json.*;
import org.redkale.net.sncp.Sncp;
import org.redkale.net.sncp.*;
import org.redkale.service.*;
import org.redkale.util.*;
@@ -84,7 +84,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
String storeValueStr = prop.getValue("value-type");
if (storeKeyStr != null && storeValueStr != null) {
try {
this.setStoreType(Class.forName(storeKeyStr), Class.forName(storeValueStr));
this.setStoreType(Thread.currentThread().getContextClassLoader().loadClass(storeKeyStr), Thread.currentThread().getContextClassLoader().loadClass(storeValueStr));
} catch (Throwable e) {
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " load key & value store class (" + storeKeyStr + ", " + storeValueStr + ") error", e);
}
@@ -94,7 +94,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler");
if (expireHandlerClass != null) {
try {
this.expireHandler = (Consumer<CacheEntry>) Class.forName(expireHandlerClass).newInstance();
this.expireHandler = (Consumer<CacheEntry>) Thread.currentThread().getContextClassLoader().loadClass(expireHandlerClass).newInstance();
} catch (Throwable e) {
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " new expirehandler class (" + expireHandlerClass + ") instance error", e);
}
@@ -155,6 +155,8 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
}
if (remoteSource != null && !Sncp.isRemote(this)) {
SncpClient client = Sncp.getSncpClient((Service) remoteSource);
if (client != null && client.getRemoteGroupTransport() != null) {
super.runAsync(() -> {
try {
CompletableFuture<List<CacheEntry<K, Object>>> listFuture = remoteSource.queryListAsync();
@@ -173,6 +175,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
});
}
}
}
@Override
public void close() throws Exception { //给Application 关闭时调用

View File

@@ -613,7 +613,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
}
@Override
public <T> CompletableFuture<Integer> updateColumnupdateColumnAsync(final Class<T> clazz, final Serializable id, final String column, final Serializable value) {
public <T> CompletableFuture<Integer> updateColumnAsync(final Class<T> clazz, final Serializable id, final String column, final Serializable value) {
return CompletableFuture.supplyAsync(() -> updateColumn(clazz, id, column, value), getExecutor());
}
@@ -776,7 +776,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
List<byte[]> blobs = null;
for (ColumnValue col : values) {
Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn());
if (attr == null) continue;
if (attr == null) throw new RuntimeException(info.getType() + " cannot found column " + col.getColumn());
attrs.add(attr);
cols.add(col);
if (!virtual) {
@@ -1266,7 +1266,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
for (FilterFuncColumn ffc : columns) {
for (String col : ffc.cols()) {
if (sb.length() > 0) sb.append(", ");
sb.append(ffc.func.getColumn((col == null || col.isEmpty() ? "*" : ("a." + col))));
sb.append(ffc.func.getColumn((col == null || col.isEmpty() ? "*" : info.getSQLColumn("a", col))));
}
}
final String sql = "SELECT " + sb + " FROM " + info.getTable(node) + " a"
@@ -1325,7 +1325,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final Set<String> haset = new HashSet<>();
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info);
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : ("a." + column))) + " FROM " + info.getTable(node) + " a"
final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : info.getSQLColumn("a", column))) + " FROM " + info.getTable(node) + " a"
+ (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
conn.setReadOnly(true);
@@ -1384,7 +1384,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final Set<String> haset = new HashSet<>();
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info);
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT a." + sqlkey + ", " + func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : ("a." + funcColumn)))
final String sql = "SELECT a." + sqlkey + ", " + func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : info.getSQLColumn("a", funcColumn)))
+ " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + sqlkey;
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
conn.setReadOnly(true);

View File

@@ -198,7 +198,7 @@ public interface DataSource {
*
* @return 影响的记录条数CompletableFuture
*/
public <T> CompletableFuture<Integer> updateColumnupdateColumnAsync(final Class<T> clazz, final Serializable id, final String column, final Serializable value);
public <T> CompletableFuture<Integer> updateColumnAsync(final Class<T> clazz, final Serializable id, final String column, final Serializable value);
/**
* 更新符合过滤条件记录的单个字段 <br>

View File

@@ -85,7 +85,7 @@ public final class DataSources {
String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName());
if (DataJdbcSource.class.getName().equals(impl)) return new DataJdbcSource(unitName, readprop, writeprop);
try {
Class ds = Class.forName(impl);
Class ds = Thread.currentThread().getContextClassLoader().loadClass(impl);
for (Constructor d : ds.getConstructors()) {
Class<?>[] paramtypes = d.getParameterTypes();
if (paramtypes.length == 1 && paramtypes[0] == Properties.class) {

View File

@@ -199,7 +199,8 @@ public final class EntityInfo<T> {
this.fullloader = loader;
} else {
this.fullloader = fullloader;
this.table = (t == null) ? type.getSimpleName().toLowerCase() : (t.catalog().isEmpty()) ? t.name() : (t.catalog() + '.' + (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name()));
if (t != null && !t.name().isEmpty() && t.name().indexOf('.') >= 0) throw new RuntimeException(type + " have illegal table.name on @Table");
this.table = (t == null) ? type.getSimpleName().toLowerCase() : (t.catalog().isEmpty()) ? (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name()) : (t.catalog() + '.' + (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name()));
}
DistributeTable dt = type.getAnnotation(DistributeTable.class);
DistributeTableStrategy dts = null;

View File

@@ -44,4 +44,11 @@ public @interface FilterJoinColumn {
* @return 关联字段
*/
String[] columns();
/**
* 备注描述
*
* @return 备注描述
*/
String comment() default "";
}

View File

@@ -129,7 +129,7 @@ public class PoolJdbcSource {
case "com.mysql.cj.jdbc.Driver":
case "com.mysql.jdbc.Driver":
try {
Class.forName("com.mysql.cj.jdbc.MysqlConnectionPoolDataSource");
Thread.currentThread().getContextClassLoader().loadClass("com.mysql.cj.jdbc.MysqlConnectionPoolDataSource");
source = "com.mysql.cj.jdbc.MysqlConnectionPoolDataSource";
} catch (Throwable e) {
source = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource";
@@ -146,10 +146,10 @@ public class PoolJdbcSource {
break;
}
}
final Class clazz = Class.forName(source);
final Class clazz = Thread.currentThread().getContextClassLoader().loadClass(source);
Object pdsource = clazz.newInstance();
if (source.contains(".postgresql.")) {
Class driver = Class.forName("org.postgresql.Driver");
Class driver = Thread.currentThread().getContextClassLoader().loadClass("org.postgresql.Driver");
Properties properties = (Properties) driver.getMethod("parseURL", String.class, Properties.class).invoke(null, url, new Properties());
clazz.getMethod("setServerName", String.class).invoke(pdsource, properties.getProperty("PGHOST"));
clazz.getMethod("setDatabaseName", String.class).invoke(pdsource, properties.getProperty("PGDBNAME"));

View File

@@ -302,6 +302,18 @@ public abstract class AnyValue {
return this;
}
public DefaultAnyValue removeValue(String name, AnyValue value) {
if (name == null || value == null || this.anyEntrys == null) return this;
this.anyEntrys = Utility.remove(this.anyEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value));
return this;
}
public DefaultAnyValue removeValue(String name, String value) {
if (name == null || value == null || this.stringEntrys == null) return this;
this.stringEntrys = Utility.remove(this.stringEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value));
return this;
}
@Override
public AnyValue getAnyValue(String name) {
for (Entry<DefaultAnyValue> en : this.anyEntrys) {
@@ -533,4 +545,52 @@ public abstract class AnyValue {
return value == null ? defaultValue : value;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof AnyValue)) return false;
AnyValue conf = (AnyValue) other;
if (!equals(this.getStringEntrys(), conf.getStringEntrys())) return false;
return equals(this.getAnyEntrys(), conf.getAnyEntrys());
}
private static <T> boolean equals(Entry<? extends T>[] entry1, Entry<T>[] entry2) {
if ((entry1 == null || entry1.length == 0) && (entry2 == null || entry2.length == 0)) return true;
if (entry1.length != entry2.length) return false;
for (int i = 0; i < entry1.length; i++) {
if (!entry1[i].name.equals(entry2[i].name)) return false;
if (!entry1[i].getValue().equals(entry2[i].getValue())) return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 19 * hash + Arrays.deepHashCode(this.getStringEntrys());
hash = 19 * hash + Arrays.deepHashCode(this.getAnyEntrys());
return hash;
}
public String toXML(String rootName) {
return toXMLString(new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n\r\n"), rootName, this, 0).toString();
}
protected static StringBuilder toXMLString(StringBuilder sb, String nodeName, AnyValue conf, int indent) { //indent: 缩进长度
if (indent < 0) indent = 0;
char[] chars = new char[indent];
Arrays.fill(chars, ' ');
final String space = new String(chars);
Entry<AnyValue>[] anys = conf.getAnyEntrys();
sb.append(space).append('<').append(nodeName);
for (Entry<String> en : conf.getStringEntrys()) {
sb.append(' ').append(en.name).append("=\"").append(en.value).append("\"");
}
if (anys == null || anys.length == 0) return sb.append("/>\r\n\r\n");
sb.append(">\r\n\r\n");
for (Entry<AnyValue> en : conf.getAnyEntrys()) {
toXMLString(sb, en.name, en.getValue(), indent + 4);
}
return sb.append(space).append("</").append(nodeName).append(">\r\n\r\n");
}
}

View File

@@ -431,7 +431,7 @@ public interface Attribute<T, F> {
final String interDesc = Type.getDescriptor(clazz);
final String columnDesc = Type.getDescriptor(column);
ClassLoader loader = Attribute.class.getClassLoader();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String newDynName = supDynName + "_Dyn_" + clazz.getSimpleName() + "_"
+ fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array");
if (String.class.getClassLoader() != clazz.getClassLoader()) {
@@ -440,7 +440,7 @@ public interface Attribute<T, F> {
+ fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array");
}
try {
return (Attribute) Class.forName(newDynName.replace('/', '.')).newInstance();
return (Attribute) loader.loadClass(newDynName.replace('/', '.')).newInstance();
} catch (Throwable ex) {
}
//---------------------------------------------------

View File

@@ -214,14 +214,14 @@ public interface Creator<T> {
final String supDynName = Creator.class.getName().replace('.', '/');
final String interName = clazz.getName().replace('.', '/');
final String interDesc = Type.getDescriptor(clazz);
ClassLoader loader = Creator.class.getClassLoader();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String newDynName = supDynName + "_" + clazz.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
if (String.class.getClassLoader() != clazz.getClassLoader()) {
loader = clazz.getClassLoader();
newDynName = interName + "_Dyn" + Creator.class.getSimpleName();
}
try {
return (Creator) Class.forName(newDynName.replace('/', '.')).newInstance();
return (Creator) loader.loadClass(newDynName.replace('/', '.')).newInstance();
} catch (Throwable ex) {
}

View File

@@ -17,7 +17,7 @@ public final class Redkale {
}
public static String getDotedVersion() {
return "1.8.0-rc1";
return "1.8.0";
}
public static int getMajorVersion() {

View File

@@ -0,0 +1,49 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.util;
import java.net.*;
import java.util.HashSet;
/**
*
* @author zhangjx
*/
public class RedkaleClassLoader extends URLClassLoader {
public RedkaleClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
public Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
@Override
public URL[] getURLs() {
return super.getURLs();
}
public URL[] getAllURLs() {
ClassLoader loader = this;
HashSet<URL> set = new HashSet<>();
do {
String loaderName = loader.getClass().getName();
if (loaderName.startsWith("sun.") && loaderName.contains("ExtClassLoader")) continue;
if (loader instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) loader).getURLs()) {
set.add(url);
}
}
} while ((loader = loader.getParent()) != null);
return set.toArray(new URL[set.size()]);
}
}

View File

@@ -35,13 +35,13 @@ public interface Reproduce<D, S> extends BiFunction<D, S, D> {
final String destDesc = Type.getDescriptor(destClass);
final String srcDesc = Type.getDescriptor(srcClass);
String newDynName = supDynName + "Dyn_" + destClass.getSimpleName() + "_" + srcClass.getSimpleName();
ClassLoader loader = Reproduce.class.getClassLoader();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (String.class.getClassLoader() != destClass.getClassLoader()) {
loader = destClass.getClassLoader();
newDynName = destName + "_Dyn" + Reproduce.class.getSimpleName() + "_" + srcClass.getSimpleName();
}
try {
return (Reproduce) Class.forName(newDynName.replace('/', '.')).newInstance();
return (Reproduce) loader.loadClass(newDynName.replace('/', '.')).newInstance();
} catch (Throwable ex) {
}
// ------------------------------------------------------------------------------

View File

@@ -177,11 +177,11 @@ public abstract class TypeToken<T> {
}
private static Type createParameterizedType(final Class rawType, final Type... actualTypeArguments) {
ClassLoader loader = TypeToken.class.getClassLoader();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String newDynName = TypeToken.class.getName().replace('.', '/') + "_Dyn" + System.currentTimeMillis();
for (;;) {
try {
Class.forName(newDynName.replace('/', '.'));
loader.loadClass(newDynName.replace('/', '.'));
newDynName = TypeToken.class.getName().replace('.', '/') + "_Dyn" + Math.abs(System.nanoTime());
} catch (Throwable ex) { //异常说明类不存在
break;

View File

@@ -277,6 +277,19 @@ public final class Utility {
return news;
}
/**
* 将元素从数组中删除
*
* @param <T> 泛型
* @param array 原数组
* @param item 元素
*
* @return 新数组
*/
public static <T> T[] remove(final T[] array, final T item) {
return remove(array, (i) -> item.equals(item));
}
/**
* 将符合条件的元素从数组中删除
*

View File

@@ -10,8 +10,7 @@ import java.net.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*;
import org.redkale.convert.Convert;
import org.redkale.net.http.*;
/**
@@ -27,10 +26,10 @@ public interface WebSocketDesc<G, T> {
public CompletableFuture<Integer> send(Object message, boolean last);
//给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功非0表示错误码
public CompletableFuture<Integer> send(JsonConvert convert, Object message);
public CompletableFuture<Integer> send(Convert convert, Object message);
//给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功非0表示错误码
public CompletableFuture<Integer> send(JsonConvert convert, Object message, boolean last);
public CompletableFuture<Integer> send(Convert convert, Object message, boolean last);
//给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功非0表示错误码
public CompletableFuture<Integer> sendMessage(Object message, G... userids);
@@ -79,9 +78,6 @@ public interface WebSocketDesc<G, T> {
//创建userid null表示异常 必须实现该方法
/* protected abstract */ G createUserid();
//标记为@WebSocketBinary才需要重写此方法
public void onRead(AsyncConnection channel);
//WebSokcet连接成功后的回调方法
public void onConnected();
@@ -94,6 +90,9 @@ public interface WebSocketDesc<G, T> {
//接收到消息的回调方法
public void onMessage(T message, boolean last);
//接收到文本消息的回调方法
public void onMessage(String message, boolean last);
//接收到二进制消息的回调方法
public void onMessage(byte[] bytes, boolean last);

View File

@@ -23,8 +23,8 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
HelloService service = new HelloService();
HttpServer server = new HttpServer();
System.out.println(server.addRestServlet(service, null, SimpleRestServlet.class, "/pipes"));
System.out.println(server.addRestServlet(new HelloService(3), null, SimpleRestServlet.class, "/pipes"));
System.out.println(server.addRestServlet(null, service, null, SimpleRestServlet.class, "/pipes"));
System.out.println(server.addRestServlet(null, new HelloService(3), null, SimpleRestServlet.class, "/pipes"));
DefaultAnyValue conf = DefaultAnyValue.create("port", "" + port);
server.init(conf);

View File

@@ -73,7 +73,7 @@ public class ABMainService implements Service {
HttpServer server = new HttpServer();
server.getLogger().setLevel(Level.WARNING);
server.addRestServlet(service, null, HttpServlet.class, "/pipes");
server.addRestServlet(null, service, null, HttpServlet.class, "/pipes");
resFactory.inject(cservice);
resFactory.inject(bcservice);
@@ -114,7 +114,7 @@ public class ABMainService implements Service {
HttpServer server = new HttpServer();
server.getLogger().setLevel(Level.WARNING);
server.addRestServlet(service, null, HttpServlet.class, "/pipes");
server.addRestServlet(null, service, null, HttpServlet.class, "/pipes");
server.init(DefaultAnyValue.create("port", "" + abport));
server.start();

View File

@@ -30,15 +30,15 @@ public class ChatWebSocket extends WebSocket<Integer, Object> {
@RestOnMessage(name = "sendmessage")
public void onChatMessage(ChatMessage message, Map<String, String> extmap) {
message.fromuserid = userid();
message.fromusername = "用户" + userid();
message.fromuserid = getUserid();
message.fromusername = "用户" + getUserid();
System.out.println("获取消息: message: " + message + ", map: " + extmap);
super.broadcastMessage(message);
}
@RestOnMessage(name = "joinroom")
public void onJoinRoom(int roomid) {
service.joinRoom(userid(), roomid);
service.joinRoom(getUserid(), roomid);
System.out.println("加入房间: roomid: " + roomid);
}