This commit is contained in:
@@ -23,6 +23,7 @@ import java.nio.*;
|
|||||||
import java.nio.channels.*;
|
import java.nio.channels.*;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.logging.*;
|
import java.util.logging.*;
|
||||||
import javax.annotation.*;
|
import javax.annotation.*;
|
||||||
@@ -31,7 +32,7 @@ import org.w3c.dom.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 编译时需要加入: -XDignore.symbol.file=true
|
* 编译时需要加入: -XDignore.symbol.file=true
|
||||||
*
|
* <p>
|
||||||
* 进程启动类,程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet,
|
* 进程启动类,程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet,
|
||||||
* 再进行Service、Servlet与其他资源之间的依赖注入。
|
* 再进行Service、Servlet与其他资源之间的依赖注入。
|
||||||
*
|
*
|
||||||
@@ -39,14 +40,28 @@ import org.w3c.dom.*;
|
|||||||
*/
|
*/
|
||||||
public final class Application {
|
public final class Application {
|
||||||
|
|
||||||
|
//进程启动的时间, 类型: long
|
||||||
public static final String RESNAME_TIME = "APP_TIME";
|
public static final String RESNAME_TIME = "APP_TIME";
|
||||||
|
|
||||||
|
//本地进程的根目录, 类型:String
|
||||||
public static final String RESNAME_HOME = "APP_HOME";
|
public static final String RESNAME_HOME = "APP_HOME";
|
||||||
|
|
||||||
|
//本地节点的名称, 类型:String
|
||||||
public static final String RESNAME_NODE = "APP_NODE";
|
public static final String RESNAME_NODE = "APP_NODE";
|
||||||
|
|
||||||
|
//本地节点的所属组, 类型:String、Map<String, Set<String>>、Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>
|
||||||
|
public static final String RESNAME_GROUP = "APP_GROUP";
|
||||||
|
|
||||||
|
//本地节点的所属组所有节点名, 类型:Set<String> 、List<SimpleEntry<String, InetSocketAddress[]>>包含自身节点名
|
||||||
|
public static final String RESNAME_INGROUP = "APP_INGROUP";
|
||||||
|
|
||||||
|
//除本地节点的所属组外其他所有组的所有节点名, 类型:Map<String, Set<String>>、Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>
|
||||||
|
public static final String RESNAME_OUTGROUP = "APP_OUTGROUP";
|
||||||
|
|
||||||
|
//本地节点的IP地址, 类型:InetAddress、String
|
||||||
public static final String RESNAME_ADDR = "APP_ADDR";
|
public static final String RESNAME_ADDR = "APP_ADDR";
|
||||||
|
|
||||||
|
//application.xml 文件中resources节点的内容, 类型: AnyValue
|
||||||
public static final String RESNAME_GRES = "APP_GRES";
|
public static final String RESNAME_GRES = "APP_GRES";
|
||||||
|
|
||||||
protected final ResourceFactory factory = ResourceFactory.root();
|
protected final ResourceFactory factory = ResourceFactory.root();
|
||||||
@@ -61,6 +76,8 @@ public final class Application {
|
|||||||
|
|
||||||
protected final InetAddress localAddress = Utility.localInetAddress();
|
protected final InetAddress localAddress = Utility.localInetAddress();
|
||||||
|
|
||||||
|
protected String nodeGroup = "";
|
||||||
|
|
||||||
protected String nodeName = "";
|
protected String nodeName = "";
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------
|
||||||
@@ -92,7 +109,12 @@ public final class Application {
|
|||||||
}
|
}
|
||||||
final File logconf = new File(root, "conf/logging.properties");
|
final File logconf = new File(root, "conf/logging.properties");
|
||||||
this.nodeName = config.getValue("node", "");
|
this.nodeName = config.getValue("node", "");
|
||||||
|
this.nodeGroup = config.getValue("group", "");
|
||||||
this.factory.register(RESNAME_NODE, this.nodeName);
|
this.factory.register(RESNAME_NODE, this.nodeName);
|
||||||
|
this.factory.register(RESNAME_GROUP, this.nodeGroup);
|
||||||
|
System.setProperty(RESNAME_NODE, this.nodeName);
|
||||||
|
System.setProperty(RESNAME_GROUP, this.nodeGroup);
|
||||||
|
|
||||||
this.factory.register(RESNAME_ADDR, this.localAddress.getHostAddress());
|
this.factory.register(RESNAME_ADDR, this.localAddress.getHostAddress());
|
||||||
this.factory.register(RESNAME_ADDR, InetAddress.class, this.localAddress);
|
this.factory.register(RESNAME_ADDR, InetAddress.class, this.localAddress);
|
||||||
if (logconf.isFile() && logconf.canRead()) {
|
if (logconf.isFile() && logconf.canRead()) {
|
||||||
@@ -415,14 +437,25 @@ public final class Application {
|
|||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
final String host = this.localAddress.getHostAddress();
|
final String host = this.localAddress.getHostAddress();
|
||||||
|
final Map<String, Set<String>> groups = new HashMap<>();
|
||||||
|
final Map<String, List<SimpleEntry<String, InetSocketAddress[]>>> groups2 = new HashMap<>();
|
||||||
|
|
||||||
for (AnyValue conf : resources.getAnyValues("remote")) {
|
for (AnyValue conf : resources.getAnyValues("remote")) {
|
||||||
final String name = conf.getValue("name");
|
final String name = conf.getValue("name");
|
||||||
|
final String group = conf.getValue("group", "");
|
||||||
if (name == null) throw new RuntimeException("remote name is null");
|
if (name == null) throw new RuntimeException("remote name is null");
|
||||||
String protocol = conf.getValue("protocol", "UDP").toUpperCase();
|
String protocol = conf.getValue("protocol", "UDP").toUpperCase();
|
||||||
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
|
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
|
||||||
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
|
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
Set<String> set = groups.get(group);
|
||||||
|
if (set == null) {
|
||||||
|
set = new HashSet<>();
|
||||||
|
groups.put(group, set);
|
||||||
|
}
|
||||||
|
set.add(name);
|
||||||
|
}
|
||||||
AnyValue[] addrs = conf.getAnyValues("address");
|
AnyValue[] addrs = conf.getAnyValues("address");
|
||||||
InetSocketAddress[] addresses = new InetSocketAddress[addrs.length];
|
InetSocketAddress[] addresses = new InetSocketAddress[addrs.length];
|
||||||
int i = -1;
|
int i = -1;
|
||||||
@@ -430,17 +463,51 @@ public final class Application {
|
|||||||
addresses[++i] = new InetSocketAddress(addr.getValue("addr"), addr.getIntValue("port"));
|
addresses[++i] = new InetSocketAddress(addr.getValue("addr"), addr.getIntValue("port"));
|
||||||
}
|
}
|
||||||
if (addresses.length < 1) throw new RuntimeException("Transport(" + name + ") have no address ");
|
if (addresses.length < 1) throw new RuntimeException("Transport(" + name + ") have no address ");
|
||||||
Transport transport = new Transport(name, protocol, watch, 100, addresses[0]);
|
{
|
||||||
|
List<SimpleEntry<String, InetSocketAddress[]>> list = groups2.get(group);
|
||||||
|
if (list == null) {
|
||||||
|
list = new ArrayList<>();
|
||||||
|
groups2.put(group, list);
|
||||||
|
}
|
||||||
|
list.add(new SimpleEntry<>(name, addresses));
|
||||||
|
}
|
||||||
|
Transport transport = new Transport(name, protocol, watch, 100, addresses);
|
||||||
factory.register(name, Transport.class, transport);
|
factory.register(name, Transport.class, transport);
|
||||||
if (this.nodeName.isEmpty() && host.equals(addrs[0].getValue("addr"))) {
|
if (this.nodeName.isEmpty() && host.equals(addrs[0].getValue("addr"))) {
|
||||||
this.nodeName = name;
|
this.nodeName = name;
|
||||||
|
this.nodeGroup = group;
|
||||||
this.factory.register(RESNAME_NODE, this.nodeName);
|
this.factory.register(RESNAME_NODE, this.nodeName);
|
||||||
|
this.factory.register(RESNAME_GROUP, this.nodeGroup);
|
||||||
|
System.setProperty(RESNAME_NODE, this.nodeName);
|
||||||
|
System.setProperty(RESNAME_GROUP, this.nodeGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.factory.register(RESNAME_GROUP, new TypeToken<Map<String, Set<String>>>() {
|
||||||
|
}.getType(), groups);
|
||||||
|
this.factory.register(RESNAME_GROUP, new TypeToken<Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>>() {
|
||||||
|
}.getType(), groups2);
|
||||||
|
|
||||||
|
final Map<String, List<SimpleEntry<String, InetSocketAddress[]>>> outgroups2 = new HashMap<>();
|
||||||
|
final Map<String, Set<String>> outgroups = new HashMap<>();
|
||||||
|
groups.entrySet().stream().filter(x -> !x.getKey().equals(nodeName)).forEach(x -> outgroups.put(x.getKey(), x.getValue()));
|
||||||
|
groups2.entrySet().stream().filter(x -> !x.getKey().equals(nodeName)).forEach(x -> outgroups2.put(x.getKey(), x.getValue()));
|
||||||
|
|
||||||
|
this.factory.register(RESNAME_OUTGROUP, new TypeToken<Map<String, Set<String>>>() {
|
||||||
|
}.getType(), outgroups);
|
||||||
|
this.factory.register(RESNAME_OUTGROUP, new TypeToken<Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>>() {
|
||||||
|
}.getType(), outgroups2);
|
||||||
|
|
||||||
|
Set<String> ingroup = groups.get(this.nodeGroup);
|
||||||
|
if (ingroup != null) this.factory.register(RESNAME_INGROUP, new TypeToken<Set<String>>() {
|
||||||
|
}.getType(), ingroup);
|
||||||
|
List<SimpleEntry<String, InetSocketAddress[]>> inengroup = groups2.get(this.nodeGroup);
|
||||||
|
if (inengroup != null) this.factory.register(RESNAME_INGROUP, new TypeToken<List<SimpleEntry<String, InetSocketAddress[]>>>() {
|
||||||
|
}.getType(), inengroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
logger.info(RESNAME_NODE + "=" + this.nodeName);
|
logger.info(RESNAME_NODE + "=" + this.nodeName + "; " + RESNAME_GROUP + "=" + this.nodeGroup);
|
||||||
logger.info("datasource.nodeid=" + this.factory.find("property.datasource.nodeid", String.class));
|
logger.info("datasource.nodeid=" + this.factory.find("property.datasource.nodeid", String.class));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ public final class AsyncDatagramChannel implements AsynchronousByteChannel, Mult
|
|||||||
if (buffers[index].hasRemaining()) {
|
if (buffers[index].hasRemaining()) {
|
||||||
implSend(buffers[index], target, attachment, this);
|
implSend(buffers[index], target, attachment, this);
|
||||||
} else if (index == max) {
|
} else if (index == max) {
|
||||||
handler.completed(resultSum, attachment);
|
if (handler != null) handler.completed(resultSum, attachment);
|
||||||
} else {
|
} else {
|
||||||
implSend(buffers[++index], target, attachment, this);
|
implSend(buffers[++index], target, attachment, this);
|
||||||
}
|
}
|
||||||
@@ -360,7 +360,7 @@ public final class AsyncDatagramChannel implements AsynchronousByteChannel, Mult
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failed(Throwable exc, A attachment) {
|
public void failed(Throwable exc, A attachment) {
|
||||||
handler.failed(exc, attachment);
|
if (handler != null) handler.failed(exc, attachment);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public abstract class Response<R extends Request> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void finish(ByteBuffer buffer) {
|
public void finish(ByteBuffer buffer) {
|
||||||
finish(false, buffer);
|
this.channel.write(buffer, buffer, finishHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish(boolean kill, ByteBuffer buffer) {
|
public void finish(boolean kill, ByteBuffer buffer) {
|
||||||
@@ -134,7 +134,7 @@ public abstract class Response<R extends Request> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void finish(ByteBuffer... buffers) {
|
public void finish(ByteBuffer... buffers) {
|
||||||
finish(false, buffers);
|
this.channel.write(buffers, buffers, finishHandler2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish(boolean kill, ByteBuffer... buffers) {
|
public void finish(boolean kill, ByteBuffer... buffers) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.wentch.redkale.net;
|
package com.wentch.redkale.net;
|
||||||
|
|
||||||
|
import java.nio.*;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
import javax.net.ssl.*;
|
import javax.net.ssl.*;
|
||||||
|
|
||||||
@@ -35,4 +36,114 @@ public class SSLBuilder {
|
|||||||
sslEngine.setNeedClientAuth(false);
|
sslEngine.setNeedClientAuth(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final byte CHANGE_CIPHER_SPECT_CONTENT_TYPE = 20;
|
||||||
|
|
||||||
|
private static final byte APPLICATION_DATA_CONTENT_TYPE = 23;
|
||||||
|
|
||||||
|
private static final int SSLV3_RECORD_HEADER_SIZE = 5; // SSLv3 record header
|
||||||
|
|
||||||
|
private static final int SSL20_HELLO_VERSION = 0x0002;
|
||||||
|
|
||||||
|
private static final int MIN_VERSION = 0x0300;
|
||||||
|
|
||||||
|
private static final int MAX_MAJOR_VERSION = 0x03;
|
||||||
|
|
||||||
|
private static int getSSLPacketSize(final ByteBuffer buf) throws SSLException {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SSLv2 length field is in bytes 0/1
|
||||||
|
* SSLv3/TLS length field is in bytes 3/4
|
||||||
|
*/
|
||||||
|
if (buf.remaining() < 5) return -1;
|
||||||
|
|
||||||
|
final byte byte0;
|
||||||
|
final byte byte1;
|
||||||
|
final byte byte2;
|
||||||
|
final byte byte3;
|
||||||
|
final byte byte4;
|
||||||
|
|
||||||
|
if (buf.hasArray()) {
|
||||||
|
final byte[] array = buf.array();
|
||||||
|
int pos = buf.arrayOffset() + buf.position();
|
||||||
|
byte0 = array[pos++];
|
||||||
|
byte1 = array[pos++];
|
||||||
|
byte2 = array[pos++];
|
||||||
|
byte3 = array[pos++];
|
||||||
|
byte4 = array[pos];
|
||||||
|
} else {
|
||||||
|
int pos = buf.position();
|
||||||
|
byte0 = buf.get(pos++);
|
||||||
|
byte1 = buf.get(pos++);
|
||||||
|
byte2 = buf.get(pos++);
|
||||||
|
byte3 = buf.get(pos++);
|
||||||
|
byte4 = buf.get(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have already verified previous packets, we can
|
||||||
|
* ignore the verifications steps, and jump right to the
|
||||||
|
* determination. Otherwise, try one last hueristic to
|
||||||
|
* see if it's SSL/TLS.
|
||||||
|
*/
|
||||||
|
if (byte0 >= CHANGE_CIPHER_SPECT_CONTENT_TYPE && byte0 <= APPLICATION_DATA_CONTENT_TYPE) {
|
||||||
|
/*
|
||||||
|
* Last sanity check that it's not a wild record
|
||||||
|
*/
|
||||||
|
final byte major = byte1;
|
||||||
|
final byte minor = byte2;
|
||||||
|
final int v = (major << 8) | minor & 0xff;
|
||||||
|
|
||||||
|
// Check if too old (currently not possible)
|
||||||
|
// or if the major version does not match.
|
||||||
|
// The actual version negotiation is in the handshaker classes
|
||||||
|
if ((v < MIN_VERSION) || (major > MAX_MAJOR_VERSION)) {
|
||||||
|
throw new SSLException("Unsupported record version major=" + major + " minor=" + minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* One of the SSLv3/TLS message types.
|
||||||
|
*/
|
||||||
|
len = ((byte3 & 0xff) << 8) + (byte4 & 0xff) + SSLV3_RECORD_HEADER_SIZE;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Must be SSLv2 or something unknown.
|
||||||
|
* Check if it's short (2 bytes) or
|
||||||
|
* long (3) header.
|
||||||
|
*
|
||||||
|
* Internals can warn about unsupported SSLv2
|
||||||
|
*/
|
||||||
|
boolean isShort = ((byte0 & 0x80) != 0);
|
||||||
|
|
||||||
|
if (isShort && ((byte2 == 1) || byte2 == 4)) {
|
||||||
|
|
||||||
|
final byte major = byte3;
|
||||||
|
final byte minor = byte4;
|
||||||
|
final int v = (major << 8) | minor & 0xff;
|
||||||
|
|
||||||
|
// Check if too old (currently not possible)
|
||||||
|
// or if the major version does not match.
|
||||||
|
// The actual version negotiation is in the handshaker classes
|
||||||
|
if ((v < MIN_VERSION) || (major > MAX_MAJOR_VERSION)) {
|
||||||
|
// if it's not SSLv2, we're out of here.
|
||||||
|
if (v != SSL20_HELLO_VERSION) throw new SSLException("Unsupported record version major=" + major + " minor=" + minor);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Client or Server Hello
|
||||||
|
*/
|
||||||
|
int mask = 0x7f;
|
||||||
|
len = ((byte0 & mask) << 8) + (byte1 & 0xff) + (2);
|
||||||
|
} else {
|
||||||
|
// Gobblygook!
|
||||||
|
throw new SSLException("Unrecognized SSL message, plaintext connection?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ public final class Transport {
|
|||||||
bufferPool.offer(buffer);
|
bufferPool.offer(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void offerBuffer(ByteBuffer... buffers) {
|
||||||
|
for (ByteBuffer buffer : buffers) offerBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
public AsyncConnection pollConnection() {
|
public AsyncConnection pollConnection() {
|
||||||
SocketAddress addr = remoteAddres[0];
|
SocketAddress addr = remoteAddres[0];
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
package com.wentch.redkale.net.http;
|
package com.wentch.redkale.net.http;
|
||||||
|
|
||||||
import com.wentch.redkale.net.*;
|
import com.wentch.redkale.net.*;
|
||||||
import com.wentch.redkale.util.AnyValue.DefaultAnyValue;
|
|
||||||
import com.wentch.redkale.util.*;
|
import com.wentch.redkale.util.*;
|
||||||
|
import com.wentch.redkale.util.AnyValue.DefaultAnyValue;
|
||||||
import com.wentch.redkale.watch.*;
|
import com.wentch.redkale.watch.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
@@ -107,7 +107,7 @@ public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpRe
|
|||||||
servlet.execute(request, response);
|
servlet.execute(request, response);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel ", e);
|
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel ", e);
|
||||||
response.finish(505, null);
|
response.finish(500, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,12 +99,21 @@ public final class HttpResponse extends Response<HttpRequest> {
|
|||||||
|
|
||||||
private final DefaultAnyValue header = new DefaultAnyValue();
|
private final DefaultAnyValue header = new DefaultAnyValue();
|
||||||
|
|
||||||
|
private final String[][] defaultAddHeaders;
|
||||||
|
|
||||||
|
private final String[][] defaultSetHeaders;
|
||||||
|
|
||||||
|
private final HttpCookie defcookie;
|
||||||
|
|
||||||
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) {
|
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) {
|
||||||
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((HttpResponse) x).recycle());
|
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((HttpResponse) x).recycle());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpResponse(HttpContext context, HttpRequest request) {
|
protected HttpResponse(HttpContext context, HttpRequest request, String[][] defaultAddHeaders, String[][] defaultSetHeaders, HttpCookie defcookie) {
|
||||||
super(context, request);
|
super(context, request);
|
||||||
|
this.defaultAddHeaders = defaultAddHeaders;
|
||||||
|
this.defaultSetHeaders = defaultSetHeaders;
|
||||||
|
this.defcookie = defcookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -204,6 +213,50 @@ public final class HttpResponse extends Response<HttpRequest> {
|
|||||||
super.finish(buffer404.duplicate());
|
super.finish(buffer404.duplicate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(ByteBuffer buffer) {
|
||||||
|
finish(false, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(boolean kill, ByteBuffer buffer) {
|
||||||
|
if (!this.headsended) {
|
||||||
|
ByteBuffer headbuf = createHeader();
|
||||||
|
headbuf.flip();
|
||||||
|
if (buffer == null) {
|
||||||
|
super.finish(kill, headbuf);
|
||||||
|
} else {
|
||||||
|
super.finish(kill, new ByteBuffer[]{headbuf, buffer});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.finish(kill, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(ByteBuffer... buffers) {
|
||||||
|
finish(false, buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(boolean kill, ByteBuffer... buffers) {
|
||||||
|
if (kill) refuseAlive();
|
||||||
|
if (!this.headsended) {
|
||||||
|
ByteBuffer headbuf = createHeader();
|
||||||
|
headbuf.flip();
|
||||||
|
if (buffers == null) {
|
||||||
|
super.finish(kill, headbuf);
|
||||||
|
} else {
|
||||||
|
ByteBuffer[] newbuffers = new ByteBuffer[buffers.length + 1];
|
||||||
|
newbuffers[0] = headbuf;
|
||||||
|
System.arraycopy(buffers, 0, newbuffers, 1, buffers.length);
|
||||||
|
super.finish(kill, newbuffers);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.finish(kill, buffers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public <A> void sendBody(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler) {
|
public <A> void sendBody(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler) {
|
||||||
if (!this.headsended) {
|
if (!this.headsended) {
|
||||||
ByteBuffer headbuf = createHeader();
|
ByteBuffer headbuf = createHeader();
|
||||||
@@ -211,7 +264,7 @@ public final class HttpResponse extends Response<HttpRequest> {
|
|||||||
if (buffer == null) {
|
if (buffer == null) {
|
||||||
super.send(headbuf, attachment, handler);
|
super.send(headbuf, attachment, handler);
|
||||||
} else {
|
} else {
|
||||||
super.send(new ByteBuffer[]{headbuf, headbuf}, attachment, handler);
|
super.send(new ByteBuffer[]{headbuf, buffer}, attachment, handler);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
super.send(buffer, attachment, handler);
|
super.send(buffer, attachment, handler);
|
||||||
@@ -284,19 +337,50 @@ public final class HttpResponse extends Response<HttpRequest> {
|
|||||||
if (!this.request.isKeepAlive()) {
|
if (!this.request.isKeepAlive()) {
|
||||||
buffer.put("Connection: close\r\n".getBytes());
|
buffer.put("Connection: close\r\n".getBytes());
|
||||||
}
|
}
|
||||||
|
if (this.defaultAddHeaders != null) {
|
||||||
|
for (String[] headers : this.defaultAddHeaders) {
|
||||||
|
if (headers.length > 2) {
|
||||||
|
String v = request.getHeader(headers[2]);
|
||||||
|
if (v != null) this.header.addValue(headers[0], v);
|
||||||
|
} else {
|
||||||
|
this.header.addValue(headers[0], headers[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.defaultSetHeaders != null) {
|
||||||
|
for (String[] headers : this.defaultSetHeaders) {
|
||||||
|
if (headers.length > 2) {
|
||||||
|
this.header.setValue(headers[0], request.getHeader(headers[2]));
|
||||||
|
} else {
|
||||||
|
this.header.setValue(headers[0], headers[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (Entry<String> en : this.header.getStringEntrys()) {
|
for (Entry<String> en : this.header.getStringEntrys()) {
|
||||||
buffer.put((en.name + ": " + en.getValue() + "\r\n").getBytes());
|
buffer.put((en.name + ": " + en.getValue() + "\r\n").getBytes());
|
||||||
}
|
}
|
||||||
if (request.newsessionid != null) {
|
if (request.newsessionid != null) {
|
||||||
if (request.newsessionid.isEmpty()) {
|
String domain = defcookie == null ? null : defcookie.getDomain();
|
||||||
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; path=/; Max-Age=0; HttpOnly\r\n").getBytes());
|
if (domain == null) {
|
||||||
|
domain = "";
|
||||||
} else {
|
} else {
|
||||||
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=" + request.newsessionid + "; path=/; HttpOnly\r\n").getBytes());
|
domain = "Domain=" + domain + "; ";
|
||||||
|
}
|
||||||
|
String path = defcookie == null ? null : defcookie.getPath();
|
||||||
|
if (path == null) path = "/";
|
||||||
|
if (request.newsessionid.isEmpty()) {
|
||||||
|
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=" + path + "; Max-Age=0; HttpOnly\r\n").getBytes());
|
||||||
|
} else {
|
||||||
|
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=" + request.newsessionid + "; " + domain + "Path=" + path + "; HttpOnly\r\n").getBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.cookies != null) {
|
if (this.cookies != null) {
|
||||||
for (HttpCookie cookie : this.cookies) {
|
for (HttpCookie cookie : this.cookies) {
|
||||||
if (cookie == null) continue;
|
if (cookie == null) continue;
|
||||||
|
if (defcookie != null) {
|
||||||
|
if (defcookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defcookie.getDomain());
|
||||||
|
if (defcookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defcookie.getPath());
|
||||||
|
}
|
||||||
buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes());
|
buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,8 +391,8 @@ public final class HttpResponse extends Response<HttpRequest> {
|
|||||||
private CharSequence genString(HttpCookie cookie) {
|
private CharSequence genString(HttpCookie cookie) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(cookie.getName()).append("=\"").append(cookie.getValue()).append('"').append("; Version=1");
|
sb.append(cookie.getName()).append("=\"").append(cookie.getValue()).append('"').append("; Version=1");
|
||||||
if (cookie.getPath() != null) sb.append("; Path=").append(cookie.getPath());
|
|
||||||
if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain());
|
if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain());
|
||||||
|
if (cookie.getPath() != null) sb.append("; Path=").append(cookie.getPath());
|
||||||
if (cookie.getPortlist() != null) sb.append("; Port=").append(cookie.getPortlist());
|
if (cookie.getPortlist() != null) sb.append("; Port=").append(cookie.getPortlist());
|
||||||
if (cookie.getMaxAge() > 0) {
|
if (cookie.getMaxAge() > 0) {
|
||||||
sb.append("; Max-Age=").append(cookie.getMaxAge());
|
sb.append("; Max-Age=").append(cookie.getMaxAge());
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package com.wentch.redkale.net.http;
|
|||||||
import com.wentch.redkale.net.*;
|
import com.wentch.redkale.net.*;
|
||||||
import com.wentch.redkale.util.*;
|
import com.wentch.redkale.util.*;
|
||||||
import com.wentch.redkale.watch.*;
|
import com.wentch.redkale.watch.*;
|
||||||
|
import java.net.*;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
@@ -60,12 +61,62 @@ public final class HttpServer extends Server {
|
|||||||
prepare.addHttpServlet(en.getKey().getKey(), en.getKey().getValue(), en.getValue());
|
prepare.addHttpServlet(en.getKey().getKey(), en.getKey().getValue(), en.getValue());
|
||||||
});
|
});
|
||||||
this.servlets.clear();
|
this.servlets.clear();
|
||||||
|
String[][] defaultAddHeaders = null;
|
||||||
|
String[][] defaultSetHeaders = null;
|
||||||
|
HttpCookie defaultCookie = null;
|
||||||
|
if (config != null) {
|
||||||
|
AnyValue resps = config == null ? null : config.getAnyValue("response");
|
||||||
|
if (resps != null) {
|
||||||
|
AnyValue[] addHeaders = resps.getAnyValues("addheader");
|
||||||
|
if (addHeaders.length > 0) {
|
||||||
|
defaultAddHeaders = new String[addHeaders.length][];
|
||||||
|
for (int i = 0; i < addHeaders.length; i++) {
|
||||||
|
String val = addHeaders[i].getValue("value");
|
||||||
|
if (val == null) continue;
|
||||||
|
if (val.startsWith("request.headers.")) {
|
||||||
|
defaultAddHeaders[i] = new String[]{addHeaders[i].getValue("name"), val, val.substring("request.headers.".length())};
|
||||||
|
} else if (val.startsWith("system.property.")) {
|
||||||
|
String v = System.getProperty(val.substring("system.property.".length()));
|
||||||
|
if (v != null) defaultAddHeaders[i] = new String[]{addHeaders[i].getValue("name"), v};
|
||||||
|
} else {
|
||||||
|
defaultAddHeaders[i] = new String[]{addHeaders[i].getValue("name"), val};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnyValue[] setHeaders = resps.getAnyValues("setheader");
|
||||||
|
if (setHeaders.length > 0) {
|
||||||
|
defaultSetHeaders = new String[setHeaders.length][];
|
||||||
|
for (int i = 0; i < setHeaders.length; i++) {
|
||||||
|
String val = setHeaders[i].getValue("value");
|
||||||
|
if (val != null && val.startsWith("request.headers.")) {
|
||||||
|
defaultSetHeaders[i] = new String[]{setHeaders[i].getValue("name"), val, val.substring("request.headers.".length())};
|
||||||
|
} else {
|
||||||
|
defaultSetHeaders[i] = new String[]{setHeaders[i].getValue("name"), val};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnyValue defcookieValue = resps.getAnyValue("defcookie");
|
||||||
|
if (defcookieValue != null) {
|
||||||
|
String domain = defcookieValue.getValue("domain");
|
||||||
|
String path = defcookieValue.getValue("path");
|
||||||
|
if (domain != null || path != null) {
|
||||||
|
defaultCookie = new HttpCookie("DEFAULTCOOKIE", "");
|
||||||
|
defaultCookie.setDomain(domain);
|
||||||
|
defaultCookie.setPath(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String[][] addHeaders = defaultAddHeaders;
|
||||||
|
final String[][] setHeaders = defaultSetHeaders;
|
||||||
|
final HttpCookie defCookie = defaultCookie;
|
||||||
AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.creatCounter");
|
AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.creatCounter");
|
||||||
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.cycleCounter");
|
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.cycleCounter");
|
||||||
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
|
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
|
||||||
HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, bufferPool, responsePool,
|
HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, bufferPool, responsePool,
|
||||||
this.maxbody, this.charset, this.address, prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond, contextPath);
|
this.maxbody, this.charset, this.address, prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond, contextPath);
|
||||||
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, httpcontext.jsonFactory)));
|
responsePool.setCreator((Object... params)
|
||||||
|
-> new HttpResponse(httpcontext, new HttpRequest(httpcontext, httpcontext.jsonFactory), addHeaders, setHeaders, defCookie));
|
||||||
return httpcontext;
|
return httpcontext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public final class SncpClient {
|
|||||||
this.resultTypes = rt == void.class ? null : rt;
|
this.resultTypes = rt == void.class ? null : rt;
|
||||||
this.paramTypes = method.getGenericParameterTypes();
|
this.paramTypes = method.getGenericParameterTypes();
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.async = method.getReturnType() == void.class && method.getAnnotation(Async.class) != null;
|
this.async = false;// method.getReturnType() == void.class && method.getAnnotation(Async.class) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -31,24 +31,37 @@ public class DataCacheListenerService implements DataCacheListener, Service {
|
|||||||
|
|
||||||
private final ConcurrentHashMap<String, BlockingQueue<Map.Entry<Class, Serializable[]>>> deleteQueues = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<String, BlockingQueue<Map.Entry<Class, Serializable[]>>> deleteQueues = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private boolean finer;
|
private final boolean finest = logger.isLoggable(Level.FINEST);
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
@Resource(name = "APP_NODE")
|
@Resource(name = "APP_NODE")
|
||||||
private String localNodeName = "";
|
private String localNodeName = "";
|
||||||
|
|
||||||
@Resource(name = ".*")
|
@Resource(name = "APP_GROUP")
|
||||||
HashMap<String, DataSource> sourcemap;
|
private String localGroupName = "";
|
||||||
|
|
||||||
|
@Resource(name = "APP_GROUP")
|
||||||
|
private Map<String, Set<String>> groups;
|
||||||
|
|
||||||
@Resource(name = ".*")
|
@Resource(name = ".*")
|
||||||
HashMap<String, DataCacheListenerService> nodemap;
|
private HashMap<String, DataSource> sourcesmap;
|
||||||
|
|
||||||
|
@Resource(name = ".*")
|
||||||
|
private HashMap<String, DataCacheListenerService> nodesmap;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(AnyValue config) {
|
public void init(AnyValue config) {
|
||||||
finer = logger.isLoggable(Level.FINER);
|
if (finest) {
|
||||||
|
logger.finest(this.getClass().getSimpleName() + "-localgroup: " + localGroupName);
|
||||||
|
logger.finest(this.getClass().getSimpleName() + "-groups: " + groups);
|
||||||
|
logger.finest(this.getClass().getSimpleName() + "-sources: " + sourcesmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void insert(String sourceName, Class<T> clazz, T... entitys) {
|
public <T> void insert(String sourceName, Class<T> clazz, T... entitys) {
|
||||||
|
if (finest) logger.finest("(source:" + sourceName + ") insert " + clazz + " --> " + Arrays.toString(entitys));
|
||||||
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.insertQueues.get(sourceName);
|
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.insertQueues.get(sourceName);
|
||||||
if (queue == null) {
|
if (queue == null) {
|
||||||
synchronized (this.insertQueues) {
|
synchronized (this.insertQueues) {
|
||||||
@@ -68,7 +81,7 @@ public class DataCacheListenerService implements DataCacheListener, Service {
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Map.Entry<Class, Object[]> entry = tq.take();
|
Map.Entry<Class, Object[]> entry = tq.take();
|
||||||
sendInsert(sourceName, entry.getKey(), entry.getValue());
|
sendInsert(localGroupName, false, sourceName, entry.getKey(), entry.getValue());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, this.getName() + " sendInsert occur error", e);
|
logger.log(Level.SEVERE, this.getName() + " sendInsert occur error", e);
|
||||||
}
|
}
|
||||||
@@ -87,24 +100,48 @@ public class DataCacheListenerService implements DataCacheListener, Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RemoteOn
|
@RemoteOn
|
||||||
public <T> void sendInsert(String sourceName, Class<T> clazz, T... entitys) {
|
public <T> void sendInsert(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
|
||||||
if (nodemap == null) return;
|
if (nodesmap == null || groups == null) return;
|
||||||
nodemap.forEach((x, y) -> {
|
if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendInsert " + Arrays.toString(entitys));
|
||||||
try {
|
for (Map.Entry<String, Set<String>> en : groups.entrySet()) {
|
||||||
y.sendInsert(sourceName, clazz, entitys);
|
if (group != null && group.equals(en.getKey())) { //同机房
|
||||||
} catch (Exception e) {
|
for (String onode : en.getValue()) {
|
||||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send insert error (" + x + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e);
|
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 <T> void onSendInsert(String sourceName, Class<T> clazz, T... entitys) {
|
public final <T> void onSendInsert(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
|
||||||
((DataJDBCSource) sourcemap.get(sourceName)).insertCache(entitys);
|
if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + this.localNodeName + "," + sourceName + ") onSendInsert " + Arrays.toString(entitys));
|
||||||
if (finer) logger.finer(DataSource.class.getSimpleName() + "(" + this.localNodeName + "," + sourceName + ") onSendInsert " + Arrays.toString(entitys));
|
((DataJDBCSource) sourcesmap.get(sourceName)).insertCache(entitys);
|
||||||
|
if (!this.localGroupName.equals(group)) sendInsert(this.localGroupName, true, sourceName, clazz, entitys); //不是同一机房来的资源需要同步到其他同机房的节点上
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void update(String sourceName, Class<T> clazz, T... values) {
|
public <T> void update(String sourceName, Class<T> clazz, T... values) {
|
||||||
|
if (finest) logger.finest("(source:" + sourceName + ") update " + clazz + " --> " + Arrays.toString(values));
|
||||||
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.updateQueues.get(sourceName);
|
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.updateQueues.get(sourceName);
|
||||||
if (queue == null) {
|
if (queue == null) {
|
||||||
synchronized (this.updateQueues) {
|
synchronized (this.updateQueues) {
|
||||||
@@ -124,7 +161,7 @@ public class DataCacheListenerService implements DataCacheListener, Service {
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Map.Entry<Class, Object[]> entry = tq.take();
|
Map.Entry<Class, Object[]> entry = tq.take();
|
||||||
sendUpdate(sourceName, entry.getKey(), entry.getValue());
|
sendUpdate(localGroupName, false, sourceName, entry.getKey(), entry.getValue());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, this.getName() + " sendUpdate occur error", e);
|
logger.log(Level.SEVERE, this.getName() + " sendUpdate occur error", e);
|
||||||
}
|
}
|
||||||
@@ -143,24 +180,48 @@ public class DataCacheListenerService implements DataCacheListener, Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RemoteOn
|
@RemoteOn
|
||||||
public <T> void sendUpdate(String sourceName, Class<T> clazz, Object... values) {
|
public <T> void sendUpdate(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
|
||||||
if (nodemap == null) return;
|
if (nodesmap == null || groups == null) return;
|
||||||
nodemap.forEach((x, y) -> {
|
if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendUpdate " + Arrays.toString(entitys));
|
||||||
try {
|
for (Map.Entry<String, Set<String>> en : groups.entrySet()) {
|
||||||
y.sendUpdate(sourceName, clazz, values);
|
if (group != null && group.equals(en.getKey())) { //同机房
|
||||||
} catch (Exception e) {
|
for (String onode : en.getValue()) {
|
||||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send update error (" + x + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(values) + ")", e);
|
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 <T> void onSendUpdate(String sourceName, Class<T> clazz, T... entitys) {
|
public final <T> void onSendUpdate(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
|
||||||
((DataJDBCSource) sourcemap.get(sourceName)).updateCache(clazz, entitys);
|
if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onSendUpdate " + Arrays.toString(entitys));
|
||||||
if (finer) logger.finer(DataSource.class.getSimpleName() + "(" + this.localNodeName + "," + sourceName + ") onSendUpdate " + Arrays.toString(entitys));
|
((DataJDBCSource) sourcesmap.get(sourceName)).updateCache(clazz, entitys);
|
||||||
|
if (!this.localGroupName.equals(group)) sendUpdate(this.localGroupName, true, sourceName, clazz, entitys); //不是同一机房来的资源需要同步到其他同机房的节点上
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void delete(String sourceName, Class<T> clazz, Serializable... ids) {
|
public <T> void delete(String sourceName, Class<T> clazz, Serializable... ids) {
|
||||||
|
if (finest) logger.finest("(source:" + sourceName + ") delete " + clazz + " --> " + Arrays.toString(ids));
|
||||||
BlockingQueue<Map.Entry<Class, Serializable[]>> queue = this.deleteQueues.get(sourceName);
|
BlockingQueue<Map.Entry<Class, Serializable[]>> queue = this.deleteQueues.get(sourceName);
|
||||||
if (queue == null) {
|
if (queue == null) {
|
||||||
synchronized (this.deleteQueues) {
|
synchronized (this.deleteQueues) {
|
||||||
@@ -180,7 +241,7 @@ public class DataCacheListenerService implements DataCacheListener, Service {
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Map.Entry<Class, Serializable[]> entry = tq.take();
|
Map.Entry<Class, Serializable[]> entry = tq.take();
|
||||||
sendDelete(sourceName, entry.getKey(), entry.getValue());
|
sendDelete(localGroupName, false, sourceName, entry.getKey(), entry.getValue());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, this.getName() + " sendDelete occur error", e);
|
logger.log(Level.SEVERE, this.getName() + " sendDelete occur error", e);
|
||||||
}
|
}
|
||||||
@@ -199,19 +260,42 @@ public class DataCacheListenerService implements DataCacheListener, Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RemoteOn
|
@RemoteOn
|
||||||
public <T> void sendDelete(String sourceName, Class<T> clazz, Serializable... ids) {
|
public <T> void sendDelete(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, Serializable... ids) {
|
||||||
if (nodemap == null) return;
|
if (nodesmap == null || groups == null) return;
|
||||||
nodemap.forEach((x, y) -> {
|
if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendDelete " + Arrays.toString(ids));
|
||||||
try {
|
for (Map.Entry<String, Set<String>> en : groups.entrySet()) {
|
||||||
y.sendDelete(sourceName, clazz, ids);
|
if (group != null && group.equals(en.getKey())) { //同机房
|
||||||
} catch (Exception e) {
|
for (String onode : en.getValue()) {
|
||||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send delete error (" + x + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(ids) + ")", e);
|
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 <T> void onSendDelete(String sourceName, Class<T> clazz, Serializable... ids) {
|
public final <T> void onSendDelete(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, Serializable... ids) {
|
||||||
((DataJDBCSource) sourcemap.get(sourceName)).deleteCache(clazz, ids);
|
if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onSendDelete " + clazz.getName() + " " + Arrays.toString(ids));
|
||||||
if (finer) logger.finer(DataSource.class.getSimpleName() + "(" + this.localNodeName + "," + sourceName + ") onSendDelete " + clazz.getName() + " " + Arrays.toString(ids));
|
((DataJDBCSource) sourcesmap.get(sourceName)).deleteCache(clazz, ids);
|
||||||
|
if (!this.localGroupName.equals(group)) sendDelete(this.localGroupName, true, sourceName, clazz, ids); //不是同一机房来的资源需要同步到其他同机房的节点上
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,14 @@
|
|||||||
*/
|
*/
|
||||||
package com.wentch.redkale.service;
|
package com.wentch.redkale.service;
|
||||||
|
|
||||||
import com.wentch.redkale.util.AnyValue;
|
import com.wentch.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所有Service的实现类不得声明为final, 允许远程模式的public方法不能声明为final。
|
* 所有Service的实现类不得声明为final, 允许远程模式的public方法不能声明为final。
|
||||||
|
*
|
||||||
|
* @Resource(name = ".*")
|
||||||
|
* private HashMap<String, XXXService> nodemap;
|
||||||
|
* 被注入的多个XXXService实例 但不会包含自身的XXXService。
|
||||||
*
|
*
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1666,7 +1666,7 @@ public final class DataJDBCSource implements DataSource {
|
|||||||
public void connectionErrorOccurred(ConnectionEvent event) {
|
public void connectionErrorOccurred(ConnectionEvent event) {
|
||||||
usingCounter.decrementAndGet();
|
usingCounter.decrementAndGet();
|
||||||
if ("08S01".equals(event.getSQLException().getSQLState())) return; //MySQL特性, 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
|
if ("08S01".equals(event.getSQLException().getSQLState())) return; //MySQL特性, 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
|
||||||
dataSource.logger.log(Level.WARNING, "connectionErronOccurred " + event.getSQLException().getSQLState(), event.getSQLException());
|
dataSource.logger.log(Level.WARNING, "connectionErronOccurred [" + event.getSQLException().getSQLState() + "]", event.getSQLException());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
@@ -1797,7 +1797,7 @@ public final class DataJDBCSource implements DataSource {
|
|||||||
}
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
if (!"08S01".equals(ex.getSQLState())) {//MySQL特性, 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
|
if (!"08S01".equals(ex.getSQLState())) {//MySQL特性, 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
|
||||||
dataSource.logger.log(Level.FINER, "result.getConnection from pooled connection abort " + ex.getSQLState(), ex);
|
dataSource.logger.log(Level.FINER, "result.getConnection from pooled connection abort [" + ex.getSQLState() + "]", ex);
|
||||||
}
|
}
|
||||||
return poll(0, null);
|
return poll(0, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ package com.wentch.redkale.util;
|
|||||||
|
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.*;
|
||||||
import java.util.logging.*;
|
import java.util.logging.*;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.*;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -29,6 +29,8 @@ public final class ResourceFactory {
|
|||||||
|
|
||||||
private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, ?>> store = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, ?>> store = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Type, ConcurrentHashMap<String, ?>> gencstore = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private ResourceFactory(ResourceFactory parent) {
|
private ResourceFactory(ResourceFactory parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
@@ -73,10 +75,35 @@ public final class ResourceFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <A> void register(final String name, final Type clazz, final A rs) {
|
||||||
|
if (clazz instanceof Class) {
|
||||||
|
register(name, (Class) clazz, rs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ConcurrentHashMap map = this.gencstore.get(clazz);
|
||||||
|
if (map == null) {
|
||||||
|
ConcurrentHashMap<String, A> sub = new ConcurrentHashMap<>();
|
||||||
|
sub.put(name, rs);
|
||||||
|
gencstore.put(clazz, sub);
|
||||||
|
} else {
|
||||||
|
map.put(name, rs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public <A> A find(Class<? extends A> clazz) {
|
public <A> A find(Class<? extends A> clazz) {
|
||||||
return find("", clazz);
|
return find("", clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <A> A find(String name, Type clazz) {
|
||||||
|
Map<String, ?> map = this.gencstore.get(clazz);
|
||||||
|
if (map != null) {
|
||||||
|
A rs = (A) map.get(name);
|
||||||
|
if (rs != null) return rs;
|
||||||
|
}
|
||||||
|
if (parent != null) return parent.find(name, clazz);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public <A> A find(String name, Class<? extends A> clazz) {
|
public <A> A find(String name, Class<? extends A> clazz) {
|
||||||
Map<String, ?> map = this.store.get(clazz);
|
Map<String, ?> map = this.store.get(clazz);
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
@@ -125,7 +152,8 @@ public final class ResourceFactory {
|
|||||||
for (Field field : clazz.getDeclaredFields()) {
|
for (Field field : clazz.getDeclaredFields()) {
|
||||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
final Class type = field.getType();
|
final Class classtype = field.getType();
|
||||||
|
final Type genctype = field.getGenericType();
|
||||||
Resource rc = field.getAnnotation(Resource.class);
|
Resource rc = field.getAnnotation(Resource.class);
|
||||||
if (rc == null) {
|
if (rc == null) {
|
||||||
boolean flag = true;
|
boolean flag = true;
|
||||||
@@ -142,14 +170,16 @@ public final class ResourceFactory {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (Modifier.isFinal(field.getModifiers())) continue;
|
if (Modifier.isFinal(field.getModifiers())) continue;
|
||||||
Object rs;
|
Object rs = genctype == classtype ? null : find(rc.name(), genctype);
|
||||||
if (Map.class.isAssignableFrom(type)) {
|
if (rs == null) {
|
||||||
rs = find(Pattern.compile(rc.name().isEmpty() ? ".+" : rc.name()), (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1], src);
|
if (Map.class.isAssignableFrom(classtype)) {
|
||||||
} else {
|
rs = find(Pattern.compile(rc.name().isEmpty() ? ".+" : rc.name()), (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1], src);
|
||||||
if (rc.name().startsWith("property.")) {
|
|
||||||
rs = find(rc.name(), String.class);
|
|
||||||
} else {
|
} else {
|
||||||
rs = find(rc.name(), type);
|
if (rc.name().startsWith("property.")) {
|
||||||
|
rs = find(rc.name(), String.class);
|
||||||
|
} else {
|
||||||
|
rs = find(rc.name(), classtype);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rs == null) {
|
if (rs == null) {
|
||||||
@@ -157,20 +187,20 @@ public final class ResourceFactory {
|
|||||||
if (it != null) it.invoke(this, src, field);
|
if (it != null) it.invoke(this, src, field);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!rs.getClass().isPrimitive() && type.isPrimitive()) {
|
if (!rs.getClass().isPrimitive() && classtype.isPrimitive()) {
|
||||||
if (type == int.class) {
|
if (classtype == int.class) {
|
||||||
rs = Integer.decode(rs.toString());
|
rs = Integer.decode(rs.toString());
|
||||||
} else if (type == long.class) {
|
} else if (classtype == long.class) {
|
||||||
rs = Long.decode(rs.toString());
|
rs = Long.decode(rs.toString());
|
||||||
} else if (type == short.class) {
|
} else if (classtype == short.class) {
|
||||||
rs = Short.decode(rs.toString());
|
rs = Short.decode(rs.toString());
|
||||||
} else if (type == boolean.class) {
|
} else if (classtype == boolean.class) {
|
||||||
rs = "true".equalsIgnoreCase(rs.toString());
|
rs = "true".equalsIgnoreCase(rs.toString());
|
||||||
} else if (type == byte.class) {
|
} else if (classtype == byte.class) {
|
||||||
rs = Byte.decode(rs.toString());
|
rs = Byte.decode(rs.toString());
|
||||||
} else if (type == float.class) {
|
} else if (classtype == float.class) {
|
||||||
rs = Float.parseFloat(rs.toString());
|
rs = Float.parseFloat(rs.toString());
|
||||||
} else if (type == double.class) {
|
} else if (classtype == double.class) {
|
||||||
rs = Double.parseDouble(rs.toString());
|
rs = Double.parseDouble(rs.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user