From a50b904acf8b2f32aef9c2b0314651afd6888dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9C=B0=E5=B9=B3=E7=BA=BF?= <22250530@qq.com> Date: Fri, 7 Aug 2015 19:39:19 +0800 Subject: [PATCH] --- src/META-INF/application-template.xml | 54 +- src/com/wentch/redkale/boot/Application.java | 321 ++++------ src/com/wentch/redkale/boot/ClassFilter.java | 53 +- .../wentch/redkale/boot/NodeHttpServer.java | 106 +++- src/com/wentch/redkale/boot/NodeServer.java | 362 ++++++----- .../wentch/redkale/boot/NodeSncpServer.java | 42 +- .../redkale/convert/ConvertException.java | 56 +- src/com/wentch/redkale/convert/Factory.java | 1 - src/com/wentch/redkale/convert/HashedMap.java | 136 ++-- .../redkale/convert/bson/BsonConvert.java | 23 + .../redkale/convert/bson/BsonWriter.java | 5 + .../convert/ext/InetAddressSimpledCoder.java | 2 +- .../redkale/convert/ext/LongSimpledCoder.java | 2 +- .../json/InetAddressJsonSimpledCoder.java | 68 ++ .../redkale/convert/json/JsonFactory.java | 3 + src/com/wentch/redkale/net/Server.java | 27 +- src/com/wentch/redkale/net/Transport.java | 71 +-- .../wentch/redkale/net/http/HttpContext.java | 4 +- .../redkale/net/http/HttpPrepareServlet.java | 17 +- .../wentch/redkale/net/http/HttpServer.java | 34 +- .../wentch/redkale/net/http/HttpServlet.java | 3 + .../wentch/redkale/net/http/WebSocket.java | 33 +- .../redkale/net/http/WebSocketEngine.java | 13 +- .../redkale/net/http/WebSocketGroup.java | 23 +- .../redkale/net/http/WebSocketNode.java | 143 +++++ .../redkale/net/http/WebSocketPacket.java | 13 + .../redkale/net/http/WebSocketRunner.java | 8 +- .../redkale/net/http/WebSocketServlet.java | 24 +- .../wentch/redkale/net/sncp/ServiceEntry.java | 83 --- .../redkale/net/sncp/ServiceWrapper.java | 84 +++ src/com/wentch/redkale/net/sncp/Sncp.java | 584 ++++++++++++++++-- .../wentch/redkale/net/sncp/SncpClient.java | 168 +++-- .../wentch/redkale/net/sncp/SncpContext.java | 4 +- src/com/wentch/redkale/net/sncp/SncpDyn.java | 24 + .../redkale/net/sncp/SncpDynServlet.java | 61 +- .../redkale/net/sncp/SncpPrepareServlet.java | 28 +- .../wentch/redkale/net/sncp/SncpRequest.java | 74 ++- .../wentch/redkale/net/sncp/SncpResponse.java | 20 +- .../wentch/redkale/net/sncp/SncpServer.java | 46 +- .../service/DataCacheListenerService.java | 280 +-------- .../service/DataSQLListenerService.java | 158 ++--- src/com/wentch/redkale/service/MultiRun.java | 27 + src/com/wentch/redkale/service/RemoteOn.java | 42 -- src/com/wentch/redkale/service/Service.java | 15 +- .../redkale/service/WebSocketNodeService.java | 285 ++------- .../redkale/source/DataCacheListener.java | 6 +- .../redkale/source/DataDefaultSource.java | 59 +- .../redkale/source/DataSQLListener.java | 6 +- src/com/wentch/redkale/source/EntityInfo.java | 8 +- .../wentch/redkale/source/FilterBeanNode.java | 8 +- src/com/wentch/redkale/source/FilterInfo.java | 494 --------------- src/com/wentch/redkale/util/Creator.java | 414 ++++++------- src/com/wentch/redkale/util/DLong.java | 13 +- .../redkale/util/DebugMethodVisitor.java | 45 ++ .../MultiService.java => util/Nameable.java} | 5 +- src/com/wentch/redkale/util/Reproduce.java | 7 - .../wentch/redkale/util/ResourceFactory.java | 14 +- src/com/wentch/redkale/util/Utility.java | 11 + 58 files changed, 2411 insertions(+), 2309 deletions(-) create mode 100644 src/com/wentch/redkale/convert/json/InetAddressJsonSimpledCoder.java create mode 100644 src/com/wentch/redkale/net/http/WebSocketNode.java delete mode 100644 src/com/wentch/redkale/net/sncp/ServiceEntry.java create mode 100644 src/com/wentch/redkale/net/sncp/ServiceWrapper.java create mode 100644 src/com/wentch/redkale/net/sncp/SncpDyn.java create mode 100644 src/com/wentch/redkale/service/MultiRun.java delete mode 100644 src/com/wentch/redkale/service/RemoteOn.java delete mode 100644 src/com/wentch/redkale/source/FilterInfo.java rename src/com/wentch/redkale/{service/MultiService.java => util/Nameable.java} (69%) diff --git a/src/META-INF/application-template.xml b/src/META-INF/application-template.xml index 6c3e67eed..7d1efbfac 100644 --- a/src/META-INF/application-template.xml +++ b/src/META-INF/application-template.xml @@ -1,8 +1,24 @@ + + + + + + + + + + + + + - + " + Arrays.toString(entitys)); - BlockingQueue> queue = this.insertQueues.get(sourceName); - if (queue == null) { - synchronized (this.insertQueues) { - queue = this.insertQueues.get(sourceName); - if (queue == null) { - queue = new ArrayBlockingQueue<>(10240); - this.insertQueues.put(sourceName, queue); - final BlockingQueue> tq = queue; - new Thread() { - { - setName(DataCacheListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Insert-Thread"); - setDaemon(true); - } - - @Override - public void run() { - while (true) { - try { - Map.Entry entry = tq.take(); - sendInsert(localGroupName, false, sourceName, entry.getKey(), entry.getValue()); - } catch (Exception e) { - logger.log(Level.SEVERE, this.getName() + " sendInsert occur error", e); - } - } - - } - }.start(); - } - } - } - try { - queue.put(new SimpleEntry<>(clazz, entitys)); - } catch (Exception e) { - logger.log(Level.WARNING, this.getClass().getSimpleName() + " put insert queue error " + Arrays.toString(entitys), e); - } - } - - @RemoteOn - public void sendInsert(String group, boolean ignoreRemote, String sourceName, Class clazz, T... entitys) { - if (nodesmap == null || groups == null) return; - if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendInsert " + Arrays.toString(entitys)); - for (Map.Entry> en : groups.entrySet()) { - if (group != null && group.equals(en.getKey())) { //同机房 - for (String onode : en.getValue()) { - if (onode.equals(localNodeName)) continue; - DataCacheListenerService service = nodesmap.get(onode); - if (service != null) { - try { - service.sendInsert(group, false, sourceName, clazz, entitys); - } catch (Exception e) { - logger.log(Level.FINE, this.getClass().getSimpleName() + " send insert error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e); - } - } - } - if (ignoreRemote) break; - } else if (!ignoreRemote) { - for (String onode : en.getValue()) { - DataCacheListenerService service = nodesmap.get(onode); - if (service != null) { - try { - service.sendInsert(group, false, sourceName, clazz, entitys); - break; //有一个成功就退出 - } catch (Exception e) { - logger.log(Level.FINE, this.getClass().getSimpleName() + " send insert error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e); - } - } - } - } - } - } - - public final void onSendInsert(String group, boolean ignoreRemote, String sourceName, Class clazz, T... entitys) { - if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + this.localNodeName + "," + sourceName + ") onSendInsert " + Arrays.toString(entitys)); - ((DataDefaultSource) sourcesmap.get(sourceName)).insertCache(entitys); - if (!this.localGroupName.equals(group)) sendInsert(this.localGroupName, true, sourceName, clazz, entitys); //不是同一机房来的资源需要同步到其他同机房的节点上 + @MultiRun(async = true) + public void updateCache(Class clazz, T... entitys) { + ((DataDefaultSource) source).updateCache(clazz, entitys); } @Override - public void update(String sourceName, Class clazz, T... values) { - if (finest) logger.finest("(source:" + sourceName + ") update " + clazz + " --> " + Arrays.toString(values)); - BlockingQueue> queue = this.updateQueues.get(sourceName); - if (queue == null) { - synchronized (this.updateQueues) { - queue = this.updateQueues.get(sourceName); - if (queue == null) { - queue = new ArrayBlockingQueue<>(10240); - this.updateQueues.put(sourceName, queue); - final BlockingQueue> tq = queue; - new Thread() { - { - setName(DataCacheListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Update-Thread"); - setDaemon(true); - } - - @Override - public void run() { - while (true) { - try { - Map.Entry entry = tq.take(); - sendUpdate(localGroupName, false, sourceName, entry.getKey(), entry.getValue()); - } catch (Exception e) { - logger.log(Level.SEVERE, this.getName() + " sendUpdate occur error", e); - } - } - - } - }.start(); - } - } - } - try { - queue.put(new SimpleEntry<>(clazz, values)); - } catch (Exception e) { - logger.log(Level.WARNING, this.getClass().getSimpleName() + " put update queue error " + clazz + "," + Arrays.toString(values), e); - } + @MultiRun(async = true) + public void deleteCache(Class clazz, Serializable... ids) { + ((DataDefaultSource) source).deleteCache(clazz, ids); } - @RemoteOn - public void sendUpdate(String group, boolean ignoreRemote, String sourceName, Class clazz, T... entitys) { - if (nodesmap == null || groups == null) return; - if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendUpdate " + Arrays.toString(entitys)); - for (Map.Entry> en : groups.entrySet()) { - if (group != null && group.equals(en.getKey())) { //同机房 - for (String onode : en.getValue()) { - if (onode.equals(localNodeName)) continue; - DataCacheListenerService service = nodesmap.get(onode); - if (service != null) { - try { - service.sendUpdate(group, false, sourceName, clazz, entitys); - } catch (Exception e) { - logger.log(Level.FINE, this.getClass().getSimpleName() + " send update error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e); - } - } - } - if (ignoreRemote) break; - } else if (!ignoreRemote) { - for (String onode : en.getValue()) { - DataCacheListenerService service = nodesmap.get(onode); - if (service != null) { - try { - service.sendUpdate(group, false, sourceName, clazz, entitys); - break; //有一个成功就退出 - } catch (Exception e) { - logger.log(Level.FINE, this.getClass().getSimpleName() + " send update error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e); - } - } - } - } - } - } - - public final void onSendUpdate(String group, boolean ignoreRemote, String sourceName, Class clazz, T... entitys) { - if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onSendUpdate " + Arrays.toString(entitys)); - ((DataDefaultSource) sourcesmap.get(sourceName)).updateCache(clazz, entitys); - if (!this.localGroupName.equals(group)) sendUpdate(this.localGroupName, true, sourceName, clazz, entitys); //不是同一机房来的资源需要同步到其他同机房的节点上 - } - - @Override - public void delete(String sourceName, Class clazz, Serializable... ids) { - if (finest) logger.finest("(source:" + sourceName + ") delete " + clazz + " --> " + Arrays.toString(ids)); - BlockingQueue> queue = this.deleteQueues.get(sourceName); - if (queue == null) { - synchronized (this.deleteQueues) { - queue = this.deleteQueues.get(sourceName); - if (queue == null) { - queue = new ArrayBlockingQueue<>(10240); - this.deleteQueues.put(sourceName, queue); - final BlockingQueue> tq = queue; - new Thread() { - { - setName(DataCacheListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Delete-Thread"); - setDaemon(true); - } - - @Override - public void run() { - while (true) { - try { - Map.Entry entry = tq.take(); - sendDelete(localGroupName, false, sourceName, entry.getKey(), entry.getValue()); - } catch (Exception e) { - logger.log(Level.SEVERE, this.getName() + " sendDelete occur error", e); - } - } - - } - }.start(); - } - } - } - try { - queue.put(new SimpleEntry<>(clazz, ids)); - } catch (Exception e) { - logger.log(Level.WARNING, this.getClass().getSimpleName() + " put delete queue error " + clazz + "," + Arrays.toString(ids), e); - } - } - - @RemoteOn - public void sendDelete(String group, boolean ignoreRemote, String sourceName, Class clazz, Serializable... ids) { - if (nodesmap == null || groups == null) return; - if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendDelete " + Arrays.toString(ids)); - for (Map.Entry> en : groups.entrySet()) { - if (group != null && group.equals(en.getKey())) { //同机房 - for (String onode : en.getValue()) { - if (onode.equals(localNodeName)) continue; - DataCacheListenerService service = nodesmap.get(onode); - if (service != null) { - try { - service.sendDelete(group, false, sourceName, clazz, ids); - } catch (Exception e) { - logger.log(Level.FINE, this.getClass().getSimpleName() + " send delete error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(ids) + ")", e); - } - } - } - if (ignoreRemote) break; - } else if (!ignoreRemote) { - for (String onode : en.getValue()) { - DataCacheListenerService service = nodesmap.get(onode); - if (service != null) { - try { - service.sendDelete(group, false, sourceName, clazz, ids); - break; //有一个成功就退出 - } catch (Exception e) { - logger.log(Level.FINE, this.getClass().getSimpleName() + " send delete error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(ids) + ")", e); - } - } - } - } - } - } - - public final void onSendDelete(String group, boolean ignoreRemote, String sourceName, Class clazz, Serializable... ids) { - if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onSendDelete " + clazz.getName() + " " + Arrays.toString(ids)); - ((DataDefaultSource) sourcesmap.get(sourceName)).deleteCache(clazz, ids); - if (!this.localGroupName.equals(group)) sendDelete(this.localGroupName, true, sourceName, clazz, ids); //不是同一机房来的资源需要同步到其他同机房的节点上 - } } diff --git a/src/com/wentch/redkale/service/DataSQLListenerService.java b/src/com/wentch/redkale/service/DataSQLListenerService.java index c9795ff1d..1b1961fa0 100644 --- a/src/com/wentch/redkale/service/DataSQLListenerService.java +++ b/src/com/wentch/redkale/service/DataSQLListenerService.java @@ -17,162 +17,102 @@ import java.util.logging.*; import javax.annotation.Resource; /** + * 暂时不实现 * * @author zhangjx */ @AutoLoad(false) public class DataSQLListenerService implements DataSQLListener, Service { - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL"; - private boolean finest; + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - @Resource(name = "APP_NODE") - private String localNodeName = ""; - - private String localIDCName = ""; + private final boolean finest = logger.isLoggable(Level.FINEST); @Resource(name = "APP_HOME") private File home; - private File root; + @Resource(name = "$") + private DataSource source; - @Resource(name = ".*") - HashMap sourcemaps; + private final BlockingQueue queue = new ArrayBlockingQueue<>(1024 * 1024); - private ConcurrentHashMap> queues = new ConcurrentHashMap<>(); - - @Resource - private HashMap nodemaps; - - private final HashSet allidcs = new HashSet<>(); - - private ConcurrentHashMap syncfiles = new ConcurrentHashMap<>(); + private PrintStream syncfile; @Override public void init(AnyValue config) { - finest = logger.isLoggable(Level.FINEST); - //nodename的前两位字符表示机房ID - if (localNodeName.length() > 2) localIDCName = getIDC(localNodeName); - if (finest) logger.fine("LocalNodeName: " + localNodeName + ", " + localIDCName + " " + this.nodemaps); - if (this.nodemaps == null) return; - this.nodemaps.forEach((x, y) -> allidcs.add(x.substring(0, 2))); + new Thread() { + { + setName(DataSQLListener.class.getSimpleName() + "-Thread"); + setDaemon(true); + } + + @Override + public void run() { + while (true) { + try { + String sql = queue.take(); + send(sql); + } catch (Exception e) { + logger.log(Level.SEVERE, this.getName() + " occur error"); + } + } + } + }.start(); + } @Override public void destroy(AnyValue config) { - this.syncfiles.forEach((x, y) -> { - y.close(); - }); + if (syncfile != null) syncfile.close(); } - private void write(String node, String sourceName, String... sqls) { - if (sourceName == null || sourceName.isEmpty()) sourceName = "<>"; - String key = node + "-" + sourceName; - PrintStream channel = syncfiles.get(key); + private void write(String... sqls) { try { - if (channel == null) { - if (this.root == null) { - this.root = new File(home, "dbsync"); - this.root.mkdirs(); - } - channel = new PrintStream(new FileOutputStream(new File(this.root, key + ".sql"), true), false, "UTF-8"); - syncfiles.put(key, channel); + if (syncfile == null) { + File root = new File(home, "dbsync"); + root.mkdirs(); + syncfile = new PrintStream(new FileOutputStream(new File(root, "sql-" + name() + ".sql"), true), false, "UTF-8"); } for (String sql : sqls) { - channel.print(sql + ";\r\n"); + syncfile.print(sql + ";\r\n"); } - channel.flush(); + syncfile.flush(); } catch (Exception e) { - logger.log(Level.WARNING, "write sql file error. (" + node + ", " + sourceName + ", " + Arrays.toString(sqls) + ")", e); + logger.log(Level.WARNING, "write sql file error. (" + name() + ", " + Arrays.toString(sqls) + ")", e); } } @Override - public void insert(String sourceName, String... sqls) { - put(sourceName, sqls); + public void insert(String... sqls) { + put(sqls); } @Override - public void update(String sourceName, String... sqls) { - put(sourceName, sqls); + public void update(String... sqls) { + put(sqls); } @Override - public void delete(String sourceName, String... sqls) { - put(sourceName, sqls); + public void delete(String... sqls) { + put(sqls); } - private void put(final String sourceName, String... sqls) { + private void put(String... sqls) { String date = String.format(format, System.currentTimeMillis()); - BlockingQueue queue = this.queues.get(sourceName); - if (queue == null) { - synchronized (this) { - queue = this.queues.get(sourceName); - if (queue == null) { - queue = new ArrayBlockingQueue<>(1024 * 1024); - this.queues.put(sourceName, queue); - final BlockingQueue tq = queue; - new Thread() { - { - setName(DataSQLListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Thread"); - setDaemon(true); - } - - @Override - public void run() { - while (true) { - try { - String sql = tq.take(); - send(sourceName, sql); - } catch (Exception e) { - logger.log(Level.SEVERE, this.getName() + " occur error"); - } - } - - } - }.start(); - } - } - } - try { - for (String sql : sqls) { - queue.put("/* " + date + " */ " + sql); - } - } catch (Exception e) { - logger.log(Level.WARNING, this.getClass().getSimpleName() + " put queue error" + Arrays.toString(sqls), e); - } - } - - private String getIDC(String nodeName) { - return nodeName.substring(0, 2); - } - - @RemoteOn - public void send(String sourceName, String... sqls) { - if (this.nodemaps == null) return; - final Set idcs = new HashSet<>(); - idcs.add(localIDCName); - nodemaps.forEach((x, y) -> { + for (String sql : sqls) { try { - String idc = getIDC(x); - if (!idcs.contains(idc)) { - y.send(sourceName, sqls); - idcs.add(idc); - } + queue.put("/* " + date + " */ " + sql); } catch (Exception e) { - logger.log(Level.FINE, this.getClass().getSimpleName() + " send error (" + x + ", " + sourceName + ", " + Arrays.toString(sqls) + ")", e); + write(sql); } - }); - allidcs.forEach(x -> { - if (!idcs.contains(x)) write(x, sourceName, sqls); - }); + } } - public final void onSend(String sourceName, String... sqls) { - ((DataDefaultSource) sourcemaps.get(sourceName)).execute(sqls); + @MultiRun + public void send(String... sqls) { + ((DataDefaultSource) source).execute(sqls); } } diff --git a/src/com/wentch/redkale/service/MultiRun.java b/src/com/wentch/redkale/service/MultiRun.java new file mode 100644 index 000000000..d39b43aaa --- /dev/null +++ b/src/com/wentch/redkale/service/MultiRun.java @@ -0,0 +1,27 @@ +/* + * 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 com.wentch.redkale.service; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +public @interface MultiRun { + + boolean samerun() default true; //是否同组节点也运营指定操作 + + boolean diffrun() default true; //是否不同组节点也运营指定操作 + + boolean async() default true; //分布式运行是否采用异步模式 +} diff --git a/src/com/wentch/redkale/service/RemoteOn.java b/src/com/wentch/redkale/service/RemoteOn.java deleted file mode 100644 index a1b244409..000000000 --- a/src/com/wentch/redkale/service/RemoteOn.java +++ /dev/null @@ -1,42 +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 com.wentch.redkale.service; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * * @author zhangjx - */ -/* - * 只能标识在Service类的方法上, 且Service类被实例成RemoteService时才有效。 - * 被@RemoteOn 标记的xxx方法必须存在onXxx方法, 且参数和返回值必须一致, onXxx方法必须声明为public final。 且onXxx方法不会被RemoteService重载。 - * 例如: - * public class XXXService implements Service { - * - * @Resource - * private HashMap nodemaps; - * - * @RemoteOn - * public void send(XXXBean bean){ - * nodemaps.forEach((x, y) -> {if(y != this) y.send(bean);}); - * } - * - * public final void onSend(XXXBean bean){ - * ... - * } - * } - * - * 如果没有public final void onSend(XXXBean bean)方法,生成RemoteService会抛出异常。 - */ -@Inherited -@Documented -@Target({METHOD, TYPE}) -@Retention(RUNTIME) -public @interface RemoteOn { - -} diff --git a/src/com/wentch/redkale/service/Service.java b/src/com/wentch/redkale/service/Service.java index 31f8fa860..1cccd9a41 100644 --- a/src/com/wentch/redkale/service/Service.java +++ b/src/com/wentch/redkale/service/Service.java @@ -8,15 +8,15 @@ package com.wentch.redkale.service; import com.wentch.redkale.util.*; /** - * 所有Service的实现类不得声明为final, 允许远程模式的public方法不能声明为final。 - * + * 所有Service的实现类不得声明为final, 允许远程模式的public方法和public String name()方法都不能声明为final。 + *

* @Resource(name = ".*") * private HashMap nodemap; * 被注入的多个XXXService实例 但不会包含自身的XXXService。 * * @author zhangjx */ -public interface Service { +public interface Service extends Nameable { /** * 该方法必须是可以重复调用, 当reload时需要重复调用init方法 @@ -30,4 +30,13 @@ public interface Service { default void destroy(AnyValue config) { } + + /** + * Service的name, 一个Service在同一进程内可以包含多个实例, 使用name区分 + *

+ * @return + */ + default String name() { + return ""; + } } diff --git a/src/com/wentch/redkale/service/WebSocketNodeService.java b/src/com/wentch/redkale/service/WebSocketNodeService.java index 6c2da413c..982f72eb9 100644 --- a/src/com/wentch/redkale/service/WebSocketNodeService.java +++ b/src/com/wentch/redkale/service/WebSocketNodeService.java @@ -8,265 +8,66 @@ package com.wentch.redkale.service; import com.wentch.redkale.net.http.*; import com.wentch.redkale.util.*; import java.io.*; +import java.net.*; import java.util.*; import java.util.concurrent.*; -import java.util.logging.*; -import javax.annotation.*; /** * * @author zhangjx */ @AutoLoad(false) -public class WebSocketNodeService implements Service { +public class WebSocketNodeService extends WebSocketNode implements Service { - public static final int RETCODE_ENGINE_NULL = 5001; - - public static final int RETCODE_NODESERVICE_NULL = 5002; - - public static final int RETCODE_GROUP_EMPTY = 5005; - - public static final int RETCODE_WSOFFLINE = 5011; - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - protected final boolean finest = logger.isLoggable(Level.FINEST); - - @Resource(name = "APP_NODE") - protected String localNodeName = ""; - - @Resource - protected HashMap nodemaps; - - //用户分布在节点上的队列信息,只保存远程节点的用户分布信息 - protected final ConcurrentHashMap> usernodes = new ConcurrentHashMap(); - - protected final ConcurrentHashMap engines = new ConcurrentHashMap(); - - public void initUserNodes() { - if (this.nodemaps == null || this.nodemaps.isEmpty()) return; - new Thread() { - { - setDaemon(true); - } - - @Override - public void run() { - usernodes.putAll(queryNodes()); - } - }.start(); + @Override + public void init(AnyValue conf) { + super.init(conf); } - public final void addWebSocketEngine(WebSocketEngine engine) { - engines.put(engine.getEngineid(), engine); + @Override + public void destroy(AnyValue conf) { + super.destroy(conf); } - @RemoteOn - public Map> queryNodes() { - Map> rs = new HashMap<>(); - this.nodemaps.forEach((x, y) -> { - if (!rs.isEmpty()) return; - try { - rs.putAll(y.queryNodes()); - } catch (Exception e) { - logger.log(Level.WARNING, this.getClass().getSimpleName() + " query error (" + x + ")", e); - } - }); - return rs; - } - - public final Map> onQueryNodes() { - Map> rs = new HashMap<>(); - rs.putAll(this.usernodes); - return rs; - } - - public void connectSelf(Serializable userid) { - connect(this.localNodeName, userid); - } - - public void disconnectSelf(Serializable userid) { - disconnect(this.localNodeName, userid); - } - - @RemoteOn - public void connect(String nodeid, Serializable userid) { - onConnect(nodeid, userid); - if (this.nodemaps == null) return; - this.nodemaps.forEach((x, y) -> { - try { - if (finest) logger.finest("Node(" + localNodeName + "->" + x + ") send websocket connect event (" + userid + " on " + nodeid + ")"); - y.connect(nodeid, userid); - } catch (Exception e) { - logger.log(Level.WARNING, "Node(" + localNodeName + "->" + x + ") send websocket connect event (" + userid + " on " + nodeid + ")", e); - } - }); - } - - public final void onConnect(String nodeid, Serializable userid) { - if (finest) logger.finest("Node (" + localNodeName + ") receive websocket connect event (" + userid + " on " + nodeid + ")."); - Set userNodelist = usernodes.get(userid); - if (userNodelist == null) { - userNodelist = new CopyOnWriteArraySet<>(); - usernodes.put(userid, userNodelist); - } - userNodelist.add(nodeid); - } - - @RemoteOn - public void disconnect(String nodeid, Serializable userid) { - onDisconnect(nodeid, userid); - if (this.nodemaps == null) return; - this.nodemaps.forEach((x, y) -> { - try { - if (finest) logger.finest("Node(" + localNodeName + "->" + x + ") send websocket disconnect event (" + userid + " on " + nodeid + ")"); - y.disconnect(nodeid, userid); - } catch (Exception e) { - logger.log(Level.WARNING, "Node(" + localNodeName + "->" + x + ") send websocket disconnect event (" + userid + " on " + nodeid + ")", e); - } - }); - } - - public final void onDisconnect(String nodeid, Serializable userid) { - if (finest) logger.finest("Node (" + localNodeName + ") receive websocket disconnect event (" + userid + " on " + nodeid + ")."); - Set userNodelist = usernodes.get(userid); - if (userNodelist == null) return; - userNodelist.remove(nodeid); - if (userNodelist.isEmpty()) usernodes.remove(userid); - } - - @RemoteOn - public int send(String engineid, Serializable groupid, String text) { - return send(engineid, groupid, text, true); - } - - public final int onSend(String engineid, Serializable groupid, String text) { - return onSend(engineid, groupid, text, true); - } - - @RemoteOn - public int send(String engineid, Serializable groupid, String text, boolean last) { - return send0(engineid, groupid, false, text, last); - } - - public final int onSend(String engineid, Serializable groupid, String text, boolean last) { - return onSend0(engineid, groupid, false, text, last); - } - - @RemoteOn - public int send(String engineid, Serializable groupid, boolean recent, String text) { - return send0(engineid, groupid, recent, text, true); - } - - public final int onSend(String engineid, Serializable groupid, boolean recent, String text) { - return onSend0(engineid, groupid, recent, text, true); - } - - @RemoteOn - public int send(String engineid, Serializable groupid, boolean recent, String text, boolean last) { - return send0(engineid, groupid, recent, text, last); - } - - public final int onSend(String engineid, Serializable groupid, boolean recent, String text, boolean last) { - return onSend0(engineid, groupid, recent, text, last); - } - - @RemoteOn - public int send(String engineid, Serializable groupid, byte[] data) { - return send(engineid, groupid, data, true); - } - - public final int onSend(String engineid, Serializable groupid, byte[] data) { - return onSend(engineid, groupid, data, true); - } - - @RemoteOn - public int send(String engineid, Serializable groupid, byte[] data, boolean last) { - return send0(engineid, groupid, false, data, last); - } - - public final int onSend(String engineid, Serializable groupid, byte[] data, boolean last) { - return onSend0(engineid, groupid, false, data, last); - } - - @RemoteOn - public int send(String engineid, Serializable groupid, boolean recent, byte[] data) { - return send0(engineid, groupid, recent, data, true); - } - - public final int onSend(String engineid, Serializable groupid, boolean recent, byte[] data) { - return onSend0(engineid, groupid, recent, data, true); - } - - @RemoteOn - public int send(String engineid, Serializable groupid, boolean recent, byte[] data, boolean last) { - return send0(engineid, groupid, recent, data, last); - } - - public final int onSend(String engineid, Serializable groupid, boolean recent, byte[] data, boolean last) { - return onSend0(engineid, groupid, recent, data, last); - } - - private int send0(String engineid, Serializable groupid, boolean recent, Serializable text, boolean last) { - final Set nodes = usernodes.get(groupid); - if (nodes == null) return RETCODE_WSOFFLINE; //未登录 - int rs = 0; - if (nodes.contains(this.localNodeName)) rs = onSend0(engineid, groupid, recent, text, last); - if (nodemaps == null) return rs; - this.nodemaps.forEach((x, y) -> { - if (nodes.contains(x)) { - int irs = -1; - try { - if (text != null && text.getClass() == byte[].class) { - irs = y.send(engineid, groupid, (byte[]) text, last); - } else { - irs = y.send(engineid, groupid, (String) text, last); - } - if (finest) logger.finest("Node(" + localNodeName + "->" + x + ") send websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} finish and result is " + irs); - } catch (Exception e) { - onDisconnect(x, groupid); - logger.log(Level.WARNING, "Node(" + localNodeName + "->" + x + ") send websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} failed and result is " + irs, e); + @Override + public int sendMessage(Serializable groupid, boolean recent, Serializable message, boolean last) { + final Set engineids = localNodes.get(groupid); + if (engineids == null || engineids.isEmpty()) return RETCODE_GROUP_EMPTY; + for (String engineid : engineids) { + final WebSocketEngine engine = engines.get(engineid); + if (engine != null) { //在本地 + final WebSocketGroup group = engine.getWebSocketGroup(groupid); + if (group == null || group.isEmpty()) { + if (finest) logger.finest("receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + message + "'} but result is " + RETCODE_GROUP_EMPTY); + return RETCODE_GROUP_EMPTY; } - } - }); - return rs; - } - - /** - * 消息接受者存在WebSocket并发送成功返回true, 否则返回false - * - * @param engineid - * @param groupid 接收方 - * @param recent 是否只发送最近的WebSocket端 - * @param text - * @return - */ - private int onSend0(String engineid, Serializable groupid, boolean recent, Serializable text, boolean last) { - WebSocketEngine webSocketEngine = engines.get(engineid); - if (webSocketEngine == null) { - if (finest) logger.finest("Node(" + localNodeName + ") receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} but result is " + RETCODE_ENGINE_NULL); - return RETCODE_ENGINE_NULL; - } - WebSocketGroup group = webSocketEngine.getWebSocketGroup(groupid); - if (group == null || group.isEmpty()) { - if (finest) logger.finest("Node(" + localNodeName + ") receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} but result is " + RETCODE_GROUP_EMPTY); - return RETCODE_GROUP_EMPTY; - } - if (finest) logger.finest("Node (" + localNodeName + ") receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'}."); - if (text != null && text.getClass() == byte[].class) { - if (recent) { - group.getRecentWebSocket().send((byte[]) text, last); - } else { - group.getWebSockets().forEach(x -> x.send((byte[]) text, last)); - } - } else { - if (recent) { - group.getRecentWebSocket().send(text.toString(), last); - } else { - group.getWebSockets().forEach(x -> x.send(text.toString(), last)); + group.send(recent, message, last); + } else { //对方连接在远程节点 + return RETCODE_WSOFFLINE; } } return 0; } + @Override + @MultiRun + public void connect(Serializable groupid, InetSocketAddress addr) { + Set addrs = dataNodes.get(groupid); + if (addrs == null) { + addrs = new CopyOnWriteArraySet<>(); + dataNodes.put(groupid, addrs); + } + addrs.add(addr); + if(finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + groupid +" connect from " + addr); + } + + @Override + @MultiRun + public void disconnect(Serializable groupid, InetSocketAddress addr) { + Set addrs = dataNodes.get(groupid); + if (addrs == null) return; + addrs.remove(addr); + if (addrs.isEmpty()) dataNodes.remove(groupid); + if(finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + groupid +" disconnect from " + addr); + } } diff --git a/src/com/wentch/redkale/source/DataCacheListener.java b/src/com/wentch/redkale/source/DataCacheListener.java index 0fef119f2..62daf311a 100644 --- a/src/com/wentch/redkale/source/DataCacheListener.java +++ b/src/com/wentch/redkale/source/DataCacheListener.java @@ -13,9 +13,9 @@ import java.io.Serializable; */ public interface DataCacheListener { - public void insert(String sourceName, Class clazz, T... entitys); + public void insertCache(Class clazz, T... entitys); - public void update(String sourceName, Class clazz, T... entitys); + public void updateCache(Class clazz, T... entitys); - public void delete(String sourceName, Class clazz, Serializable... ids); + public void deleteCache(Class clazz, Serializable... ids); } diff --git a/src/com/wentch/redkale/source/DataDefaultSource.java b/src/com/wentch/redkale/source/DataDefaultSource.java index f517f86cf..dc61f9dc3 100644 --- a/src/com/wentch/redkale/source/DataDefaultSource.java +++ b/src/com/wentch/redkale/source/DataDefaultSource.java @@ -24,7 +24,7 @@ import javax.xml.stream.*; * @author zhangjx */ @SuppressWarnings("unchecked") -public final class DataDefaultSource implements DataSource { +public final class DataDefaultSource implements DataSource, Nameable { public static final String DATASOURCE_CONFPATH = "DATASOURCE_CONFPATH"; @@ -50,6 +50,8 @@ public final class DataDefaultSource implements DataSource { final URL conf; + final boolean cacheForbidden; + private final JDBCPoolSource readPool; private final JDBCPoolSource writePool; @@ -57,10 +59,10 @@ public final class DataDefaultSource implements DataSource { @Resource(name = "property.datasource.nodeid") private int nodeid; - @Resource + @Resource(name = "$") private DataSQLListener writeListener; - @Resource + @Resource(name = "$") private DataCacheListener cacheListener; private static class DataJDBCConnection extends DataConnection { @@ -153,7 +155,7 @@ public final class DataDefaultSource implements DataSource { this.conf = url; this.readPool = new JDBCPoolSource(this, "read", readprop); this.writePool = new JDBCPoolSource(this, "write", writeprop); - EntityInfo.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty("shared-cache-mode")); + this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty("shared-cache-mode")); } public DataDefaultSource(String unitName, Properties readprop, Properties writeprop) { @@ -161,7 +163,7 @@ public final class DataDefaultSource implements DataSource { this.conf = null; this.readPool = new JDBCPoolSource(this, "read", readprop); this.writePool = new JDBCPoolSource(this, "write", writeprop); - EntityInfo.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty("shared-cache-mode")); + this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty("shared-cache-mode")); } public static Map create(final InputStream in) { @@ -257,6 +259,11 @@ public final class DataDefaultSource implements DataSource { return (ConnectionPoolDataSource) pdsource; } + @Override + public final String name() { + return name; + } + @Override public DataConnection createReadConnection() { return new DataJDBCConnection(createReadSQLConnection()); @@ -320,11 +327,11 @@ public final class DataDefaultSource implements DataSource { } private EntityInfo loadEntityInfo(Class clazz) { - return EntityInfo.load(clazz, this.nodeid, fullloader); + return EntityInfo.load(clazz, this.nodeid, this.cacheForbidden, fullloader); } private FilterBeanNode loadFilterBeanNode(Class clazz) { - return FilterBeanNode.load(clazz, this.nodeid, fullloader); + return FilterBeanNode.load(clazz, this.nodeid, this.cacheForbidden, fullloader); } /** @@ -335,13 +342,13 @@ public final class DataDefaultSource implements DataSource { */ @Override public void refreshCache(Class clazz) { - EntityInfo info = EntityInfo.load(clazz, this.nodeid, fullloader); + EntityInfo info = loadEntityInfo(clazz); EntityCache cache = info.getCache(); if (cache == null) return; cache.fullLoad(queryList(clazz, (FilterNode) null)); } - //----------------------insert----------------------------- + //----------------------insertCache----------------------------- /** * 新增对象, 必须是Entity对象 * @@ -467,7 +474,7 @@ public final class DataDefaultSource implements DataSource { } } prestmt.executeBatch(); - if (writeListener != null) writeListener.insert(name, sqls); + if (writeListener != null) writeListener.insert(sqls); if (info.autoGenerated) { ResultSet set = prestmt.getGeneratedKeys(); int i = -1; @@ -488,16 +495,16 @@ public final class DataDefaultSource implements DataSource { for (final T value : values) { cache.insert(value); } - if (cacheListener != null) cacheListener.insert(name, info.getType(), values); + if (cacheListener != null) cacheListener.insertCache(info.getType(), values); } } catch (SQLException e) { throw new RuntimeException(e); } } - public void insertCache(T... values) { + public void insertCache(Class clazz, T... values) { if (values.length == 0) return; - final EntityInfo info = EntityInfo.load((Class) values[0].getClass(), this.nodeid, fullloader); + final EntityInfo info = EntityInfo.load(clazz, this.nodeid, this.cacheForbidden, fullloader); final EntityCache cache = info.getCache(); if (cache == null) return; for (T value : values) { @@ -505,7 +512,7 @@ public final class DataDefaultSource implements DataSource { } } - //-------------------------delete-------------------------- + //-------------------------deleteCache-------------------------- /** * 删除对象, 必须是Entity对象 * @@ -576,7 +583,7 @@ public final class DataDefaultSource implements DataSource { final Statement stmt = conn.createStatement(); stmt.execute(sql); stmt.close(); - if (writeListener != null) writeListener.delete(name, sql); + if (writeListener != null) writeListener.delete(sql); } //------------------------------------ final EntityCache cache = info.getCache(); @@ -584,7 +591,7 @@ public final class DataDefaultSource implements DataSource { final Attribute attr = info.getPrimary(); final Serializable[] keys2 = keys; Serializable[] ids = cache.delete((T t) -> Arrays.binarySearch(keys2, attr.get(t)) >= 0); - if (cacheListener != null) cacheListener.delete(name, info.getType(), ids); + if (cacheListener != null) cacheListener.deleteCache(info.getType(), ids); } catch (SQLException e) { throw new RuntimeException(e); } @@ -618,13 +625,13 @@ public final class DataDefaultSource implements DataSource { final Statement stmt = conn.createStatement(); stmt.execute(sql); stmt.close(); - if (writeListener != null) writeListener.delete(name, sql); + if (writeListener != null) writeListener.delete(sql); } //------------------------------------ final EntityCache cache = info.getCache(); if (cache == null) return; Serializable[] ids = cache.delete(node.createFilterPredicate(info, null)); - if (cacheListener != null) cacheListener.delete(name, info.getType(), ids); + if (cacheListener != null) cacheListener.deleteCache(info.getType(), ids); } catch (SQLException e) { throw new RuntimeException(e); } @@ -717,7 +724,7 @@ public final class DataDefaultSource implements DataSource { } prestmt.executeBatch(); prestmt.close(); - if (writeListener != null) writeListener.update(name, sqls); + if (writeListener != null) writeListener.update(sqls); } //--------------------------------------------------- final EntityCache cache = info.getCache(); @@ -725,7 +732,7 @@ public final class DataDefaultSource implements DataSource { for (final T value : values) { cache.update(value); } - if (cacheListener != null) cacheListener.update(name, clazz, values); + if (cacheListener != null) cacheListener.updateCache(clazz, values); } catch (SQLException e) { throw new RuntimeException(e); } @@ -769,13 +776,13 @@ public final class DataDefaultSource implements DataSource { final Statement stmt = conn.createStatement(); stmt.execute(sql); stmt.close(); - if (writeListener != null) writeListener.update(name, sql); + if (writeListener != null) writeListener.update(sql); } //--------------------------------------------------- final EntityCache cache = info.getCache(); if (cache == null) return; T rs = cache.update(id, (Attribute) info.getAttribute(column), value); - if (cacheListener != null) cacheListener.update(name, info.getType(), rs); + if (cacheListener != null) cacheListener.updateCache(info.getType(), rs); } catch (SQLException e) { throw new RuntimeException(e); } finally { @@ -822,14 +829,14 @@ public final class DataDefaultSource implements DataSource { final Statement stmt = conn.createStatement(); stmt.execute(sql); stmt.close(); - if (writeListener != null) writeListener.update(name, sql); + if (writeListener != null) writeListener.update(sql); } //--------------------------------------------------- final EntityCache cache = info.getCache(); if (cache == null) return; Attribute attr = info.getAttribute(column); T value = cache.updateColumnIncrement(id, attr, incvalue); - if (value != null && cacheListener != null) cacheListener.update(name, info.getType(), value); + if (value != null && cacheListener != null) cacheListener.updateCache(info.getType(), value); } catch (SQLException e) { throw new RuntimeException(e); } finally { @@ -888,13 +895,13 @@ public final class DataDefaultSource implements DataSource { final Statement stmt = conn.createStatement(); stmt.execute(sql); stmt.close(); - if (writeListener != null) writeListener.update(name, sql); + if (writeListener != null) writeListener.update(sql); } //--------------------------------------------------- final EntityCache cache = info.getCache(); if (cache == null) return; cache.update(value, attrs); - if (cacheListener != null) cacheListener.update(name, clazz, value); + if (cacheListener != null) cacheListener.updateCache(clazz, value); } catch (SQLException e) { throw new RuntimeException(e); } diff --git a/src/com/wentch/redkale/source/DataSQLListener.java b/src/com/wentch/redkale/source/DataSQLListener.java index db89d35a6..5697176e2 100644 --- a/src/com/wentch/redkale/source/DataSQLListener.java +++ b/src/com/wentch/redkale/source/DataSQLListener.java @@ -12,9 +12,9 @@ package com.wentch.redkale.source; */ public interface DataSQLListener { - public void insert(String sourceName, String... sqls); + public void insert(String... sqls); - public void update(String sourceName, String... sqls); + public void update(String... sqls); - public void delete(String sourceName, String... sqls); + public void delete(String... sqls); } diff --git a/src/com/wentch/redkale/source/EntityInfo.java b/src/com/wentch/redkale/source/EntityInfo.java index 07daee984..5469e889a 100644 --- a/src/com/wentch/redkale/source/EntityInfo.java +++ b/src/com/wentch/redkale/source/EntityInfo.java @@ -29,8 +29,6 @@ public final class EntityInfo { private static final Logger logger = Logger.getLogger(EntityInfo.class); - static boolean cacheForbidden = false; - //Entity类的类名 private final Class type; @@ -86,14 +84,14 @@ public final class EntityInfo { final int allocationSize; //------------------------------------------------------------ - public static EntityInfo load(Class clazz, final int nodeid, + public static EntityInfo load(Class clazz, final int nodeid, final boolean cacheForbidden, Function fullloader) { EntityInfo rs = entityInfos.get(clazz); if (rs != null) return rs; synchronized (entityInfos) { rs = entityInfos.get(clazz); if (rs == null) { - rs = new EntityInfo(clazz, nodeid, fullloader); + rs = new EntityInfo(clazz, nodeid, cacheForbidden, fullloader); entityInfos.put(clazz, rs); AutoLoad auto = clazz.getAnnotation(AutoLoad.class); if (rs.cache != null && auto != null && auto.value() && fullloader != null) { @@ -104,7 +102,7 @@ public final class EntityInfo { } } - private EntityInfo(Class type, int nodeid, Function, List> fullloader) { + private EntityInfo(Class type, int nodeid, final boolean cacheForbidden, Function, List> fullloader) { this.type = type; //--------------------------------------------- this.nodeid = nodeid; diff --git a/src/com/wentch/redkale/source/FilterBeanNode.java b/src/com/wentch/redkale/source/FilterBeanNode.java index cdf2c309b..3bd2fadd6 100644 --- a/src/com/wentch/redkale/source/FilterBeanNode.java +++ b/src/com/wentch/redkale/source/FilterBeanNode.java @@ -26,21 +26,21 @@ final class FilterBeanNode extends FilterNode { private static final ConcurrentHashMap beanodes = new ConcurrentHashMap<>(); - public static FilterBeanNode load(Class clazz, final int nodeid, + public static FilterBeanNode load(Class clazz, final int nodeid, final boolean cacheForbidden, Function fullloader) { FilterBeanNode rs = beanodes.get(clazz); if (rs != null) return rs; synchronized (beanodes) { rs = beanodes.get(clazz); if (rs == null) { - rs = createNode(clazz, nodeid, fullloader); + rs = createNode(clazz, nodeid, cacheForbidden, fullloader); beanodes.put(clazz, rs); } return rs; } } - private static FilterBeanNode createNode(Class clazz, final int nodeid, + private static FilterBeanNode createNode(Class clazz, final int nodeid, final boolean cacheForbidden, Function fullloader) { Class cltmp = clazz; Set fields = new HashSet<>(); @@ -78,7 +78,7 @@ final class FilterBeanNode extends FilterNode { joinTables.put(joinClass, String.valueOf((char) ('b' + joinTables.size()))); } final String alias = joinTables.get(joinClass); - final EntityInfo secinfo = EntityInfo.load(joinClass, nodeid, fullloader); + final EntityInfo secinfo = EntityInfo.load(joinClass, nodeid, cacheForbidden, fullloader); if (secinfo.getCache() == null || !secinfo.getCache().isFullLoaded()) { joinallcached = false; } diff --git a/src/com/wentch/redkale/source/FilterInfo.java b/src/com/wentch/redkale/source/FilterInfo.java deleted file mode 100644 index 17943e147..000000000 --- a/src/com/wentch/redkale/source/FilterInfo.java +++ /dev/null @@ -1,494 +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 com.wentch.redkale.source; - -import static com.wentch.redkale.source.FilterExpress.*; -import com.wentch.redkale.util.Attribute; -import com.wentch.redkale.util.Ignore; -import java.lang.reflect.*; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Predicate; -import java.util.logging.Logger; -import javax.persistence.*; - -/** - * - * @author zhangjx - * @param - */ -@SuppressWarnings("unchecked") -final class FilterInfo { - - private final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - private static final ConcurrentHashMap infos = new ConcurrentHashMap<>(); - - private final String joinsql; - - private final boolean validCacheJoin; - - private final Class type; - - private final FilterExpressNode rootNode; - - public static FilterInfo load(Class clazz, DataSource source) { - FilterInfo rs = infos.get(clazz); - if (rs != null) return rs; - synchronized (infos) { - rs = infos.get(clazz); - if (rs == null) { - rs = new FilterInfo(clazz, source); - infos.put(clazz, rs); - } - return rs; - } - } - - private FilterInfo(Class type, DataSource source) { - this.type = type; - Class cltmp = type; - Set fields = new HashSet<>(); - StringBuilder joinsb = new StringBuilder(); - final Map joinTables = new HashMap<>(); - final Map getters = new HashMap<>(); - final Map> nodes = new HashMap<>(); - boolean cachejoin = true; - int index = 0; - do { - for (Field field : cltmp.getDeclaredFields()) { - if (field.getAnnotation(Ignore.class) != null) continue; - if (field.getAnnotation(Transient.class) != null) continue; - if (Modifier.isStatic(field.getModifiers())) continue; - if (fields.contains(field.getName())) continue; - char[] chars = field.getName().toCharArray(); - chars[0] = Character.toUpperCase(chars[0]); - final Class t = field.getType(); - try { - type.getMethod(((t == boolean.class || t == Boolean.class) ? "is" : "get") + new String(chars)); - } catch (Exception ex) { - continue; - } - fields.add(field.getName()); - FilterItem item = new FilterItem(field, "a", null); - FilterJoinColumn joinCol = field.getAnnotation(FilterJoinColumn.class); - boolean again = true; - if (joinCol != null) { - if (!joinTables.containsKey(joinCol.table())) { - again = false; - joinTables.put(joinCol.table(), String.valueOf((char) ('a' + (++index)))); - } - String alias = joinTables.get(joinCol.table()); - EntityInfo info = EntityInfo.load(joinCol.table(), 0, null); - EntityCache cache = null; - if (info.getCache() != null && info.getCache().isFullLoaded()) { - cache = info.getCache(); - } else { - cachejoin = false; - } - item = new FilterItem(field, alias, cache); - EntityInfo secinfo = EntityInfo.load(joinCol.table(), 0, null); - if (!again) { - joinsb.append(" ").append(joinCol.type().name()).append(" JOIN ").append(secinfo.getTable()) - .append(" ").append(alias).append(" ON a.# = ").append(alias).append(".") - .append(joinCol.column().isEmpty() ? secinfo.getPrimary().field() : joinCol.column()); - } - } - getters.put(field.getName(), item); - FilterGroup[] refs = field.getAnnotationsByType(FilterGroup.class); - String[] groups = new String[refs.length]; - for (int i = 0; i < refs.length; i++) { - groups[i] = refs[i].value(); - } - if (groups.length == 0) groups = new String[]{"[AND]"}; - for (String key : groups) { - if (!key.startsWith("[AND]") && !key.startsWith("[OR]")) { - throw new RuntimeException(field + "'s FilterGroup.value(" + key + ") illegal, must be [AND] or [OR] startsWith"); - } - List nd = nodes.get(key); - if (nd == null) { - nd = new ArrayList(); - nodes.put(key, nd); - } - nd.add(item); - } - } - } while ((cltmp = cltmp.getSuperclass()) != Object.class); - //--------------------------------------------------------------- - List expnodes = new ArrayList<>(); - for (Map.Entry> en : nodes.entrySet()) { - if (en.getValue().size() == 1) { - expnodes.add(en.getValue().get(0)); - } else { - List sitems = en.getValue(); - expnodes.add(new FilterGroupNode(en.getKey(), sitems.toArray(new FilterExpressNode[sitems.size()]))); - } - } - rootNode = new FilterGroupNode("AND", expnodes.toArray(new FilterExpressNode[expnodes.size()])); - //--------------------------------------------------------------- - this.validCacheJoin = cachejoin; - if (!joinTables.isEmpty()) { - this.joinsql = joinsb.toString(); - } else { - this.joinsql = null; - } - } - - public FilterItem[] getFilters() { - return new FilterItem[0]; - } - - public Class getType() { - return type; - } - - public boolean isJoin() { - return joinsql != null; - } - - public boolean isValidCacheJoin() { - return validCacheJoin; - } - - public Predicate getFilterPredicate(EntityInfo info, T bean) { - return rootNode.getFilterPredicate(info, bean); - } - - public StringBuilder createWhereSql(String primaryColumn, T obj) { - StringBuilder sb = rootNode.getFilterExpress(obj); - if (sb == null) return null; - final StringBuilder all = new StringBuilder(128); - if (this.isJoin()) { - all.append(this.joinsql.replace("#", primaryColumn)); - } - all.append(" WHERE ").append(sb); - return all; - } - - public static String formatToString(Object rs) { - if (rs == null) return null; - Class clazz = rs.getClass(); - if (CharSequence.class.isAssignableFrom(clazz)) { - return "'" + rs.toString().replace("'", "\\'") + "'"; - } else if (java.util.Date.class.isAssignableFrom(clazz)) { - return "'" + String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS", (java.util.Date) rs) + "'"; - } - return String.valueOf(rs); - } - - static final class FilterGroupNode implements FilterExpressNode { - - private final String sign; - - private final boolean or; - - private final FilterExpressNode[] nodes; - - public FilterGroupNode(String sign, FilterExpressNode[] nodes) { - this.sign = sign.indexOf("[OR]") == 0 || sign.equalsIgnoreCase("OR") ? "OR" : "AND"; - this.or = "OR".equals(this.sign); - this.nodes = nodes; - } - - @Override - public StringBuilder getFilterExpress(T obj) { - StringBuilder sb = null; - int count = 0; - for (FilterExpressNode node : nodes) { - StringBuilder sub = node.getFilterExpress(obj); - if (sub == null) continue; - if (sb == null) { - sb = new StringBuilder(); - sb.append(sub); - count++; - } else { - sb.append(' ').append(sign).append(' ').append(sub); - count++; - } - } - if (sb == null) return null; - if (count < 2) return sb; - return new StringBuilder(sb.length() + 2).append('(').append(sb).append(')'); - } - - @Override - public Predicate getFilterPredicate(EntityInfo info, T bean) { - Predicate predicate = null; - for (FilterExpressNode node : nodes) { - Predicate p = node.getFilterPredicate(info, bean); - if (p == null) continue; - if (predicate == null) { - predicate = p; - } else { - predicate = or ? predicate.or(p) : predicate.and(p); - } - } - return predicate; - } - } - - static final class FilterItem implements FilterExpressNode { - - public final Attribute attribute; - - public final String aliasfield; - - private final String field; - - public final FilterExpress express; - - public final boolean string; - - public final boolean number; - - public final boolean likefit; - - public final boolean ignoreCase; - - public final long least; - - public final Class type; - - public final EntityCache joinCache; //待实现 - - public FilterItem(Field field, String alias, EntityCache joinCache) { - this.joinCache = joinCache; - FilterColumn column = field.getAnnotation(FilterColumn.class); - String sqlfield = (column != null && !column.name().isEmpty() ? column.name() : field.getName()); - this.field = sqlfield; - sqlfield = alias + "." + sqlfield; - this.attribute = Attribute.create(sqlfield, field); - this.aliasfield = this.attribute.field(); - this.type = (Class) field.getType(); - FilterExpress exp = column == null ? FilterExpress.EQUAL : column.express(); - if (type.isArray() || Collection.class.isAssignableFrom(type)) { - if (Range.class.isAssignableFrom(type.getComponentType())) { - if (AND != exp) exp = FilterExpress.OR; - } else { - if (NOTIN != exp) exp = FilterExpress.IN; - } - } else if (Range.class.isAssignableFrom(type)) { - if (NOTBETWEEN != exp) exp = FilterExpress.BETWEEN; - } - this.express = exp; - this.least = column == null ? 1L : column.least(); - this.likefit = column == null ? true : column.likefit(); - this.ignoreCase = column == null ? true : column.ignoreCase(); - this.number = type.isPrimitive() || Number.class.isAssignableFrom(type); - this.string = CharSequence.class.isAssignableFrom(type); - } - - /** - * 返回null表示无需过滤该字段 - *

- * @param bean - * @return - */ - @Override - public StringBuilder getFilterExpress(final T bean) { - final F rs = attribute.get(bean); - if (rs == null) return null; - if (string && ((CharSequence) rs).length() == 0) return null; - if (number && ((Number) rs).longValue() < this.least) return null; - if (Range.class.isAssignableFrom(type)) return getRangeExpress((Range) rs); - if (type.isArray() || Collection.class.isAssignableFrom(type)) { - Object[] os; - if (Collection.class.isAssignableFrom(type)) { - os = ((Collection) rs).toArray(); - } else { - final int len = Array.getLength(rs); - if (len < 1) return null; - if (type.getComponentType().isPrimitive()) { - os = new Object[len]; - for (int i = 0; i < len; i++) { - os[i] = Array.get(rs, i); - } - } else { - os = (Object[]) rs; - } - } - if (Range.class.isAssignableFrom(os[0].getClass())) { - StringBuilder sb = new StringBuilder(); - sb.append('('); - boolean flag = false; - for (Object o : os) { - if (flag) sb.append(' ').append(express.value()).append(' '); - sb.append(getRangeExpress((Range) o)); - flag = true; - } - return sb.append(')'); - } else { - StringBuilder sb = new StringBuilder(); - sb.append(aliasfield).append(' ').append(express.value()).append(" ("); - boolean flag = false; - for (Object o : os) { - if (flag) sb.append(','); - sb.append(formatValue(o)); - flag = true; - } - return sb.append(')'); - } - } else if (express == OPAND || express == OPOR) { - StringBuilder sb = new StringBuilder(); - sb.append('(').append(aliasfield).append(' ').append(express.value()).append(' ').append(formatValue(rs)).append(" > 0)"); - return sb; - } else if (express == OPANDNO) { - StringBuilder sb = new StringBuilder(); - sb.append('(').append(aliasfield).append(' ').append(express.value()).append(' ').append(formatValue(rs)).append(" = 0)"); - return sb; - } else { - StringBuilder sb = new StringBuilder(); - sb.append(aliasfield).append(' ').append(express.value()).append(' ').append(formatValue(rs)); - return sb; - } - } - - private String formatValue(Object rs) { - if ((LIKE == express || NOTLIKE == express) && this.likefit) return formatToString("%" + rs + "%"); - return formatToString(rs); - } - - private StringBuilder getRangeExpress(Range range) { - StringBuilder sb = new StringBuilder(); - return sb.append("(").append(aliasfield).append((NOTBETWEEN == express ? " NOT BETWEEN " : " BETWEEN ")) - .append(formatToString(range.getMin())).append(" AND ").append(formatToString(range.getMax())).append(")"); - } - - private Predicate getRangePredicate(final Attribute attr, Range range) { - final Comparable min = range.getMin(); - final Comparable max = range.getMax(); - Predicate p = (E t) -> { - Comparable rs = (Comparable) attr.get(t); - if (rs == null) return false; - if (min != null && min.compareTo(rs) >= 0) return false; - return !(max != null && max.compareTo(rs) <= 0); - }; - return (express == NOTBETWEEN) ? p.negate() : p; - } - - private Predicate getArrayPredicate(final Attribute attr, Object beanValue) { - if (beanValue == null) return null; - Predicate p; - if (type.isArray()) { - if (Array.getLength(beanValue) == 0) return null; - final Class comp = type.getComponentType(); - if (comp == int.class) { - p = (E t) -> { - Object rs = attr.get(t); - if (rs == null) return false; - return Arrays.binarySearch((int[]) beanValue, (int) rs) >= 0; - }; - } else if (comp == long.class) { - p = (E t) -> { - Object rs = attr.get(t); - if (rs == null) return false; - return Arrays.binarySearch((long[]) beanValue, (long) rs) >= 0; - }; - } else if (comp == short.class) { - p = (E t) -> { - Object rs = attr.get(t); - if (rs == null) return false; - return Arrays.binarySearch((short[]) beanValue, (short) rs) >= 0; - }; - } else if (comp == float.class) { - p = (E t) -> { - Object rs = attr.get(t); - if (rs == null) return false; - return Arrays.binarySearch((float[]) beanValue, (float) rs) >= 0; - }; - } else if (comp == double.class) { - p = (E t) -> { - Object rs = attr.get(t); - if (rs == null) return false; - return Arrays.binarySearch((double[]) beanValue, (double) rs) >= 0; - }; - } else if (comp == byte.class) { - p = (E t) -> { - Object rs = attr.get(t); - if (rs == null) return false; - return Arrays.binarySearch((byte[]) beanValue, (byte) rs) >= 0; - }; - } else if (comp == char.class) { - p = (E t) -> { - Object rs = attr.get(t); - if (rs == null) return false; - return Arrays.binarySearch((char[]) beanValue, (char) rs) >= 0; - }; - } else { - p = (E t) -> { - Object rs = attr.get(t); - if (rs == null) return false; - return Arrays.binarySearch((Object[]) beanValue, rs) >= 0; - }; - } - } else { // Collection - Collection collection = (Collection) beanValue; - if (collection.isEmpty()) return null; - p = (E t) -> { - Object rs = attr.get(t); - if (rs == null) return false; - return collection.contains(rs); - }; - } - return p == null ? null : (express == NOTIN) ? p.negate() : p; - } - - @Override - public Predicate getFilterPredicate(EntityInfo info, T bean) { - return getFilterPredicate(info.getAttribute(field), bean); - } - - private Predicate getFilterPredicate(final Attribute attr, T bean) { - final F beanValue = attribute.get(bean); - if (beanValue == null) return null; - if (string && ((CharSequence) beanValue).length() == 0) return null; - if (number && ((Number) beanValue).longValue() < this.least) return null; - if (Range.class.isAssignableFrom(type)) return getRangePredicate(attr, (Range) beanValue); - if (type.isArray() || Collection.class.isAssignableFrom(type)) return getArrayPredicate(attr, (Range) beanValue); - final long beanLongValue = number ? ((Number) beanValue).longValue() : 0L; - switch (express) { - case EQUAL: return (E t) -> beanValue.equals(attr.get(t)); - case NOTEQUAL: return (E t) -> !beanValue.equals(attr.get(t)); - case GREATERTHAN: return (E t) -> ((Number) attr.get(t)).longValue() > beanLongValue; - case LESSTHAN: return (E t) -> ((Number) attr.get(t)).longValue() < beanLongValue; - case GREATERTHANOREQUALTO: return (E t) -> ((Number) attr.get(t)).longValue() >= beanLongValue; - case LESSTHANOREQUALTO: return (E t) -> ((Number) attr.get(t)).longValue() <= beanLongValue; - case LIKE: if (!ignoreCase) return (E t) -> { - Object rs = attr.get(t); - return rs != null && rs.toString().contains(beanValue.toString()); - }; - String v1 = beanValue.toString().toLowerCase(); - return (E t) -> { - Object rs = attr.get(t); - return rs != null && rs.toString().toLowerCase().contains(v1); - }; - case NOTLIKE: if (!ignoreCase) return (E t) -> { - Object rs = attr.get(t); - return rs == null || !rs.toString().contains(beanValue.toString()); - }; - String v2 = beanValue.toString().toLowerCase(); - return (E t) -> { - Object rs = attr.get(t); - return rs == null || !rs.toString().toLowerCase().contains(v2); - }; - case ISNULL: return (E t) -> attr.get(t) == null; - case ISNOTNULL: return (E t) -> attr.get(t) != null; - case OPAND: return (E t) -> (((Number) attr.get(t)).longValue() & beanLongValue) > 0; - case OPOR: return (E t) -> (((Number) attr.get(t)).longValue() | beanLongValue) > 0; - case OPANDNO: return (E t) -> (((Number) attr.get(t)).longValue() & beanLongValue) == 0; - } - return null; - } - - } - - static interface FilterExpressNode { - - public StringBuilder getFilterExpress(final T bean); - - public Predicate getFilterPredicate(final EntityInfo info, final T bean); - } -} diff --git a/src/com/wentch/redkale/util/Creator.java b/src/com/wentch/redkale/util/Creator.java index c66ba2c97..6c798d66f 100644 --- a/src/com/wentch/redkale/util/Creator.java +++ b/src/com/wentch/redkale/util/Creator.java @@ -1,207 +1,207 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package com.wentch.redkale.util; - -import java.beans.ConstructorProperties; -import java.lang.reflect.*; -import java.util.*; -import jdk.internal.org.objectweb.asm.*; -import static jdk.internal.org.objectweb.asm.Opcodes.*; -import jdk.internal.org.objectweb.asm.Type; - -/** - * 实现一个类的构造方法。 代替低效的反射实现方式。 - * - * @author zhangjx - * @param - */ -public interface Creator { -// -// static class PooledCreator implements Creator { -// -// private final T defValue; -// -// private final Reproduce reproduce; -// -// private final ReferenceQueue refQueue = new ReferenceQueue(); -// -// private final Queue queue; -// -// private final Creator creator; -// -// public PooledCreator(int max, Class clazz, Creator creator) { -// this.creator = creator; -// this.defValue = creator.create(); -// this.reproduce = Reproduce.create(clazz, clazz); -// this.queue = new ArrayBlockingQueue<>(Math.max(Runtime.getRuntime().availableProcessors() * 2, max)); -// new Thread() { -// { -// setDaemon(true); -// setName(PooledCreator.class.getSimpleName() + " " + clazz.getSimpleName() + " Reference Handler"); -// } -// -// @Override -// public void run() { -// try { -// for (;;) { -// T r = refQueue.remove().get(); -// if (r == null) continue; -// reproduce.copy(r, defValue); -// queue.offer(r); -// } -// } catch (Exception e) { -// //do nothind -// } -// } -// }.start(); -// } -// -// @Override -// public T create(Object... params) { -// T rs = queue.poll(); -// if (rs == null) { -// rs = creator.create(params); -// } -// return new WeakReference<>(rs, refQueue).get(); -// } -// -// } -// -// @SuppressWarnings("unchecked") -// public static Creator create(int max, Class clazz) { -// return new PooledCreator<>(max, clazz, create(clazz)); -// } -// -// @SuppressWarnings("unchecked") -// public static Creator create(int max, Class clazz, Creator creator) { -// return new PooledCreator<>(max, clazz, creator); -// } - - public T create(Object... params); - - @SuppressWarnings("unchecked") - public static Creator create(Class clazz) { - if (clazz.isAssignableFrom(ArrayList.class)) { - clazz = (Class) ArrayList.class; - } else if (clazz.isAssignableFrom(HashMap.class)) { - clazz = (Class) HashMap.class; - } else if (clazz.isAssignableFrom(HashSet.class)) { - clazz = (Class) HashSet.class; - } - if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) { - throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator."); - } - final String supDynName = Creator.class.getName().replace('.', '/'); - final String interName = clazz.getName().replace('.', '/'); - final String interDesc = Type.getDescriptor(clazz); - ClassLoader loader = Creator.class.getClassLoader(); - 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(); - } catch (Exception ex) { - } - Constructor constructor = null; - for (Constructor c : clazz.getConstructors()) { - if (c.getParameterCount() == 0) { - constructor = c; - break; - } - } - if (constructor == null) { - for (Constructor c : clazz.getConstructors()) { - if (c.getAnnotation(ConstructorProperties.class) != null) { - constructor = c; - break; - } - } - } - if (constructor == null) throw new RuntimeException("[" + clazz + "] have no public or java.beans.ConstructorProperties-Annotation constructor."); - //------------------------------------------------------------- - ClassWriter cw = new ClassWriter(0); - FieldVisitor fv; - MethodVisitor mv; - AnnotationVisitor av0; - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + ">;", "java/lang/Object", new String[]{supDynName}); - - {//构造方法 - mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); - ConstructorProperties cps = constructor.getAnnotation(ConstructorProperties.class); - if (cps != null) { - av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true); - AnnotationVisitor av1 = av0.visitArray("value"); - for (String n : cps.value()) { - av1.visit(null, n); - } - av1.visitEnd(); - av0.visitEnd(); - } - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - {//create 方法 - mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null); - mv.visitTypeInsn(NEW, interName); - mv.visitInsn(DUP); - //--------------------------------------- - { - Parameter[] params = constructor.getParameters(); - final int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5}; - for (int i = 0; i < params.length; i++) { - mv.visitVarInsn(ALOAD, 1); - if (i < 6) { - mv.visitInsn(iconsts[i]); - } else { - mv.visitIntInsn(BIPUSH, i); - } - mv.visitInsn(AALOAD); - Class ct = params[i].getType(); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ct)); - if (ct.isPrimitive()) { - Class fct = Array.get(Array.newInstance(ct, 1), 0).getClass(); - try { - Method pm = ct.getMethod(ct.getSimpleName() + "Value"); - mv.visitMethodInsn(INVOKEVIRTUAL, fct.getName().replace('.', '/'), pm.getName(), Type.getMethodDescriptor(pm), false); - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - } - } - } - //--------------------------------------- - mv.visitMethodInsn(INVOKESPECIAL, interName, "", Type.getConstructorDescriptor(constructor), false); - mv.visitInsn(ARETURN); - mv.visitMaxs((constructor.getParameterCount() > 0 ? (constructor.getParameterCount() + 3) : 2), 2); - mv.visitEnd(); - } - { //虚拟 create 方法 - mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_VARARGS + ACC_SYNTHETIC, "create", "([Ljava/lang/Object;)Ljava/lang/Object;", null, null); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "create", "([Ljava/lang/Object;)" + interDesc, false); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - cw.visitEnd(); - byte[] bytes = cw.toByteArray(); - Class creatorClazz = new ClassLoader(loader) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - try { - return (Creator) creatorClazz.newInstance(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } -} +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.util; + +import java.beans.ConstructorProperties; +import java.lang.reflect.*; +import java.util.*; +import jdk.internal.org.objectweb.asm.*; +import static jdk.internal.org.objectweb.asm.Opcodes.*; +import jdk.internal.org.objectweb.asm.Type; + +/** + * 实现一个类的构造方法。 代替低效的反射实现方式。 + * + * @author zhangjx + * @param + */ +public interface Creator { +// +// static class PooledCreator implements Creator { +// +// private final T defValue; +// +// private final Reproduce reproduce; +// +// private final ReferenceQueue refQueue = new ReferenceQueue(); +// +// private final Queue queue; +// +// private final Creator creator; +// +// public PooledCreator(int max, Class clazz, Creator creator) { +// this.creator = creator; +// this.defValue = creator.create(); +// this.reproduce = Reproduce.create(clazz, clazz); +// this.queue = new ArrayBlockingQueue<>(Math.max(Runtime.getRuntime().availableProcessors() * 2, max)); +// new Thread() { +// { +// setDaemon(true); +// setName(PooledCreator.class.getSimpleName() + " " + clazz.getSimpleName() + " Reference Handler"); +// } +// +// @Override +// public void run() { +// try { +// for (;;) { +// T r = refQueue.remove().get(); +// if (r == null) continue; +// reproduce.copy(r, defValue); +// queue.offer(r); +// } +// } catch (Exception e) { +// //do nothind +// } +// } +// }.start(); +// } +// +// @Override +// public T create(Object... params) { +// T rs = queue.poll(); +// if (rs == null) { +// rs = creator.create(params); +// } +// return new WeakReference<>(rs, refQueue).get(); +// } +// +// } +// +// @SuppressWarnings("unchecked") +// public static Creator create(int max, Class clazz) { +// return new PooledCreator<>(max, clazz, create(clazz)); +// } +// +// @SuppressWarnings("unchecked") +// public static Creator create(int max, Class clazz, Creator creator) { +// return new PooledCreator<>(max, clazz, creator); +// } + + public T create(Object... params); + + @SuppressWarnings("unchecked") + public static Creator create(Class clazz) { + if (clazz.isAssignableFrom(ArrayList.class)) { + clazz = (Class) ArrayList.class; + } else if (clazz.isAssignableFrom(HashMap.class)) { + clazz = (Class) HashMap.class; + } else if (clazz.isAssignableFrom(HashSet.class)) { + clazz = (Class) HashSet.class; + } + if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) { + throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator."); + } + final String supDynName = Creator.class.getName().replace('.', '/'); + final String interName = clazz.getName().replace('.', '/'); + final String interDesc = Type.getDescriptor(clazz); + ClassLoader loader = Creator.class.getClassLoader(); + 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(); + } catch (Exception ex) { + } + Constructor constructor = null; + for (Constructor c : clazz.getConstructors()) { + if (c.getParameterCount() == 0) { + constructor = c; + break; + } + } + if (constructor == null) { + for (Constructor c : clazz.getConstructors()) { + if (c.getAnnotation(ConstructorProperties.class) != null) { + constructor = c; + break; + } + } + } + if (constructor == null) throw new RuntimeException("[" + clazz + "] have no public or java.beans.ConstructorProperties-Annotation constructor."); + //------------------------------------------------------------- + ClassWriter cw = new ClassWriter(0); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + ">;", "java/lang/Object", new String[]{supDynName}); + + {//构造方法 + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + ConstructorProperties cps = constructor.getAnnotation(ConstructorProperties.class); + if (cps != null) { + av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true); + AnnotationVisitor av1 = av0.visitArray("value"); + for (String n : cps.value()) { + av1.visit(null, n); + } + av1.visitEnd(); + av0.visitEnd(); + } + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + {//create 方法 + mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null); + mv.visitTypeInsn(NEW, interName); + mv.visitInsn(DUP); + //--------------------------------------- + { + Parameter[] params = constructor.getParameters(); + final int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5}; + for (int i = 0; i < params.length; i++) { + mv.visitVarInsn(ALOAD, 1); + if (i < 6) { + mv.visitInsn(iconsts[i]); + } else { + mv.visitIntInsn(BIPUSH, i); + } + mv.visitInsn(AALOAD); + Class ct = params[i].getType(); + mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ct)); + if (ct.isPrimitive()) { + Class fct = Array.get(Array.newInstance(ct, 1), 0).getClass(); + try { + Method pm = ct.getMethod(ct.getSimpleName() + "Value"); + mv.visitMethodInsn(INVOKEVIRTUAL, fct.getName().replace('.', '/'), pm.getName(), Type.getMethodDescriptor(pm), false); + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + } + } + //--------------------------------------- + mv.visitMethodInsn(INVOKESPECIAL, interName, "", Type.getConstructorDescriptor(constructor), false); + mv.visitInsn(ARETURN); + mv.visitMaxs((constructor.getParameterCount() > 0 ? (constructor.getParameterCount() + 3) : 2), 2); + mv.visitEnd(); + } + { //虚拟 create 方法 + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_VARARGS + ACC_SYNTHETIC, "create", "([Ljava/lang/Object;)Ljava/lang/Object;", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "create", "([Ljava/lang/Object;)" + interDesc, false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + Class creatorClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + try { + return (Creator) creatorClazz.newInstance(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/src/com/wentch/redkale/util/DLong.java b/src/com/wentch/redkale/util/DLong.java index 56ad110b9..919e31568 100644 --- a/src/com/wentch/redkale/util/DLong.java +++ b/src/com/wentch/redkale/util/DLong.java @@ -32,11 +32,6 @@ public final class DLong extends Number implements Comparable { return this.first == one && this.second == two; } - @Override - public int hashCode() { - return intValue(); - } - @Override public boolean equals(Object obj) { if (obj == null) return false; @@ -45,6 +40,14 @@ public final class DLong extends Number implements Comparable { return (this.first == other.first && this.second == other.second); } + @Override + public int hashCode() { + int hash = 7; + hash = 89 * hash + (int) (this.first ^ (this.first >>> 32)); + hash = 89 * hash + (int) (this.second ^ (this.second >>> 32)); + return hash; + } + @Override public String toString() { return this.first + "_" + this.second; diff --git a/src/com/wentch/redkale/util/DebugMethodVisitor.java b/src/com/wentch/redkale/util/DebugMethodVisitor.java index 3add2e39c..291f4dcff 100644 --- a/src/com/wentch/redkale/util/DebugMethodVisitor.java +++ b/src/com/wentch/redkale/util/DebugMethodVisitor.java @@ -5,6 +5,7 @@ */ package com.wentch.redkale.util; +import java.util.*; import jdk.internal.org.objectweb.asm.*; /** @@ -21,6 +22,8 @@ public class DebugMethodVisitor { debug = d; } + private final Map labels = new LinkedHashMap<>(); + private static final String[] opcodes = new String[200]; //0 -18 static { @@ -46,6 +49,12 @@ public class DebugMethodVisitor { this.visitor = visitor; } + public AnnotationVisitor visitAnnotation(String desc, boolean flag) { + AnnotationVisitor av = visitor.visitAnnotation(desc, flag); + if (debug) System.out.println("mv.visitAnnotation(\"" + desc + "\", " + flag + ");"); + return av; + } + public void visitParameter(String name, int access) { visitor.visitParameter(name, access); if (debug) System.out.println("mv.visitParameter(" + name + ", " + access + ");"); @@ -56,6 +65,37 @@ public class DebugMethodVisitor { if (debug) System.out.println("mv.visitVarInsn(" + opcodes[opcode] + ", " + var + ");"); } + public void visitJumpInsn(int opcode, Label var) { + visitor.visitJumpInsn(opcode, var); + if (debug) { + Integer index = labels.get(var); + if (index == null) { + index = labels.size(); + labels.put(var, index); + System.out.println("Label l" + index + " = new Label();"); + } + System.out.println("mv.visitJumpInsn(" + opcodes[opcode] + ", l" + index + ");"); + } + } + + public void visitCode() { + visitor.visitCode(); + if (debug) System.out.println("mv.visitCode();"); + } + + public void visitLabel(Label var) { + visitor.visitLabel(var); + if (debug) { + Integer index = labels.get(var); + if (index == null) { + index = labels.size(); + labels.put(var, index); + System.out.println("Label l" + index + " = new Label();"); + } + System.out.println("mv.visitLabel(l" + index + ");"); + } + } + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { visitor.visitMethodInsn(opcode, owner, name, desc, itf); if (debug) System.out.println("mv.visitMethodInsn(" + opcodes[opcode] + ", \"" + owner + "\", \"" + name + "\", \"" + desc + "\", " + itf + ");"); @@ -81,6 +121,11 @@ public class DebugMethodVisitor { if (debug) System.out.println("mv.visitIntInsn(" + opcodes[opcode] + ", " + value + ");"); } + public void visitIincInsn(int opcode, int value) { + visitor.visitIincInsn(opcode, value); + if (debug) System.out.println("mv.visitIincInsn(" + opcode + ", " + value + ");"); + } + public void visitLdcInsn(Object o) { visitor.visitLdcInsn(o); if (debug) System.out.println("mv.visitLdcInsn(" + o + ");"); diff --git a/src/com/wentch/redkale/service/MultiService.java b/src/com/wentch/redkale/util/Nameable.java similarity index 69% rename from src/com/wentch/redkale/service/MultiService.java rename to src/com/wentch/redkale/util/Nameable.java index 4a39fd6d8..af0128430 100644 --- a/src/com/wentch/redkale/service/MultiService.java +++ b/src/com/wentch/redkale/util/Nameable.java @@ -3,12 +3,13 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package com.wentch.redkale.service; +package com.wentch.redkale.util; /** * * @author zhangjx */ -public interface MultiService extends Service { +public interface Nameable { + String name(); } diff --git a/src/com/wentch/redkale/util/Reproduce.java b/src/com/wentch/redkale/util/Reproduce.java index a91336b06..3efd59a14 100644 --- a/src/com/wentch/redkale/util/Reproduce.java +++ b/src/com/wentch/redkale/util/Reproduce.java @@ -5,13 +5,6 @@ import java.util.function.Predicate; import static jdk.internal.org.objectweb.asm.Opcodes.*; import jdk.internal.org.objectweb.asm.*; -/** - * 该类提供对象拷贝, 两对象存在相同的getter、setter的字段值会被拷贝 - * - * @author zhangjx - * @param - * @param - */ public interface Reproduce { public D copy(D dest, S src); diff --git a/src/com/wentch/redkale/util/ResourceFactory.java b/src/com/wentch/redkale/util/ResourceFactory.java index df015e927..c1f622bb7 100644 --- a/src/com/wentch/redkale/util/ResourceFactory.java +++ b/src/com/wentch/redkale/util/ResourceFactory.java @@ -13,12 +13,15 @@ import java.util.regex.*; import javax.annotation.*; /** + * 如果Resource(name = "$") 表示资源name采用所属对象的name * * @author zhangjx */ @SuppressWarnings("unchecked") public final class ResourceFactory { + public static final String RESOURCE_PARENT_NAME = "$"; + private static final Logger logger = Logger.getLogger(ResourceFactory.class.getSimpleName()); private final ResourceFactory parent; @@ -170,15 +173,16 @@ public final class ResourceFactory { continue; } if (Modifier.isFinal(field.getModifiers())) continue; - Object rs = genctype == classtype ? null : find(rc.name(), genctype); + final String rcname = (rc.name().equals(RESOURCE_PARENT_NAME) && src instanceof Nameable) ? ((Nameable) src).name() : rc.name(); + Object rs = genctype == classtype ? null : find(rcname, genctype); if (rs == null) { if (Map.class.isAssignableFrom(classtype)) { - rs = find(Pattern.compile(rc.name().isEmpty() ? ".+" : rc.name()), (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1], src); + rs = find(Pattern.compile(rcname.isEmpty() ? ".*" : rcname), (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1], src); } else { - if (rc.name().startsWith("property.")) { - rs = find(rc.name(), String.class); + if (rcname.startsWith("property.")) { + rs = find(rcname, String.class); } else { - rs = find(rc.name(), classtype); + rs = find(rcname, classtype); } } } diff --git a/src/com/wentch/redkale/util/Utility.java b/src/com/wentch/redkale/util/Utility.java index 28df970e3..49ad74526 100644 --- a/src/com/wentch/redkale/util/Utility.java +++ b/src/com/wentch/redkale/util/Utility.java @@ -324,6 +324,17 @@ public final class Utility { return size; } + /** + * 将两个数字组装成一个long + *

+ * @param high + * @param low + * @return + */ + public static long merge(long high, long low) { + return high << 32 | low; + } + public static ByteBuffer encodeUTF8(final ByteBuffer buffer, final char[] text, final int start, final int len) { return encodeUTF8(buffer, encodeUTF8Length(text, start, len), text, start, len); }