Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb77681445 | ||
|
|
d2a3291f13 | ||
|
|
116e82115b | ||
|
|
e0b5067949 | ||
|
|
8e639e32a3 | ||
|
|
5a1586bc6a | ||
|
|
30b6ddc4f4 | ||
|
|
0667c6a12e | ||
|
|
6eb9429b68 | ||
|
|
91f32684ef | ||
|
|
fc475d6253 | ||
|
|
3df13a3b17 | ||
|
|
3d0bd3a5c2 | ||
|
|
42c188867a | ||
|
|
a3647f79ac | ||
|
|
f83fded877 | ||
|
|
df42be86d6 | ||
|
|
d22b44f1f9 | ||
|
|
1592938d99 | ||
|
|
a3681ca698 | ||
|
|
5f1556e5cb | ||
|
|
64bc543df2 | ||
|
|
36f03344bd | ||
|
|
d764b741d4 | ||
|
|
e5a83c51f3 | ||
|
|
42e7ac298e | ||
|
|
bf9098df86 | ||
|
|
d44e45166e | ||
|
|
07304e61d9 | ||
|
|
c69fcc25db | ||
|
|
72e25d653b | ||
|
|
c3fde23d77 | ||
|
|
1369bd3e9e | ||
|
|
2c70162798 | ||
|
|
5db8426553 | ||
|
|
3c667d88aa | ||
|
|
7a11b7887c | ||
|
|
bfbc48a597 | ||
|
|
ba14c59baa | ||
|
|
63d7e85fb4 | ||
|
|
66c2f0970f | ||
|
|
e78d590fc2 | ||
|
|
8f7ca376a9 | ||
|
|
82b041e6d5 | ||
|
|
5ae6afa20e | ||
|
|
1aeae47a89 | ||
|
|
7bf73b60b5 | ||
|
|
7e26ddddd5 | ||
|
|
593ed3e7e0 | ||
|
|
c0ff67df73 | ||
|
|
5489e01987 | ||
|
|
65910f8b66 | ||
|
|
ea277f4ea3 | ||
|
|
02fc248fcf | ||
|
|
1b42a4241a | ||
|
|
7ed0020b1d | ||
|
|
94bb11329d | ||
|
|
fccf314282 | ||
|
|
35029ad796 | ||
|
|
ea059594f0 | ||
|
|
839c742423 | ||
|
|
00b6910986 | ||
|
|
01da67dbf8 | ||
|
|
73a7864e09 | ||
|
|
a996ab5eb5 | ||
|
|
0d6a778d9c | ||
|
|
34ddd0d65e | ||
|
|
4f950d5874 | ||
|
|
6b228f1fc9 | ||
|
|
521a0d4cbb | ||
|
|
9ba2571ed1 | ||
|
|
cfcdb093e7 | ||
|
|
9c3c7e414a | ||
|
|
a7781322c7 | ||
|
|
34a0fef035 | ||
|
|
4dfda3ec51 | ||
|
|
fd0a6b94e9 | ||
|
|
1af2457cc7 | ||
|
|
f4bd6074f1 | ||
|
|
5b6e01ecb8 | ||
|
|
85844276ed | ||
|
|
af74bad593 | ||
|
|
f82becfe62 | ||
|
|
a98d5f32a1 | ||
|
|
a2d4b6ec37 | ||
|
|
29a80e95c7 | ||
|
|
41c1ea32d1 | ||
|
|
c100207302 | ||
|
|
5b4b08b6ce | ||
|
|
a6a3890e70 | ||
|
|
d3d24529a6 | ||
|
|
5593a518fc | ||
|
|
511a31bf95 | ||
|
|
999f55222b | ||
|
|
56cd881a99 | ||
|
|
a0439518f3 |
@@ -21,4 +21,4 @@
|
||||
|
||||
由于RedKale使用了JDK 8 内置的ASM包,所以需要在源码工程中的编译器选项中加入: <b>-XDignore.symbol.file=true</b>
|
||||
|
||||
<h5>详情请访问: <a href='http://www.redkale.org' target='_blank'>http://www.redkale.org</a></h5>
|
||||
<h5>详情请访问: <a href='http://redkale.org' target='_blank'>http://redkale.org</a></h5>
|
||||
|
||||
@@ -5,10 +5,12 @@ handlers = java.util.logging.ConsoleHandler
|
||||
############################################################
|
||||
.level = FINE
|
||||
|
||||
org.level = INFO
|
||||
java.level = INFO
|
||||
javax.level = INFO
|
||||
com.sun.level = INFO
|
||||
sun.level = INFO
|
||||
jdk.level = INFO
|
||||
|
||||
|
||||
java.util.logging.FileHandler.level = FINE
|
||||
#10M
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
responsePoolSize: Response池的大小,默认: CPU核数*256
|
||||
readTimeoutSecond: 读操作超时秒数, 默认0, 表示永久不超时
|
||||
writeTimeoutSecond: 写操作超时秒数, 默认0, 表示永久不超时
|
||||
nodeInterceptor: 启动/关闭NodeServer时被调用的拦截器实现类,必须是org.redkale.boot.NodeInterceptor的子类,默认为null
|
||||
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类,必须是org.redkale.boot.NodeInterceptor的子类,默认为null
|
||||
-->
|
||||
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
|
||||
|
||||
@@ -114,8 +114,9 @@
|
||||
-->
|
||||
<service value="com.xxx.XXX2Service" name="" groups="xxx;yyy"/>
|
||||
<!-- 给Service增加配置属性 -->
|
||||
<service value="com.xxx.XXX1Service">
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
<service value="com.xxx.XXX1Service">
|
||||
<!-- property节点值在 public void init(AnyValue conf) 方法中可以通过 AnyValue properties = conf.getAnyValue("properties");获取 -->
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||
</service>
|
||||
</services>
|
||||
@@ -156,14 +157,16 @@
|
||||
<!--
|
||||
当Server为HTTP协议时,ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点
|
||||
webroot: web资源的根目录, 默认取server节点中的root值
|
||||
index : 启始页,默认值:index.html
|
||||
-->
|
||||
<resource-servlet webroot="root">
|
||||
<resource-servlet webroot="root" index="index.html">
|
||||
<!--
|
||||
资源缓存的配置, 默认存在一个含默认属性的caches节点
|
||||
limit: 资源缓存最大容量, 默认: 0, 为0表示不缓存, 单位可以是B、K、M、G,不区分大小写
|
||||
lengthmax: 可缓存的文件大小上限, 默认: 1M(超过1M的文件不会被缓存)
|
||||
watch: 是否监控缓存文件的变化, 默认不监控
|
||||
-->
|
||||
<caches limit="0M" lengthmax="1M" />
|
||||
<cache limit="0M" lengthmax="1M" watch="false"/>
|
||||
<!--
|
||||
支持类似nginx中的rewrite, 目前只支持静态资源对静态资源的跳转。
|
||||
type: 匹配的类型, 目前只支持location(匹配requestURI), 默认: location
|
||||
|
||||
@@ -5,7 +5,6 @@ handlers = java.util.logging.ConsoleHandler,java.util.logging.FileHandler
|
||||
############################################################
|
||||
.level = FINE
|
||||
|
||||
org.level = INFO
|
||||
sun.level = INFO
|
||||
java.level = INFO
|
||||
javax.level = INFO
|
||||
|
||||
@@ -33,8 +33,6 @@ import org.w3c.dom.*;
|
||||
* <p>
|
||||
* 进程启动类,程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet 优先加载所有SNCP协议的服务, 再加载其他协议服务, 最后进行Service、Servlet与其他资源之间的依赖注入。
|
||||
* <p>
|
||||
* <p>
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
|
||||
@@ -14,13 +14,11 @@ import java.util.concurrent.*;
|
||||
import java.util.jar.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.regex.*;
|
||||
import org.redkale.util.AnyValue;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
import org.redkale.util.AutoLoad;
|
||||
|
||||
/**
|
||||
* class过滤器, 符合条件的class会保留下来存入FilterEntry。
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
@@ -30,8 +28,14 @@ import org.redkale.util.AutoLoad;
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class ClassFilter<T> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ClassFilter.class.getName());
|
||||
|
||||
private static final boolean finer = logger.isLoggable(Level.FINER);
|
||||
|
||||
private final Set<FilterEntry<T>> entrys = new HashSet<>();
|
||||
|
||||
private final Set<FilterEntry<T>> expectEntrys = new HashSet<>();
|
||||
|
||||
private boolean refused;
|
||||
|
||||
private Class superClass;
|
||||
@@ -79,10 +83,31 @@ public final class ClassFilter<T> {
|
||||
return entrys;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预留的class集合
|
||||
*
|
||||
* @return Set<FilterEntry<T>>
|
||||
*/
|
||||
public final Set<FilterEntry<T>> getFilterExpectEntrys() {
|
||||
return expectEntrys;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的class集合
|
||||
*
|
||||
* @return Set<FilterEntry<T>>
|
||||
*/
|
||||
public final Set<FilterEntry<T>> getAllFilterEntrys() {
|
||||
HashSet<FilterEntry<T>> rs = new HashSet<>();
|
||||
rs.addAll(entrys);
|
||||
rs.addAll(expectEntrys);
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动扫描地过滤指定的class
|
||||
*
|
||||
* @param property AnyValue
|
||||
* @param property AnyValue
|
||||
* @param clazzname String
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -93,9 +118,9 @@ public final class ClassFilter<T> {
|
||||
/**
|
||||
* 过滤指定的class
|
||||
*
|
||||
* @param property application.xml中对应class节点下的property属性项
|
||||
* @param property application.xml中对应class节点下的property属性项
|
||||
* @param clazzname class名称
|
||||
* @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略
|
||||
* @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略
|
||||
*/
|
||||
public final void filter(AnyValue property, String clazzname, boolean autoscan) {
|
||||
boolean r = accept0(property, clazzname);
|
||||
@@ -113,7 +138,7 @@ public final class ClassFilter<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cf == null) return;
|
||||
if (cf == null || clazzname.startsWith("sun.")) return;
|
||||
try {
|
||||
Class clazz = Class.forName(clazzname);
|
||||
if (!cf.accept(property, clazz, autoscan)) return;
|
||||
@@ -129,12 +154,22 @@ public final class ClassFilter<T> {
|
||||
property = dav;
|
||||
}
|
||||
}
|
||||
entrys.add(new FilterEntry(clazz, autoscan, property));
|
||||
|
||||
AutoLoad auto = (AutoLoad) clazz.getAnnotation(AutoLoad.class);
|
||||
if (autoscan && auto != null && !auto.value()) { //自动扫描且被标记为@AutoLoad(false)的
|
||||
expectEntrys.add(new FilterEntry(clazz, autoscan, true, property));
|
||||
} else {
|
||||
entrys.add(new FilterEntry(clazz, autoscan, false, property));
|
||||
}
|
||||
} catch (Throwable cfe) {
|
||||
if (finer && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
|
||||
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.")) {
|
||||
logger.log(Level.FINER, ClassFilter.class.getSimpleName() + " filter error", cfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Pattern[] toPattern(String[] regs) {
|
||||
public static Pattern[] toPattern(String[] regs) {
|
||||
if (regs == null) return null;
|
||||
int i = 0;
|
||||
Pattern[] rs = new Pattern[regs.length];
|
||||
@@ -152,8 +187,9 @@ public final class ClassFilter<T> {
|
||||
/**
|
||||
* 判断class是否有效
|
||||
*
|
||||
* @param property AnyValue
|
||||
* @param property AnyValue
|
||||
* @param classname String
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean accept(AnyValue property, String classname) {
|
||||
@@ -191,17 +227,14 @@ public final class ClassFilter<T> {
|
||||
* 判断class是否有效
|
||||
*
|
||||
* @param property AnyValue
|
||||
* @param clazz Class
|
||||
* @param clazz Class
|
||||
* @param autoscan boolean
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean accept(AnyValue property, Class clazz, boolean autoscan) {
|
||||
if (this.refused || !Modifier.isPublic(clazz.getModifiers())) return false;
|
||||
if (autoscan) {
|
||||
AutoLoad auto = (AutoLoad) clazz.getAnnotation(AutoLoad.class);
|
||||
if (auto != null && !auto.value()) return false;
|
||||
}
|
||||
if (annotationClass != null && clazz.getAnnotation(annotationClass) == null) return false;
|
||||
return superClass == null || (clazz != superClass && superClass.isAssignableFrom(clazz));
|
||||
}
|
||||
@@ -263,11 +296,13 @@ public final class ClassFilter<T> {
|
||||
|
||||
private final boolean autoload;
|
||||
|
||||
private final boolean expect;
|
||||
|
||||
public FilterEntry(Class<T> type, AnyValue property) {
|
||||
this(type, false, property);
|
||||
this(type, false, false, property);
|
||||
}
|
||||
|
||||
public FilterEntry(Class<T> type, final boolean autoload, AnyValue property) {
|
||||
public FilterEntry(Class<T> type, final boolean autoload, boolean expect, AnyValue property) {
|
||||
this.type = type;
|
||||
String str = property == null ? null : property.getValue("groups");
|
||||
if (str != null) {
|
||||
@@ -277,6 +312,7 @@ public final class ClassFilter<T> {
|
||||
if (str != null) groups.addAll(Arrays.asList(str.split(";")));
|
||||
this.property = property;
|
||||
this.autoload = autoload;
|
||||
this.expect = expect;
|
||||
this.name = property == null ? "" : property.getValue("name", "");
|
||||
}
|
||||
|
||||
@@ -322,6 +358,9 @@ public final class ClassFilter<T> {
|
||||
return autoload;
|
||||
}
|
||||
|
||||
public boolean isExpect() {
|
||||
return expect;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,6 +381,7 @@ public final class ClassFilter<T> {
|
||||
*
|
||||
* @param exclude 不需要扫描的文件夹, 可以为null
|
||||
* @param filters 过滤器
|
||||
*
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
public static void load(final File exclude, final ClassFilter... filters) throws IOException {
|
||||
|
||||
@@ -85,7 +85,7 @@ public final class NodeHttpServer extends NodeServer {
|
||||
}
|
||||
|
||||
protected void loadHttpServlet(final AnyValue conf, final ClassFilter<? extends Servlet> filter) throws Exception {
|
||||
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
final String prefix = conf == null ? "" : conf.getValue("path", "");
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
List<FilterEntry<? extends Servlet>> list = new ArrayList(filter.getFilterEntrys());
|
||||
@@ -135,7 +135,7 @@ public final class NodeHttpServer extends NodeServer {
|
||||
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
|
||||
}
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.redkale.service.Service;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
@@ -21,4 +24,64 @@ public class NodeInterceptor {
|
||||
public void preShutdown(NodeServer server) {
|
||||
|
||||
}
|
||||
|
||||
public static class InterceptorServiceWrapper<T extends Service> {
|
||||
|
||||
private String name;
|
||||
|
||||
private Class<T> type;
|
||||
|
||||
private T service;
|
||||
|
||||
public InterceptorServiceWrapper() {
|
||||
}
|
||||
|
||||
public InterceptorServiceWrapper(String name, Class<T> type, T service) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Class<T> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Class<T> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public T getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
public void setService(T service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 97 * hash + Objects.hashCode(this.name);
|
||||
hash = 97 * hash + Objects.hashCode(this.type);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
final InterceptorServiceWrapper<?> other = (InterceptorServiceWrapper<?>) obj;
|
||||
return Objects.equals(this.name, other.name) && Objects.equals(this.type, other.type);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.net.InetSocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
@@ -21,6 +21,7 @@ import javax.persistence.Transient;
|
||||
import static org.redkale.boot.Application.*;
|
||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.http.WebSocketNode;
|
||||
import org.redkale.net.sncp.*;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.source.*;
|
||||
@@ -29,8 +30,8 @@ import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* Server节点的初始化配置类
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -74,6 +75,9 @@ public abstract class NodeServer {
|
||||
//加载server节点后的拦截器
|
||||
protected NodeInterceptor interceptor;
|
||||
|
||||
//供interceptor使用的Service对象集合
|
||||
protected final Set<NodeInterceptor.InterceptorServiceWrapper> interceptorServiceWrappers = new LinkedHashSet<>();
|
||||
|
||||
//本地模式的Service对象集合
|
||||
protected final Set<ServiceWrapper> localServiceWrappers = new LinkedHashSet<>();
|
||||
|
||||
@@ -106,7 +110,11 @@ public abstract class NodeServer {
|
||||
logger.log(Level.SEVERE, "Server (" + server.getSocketAddress() + ") cannot find Context", e);
|
||||
}
|
||||
}
|
||||
context.submit(t);
|
||||
if (context == null) {
|
||||
t.run();
|
||||
} else {
|
||||
context.submit(t);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
@@ -134,7 +142,7 @@ public abstract class NodeServer {
|
||||
if (this.sncpGroup != null) this.resourceFactory.register(RESNAME_SERVER_GROUP, this.sncpGroup);
|
||||
{
|
||||
//设置root文件夹
|
||||
String webroot = config.getValue("root", "root");
|
||||
String webroot = this.serverConf.getValue("root", "root");
|
||||
File myroot = new File(webroot);
|
||||
if (!webroot.contains(":") && !webroot.startsWith("/")) {
|
||||
myroot = new File(System.getProperty(Application.RESNAME_APP_HOME), webroot);
|
||||
@@ -146,13 +154,13 @@ public abstract class NodeServer {
|
||||
|
||||
final String homepath = myroot.getCanonicalPath();
|
||||
//加入指定的classpath
|
||||
Server.loadLib(logger, config.getValue("lib", "").replace("${APP_HOME}", homepath) + ";" + homepath + "/lib/*;" + homepath + "/classes");
|
||||
Server.loadLib(logger, this.serverConf.getValue("lib", "").replace("${APP_HOME}", homepath) + ";" + homepath + "/lib/*;" + homepath + "/classes");
|
||||
}
|
||||
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
|
||||
server.init(config);
|
||||
server.init(this.serverConf);
|
||||
|
||||
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
|
||||
String interceptorClass = config.getValue("nodeInterceptor", "");
|
||||
String interceptorClass = this.serverConf.getValue("interceptor", "");
|
||||
if (!interceptorClass.isEmpty()) {
|
||||
Class clazz = forName(interceptorClass);
|
||||
this.interceptor = (NodeInterceptor) clazz.newInstance();
|
||||
@@ -201,7 +209,7 @@ public abstract class NodeServer {
|
||||
localServiceWrappers.add(wrapper);
|
||||
sncpServer.consumerAccept(wrapper);
|
||||
rf.inject(cacheListenerService, self);
|
||||
if (fine) logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService());
|
||||
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService());
|
||||
}
|
||||
field.set(src, source);
|
||||
rf.inject(source, self); // 给 "datasource.nodeid" 赋值;
|
||||
@@ -236,9 +244,9 @@ public abstract class NodeServer {
|
||||
Set<String> gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports);
|
||||
ServiceWrapper wrapper = new ServiceWrapper(CacheSourceService.class, (Service) source, resourceName, sncpServer.getSncpGroup(), gs, null);
|
||||
sncpServer.getSncpServer().addService(wrapper);
|
||||
if (finer) logger.finer("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService());
|
||||
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService());
|
||||
}
|
||||
logger.finer("[" + Thread.currentThread().getName() + "] Load Source " + source);
|
||||
logger.info("[" + Thread.currentThread().getName() + "] Load Source " + source);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "DataSource inject error", e);
|
||||
}
|
||||
@@ -249,15 +257,27 @@ public abstract class NodeServer {
|
||||
protected void loadService(ClassFilter serviceFilter) throws Exception {
|
||||
if (serviceFilter == null) return;
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
final Set<FilterEntry<Service>> entrys = serviceFilter.getFilterEntrys();
|
||||
final Set<FilterEntry<Service>> entrys = serviceFilter.getAllFilterEntrys();
|
||||
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
|
||||
|
||||
for (FilterEntry<Service> entry : entrys) { //service实现类
|
||||
final Class<? extends Service> type = entry.getType();
|
||||
if (Modifier.isFinal(type.getModifiers())) continue; //修饰final的类跳过
|
||||
if (!Modifier.isPublic(type.getModifiers())) continue;
|
||||
if (entry.isExpect()) {
|
||||
if (Modifier.isAbstract(type.getModifiers())) continue; //修饰abstract的类跳过
|
||||
if (DataSource.class.isAssignableFrom(type)) continue;
|
||||
if (CacheSource.class.isAssignableFrom(type)) continue;
|
||||
if (DataSQLListener.class.isAssignableFrom(type)) continue;
|
||||
if (DataCacheListener.class.isAssignableFrom(type)) continue;
|
||||
if (WebSocketNode.class.isAssignableFrom(type)) continue;
|
||||
}
|
||||
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
|
||||
if (resourceFactory.find(entry.getName(), type) != null) continue; //Server加载Service时需要判断是否已经加载过了。
|
||||
if (resourceFactory.find(entry.getName(), type) != null) { //Server加载Service时需要判断是否已经加载过了。
|
||||
Service oldother = resourceFactory.find(entry.getName(), type);
|
||||
interceptorServiceWrappers.add(new NodeInterceptor.InterceptorServiceWrapper(entry.getName(), type, oldother));
|
||||
continue;
|
||||
}
|
||||
final HashSet<String> groups = entry.getGroups(); //groups.isEmpty()表示<services>没有配置groups属性。
|
||||
if (groups.isEmpty() && isSNCP() && this.sncpGroup != null) groups.add(this.sncpGroup);
|
||||
|
||||
@@ -266,29 +286,51 @@ public abstract class NodeServer {
|
||||
|| (this.sncpGroup == null && entry.isEmptyGroups()) //空的SNCP配置
|
||||
|| type.getAnnotation(LocalService.class) != null;//本地模式
|
||||
if (localed && (type.isInterface() || Modifier.isAbstract(type.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类
|
||||
|
||||
Service service;
|
||||
if (localed) { //本地模式
|
||||
service = Sncp.createLocalService(entry.getName(), getExecutor(), application.getResourceFactory(), type, this.sncpAddress, loadTransport(this.sncpGroup), loadTransports(groups));
|
||||
} else {
|
||||
service = Sncp.createRemoteService(entry.getName(), getExecutor(), type, this.sncpAddress, loadTransport(groups));
|
||||
}
|
||||
if (SncpClient.parseMethod(type).isEmpty()) continue; //class没有可用的方法, 通常为BaseService
|
||||
final ServiceWrapper wrapper = new ServiceWrapper(type, service, entry.getName(), localed ? this.sncpGroup : null, groups, entry.getProperty());
|
||||
for (final Class restype : wrapper.getTypes()) {
|
||||
if (resourceFactory.find(wrapper.getName(), restype) == null) {
|
||||
regFactory.register(wrapper.getName(), restype, wrapper.getService());
|
||||
} else if (isSNCP() && !entry.isAutoload()) {
|
||||
throw new RuntimeException(ServiceWrapper.class.getSimpleName() + "(class:" + type.getName() + ", name:" + entry.getName() + ", group:" + groups + ") is repeat.");
|
||||
final BiConsumer<ResourceFactory, Boolean> runner = (ResourceFactory rf, Boolean needinject) -> {
|
||||
try {
|
||||
Service service;
|
||||
if (localed) { //本地模式
|
||||
service = Sncp.createLocalService(entry.getName(), getExecutor(), application.getResourceFactory(), type,
|
||||
NodeServer.this.sncpAddress, loadTransport(NodeServer.this.sncpGroup), loadTransports(groups));
|
||||
} else {
|
||||
service = Sncp.createRemoteService(entry.getName(), getExecutor(), type, NodeServer.this.sncpAddress, loadTransport(groups));
|
||||
}
|
||||
if (SncpClient.parseMethod(type).isEmpty()) return; //class没有可用的方法, 通常为BaseService
|
||||
final ServiceWrapper wrapper = new ServiceWrapper(type, service, entry.getName(), localed ? NodeServer.this.sncpGroup : null, groups, entry.getProperty());
|
||||
for (final Class restype : wrapper.getTypes()) {
|
||||
if (resourceFactory.find(wrapper.getName(), restype) == null) {
|
||||
regFactory.register(wrapper.getName(), restype, wrapper.getService());
|
||||
if (needinject) rf.inject(wrapper.getService()); //动态加载的Service也存在按需加载的注入资源
|
||||
} else if (isSNCP() && !entry.isAutoload()) {
|
||||
throw new RuntimeException(ServiceWrapper.class.getSimpleName() + "(class:" + type.getName() + ", name:" + entry.getName() + ", group:" + groups + ") is repeat.");
|
||||
}
|
||||
}
|
||||
if (wrapper.isRemote()) {
|
||||
remoteServiceWrappers.add(wrapper);
|
||||
} else {
|
||||
localServiceWrappers.add(wrapper);
|
||||
interceptorServiceWrappers.add(new NodeInterceptor.InterceptorServiceWrapper(entry.getName(), type, service));
|
||||
if (consumer != null) consumer.accept(wrapper);
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
if (entry.isExpect()) {
|
||||
ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
|
||||
runner.accept(rf, true);
|
||||
};
|
||||
for (final Class restype : ServiceWrapper.parseTypes(entry.getType())) {
|
||||
resourceFactory.register(resourceLoader, restype);
|
||||
}
|
||||
}
|
||||
if (wrapper.isRemote()) {
|
||||
remoteServiceWrappers.add(wrapper);
|
||||
} else {
|
||||
localServiceWrappers.add(wrapper);
|
||||
if (consumer != null) consumer.accept(wrapper);
|
||||
runner.accept(resourceFactory, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
application.servicecdl.countDown();
|
||||
application.servicecdl.await();
|
||||
|
||||
@@ -297,12 +339,14 @@ public abstract class NodeServer {
|
||||
new ArrayList<>(localServiceWrappers).forEach(y -> {
|
||||
resourceFactory.inject(y.getService(), NodeServer.this);
|
||||
});
|
||||
remoteServiceWrappers.forEach(y -> {
|
||||
new ArrayList<>(remoteServiceWrappers).forEach(y -> {
|
||||
resourceFactory.inject(y.getService(), NodeServer.this);
|
||||
if (sb != null) {
|
||||
sb.append(threadName).append(y.toSimpleString()).append(" loaded and injected").append(LINE_SEPARATOR);
|
||||
}
|
||||
});
|
||||
if (sb != null) {
|
||||
remoteServiceWrappers.forEach(y -> {
|
||||
sb.append(threadName).append(y.toSimpleString()).append(" loaded and injected").append(LINE_SEPARATOR);
|
||||
});
|
||||
}
|
||||
//----------------- init -----------------
|
||||
List<ServiceWrapper> swlist = new ArrayList<>(localServiceWrappers);
|
||||
Collections.sort(swlist);
|
||||
@@ -313,7 +357,7 @@ public abstract class NodeServer {
|
||||
long s = System.currentTimeMillis();
|
||||
y.getService().init(y.getConf());
|
||||
long e = System.currentTimeMillis() - s;
|
||||
if (slist != null) slist.add(new StringBuilder().append(threadName).append(y.toSimpleString()).append(" loaded and init ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
|
||||
if (slist != null) slist.add(new StringBuilder().append(threadName).append(y.toSimpleString()).append(" loaded and inited ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
|
||||
});
|
||||
Collections.sort(slist);
|
||||
if (slist != null && sb != null) {
|
||||
@@ -406,19 +450,19 @@ public abstract class NodeServer {
|
||||
prop.addValue("groups", sc);
|
||||
}
|
||||
ClassFilter filter = new ClassFilter(ref, inter, prop);
|
||||
for (AnyValue av : list.getAnyValues(property)) {
|
||||
for (AnyValue av : list.getAnyValues(property)) { // <service> 或 <servlet> 节点
|
||||
final AnyValue[] items = av.getAnyValues("property");
|
||||
if (av instanceof DefaultAnyValue && items.length > 0) {
|
||||
if (av instanceof DefaultAnyValue && items.length > 0) { //存在 <property>节点
|
||||
DefaultAnyValue dav = DefaultAnyValue.create();
|
||||
final AnyValue.Entry<String>[] strings = av.getStringEntrys();
|
||||
if (strings != null) {
|
||||
if (strings != null) { //将<service>或<servlet>节点的属性值传给dav
|
||||
for (AnyValue.Entry<String> en : strings) {
|
||||
dav.addValue(en.name, en.getValue());
|
||||
}
|
||||
}
|
||||
final AnyValue.Entry<AnyValue>[] anys = av.getAnyEntrys();
|
||||
if (anys != null) {
|
||||
for (AnyValue.Entry<AnyValue> en : anys) {
|
||||
for (AnyValue.Entry<AnyValue> en : anys) { //将<service>或<servlet>节点的非property属性节点传给dav
|
||||
if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue());
|
||||
}
|
||||
}
|
||||
@@ -426,7 +470,7 @@ public abstract class NodeServer {
|
||||
for (AnyValue item : items) {
|
||||
ps.addValue(item.getValue("name"), item.getValue("value"));
|
||||
}
|
||||
dav.addValue("property", ps);
|
||||
dav.addValue("properties", ps);
|
||||
av = dav;
|
||||
}
|
||||
filter.filter(av, av.getValue("value"), false);
|
||||
@@ -460,6 +504,10 @@ public abstract class NodeServer {
|
||||
return serverConf;
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public String getSncpGroup() {
|
||||
return sncpGroup;
|
||||
}
|
||||
@@ -488,6 +536,10 @@ public abstract class NodeServer {
|
||||
return (T) server;
|
||||
}
|
||||
|
||||
public Set<NodeInterceptor.InterceptorServiceWrapper> getInterceptorServiceWrappers() {
|
||||
return new LinkedHashSet<>(interceptorServiceWrappers);
|
||||
}
|
||||
|
||||
public Set<ServiceWrapper> getLocalServiceWrappers() {
|
||||
return new LinkedHashSet<>(localServiceWrappers);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.lang.reflect.Type;
|
||||
/**
|
||||
* 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入Writer,JSON则不写入。
|
||||
* <p>
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
|
||||
@@ -201,6 +201,18 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
this.skipAllIgnore = skipIgnore;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使所有类的所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false
|
||||
*
|
||||
* @param skipIgnore
|
||||
*
|
||||
* @return 自身
|
||||
*/
|
||||
public ConvertFactory<R, W> skipAllIgnore(final boolean skipIgnore) {
|
||||
this.skipAllIgnore = skipIgnore;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使该类所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false
|
||||
*
|
||||
@@ -215,11 +227,11 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
register(type, column, new ConvertColumnEntry(column, ignore));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final boolean register(final Class type, String column, String alias) {
|
||||
return register(type, column, new ConvertColumnEntry(alias));
|
||||
}
|
||||
|
||||
|
||||
public final boolean register(final Class type, String column, ConvertColumnEntry entry) {
|
||||
if (type == null || column == null || entry == null) return false;
|
||||
try {
|
||||
@@ -375,7 +387,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
} else if (clazz == Object.class) {
|
||||
od = new ObjectDecoder(type);
|
||||
decoder = od;
|
||||
} else if (!clazz.getName().startsWith("java.")) {
|
||||
} else if (!clazz.getName().startsWith("java.") || clazz.getName().startsWith("java.awt.geom.Point2D")) {
|
||||
Decodeable simpleCoder = null;
|
||||
for (final Method method : clazz.getDeclaredMethods()) {
|
||||
if (!Modifier.isStatic(method.getModifiers())) continue;
|
||||
|
||||
@@ -161,6 +161,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
* 对象格式: [0x1][short字段个数][字段名][字段值]...[0x2]
|
||||
*
|
||||
* @param in 输入流
|
||||
*
|
||||
* @return 反解析后的对象结果
|
||||
*/
|
||||
@Override
|
||||
|
||||
@@ -39,6 +39,12 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BsonFactory skipAllIgnore(final boolean skipIgnore) {
|
||||
this.registerSkipAllIgnore(skipIgnore);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static BsonFactory root() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import java.util.Date;
|
||||
/**
|
||||
* Date 的SimpledCoder实现
|
||||
*
|
||||
* <p> 详情见: http://redkale.org
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
@@ -24,12 +26,13 @@ public final class DateSimpledCoder<R extends Reader, W extends Writer> extends
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, Date value) {
|
||||
out.writeLong(value.getTime());
|
||||
out.writeLong(value == null ? 0L : value.getTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date convertFrom(R in) {
|
||||
return new Date(in.readLong());
|
||||
long t = in.readLong();
|
||||
return t == 0 ? null : new Date();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,8 +9,13 @@ import java.net.*;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public class URISimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, URI> {
|
||||
|
||||
|
||||
@@ -9,8 +9,13 @@ import java.net.*;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public class URLSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, URL> {
|
||||
|
||||
|
||||
@@ -41,6 +41,12 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonFactory skipAllIgnore(final boolean skipIgnore) {
|
||||
this.registerSkipAllIgnore(skipIgnore);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static JsonFactory root() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import org.redkale.util.*;
|
||||
*
|
||||
* writeTo系列的方法输出的字符不能含特殊字符
|
||||
* <p>
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
|
||||
@@ -19,9 +19,11 @@ import org.redkale.util.*;
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <K> SessionID的类型
|
||||
* @param <C> Context的子类型
|
||||
* @param <R> Request的子类型
|
||||
* @param <P> Response的子类型
|
||||
* @param <S> Servlet的子类型
|
||||
*/
|
||||
public abstract class PrepareServlet<K extends Serializable, C extends Context, R extends Request<C>, P extends Response<C, R>, S extends Servlet<C, R, P>> extends Servlet<C, R, P> {
|
||||
|
||||
|
||||
@@ -144,6 +144,10 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
public Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
public PrepareServlet<K, C, R, P, S> getPrepareServlet(){
|
||||
return this.prepare;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addServlet(S servlet, final Object attachment, AnyValue conf, K... mappings) {
|
||||
|
||||
@@ -17,7 +17,9 @@ import java.nio.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import org.redkale.service.RetResult;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -28,9 +30,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
*/
|
||||
public abstract class BasedHttpServlet extends HttpServlet {
|
||||
|
||||
public static final int RET_METHOD_ERROR = 1800_0001;
|
||||
|
||||
/**
|
||||
* 配合 BasedHttpServlet 使用。
|
||||
* 当标记为 @AuthIgnore 的方法不会再调用之前调用authenticate 方法。
|
||||
* 当标记为 @AuthIgnore 的方法在执行execute之前不会调用authenticate 方法。
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
@@ -47,7 +51,7 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
|
||||
/**
|
||||
* 配合 BasedHttpServlet 使用。
|
||||
* 用于对@WebServlet对应的url进行细分。 其 url
|
||||
* 用于对@WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀, 且不能是正则表达式
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
@@ -62,6 +66,8 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
int actionid() default 0;
|
||||
|
||||
String url();
|
||||
|
||||
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,6 +105,10 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
for (Map.Entry<String, Entry> en : actions) {
|
||||
if (request.getRequestURI().startsWith(en.getKey())) {
|
||||
Entry entry = en.getValue();
|
||||
if (!entry.checkMethod(request.getMethod())) {
|
||||
response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error"));
|
||||
return;
|
||||
}
|
||||
if (entry.ignore || authenticate(entry.moduleid, entry.actionid, request, response)) {
|
||||
if (entry.cachetimeout > 0) {//有缓存设置
|
||||
CacheEntry ce = entry.cache.get(request.getRequestURI());
|
||||
@@ -128,6 +138,8 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
for (Map.Entry<String, Entry> en : map.entrySet()) {
|
||||
actions[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue());
|
||||
}
|
||||
//必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query
|
||||
Arrays.sort(actions, (o1, o2) -> o2.getKey().compareTo(o1.getKey()));
|
||||
}
|
||||
|
||||
public final void postDestroy(HttpContext context, AnyValue config) {
|
||||
@@ -148,7 +160,7 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
//-----------------------------------------------
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 2 || paramTypes[0] != HttpRequest.class
|
||||
|| paramTypes[1] != HttpResponse.class) continue;
|
||||
|| paramTypes[1] != HttpResponse.class) continue;
|
||||
//-----------------------------------------------
|
||||
Class[] exps = method.getExceptionTypes();
|
||||
if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue;
|
||||
@@ -160,13 +172,14 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
final String name = action.url().trim();
|
||||
|
||||
if (nameset.contains(name)) throw new RuntimeException(this.getClass().getSimpleName() + " has two same " + WebAction.class.getSimpleName() + "(" + name + ")");
|
||||
for (String n : nameset) {
|
||||
if (n.contains(name) || name.contains(n)) {
|
||||
throw new RuntimeException(this.getClass().getSimpleName() + " has two sub-contains " + WebAction.class.getSimpleName() + "(" + name + ", " + n + ")");
|
||||
}
|
||||
}
|
||||
//屏蔽以下代码,允许相互包含
|
||||
// for (String n : nameset) {
|
||||
// if (n.contains(name) || name.contains(n)) {
|
||||
// throw new RuntimeException(this.getClass().getSimpleName() + " has two sub-contains " + WebAction.class.getSimpleName() + "(" + name + ", " + n + ")");
|
||||
// }
|
||||
// }
|
||||
nameset.add(name);
|
||||
map.put(name, new Entry(typeIgnore, serviceid, actionid, name, method, createHttpServlet(method)));
|
||||
map.put(name, new Entry(typeIgnore, serviceid, actionid, name, action.methods(), method, createHttpServlet(method)));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
@@ -191,7 +204,7 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||
FieldVisitor fv;
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
@@ -252,10 +265,11 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
|
||||
private static final class Entry {
|
||||
|
||||
public Entry(boolean typeIgnore, int moduleid, int actionid, String name, Method method, HttpServlet servlet) {
|
||||
public Entry(boolean typeIgnore, int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
|
||||
this.moduleid = moduleid;
|
||||
this.actionid = actionid;
|
||||
this.name = name;
|
||||
this.methods = methods;
|
||||
this.method = method;
|
||||
this.servlet = servlet;
|
||||
this.ignore = typeIgnore || method.getAnnotation(AuthIgnore.class) != null;
|
||||
@@ -275,6 +289,14 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
return this.moduleid != 0 || this.actionid != 0;
|
||||
}
|
||||
|
||||
public boolean checkMethod(final String reqMethod) {
|
||||
if (methods.length == 0) return true;
|
||||
for (String m : methods) {
|
||||
if (reqMethod.equalsIgnoreCase(m)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final HttpResponse.BufferHandler cacheHandler;
|
||||
|
||||
public final ConcurrentHashMap<String, CacheEntry> cache;
|
||||
@@ -289,6 +311,8 @@ public abstract class BasedHttpServlet extends HttpServlet {
|
||||
|
||||
public final String name;
|
||||
|
||||
public final String[] methods;
|
||||
|
||||
public final Method method;
|
||||
|
||||
public final HttpServlet servlet;
|
||||
|
||||
@@ -56,9 +56,18 @@ public final class HttpPrepareServlet extends PrepareServlet<String, HttpContext
|
||||
((DefaultAnyValue) resConfig).addValue("webroot", config.getValue("root"));
|
||||
}
|
||||
}
|
||||
if (resConfig == null) resConfig = config.getAnyValue("resource-servlet"); //兼容
|
||||
if (resConfig == null) {
|
||||
DefaultAnyValue dresConfig = new DefaultAnyValue();
|
||||
dresConfig.addValue("webroot", config.getValue("root"));
|
||||
dresConfig.addValue("ranges", config.getValue("ranges"));
|
||||
dresConfig.addValue("cache", config.getAnyValue("cache"));
|
||||
AnyValue[] rewrites = config.getAnyValues("rewrite");
|
||||
if (rewrites != null) {
|
||||
for (AnyValue rewrite : rewrites) {
|
||||
dresConfig.addValue("rewrite", rewrite);
|
||||
}
|
||||
}
|
||||
resConfig = dresConfig;
|
||||
}
|
||||
this.resourceHttpServlet.init(context, resConfig);
|
||||
@@ -89,34 +98,44 @@ public final class HttpPrepareServlet extends PrepareServlet<String, HttpContext
|
||||
@Override
|
||||
public void addServlet(HttpServlet servlet, Object prefix, AnyValue conf, String... mappings) {
|
||||
if (prefix == null) prefix = "";
|
||||
for (String mapping : mappings) {
|
||||
if (!prefix.toString().isEmpty()) mapping = prefix + mapping;
|
||||
if (this.mapStrings.containsKey(mapping)) {
|
||||
Class old = this.mapStrings.get(mapping);
|
||||
throw new RuntimeException("mapping [" + mapping + "] repeat on " + old.getName() + " and " + servlet.getClass().getName());
|
||||
if (mappings.length < 1) {
|
||||
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null) {
|
||||
mappings = ws.value();
|
||||
if (!ws.repair()) prefix = "";//被设置为自动追加前缀则清空prefix
|
||||
}
|
||||
if (contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
||||
if (mapping.charAt(0) != '^') mapping = '^' + mapping;
|
||||
if (mapping.endsWith("/*")) {
|
||||
mapping = mapping.substring(0, mapping.length() - 1) + ".*";
|
||||
} else {
|
||||
mapping = mapping + "$";
|
||||
}
|
||||
if (regArray == null) {
|
||||
regArray = new SimpleEntry[1];
|
||||
regArray[0] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet);
|
||||
} else {
|
||||
regArray = Arrays.copyOf(regArray, regArray.length + 1);
|
||||
regArray[regArray.length - 1] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet);
|
||||
}
|
||||
} else if (mapping != null && !mapping.isEmpty()) {
|
||||
super.mappings.put(mapping, servlet);
|
||||
}
|
||||
this.mapStrings.put(mapping, servlet.getClass());
|
||||
}
|
||||
setServletConf(servlet, conf);
|
||||
servlet._prefix = prefix.toString();
|
||||
this.servlets.add(servlet);
|
||||
synchronized (mapStrings) {
|
||||
for (String mapping : mappings) {
|
||||
if(mapping == null) continue;
|
||||
if (!prefix.toString().isEmpty()) mapping = prefix + mapping;
|
||||
if (this.mapStrings.containsKey(mapping)) {
|
||||
Class old = this.mapStrings.get(mapping);
|
||||
throw new RuntimeException("mapping [" + mapping + "] repeat on " + old.getName() + " and " + servlet.getClass().getName());
|
||||
}
|
||||
if (contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
||||
if (mapping.charAt(0) != '^') mapping = '^' + mapping;
|
||||
if (mapping.endsWith("/*")) {
|
||||
mapping = mapping.substring(0, mapping.length() - 1) + ".*";
|
||||
} else {
|
||||
mapping = mapping + "$";
|
||||
}
|
||||
if (regArray == null) {
|
||||
regArray = new SimpleEntry[1];
|
||||
regArray[0] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet);
|
||||
} else {
|
||||
regArray = Arrays.copyOf(regArray, regArray.length + 1);
|
||||
regArray[regArray.length - 1] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet);
|
||||
}
|
||||
} else if (mapping != null && !mapping.isEmpty()) {
|
||||
super.mappings.put(mapping, servlet);
|
||||
}
|
||||
this.mapStrings.put(mapping, servlet.getClass());
|
||||
}
|
||||
setServletConf(servlet, conf);
|
||||
servlet._prefix = prefix.toString();
|
||||
this.servlets.add(servlet);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean contains(String string, char... values) {
|
||||
@@ -135,6 +154,10 @@ public final class HttpPrepareServlet extends PrepareServlet<String, HttpContext
|
||||
}
|
||||
}
|
||||
|
||||
public HttpServlet getResourceServlet() {
|
||||
return this.resourceHttpServlet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(HttpContext context, AnyValue config) {
|
||||
this.resourceHttpServlet.destroy(context, config);
|
||||
|
||||
@@ -16,8 +16,13 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
import org.redkale.util.ByteArray;
|
||||
|
||||
/**
|
||||
* Http请求包 与javax.servlet.http.HttpServletRequest 基本类似。 同时提供json的解析接口: public Object getJsonParameter(Class clazz, String name) RedKale提倡带简单的参数的GET请求采用类似REST风格, 因此提供了 getRequstURIPath 系列接口。 例如简单的翻页查询 /pipes/record/query/page:2/size:20 获取页号: int page = request.getRequstURIPath("page:", 1); 获取行数: int size = request.getRequstURIPath("size:", 10);
|
||||
* <p>
|
||||
* Http请求包 与javax.servlet.http.HttpServletRequest 基本类似。 <br>
|
||||
* 同时提供json的解析接口: public Object getJsonParameter(Class clazz, String name) <br>
|
||||
* RedKale提倡带简单的参数的GET请求采用类似REST风格, 因此提供了 getRequstURIPath 系列接口。 <br>
|
||||
* 例如简单的翻页查询 <br>
|
||||
* /pipes/record/query/offset:0/limit:20 <br>
|
||||
* 获取页号: int offset = request.getRequstURIPath("offset:", 0); <br>
|
||||
* 获取行数: int limit = request.getRequstURIPath("limit:", 10);
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
@@ -234,7 +239,8 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端地址IP, 与getRemoteAddres() 的区别在于:本方法优先取header中指定为RemoteAddress名的值,没有则返回getRemoteAddres()的getHostAddress()。 本方法适用于服务前端有如nginx的代理服务器进行中转,通过getRemoteAddres()是获取不到客户端的真实IP。
|
||||
* 获取客户端地址IP, 与getRemoteAddress() 的区别在于:本方法优先取header中指定为RemoteAddress名的值,没有则返回getRemoteAddress()的getHostAddress()。<br>
|
||||
* 本方法适用于服务前端有如nginx的代理服务器进行中转,通过 getRemoteAddress()是获取不到客户端的真实IP。
|
||||
*
|
||||
* @return 地址
|
||||
*/
|
||||
@@ -253,6 +259,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* 获取请求内容指定的编码字符串
|
||||
*
|
||||
* @param charset 编码
|
||||
*
|
||||
* @return 内容
|
||||
*/
|
||||
public String getBody(final Charset charset) {
|
||||
@@ -280,10 +287,10 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
@Override
|
||||
public String toString() {
|
||||
parseBody();
|
||||
return this.getClass().getSimpleName() + "{method:" + this.method + ", requestURI:" + this.requestURI
|
||||
+ ", remoteAddr:" + this.getRemoteAddr() + ", cookies:" + this.cookiestr + ", contentType:" + this.contentType
|
||||
+ ", connection:" + this.connection + ", protocol:" + this.protocol + ", contentLength:" + this.contentLength
|
||||
+ ", host:" + this.host + ", bodylength:" + this.array.size() + ", params:" + this.params + ", header:" + this.header + "}";
|
||||
return this.getClass().getSimpleName() + "{\r\n method: " + this.method + ", \r\n requestURI: " + this.requestURI
|
||||
+ ", \r\n remoteAddr: " + this.getRemoteAddr() + ", \r\n cookies: " + this.cookiestr + ", \r\n contentType: " + this.contentType
|
||||
+ ", \r\n connection: " + this.connection + ", \r\n protocol: " + this.protocol + ", \r\n contentLength: " + this.contentLength
|
||||
+ ", \r\n host: " + this.host + ", \r\n bodylength: " + this.array.size() + ", \r\n params: " + this.params.toString(4) + ", \r\n header: " + this.header.toString(4) + "\r\n}";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,6 +343,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* 获取sessionid
|
||||
*
|
||||
* @param create 无sessionid是否自动创建
|
||||
*
|
||||
* @return sessionid
|
||||
*/
|
||||
public String getSessionid(boolean create) {
|
||||
@@ -378,6 +386,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* 获取Cookie值
|
||||
*
|
||||
* @param name cookie名
|
||||
*
|
||||
* @return cookie值
|
||||
*/
|
||||
public String getCookie(String name) {
|
||||
@@ -387,8 +396,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取Cookie值, 没有返回默认值
|
||||
*
|
||||
* @param name cookie名
|
||||
* @param name cookie名
|
||||
* @param dfvalue 默认cookie值
|
||||
*
|
||||
* @return cookie值
|
||||
*/
|
||||
public String getCookie(String name, String dfvalue) {
|
||||
@@ -492,6 +502,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* 从prefix之后截取getRequestURI再对"/"进行分隔
|
||||
* <p>
|
||||
* @param prefix 前缀
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
public String[] getRequstURIPaths(String prefix) {
|
||||
@@ -500,14 +511,17 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求URL分段中含prefix段的值 例如请求URL /pipes/record/query/name:hello 获取name参数: String name = request.getRequstURIPath("name:", "none");
|
||||
* 获取请求URL分段中含prefix段的值 <br>
|
||||
* 例如请求URL /pipes/record/query/name:hello <br>
|
||||
* 获取name参数: String name = request.getRequstURIPath("name:", "none");
|
||||
*
|
||||
* @param prefix prefix段前缀
|
||||
* @param prefix prefix段前缀
|
||||
* @param defvalue 默认值
|
||||
*
|
||||
* @return prefix截断后的值
|
||||
*/
|
||||
public String getRequstURIPath(String prefix, String defvalue) {
|
||||
if (requestURI == null || prefix == null) return defvalue;
|
||||
if (requestURI == null || prefix == null || prefix.isEmpty()) return defvalue;
|
||||
int pos = requestURI.indexOf(prefix);
|
||||
if (pos < 0) return defvalue;
|
||||
String sub = requestURI.substring(pos + prefix.length());
|
||||
@@ -516,10 +530,13 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求URL分段中含prefix段的short值 例如请求URL /pipes/record/query/type:10 获取type参数: short type = request.getRequstURIPath("type:", (short)0);
|
||||
* 获取请求URL分段中含prefix段的short值 <br>
|
||||
* 例如请求URL /pipes/record/query/type:10 <br>
|
||||
* 获取type参数: short type = request.getRequstURIPath("type:", (short)0);
|
||||
*
|
||||
* @param prefix prefix段前缀
|
||||
* @param prefix prefix段前缀
|
||||
* @param defvalue 默认short值
|
||||
*
|
||||
* @return short值
|
||||
*/
|
||||
public short getRequstURIPath(String prefix, short defvalue) {
|
||||
@@ -528,10 +545,14 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求URL分段中含prefix段的int值 例如请求URL /pipes/record/query/page:2/size:50 获取page参数: int page = request.getRequstURIPath("page:", 1); 获取size参数: int size = request.getRequstURIPath("size:", 20);
|
||||
* 获取请求URL分段中含prefix段的int值 <br>
|
||||
* 例如请求URL /pipes/record/query/offset:0/limit:50 <br>
|
||||
* 获取起址参数: int offset = request.getRequstURIPath("offset:", 0); <br>
|
||||
* 获取行数参数: int limit = request.getRequstURIPath("limit:", 20); <br>
|
||||
*
|
||||
* @param prefix prefix段前缀
|
||||
* @param prefix prefix段前缀
|
||||
* @param defvalue 默认int值
|
||||
*
|
||||
* @return int值
|
||||
*/
|
||||
public int getRequstURIPath(String prefix, int defvalue) {
|
||||
@@ -540,10 +561,28 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求URL分段中含prefix段的long值 例如请求URL /pipes/record/query/time:1453104341363/id:40 获取time参数: long time = request.getRequstURIPath("time:", 0L);
|
||||
* 获取请求URL分段中含prefix段的float值 <br>
|
||||
* 例如请求URL /pipes/record/query/point:40.0 <br>
|
||||
* 获取time参数: float point = request.getRequstURIPath("point:", 0.0f);
|
||||
*
|
||||
* @param prefix prefix段前缀
|
||||
* @param prefix prefix段前缀
|
||||
* @param defvalue 默认float值
|
||||
*
|
||||
* @return float值
|
||||
*/
|
||||
public float getRequstURIPath(String prefix, float defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
return val == null ? defvalue : Float.parseFloat(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求URL分段中含prefix段的long值 <br>
|
||||
* 例如请求URL /pipes/record/query/time:1453104341363/id:40 <br>
|
||||
* 获取time参数: long time = request.getRequstURIPath("time:", 0L);
|
||||
*
|
||||
* @param prefix prefix段前缀
|
||||
* @param defvalue 默认long值
|
||||
*
|
||||
* @return long值
|
||||
*/
|
||||
public long getRequstURIPath(String prefix, long defvalue) {
|
||||
@@ -551,6 +590,21 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
return val == null ? defvalue : Long.parseLong(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求URL分段中含prefix段的double值 <br>
|
||||
* 例如请求URL /pipes/record/query/point:40.0 <br>
|
||||
* 获取time参数: double point = request.getRequstURIPath("point:", 0.0);
|
||||
*
|
||||
* @param prefix prefix段前缀
|
||||
* @param defvalue 默认double值
|
||||
*
|
||||
* @return double值
|
||||
*/
|
||||
public double getRequstURIPath(String prefix, double defvalue) {
|
||||
String val = getRequstURIPath(prefix, null);
|
||||
return val == null ? defvalue : Double.parseDouble(val);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* 获取所有的header名
|
||||
@@ -565,6 +619,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* 获取指定的header值
|
||||
*
|
||||
* @param name header名
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public String getHeader(String name) {
|
||||
@@ -574,8 +629,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header值, 没有返回默认值
|
||||
*
|
||||
* @param name header名
|
||||
* @param name header名
|
||||
* @param defaultValue 默认值
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public String getHeader(String name, String defaultValue) {
|
||||
@@ -585,9 +641,10 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header的json值
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param <T> 泛型
|
||||
* @param clazz 反序列化的类名
|
||||
* @param name header名
|
||||
* @param name header名
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public <T> T getJsonHeader(Class<T> clazz, String name) {
|
||||
@@ -598,10 +655,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header的json值
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param <T> 泛型
|
||||
* @param convert JsonConvert对象
|
||||
* @param clazz 反序列化的类名
|
||||
* @param name header名
|
||||
* @param clazz 反序列化的类名
|
||||
* @param name header名
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public <T> T getJsonHeader(JsonConvert convert, Class<T> clazz, String name) {
|
||||
@@ -612,8 +670,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header的boolean值, 没有返回默认boolean值
|
||||
*
|
||||
* @param name header名
|
||||
* @param name header名
|
||||
* @param defaultValue 默认boolean值
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public boolean getBooleanHeader(String name, boolean defaultValue) {
|
||||
@@ -623,8 +682,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header的short值, 没有返回默认short值
|
||||
*
|
||||
* @param name header名
|
||||
* @param name header名
|
||||
* @param defaultValue 默认short值
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public short getShortHeader(String name, short defaultValue) {
|
||||
@@ -634,8 +694,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header的short值, 没有返回默认short值
|
||||
*
|
||||
* @param name header名
|
||||
* @param name header名
|
||||
* @param defaultValue 默认short值
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public short getShortHeader(String name, int defaultValue) {
|
||||
@@ -645,8 +706,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header的int值, 没有返回默认int值
|
||||
*
|
||||
* @param name header名
|
||||
* @param name header名
|
||||
* @param defaultValue 默认int值
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public int getIntHeader(String name, int defaultValue) {
|
||||
@@ -656,8 +718,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header的long值, 没有返回默认long值
|
||||
*
|
||||
* @param name header名
|
||||
* @param name header名
|
||||
* @param defaultValue 默认long值
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public long getLongHeader(String name, long defaultValue) {
|
||||
@@ -667,8 +730,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header的float值, 没有返回默认float值
|
||||
*
|
||||
* @param name header名
|
||||
* @param name header名
|
||||
* @param defaultValue 默认float值
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public float getFloatHeader(String name, float defaultValue) {
|
||||
@@ -678,8 +742,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的header的double值, 没有返回默认double值
|
||||
*
|
||||
* @param name header名
|
||||
* @param name header名
|
||||
* @param defaultValue 默认double值
|
||||
*
|
||||
* @return header值
|
||||
*/
|
||||
public double getDoubleHeader(String name, double defaultValue) {
|
||||
@@ -701,6 +766,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* 获取指定的参数值
|
||||
*
|
||||
* @param name 参数名
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public String getParameter(String name) {
|
||||
@@ -711,8 +777,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数值, 没有返回默认值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认值
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public String getParameter(String name, String defaultValue) {
|
||||
@@ -723,9 +790,10 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数json值
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param <T> 泛型
|
||||
* @param clazz 反序列化的类名
|
||||
* @param name 参数名
|
||||
* @param name 参数名
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public <T> T getJsonParameter(Class<T> clazz, String name) {
|
||||
@@ -736,10 +804,11 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数json值
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param <T> 泛型
|
||||
* @param convert JsonConvert对象
|
||||
* @param clazz 反序列化的类名
|
||||
* @param name 参数名
|
||||
* @param clazz 反序列化的类名
|
||||
* @param name 参数名
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public <T> T getJsonParameter(JsonConvert convert, Class<T> clazz, String name) {
|
||||
@@ -750,8 +819,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数boolean值, 没有返回默认boolean值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认boolean值
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public boolean getBooleanParameter(String name, boolean defaultValue) {
|
||||
@@ -762,8 +832,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数short值, 没有返回默认short值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认short值
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public short getShortParameter(String name, short defaultValue) {
|
||||
@@ -774,8 +845,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数short值, 没有返回默认short值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认short值
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public short getShortParameter(String name, int defaultValue) {
|
||||
@@ -786,8 +858,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数int值, 没有返回默认int值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认int值
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public int getIntParameter(String name, int defaultValue) {
|
||||
@@ -798,8 +871,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数long值, 没有返回默认long值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认long值
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public long getLongParameter(String name, long defaultValue) {
|
||||
@@ -810,8 +884,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数float值, 没有返回默认float值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认float值
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public float getFloatParameter(String name, float defaultValue) {
|
||||
@@ -822,8 +897,9 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
/**
|
||||
* 获取指定的参数double值, 没有返回默认double值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认double值
|
||||
*
|
||||
* @return 参数值
|
||||
*/
|
||||
public double getDoubleParameter(String name, double defaultValue) {
|
||||
@@ -831,4 +907,73 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
return params.getDoubleValue(name, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取翻页对象 同 getFlipper("flipper", false, 0);
|
||||
*
|
||||
* @return Flipper翻页对象
|
||||
*/
|
||||
public org.redkale.source.Flipper getFlipper() {
|
||||
return getFlipper(false, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取翻页对象 同 getFlipper("flipper", needcreate, 0);
|
||||
*
|
||||
* @param needcreate 无参数时是否创建新Flipper对象
|
||||
*
|
||||
* @return Flipper翻页对象
|
||||
*/
|
||||
public org.redkale.source.Flipper getFlipper(boolean needcreate) {
|
||||
return getFlipper(needcreate, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取翻页对象 同 getFlipper("flipper", false, maxLimit);
|
||||
*
|
||||
* @param maxLimit 最大行数, 小于1则值为Flipper.DEFAULT_LIMIT
|
||||
*
|
||||
* @return Flipper翻页对象
|
||||
*/
|
||||
public org.redkale.source.Flipper getFlipper(int maxLimit) {
|
||||
return getFlipper(false, maxLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取翻页对象 同 getFlipper("flipper", needcreate, maxLimit)
|
||||
*
|
||||
* @param needcreate 无参数时是否创建新Flipper对象
|
||||
* @param maxLimit 最大行数, 小于1则值为Flipper.DEFAULT_LIMIT
|
||||
*
|
||||
* @return Flipper翻页对象
|
||||
*/
|
||||
public org.redkale.source.Flipper getFlipper(boolean needcreate, int maxLimit) {
|
||||
return getFlipper("flipper", needcreate, maxLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取翻页对象 http://redkale.org/pipes/records/list/offset:0/limit:20/sort:createtime%20ASC <br>
|
||||
* http://redkale.org/pipes/records/list?flipper={'offset':0,'limit':20, 'sort':'createtime ASC'} <br>
|
||||
* 以上两种接口都可以获取到翻页对象
|
||||
*
|
||||
*
|
||||
* @param name Flipper对象的参数名,默认为 "flipper"
|
||||
* @param needcreate 无参数时是否创建新Flipper对象
|
||||
* @param maxLimit 最大行数, 小于1则值为Flipper.DEFAULT_LIMIT
|
||||
*
|
||||
* @return Flipper翻页对象
|
||||
*/
|
||||
public org.redkale.source.Flipper getFlipper(String name, boolean needcreate, int maxLimit) {
|
||||
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
|
||||
org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name);
|
||||
if (flipper == null) {
|
||||
int limit = getRequstURIPath("limit:", maxLimit);
|
||||
int offset = getRequstURIPath("offset:", 0);
|
||||
String sort = getRequstURIPath("sort:", "");
|
||||
if (limit > 0) flipper = new org.redkale.source.Flipper(limit, offset, sort);
|
||||
} else if (flipper.getLimit() < 1 || flipper.getLimit() > maxLimit) {
|
||||
flipper.setLimit(maxLimit);
|
||||
}
|
||||
if (flipper != null || !needcreate) return flipper;
|
||||
return new org.redkale.source.Flipper(maxLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,16 +80,21 @@ public final class HttpResourceServlet extends HttpServlet {
|
||||
|
||||
protected final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
protected final LongAdder cachedLength = new LongAdder();
|
||||
|
||||
//缓存总大小, 默认0
|
||||
protected long cachelimit = 0 * 1024 * 1024L;
|
||||
|
||||
protected final LongAdder cachedLength = new LongAdder();
|
||||
|
||||
//最大可缓存的文件大小, 大于该值的文件将不被缓存
|
||||
protected long cachelengthmax = 1 * 1024 * 1024;
|
||||
|
||||
|
||||
//是否监控缓存文件的变化, 默认不监控
|
||||
protected boolean watch = false;
|
||||
|
||||
protected File root = new File("./root/");
|
||||
|
||||
protected String indexHtml = "index.html";
|
||||
|
||||
protected final ConcurrentHashMap<String, FileEntry> files = new ConcurrentHashMap<>();
|
||||
|
||||
protected final ConcurrentHashMap<WatchKey, Path> keymaps = new ConcurrentHashMap<>();
|
||||
@@ -104,6 +109,7 @@ public final class HttpResourceServlet extends HttpServlet {
|
||||
public void init(HttpContext context, AnyValue config) {
|
||||
if (config != null) {
|
||||
String rootstr = config.getValue("webroot", "root");
|
||||
this.indexHtml = config.getValue("index", "index.html");
|
||||
if (rootstr.indexOf(':') < 0 && rootstr.indexOf('/') != 0 && System.getProperty("APP_HOME") != null) {
|
||||
rootstr = new File(System.getProperty("APP_HOME"), rootstr).getPath();
|
||||
}
|
||||
@@ -114,10 +120,12 @@ public final class HttpResourceServlet extends HttpServlet {
|
||||
} catch (IOException ioe) {
|
||||
this.root = new File(rootstr);
|
||||
}
|
||||
AnyValue cacheconf = config.getAnyValue("caches");
|
||||
AnyValue cacheconf = config.getAnyValue("cache");
|
||||
if (cacheconf == null) cacheconf = config.getAnyValue("caches"); //兼容旧参数
|
||||
if (cacheconf != null) {
|
||||
this.cachelimit = parseLenth(cacheconf.getValue("limit"), 0 * 1024 * 1024L);
|
||||
this.cachelengthmax = parseLenth(cacheconf.getValue("lengthmax"), 1 * 1024 * 1024L);
|
||||
this.watch = cacheconf.getBoolValue("watch", false);
|
||||
}
|
||||
List<SimpleEntry<Pattern, String>> locations = new ArrayList<>();
|
||||
for (AnyValue av : config.getAnyValues("rewrite")) {
|
||||
@@ -132,7 +140,7 @@ public final class HttpResourceServlet extends HttpServlet {
|
||||
this.locationRewrites = locations.isEmpty() ? null : locations.toArray(new SimpleEntry[locations.size()]);
|
||||
}
|
||||
if (this.cachelimit < 1) return; //不缓存不需要开启WatchThread监听
|
||||
if (this.root != null) {
|
||||
if (this.root != null && this.watch) {
|
||||
try {
|
||||
this.watchThread = new WatchThread(this.root);
|
||||
this.watchThread.start();
|
||||
@@ -178,7 +186,9 @@ public final class HttpResourceServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uri.length() == 0 || uri.equals("/")) uri = "/index.html";
|
||||
if (uri.length() == 0 || uri.equals("/")) {
|
||||
uri = this.indexHtml.indexOf('/') == 0 ? this.indexHtml : ("/" + this.indexHtml);
|
||||
}
|
||||
//System.out.println(request);
|
||||
FileEntry entry;
|
||||
if (watchThread == null) {
|
||||
@@ -195,8 +205,10 @@ public final class HttpResourceServlet extends HttpServlet {
|
||||
}
|
||||
|
||||
private FileEntry createFileEntry(String uri) {
|
||||
File file = new File(root, uri);
|
||||
if (file.isDirectory()) file = new File(file, "index.html");
|
||||
File rfile = new File(root, uri);
|
||||
File file = rfile;
|
||||
if (file.isDirectory()) file = new File(rfile, this.indexHtml);
|
||||
if (file.isDirectory()) file = new File(rfile, "index.html");
|
||||
if (!file.isFile() || !file.canRead()) return null;
|
||||
FileEntry en = new FileEntry(this, file);
|
||||
if (watchThread == null) return en;
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.redkale.util.*;
|
||||
* 同时提供发送json的系列接口: public void finishJson(Type type, Object obj)
|
||||
* RedKale提倡http+json的接口风格, 所以主要输出的数据格式为json, 同时提供异步接口。
|
||||
* <p>
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -35,7 +34,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
/**
|
||||
* HttpResponse.finish 方法内调用
|
||||
* 主要给@HttpCacheable使用
|
||||
* <p>
|
||||
*/
|
||||
protected static interface BufferHandler {
|
||||
|
||||
@@ -162,6 +160,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* 获取状态码对应的状态描述
|
||||
*
|
||||
* @param status 状态码
|
||||
*
|
||||
* @return 状态描述
|
||||
*/
|
||||
protected String getHttpCode(int status) {
|
||||
@@ -181,6 +180,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* 增加Cookie值
|
||||
*
|
||||
* @param cookies cookie
|
||||
*
|
||||
* @return HttpResponse
|
||||
*/
|
||||
public HttpResponse addCookie(HttpCookie... cookies) {
|
||||
@@ -249,6 +249,58 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), objs));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将RetResult对象以JSON格式输出
|
||||
*
|
||||
* @param ret RetResult输出对象
|
||||
*/
|
||||
public void finishJson(final org.redkale.service.RetResult ret) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
if (ret != null && !ret.isSuccess()) {
|
||||
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
||||
this.header.addValue("retcode", ret.getRetinfo());
|
||||
}
|
||||
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), ret));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将RetResult对象以JSON格式输出
|
||||
*
|
||||
* @param convert 指定的JsonConvert
|
||||
* @param ret RetResult输出对象
|
||||
*/
|
||||
public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) {
|
||||
this.contentType = "text/plain; charset=utf-8";
|
||||
if (ret != null && !ret.isSuccess()) {
|
||||
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
||||
this.header.addValue("retcode", ret.getRetinfo());
|
||||
}
|
||||
finish(convert.convertTo(context.getBufferSupplier(), ret));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象以JavaScript格式输出
|
||||
*
|
||||
* @param var js变量名
|
||||
* @param result 输出对象
|
||||
*/
|
||||
public void finishJsResult(String var, Object result) {
|
||||
this.contentType = "application/javascript; charset=utf-8";
|
||||
finish("var " + var + " = " + request.getJsonConvert().convertTo(result) + ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象以JavaScript格式输出
|
||||
*
|
||||
* @param jsonConvert 指定的JsonConvert
|
||||
* @param var js变量名
|
||||
* @param result 输出对象
|
||||
*/
|
||||
public void finishJsResult(JsonConvert jsonConvert, String var, Object result) {
|
||||
this.contentType = "application/javascript; charset=utf-8";
|
||||
finish("var " + var + " = " + jsonConvert.convertTo(result) + ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定字符串以响应结果输出
|
||||
*
|
||||
@@ -302,7 +354,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
/**
|
||||
* 以304状态码输出
|
||||
* <p>
|
||||
*/
|
||||
public void finish304() {
|
||||
super.finish(buffer304.duplicate());
|
||||
@@ -310,7 +361,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
/**
|
||||
* 以404状态码输出
|
||||
* <p>
|
||||
*/
|
||||
public void finish404() {
|
||||
super.finish(buffer404.duplicate());
|
||||
@@ -419,6 +469,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* 将指定文件按响应结果输出
|
||||
*
|
||||
* @param file 输出文件
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public void finish(File file) throws IOException {
|
||||
@@ -430,6 +481,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*
|
||||
* @param filename 输出文件名
|
||||
* @param file 输出文件
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public void finish(final String filename, File file) throws IOException {
|
||||
@@ -441,6 +493,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*
|
||||
* @param file 输出文件
|
||||
* @param fileBody 文件内容, 没有则输出file
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
protected void finishFile(final File file, ByteBuffer fileBody) throws IOException {
|
||||
@@ -453,6 +506,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param filename 输出文件名
|
||||
* @param file 输出文件
|
||||
* @param fileBody 文件内容, 没有则输出file
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
protected void finishFile(final String filename, final File file, ByteBuffer fileBody) throws IOException {
|
||||
@@ -580,7 +634,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
private CharSequence genString(HttpCookie cookie) {
|
||||
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("; Version=1");
|
||||
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());
|
||||
@@ -615,6 +669,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*
|
||||
* @param name header名
|
||||
* @param value header值
|
||||
*
|
||||
* @return HttpResponse
|
||||
*/
|
||||
public HttpResponse setHeader(String name, Object value) {
|
||||
@@ -627,6 +682,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*
|
||||
* @param name header名
|
||||
* @param value header值
|
||||
*
|
||||
* @return HttpResponse
|
||||
*/
|
||||
public HttpResponse addHeader(String name, Object value) {
|
||||
@@ -638,6 +694,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* 设置状态码
|
||||
*
|
||||
* @param status 状态码
|
||||
*
|
||||
* @return HttpResponse
|
||||
*/
|
||||
public HttpResponse setStatus(int status) {
|
||||
@@ -667,6 +724,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* 设置 ContentType
|
||||
*
|
||||
* @param contentType ContentType
|
||||
*
|
||||
* @return HttpResponse
|
||||
*/
|
||||
public HttpResponse setContentType(String contentType) {
|
||||
@@ -687,7 +745,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* 设置内容长度
|
||||
*
|
||||
* @param contentLength 内容长度
|
||||
* @return
|
||||
*
|
||||
* @return HttpResponse
|
||||
*/
|
||||
public HttpResponse setContentLength(long contentLength) {
|
||||
this.contentLength = contentLength;
|
||||
|
||||
@@ -33,7 +33,6 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
|
||||
@Override
|
||||
public void init(AnyValue config) throws Exception {
|
||||
super.init(config);
|
||||
AnyValue conf = config == null ? null : config.getAnyValue("servlets");
|
||||
}
|
||||
|
||||
public void addHttpServlet(HttpServlet servlet, final String prefix, AnyValue conf, String... mappings) {
|
||||
@@ -46,7 +45,8 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
|
||||
final int port = this.address.getPort();
|
||||
AtomicLong createBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Buffer.creatCounter");
|
||||
AtomicLong cycleBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Buffer.cycleCounter");
|
||||
final int rcapacity = Math.max(this.bufferCapacity, 16 * 1024 + 8); //兼容 HTTP 2.0
|
||||
this.bufferCapacity = Math.max(this.bufferCapacity, 16 * 1024 + 16); //兼容 HTTP 2.0;
|
||||
final int rcapacity = this.bufferCapacity;
|
||||
ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize,
|
||||
(Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> {
|
||||
if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false;
|
||||
|
||||
@@ -20,12 +20,12 @@ public abstract class HttpServlet extends Servlet<HttpContext, HttpRequest, Http
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object obj) {
|
||||
return obj != null && obj.getClass() == this.getClass();
|
||||
return this == obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return this.getClass().hashCode();
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ public class MimeType {
|
||||
contentTypes.put("htm", "text/html");
|
||||
contentTypes.put("html", "text/html");
|
||||
contentTypes.put("hqx", "application/mac-binhex40");
|
||||
contentTypes.put("ico", "image/x-icon");
|
||||
contentTypes.put("ief", "image/ief");
|
||||
contentTypes.put("jad", "text/vnd.sun.j2me.app-descriptor");
|
||||
contentTypes.put("jar", "application/java-archive");
|
||||
|
||||
@@ -113,10 +113,14 @@ public final class MultiContext {
|
||||
final String disposition = readLine();
|
||||
//if (debug) System.out.println("disposition=" + disposition);
|
||||
if (disposition.contains("; filename=\"")) { //是上传文件
|
||||
String contentType = readLine();
|
||||
String contentType = "";
|
||||
//读掉HTTP Header和空白行 通常情况下Content-Type后面就是内容,但是有些特殊情况下后面会跟其他如Content-Length: xxx等HTTP header,所以需要循环读取
|
||||
String rl;
|
||||
while (!(rl = readLine()).isEmpty()) {
|
||||
if (rl.startsWith("Content-Type:")) contentType = rl.substring(rl.indexOf(':') + 1).trim();
|
||||
}
|
||||
//if (debug) System.out.println("file.contentType=" + contentType);
|
||||
contentType = contentType.substring(contentType.indexOf(':') + 1).trim();
|
||||
readLine(); //读掉空白行
|
||||
|
||||
String name = parseValue(disposition, "name");
|
||||
String filename = parseValue(disposition, "filename");
|
||||
if (filename == null || filename.isEmpty()) { //没有上传
|
||||
|
||||
@@ -81,16 +81,18 @@ public abstract class WebSocketNode {
|
||||
* 获取在线用户的节点地址列表
|
||||
*
|
||||
* @param groupid groupid
|
||||
*
|
||||
* @return 地址列表
|
||||
*/
|
||||
public Collection<InetSocketAddress> getOnlineNodes(final Serializable groupid) {
|
||||
return source.getCollection(groupid);
|
||||
return source == null ? null : source.getCollection(groupid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取在线用户的详细连接信息
|
||||
*
|
||||
* @param groupid groupid
|
||||
*
|
||||
* @return 地址集合
|
||||
*/
|
||||
public Map<InetSocketAddress, List<String>> getOnlineRemoteAddress(final Serializable groupid) {
|
||||
@@ -150,7 +152,7 @@ public abstract class WebSocketNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((recent && rscode == 0) || remoteNode == null) {
|
||||
if ((recent && rscode == 0) || remoteNode == null || source == null) {
|
||||
if (finest) {
|
||||
if ((recent && rscode == 0)) {
|
||||
logger.finest("websocket want send recent message success");
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.security.*;
|
||||
import java.util.*;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.*;
|
||||
import org.redkale.service.WebSocketNodeService;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -22,7 +23,7 @@ import org.redkale.util.*;
|
||||
* WebSocketServlet
|
||||
* |
|
||||
* |
|
||||
* WebSocketEngine
|
||||
* WebSocketEngine & WebSocketNode
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
@@ -67,6 +68,11 @@ public abstract class WebSocketServlet extends HttpServlet {
|
||||
public final void preInit(HttpContext context, AnyValue conf) {
|
||||
InetSocketAddress addr = context.getServerAddress();
|
||||
this.engine = new WebSocketEngine(addr.getHostString() + ":" + addr.getPort() + "-[" + name() + "]", this.node, logger);
|
||||
if (this.node == null) this.node = createWebSocketNode();
|
||||
if (this.node == null) {
|
||||
this.node = new WebSocketNodeService();
|
||||
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
|
||||
}
|
||||
this.node.putWebSocketEngine(engine);
|
||||
this.node.init(conf);
|
||||
this.engine.init(conf);
|
||||
@@ -86,8 +92,8 @@ public abstract class WebSocketServlet extends HttpServlet {
|
||||
public final void execute(final HttpRequest request, final HttpResponse response) throws IOException {
|
||||
final boolean debug = logger.isLoggable(Level.FINEST);
|
||||
if (!"GET".equalsIgnoreCase(request.getMethod())
|
||||
|| !request.getConnection().contains("Upgrade")
|
||||
|| !"websocket".equalsIgnoreCase(request.getHeader("Upgrade"))) {
|
||||
|| !request.getConnection().contains("Upgrade")
|
||||
|| !"websocket".equalsIgnoreCase(request.getHeader("Upgrade"))) {
|
||||
if (debug) logger.finest("WebSocket connect abort, (Not GET Method) or (Connection != Upgrade) or (Upgrade != websocket). request=" + request);
|
||||
response.finish(true);
|
||||
return;
|
||||
@@ -144,5 +150,9 @@ public abstract class WebSocketServlet extends HttpServlet {
|
||||
});
|
||||
}
|
||||
|
||||
protected WebSocketNode createWebSocketNode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract WebSocket createWebSocket();
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* Service对象的封装类
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -66,6 +66,16 @@ public final class ServiceWrapper<T extends Service> implements Comparable<Servi
|
||||
maxClassNameLength = Math.max(maxClassNameLength, s.length() + 1);
|
||||
}
|
||||
|
||||
public static Class[] parseTypes(final Class<? extends Service> servicetype) {
|
||||
ResourceType rty = servicetype.getAnnotation(ResourceType.class);
|
||||
return rty == null ? new Class[]{servicetype} : rty.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toSimpleString();
|
||||
}
|
||||
|
||||
public String toSimpleString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(remote ? "RemoteService" : "LocalService ");
|
||||
|
||||
@@ -25,8 +25,8 @@ import org.redkale.util.*;
|
||||
/**
|
||||
* Service Node Communicate Protocol
|
||||
* 生成Service的本地模式或远程模式Service-Class的工具类
|
||||
* <p>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -131,50 +131,50 @@ public abstract class Sncp {
|
||||
/**
|
||||
* <blockquote><pre>
|
||||
* public class TestService implements Service{
|
||||
* <p>
|
||||
*
|
||||
* public String findSomeThing(){
|
||||
* return "hello";
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @MultiRun(selfrun = false)
|
||||
* public void createSomeThing(TestBean bean){
|
||||
* //do something
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @MultiRun
|
||||
* public String updateSomeThing(String id){
|
||||
* return "hello" + id;
|
||||
* }
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
*
|
||||
* <blockquote><pre>
|
||||
* @Resource(name = "")
|
||||
* @SncpDyn(remote = false)
|
||||
* @ResourceType({TestService.class})
|
||||
* public final class _DynLocalTestService extends TestService{
|
||||
* <p>
|
||||
*
|
||||
* @Resource
|
||||
* private BsonConvert _redkale_convert;
|
||||
* <p>
|
||||
*
|
||||
* private Transport _redkale_sameGroupTransport;
|
||||
* <p>
|
||||
*
|
||||
* private Transport[] _redkale_diffGroupTransports;
|
||||
* <p>
|
||||
*
|
||||
* private SncpClient _redkale_client;
|
||||
* <p>
|
||||
*
|
||||
* private String _redkale_selfstring;
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public String toString() {
|
||||
* return _redkale_selfstring == null ? super.toString() : _redkale_selfstring;
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public void createSomeThing(TestBean bean){
|
||||
* this._redkale_createSomeThing(false, true, true, bean);
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @SncpDyn(remote = false, index = 0)
|
||||
* public void _redkale_createSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, TestBean bean){
|
||||
* if(selfrunnable) super.createSomeThing(bean);
|
||||
@@ -182,12 +182,12 @@ public abstract class Sncp {
|
||||
* if (samerunnable) _redkale_client.remoteSameGroup(_redkale_convert, _sameGroupTransport, 0, true, false, false, bean);
|
||||
* if (diffrunnable) _redkale_client.remoteDiffGroup(_redkale_convert, _diffGroupTransports, 0, true, true, false, bean);
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public String updateSomeThing(String id){
|
||||
* return this._redkale_updateSomeThing(true, true, true, id);
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @SncpDyn(remote = false, index = 1)
|
||||
* public String _redkale_updateSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, String id){
|
||||
* String rs = super.updateSomeThing(id);
|
||||
@@ -198,7 +198,7 @@ public abstract class Sncp {
|
||||
* }
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
*
|
||||
* 创建Service的本地模式Class
|
||||
*
|
||||
* @param <T> Service子类
|
||||
@@ -823,48 +823,48 @@ public abstract class Sncp {
|
||||
* @SncpDyn(remote = true)
|
||||
* @ResourceType({TestService.class})
|
||||
* public final class _DynRemoteTestService extends TestService{
|
||||
* <p>
|
||||
*
|
||||
* @Resource
|
||||
* private BsonConvert _redkale_convert;
|
||||
* <p>
|
||||
*
|
||||
* private Transport _redkale_transport;
|
||||
* <p>
|
||||
*
|
||||
* private SncpClient _redkale_client;
|
||||
* <p>
|
||||
*
|
||||
* private String _redkale_selfstring;
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public String toString() {
|
||||
* return _redkale_selfstring == null ? super.toString() : _redkale_selfstring;
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @SncpDyn(remote = false, index = 0)
|
||||
* public void _redkale_createSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, TestBean bean){
|
||||
* _redkale_client.remote(_redkale_convert, _redkale_transport, 0, selfrunnable, samerunnable, diffrunnable, bean);
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @SncpDyn(remote = false, index = 1)
|
||||
* public String _redkale_updateSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, String id){
|
||||
* return _redkale_client.remote(_redkale_convert, _redkale_transport, 1, selfrunnable, samerunnable, diffrunnable, id);
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public void createSomeThing(TestBean bean){
|
||||
* _redkale_client.remote(_redkale_convert, _redkale_transport, 2, bean);
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public String findSomeThing(){
|
||||
* return _redkale_client.remote(_redkale_convert, _redkale_transport, 3);
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public String updateSomeThing(String id){
|
||||
* return _redkale_client.remote(_redkale_convert, _redkale_transport, 4, id);
|
||||
* }
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
*
|
||||
* 创建远程模式的Service实例
|
||||
*
|
||||
* @param <T> Service泛型
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import org.redkale.convert.bson.*;
|
||||
@@ -24,7 +25,7 @@ import org.redkale.service.DynCall;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -91,7 +92,6 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DLong getServiceid() {
|
||||
return serviceid;
|
||||
@@ -182,9 +182,10 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param service Service
|
||||
* @param service Service
|
||||
* @param actionid 操作ID
|
||||
* @param method 方法
|
||||
* @param method 方法
|
||||
*
|
||||
* @return SncpServletAction
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -197,7 +198,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
final String convertWriterDesc = Type.getDescriptor(BsonWriter.class);
|
||||
final String serviceDesc = Type.getDescriptor(serviceClass);
|
||||
String newDynName = serviceName.substring(0, serviceName.lastIndexOf('/') + 1)
|
||||
+ "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid;
|
||||
+ "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid;
|
||||
while (true) {
|
||||
try {
|
||||
Class.forName(newDynName.replace('/', '.'));
|
||||
@@ -207,7 +208,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||
FieldVisitor fv;
|
||||
AsmMethodVisitor mv;
|
||||
|
||||
@@ -371,7 +372,6 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertTo", "(" + convertWriterDesc + "Ljava/lang/reflect/Type;Ljava/lang/Object;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
store++;
|
||||
if (maxStack < 10) maxStack = 10;
|
||||
}
|
||||
mv.visitMaxs(maxStack, store);
|
||||
mv.visitEnd();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.net.sncp;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -21,12 +22,13 @@ public abstract class SncpServlet extends Servlet<SncpContext, SncpRequest, Sncp
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object obj) {
|
||||
return obj != null && obj.getClass() == this.getClass();
|
||||
if (!(obj instanceof SncpServlet)) return false;
|
||||
return Objects.equals(getServiceid(), ((SncpServlet) obj).getServiceid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return this.getClass().hashCode();
|
||||
return Objects.hashCode(getServiceid());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -95,6 +95,17 @@ public class DataSourceService implements DataSource, Service, AutoCloseable {
|
||||
if (handler != null) handler.completed(null, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumn(final Class<T> clazz, final String column, final Serializable value, final FilterNode node) {
|
||||
source.updateColumn(clazz, column, value, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumn(final CompletionHandler<Void, FilterNode> handler, final Class<T> clazz, final String column, final Serializable value, @DynAttachment final FilterNode node) {
|
||||
source.updateColumn(clazz, column, value, node);
|
||||
if (handler != null) handler.completed(null, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumnIncrement(final Class<T> clazz, final Serializable id, final String column, long incvalue) {
|
||||
source.updateColumnIncrement(clazz, id, column, incvalue);
|
||||
@@ -129,14 +140,25 @@ public class DataSourceService implements DataSource, Service, AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumns(T value, final String... columns) {
|
||||
source.updateColumns(value, columns);
|
||||
public <T> void updateColumns(T bean, final String... columns) {
|
||||
source.updateColumns(bean, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumns(final CompletionHandler<Void, T> handler, @DynAttachment final T value, final String... columns) {
|
||||
source.updateColumns(value, columns);
|
||||
if (handler != null) handler.completed(null, value);
|
||||
public <T> void updateColumns(final CompletionHandler<Void, T> handler, @DynAttachment final T bean, final String... columns) {
|
||||
source.updateColumns(bean, columns);
|
||||
if (handler != null) handler.completed(null, bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumns(T bean, final FilterNode node, final String... columns) {
|
||||
source.updateColumns(bean, node, columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumns(final CompletionHandler<Void, FilterNode> handler, final T bean, @DynAttachment final FilterNode node, final String... columns) {
|
||||
source.updateColumns(bean, node, columns);
|
||||
if (handler != null) handler.completed(null, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,7 +11,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 本地模式注解。
|
||||
* 声明为LocalService的Service将不会变成远程模式,只能以本地模式存在, 无论配置文件中是否配置成远程模式都会被忽略。
|
||||
* 声明为LocalService的Service只能以本地模式存在, 即使配置文件中配置成远程模式也将被忽略。
|
||||
*
|
||||
* <p> 详情见: http://redkale.org
|
||||
* @author zhangjx
|
||||
|
||||
26
src/org/redkale/service/RetLabel.java
Normal file
26
src/org/redkale/service/RetLabel.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.service;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 用于定义错误码的注解
|
||||
* 结果码定义范围:
|
||||
* // 10000001 - 19999999 预留给Redkale的核心包使用
|
||||
* // 20000001 - 29999999 预留给Redkale的扩展包使用
|
||||
* // 30000001 - 99999999 预留给Dev开发系统自身使用
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target(value = {ElementType.FIELD})
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
public @interface RetLabel {
|
||||
|
||||
String value();
|
||||
}
|
||||
@@ -9,8 +9,12 @@ import org.redkale.convert.json.JsonFactory;
|
||||
|
||||
/**
|
||||
* 通用的结果对象,在常见的HTTP+JSON接口中返回的结果需要含结果码,错误信息,和实体对象。
|
||||
* <p>
|
||||
* <p>
|
||||
* 通常前四位为模块,后四位为操作。
|
||||
* 结果码定义范围:
|
||||
* // 10000001 - 19999999 预留给Redkale的核心包使用
|
||||
* // 20000001 - 29999999 预留给Redkale的扩展包使用
|
||||
* // 30000001 - 99999999 预留给Dev开发系统自身使用
|
||||
*
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -18,31 +22,18 @@ import org.redkale.convert.json.JsonFactory;
|
||||
*/
|
||||
public class RetResult<T> {
|
||||
|
||||
protected static final class RetSuccessResult<T> extends RetResult<T> {
|
||||
|
||||
public RetSuccessResult() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRetcode(int retcode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRetinfo(String retinfo) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(T result) {
|
||||
}
|
||||
}
|
||||
|
||||
public static final RetResult SUCCESS = new RetSuccessResult();
|
||||
/**
|
||||
* 使用 RetResult.success() 方法代替
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
private static final RetResult SUCCESS = new RetResult();
|
||||
|
||||
protected int retcode;
|
||||
|
||||
protected String retinfo;
|
||||
|
||||
private T result;
|
||||
protected T result;
|
||||
|
||||
public RetResult() {
|
||||
}
|
||||
@@ -66,6 +57,10 @@ public class RetResult<T> {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public static RetResult success() {
|
||||
return new RetResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断结果是否成功返回, retcode = 0 视为成功, 否则视为错误码
|
||||
*
|
||||
@@ -75,6 +70,42 @@ public class RetResult<T> {
|
||||
return retcode == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同 setRetcode
|
||||
*
|
||||
* @param retcode retcode
|
||||
*
|
||||
* @return RetResult
|
||||
*/
|
||||
public RetResult<T> retcode(int retcode) {
|
||||
this.retcode = retcode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同 setRetinfo
|
||||
*
|
||||
* @param retinfo retinfo
|
||||
*
|
||||
* @return RetResult
|
||||
*/
|
||||
public RetResult<T> retinfo(String retinfo) {
|
||||
this.retinfo = retinfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同 setResult
|
||||
*
|
||||
* @param result result
|
||||
*
|
||||
* @return RetResult
|
||||
*/
|
||||
public RetResult<T> result(T result) {
|
||||
this.result = result;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 结果码 0表示成功、 非0表示错误
|
||||
*
|
||||
@@ -109,17 +140,6 @@ public class RetResult<T> {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同 setResult
|
||||
*
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
public RetResult result(T result) {
|
||||
this.result = result;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonFactory.root().getConvert().convertTo(this);
|
||||
|
||||
@@ -17,7 +17,6 @@ import java.util.logging.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.sql.ConnectionPoolDataSource;
|
||||
import javax.xml.stream.*;
|
||||
import static org.redkale.source.FilterNode.formatToString;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -73,7 +72,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
@Resource(name = "$")
|
||||
private DataCacheListener cacheListener;
|
||||
|
||||
private final Function<Class, List> fullloader = (t) -> querySheet(false, false, t, null, null, (FilterNode) null).list(true);
|
||||
private final BiFunction<DataSource, Class, List> fullloader = (s, t) -> querySheet(false, false, t, null, null, (FilterNode) null).list(true);
|
||||
|
||||
public DataDefaultSource() throws IOException {
|
||||
this("");
|
||||
@@ -288,7 +287,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
}
|
||||
|
||||
private <T> EntityInfo<T> loadEntityInfo(Class<T> clazz) {
|
||||
return EntityInfo.load(clazz, this.nodeid, this.cacheForbidden, this.readPool.props, fullloader);
|
||||
return EntityInfo.load(clazz, this.nodeid, this.cacheForbidden, this.readPool.props, this, fullloader);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -349,7 +348,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
synchronized (info) {
|
||||
if (!info.initedPrimaryValue) { //初始化最大主键值
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT MAX(" + info.getPrimarySQLColumn() + ") FROM " + info.getTable());
|
||||
ResultSet rs = stmt.executeQuery("SELECT MAX(" + info.getPrimarySQLColumn() + ") FROM " + info.getTable(values[0]));
|
||||
if (rs.next()) {
|
||||
if (primaryType == int.class) {
|
||||
int v = rs.getInt(1) / info.allocationSize;
|
||||
@@ -361,24 +360,6 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
}
|
||||
rs.close();
|
||||
stmt.close();
|
||||
if (info.distributeTables != null) { //是否还有其他表
|
||||
for (final Class t : info.distributeTables) {
|
||||
EntityInfo<T> infox = loadEntityInfo(t);
|
||||
stmt = conn.createStatement();
|
||||
rs = stmt.executeQuery("SELECT MAX(" + info.getPrimarySQLColumn() + ") FROM " + infox.getTable()); // 必须是同一字段名
|
||||
if (rs.next()) {
|
||||
if (primaryType == int.class) {
|
||||
int v = rs.getInt(1) / info.allocationSize;
|
||||
if (v > info.primaryValue.get()) info.primaryValue.set(v);
|
||||
} else {
|
||||
long v = rs.getLong(1) / info.allocationSize;
|
||||
if (v > info.primaryValue.get()) info.primaryValue.set(v);
|
||||
}
|
||||
}
|
||||
rs.close();
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
info.initedPrimaryValue = true;
|
||||
}
|
||||
}
|
||||
@@ -402,7 +383,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
if (distributed) info.createPrimaryValue(value);
|
||||
for (Attribute<T, Serializable> attr : attrs) {
|
||||
Object a = attr.get(value);
|
||||
ps[i] = formatToString(a);
|
||||
ps[i] = FilterNode.formatToString(a);
|
||||
prestmt.setObject(++i, a);
|
||||
}
|
||||
prestmt.addBatch();
|
||||
@@ -449,7 +430,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
if (obj != null && obj.getClass().isArray()) {
|
||||
sb.append("'[length=").append(java.lang.reflect.Array.getLength(obj)).append("]'");
|
||||
} else {
|
||||
sb.append(formatToString(obj));
|
||||
sb.append(FilterNode.formatToString(obj));
|
||||
}
|
||||
} else {
|
||||
sb.append(ch);
|
||||
@@ -546,12 +527,18 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
if (keys.length == 0) return;
|
||||
try {
|
||||
if (!info.isVirtualEntity()) {
|
||||
String sql = "DELETE FROM " + info.getTable() + " WHERE " + info.getPrimarySQLColumn() + " IN " + formatToString(keys);
|
||||
if (debug.get()) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
String[] sqls = new String[keys.length];
|
||||
int index = -1;
|
||||
for (Serializable key : keys) {
|
||||
String sql = "DELETE FROM " + info.getTable(keys) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(key);
|
||||
sqls[++index] = sql;
|
||||
if (debug.get()) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
stmt.addBatch(sql);
|
||||
}
|
||||
stmt.executeBatch();
|
||||
stmt.close();
|
||||
if (writeListener != null) writeListener.delete(sql);
|
||||
if (writeListener != null) writeListener.delete(sqls);
|
||||
}
|
||||
//------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
@@ -592,7 +579,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
Map<Class, String> joinTabalis = node.getJoinTabalis();
|
||||
CharSequence join = node.createSQLJoin(this, joinTabalis, info);
|
||||
CharSequence where = node.createSQLExpress(info, joinTabalis);
|
||||
String sql = "DELETE " + (this.readPool.isMysql() ? "a" : "") + " FROM " + info.getTable() + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
String sql = "DELETE " + (this.readPool.isMysql() ? "a" : "") + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get()) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
@@ -675,7 +662,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
int i = 0;
|
||||
for (Attribute<T, Serializable> attr : attrs) {
|
||||
Object a = attr.get(value);
|
||||
ps[i] = formatToString(a);
|
||||
ps[i] = FilterNode.formatToString(a);
|
||||
prestmt.setObject(++i, a);
|
||||
}
|
||||
prestmt.setObject(++i, primary.get(value));
|
||||
@@ -742,8 +729,8 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
private <T> void updateColumn(Connection conn, final EntityInfo<T> info, Serializable id, String column, Serializable value) {
|
||||
try {
|
||||
if (!info.isVirtualEntity()) {
|
||||
String sql = "UPDATE " + info.getTable() + " SET " + info.getSQLColumn(null, column) + " = "
|
||||
+ formatToString(value) + " WHERE " + info.getPrimarySQLColumn() + " = " + formatToString(id);
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + info.getSQLColumn(null, column) + " = "
|
||||
+ FilterNode.formatToString(value) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get()) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
@@ -762,6 +749,63 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主键值更新对象的column对应的值, 必须是Entity Class
|
||||
*
|
||||
* @param <T> Entity类的泛型
|
||||
* @param clazz Entity类
|
||||
* @param column 过滤字段名
|
||||
* @param value 过滤字段值
|
||||
* @param node 过滤node 不能为null
|
||||
*/
|
||||
@Override
|
||||
public <T> void updateColumn(Class<T> clazz, String column, Serializable value, FilterNode node) {
|
||||
final EntityInfo<T> info = loadEntityInfo(clazz);
|
||||
if (info.isVirtualEntity()) {
|
||||
updateColumn(null, info, column, value, node);
|
||||
return;
|
||||
}
|
||||
Connection conn = createWriteSQLConnection();
|
||||
try {
|
||||
updateColumn(conn, info, column, value, node);
|
||||
} finally {
|
||||
closeSQLConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumn(final CompletionHandler<Void, FilterNode> handler, final Class<T> clazz, final String column, final Serializable value, FilterNode node) {
|
||||
updateColumn(clazz, column, value, node);
|
||||
if (handler != null) handler.completed(null, node);
|
||||
}
|
||||
|
||||
private <T> void updateColumn(Connection conn, final EntityInfo<T> info, String column, Serializable value, FilterNode node) {
|
||||
try {
|
||||
if (!info.isVirtualEntity()) {
|
||||
Map<Class, String> joinTabalis = node.getJoinTabalis();
|
||||
CharSequence join = node.createSQLJoin(this, joinTabalis, info);
|
||||
CharSequence where = node.createSQLExpress(info, joinTabalis);
|
||||
|
||||
String sql = "UPDATE " + info.getTable(node) + " a SET " + info.getSQLColumn("a", column) + " = "
|
||||
+ FilterNode.formatToString(value) + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get()) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
stmt.close();
|
||||
if (writeListener != null) writeListener.update(sql);
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
if (cache == null) return;
|
||||
T[] rs = cache.update(info.getAttribute(column), value, node);
|
||||
if (cacheListener != null) cacheListener.updateCache(info.getType(), rs);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (conn != null) closeSQLConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主键值给对象的column对应的值+incvalue, 必须是Entity Class
|
||||
* 等价SQL: UPDATE {clazz} SET {column} = {column} + {incvalue} WHERE {primary} = {id}
|
||||
@@ -797,8 +841,8 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
try {
|
||||
if (!info.isVirtualEntity()) {
|
||||
String col = info.getSQLColumn(null, column);
|
||||
String sql = "UPDATE " + info.getTable() + " SET " + col + " = " + col + " + (" + incvalue
|
||||
+ ") WHERE " + info.getPrimarySQLColumn() + " = " + formatToString(id);
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + col + " = " + col + " + (" + incvalue
|
||||
+ ") WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get()) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
@@ -853,8 +897,8 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
try {
|
||||
if (!info.isVirtualEntity()) {
|
||||
String col = info.getSQLColumn(null, column);
|
||||
String sql = "UPDATE " + info.getTable() + " SET " + col + " = " + col + " & (" + andvalue
|
||||
+ ") WHERE " + info.getPrimarySQLColumn() + " = " + formatToString(id);
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + col + " = " + col + " & (" + andvalue
|
||||
+ ") WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get()) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
@@ -909,8 +953,8 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
try {
|
||||
if (!info.isVirtualEntity()) {
|
||||
String col = info.getSQLColumn(null, column);
|
||||
String sql = "UPDATE " + info.getTable() + " SET " + col + " = " + col + " | (" + orvalue
|
||||
+ ") WHERE " + info.getPrimarySQLColumn() + " = " + formatToString(id);
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + col + " = " + col + " | (" + orvalue
|
||||
+ ") WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get()) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
@@ -934,36 +978,36 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
* 更新对象指定的一些字段, 必须是Entity对象
|
||||
*
|
||||
* @param <T> Entity类的泛型
|
||||
* @param value Entity对象
|
||||
* @param bean Entity对象
|
||||
* @param columns 需要更新的字段
|
||||
*/
|
||||
@Override
|
||||
public <T> void updateColumns(final T value, final String... columns) {
|
||||
final EntityInfo<T> info = loadEntityInfo((Class<T>) value.getClass());
|
||||
public <T> void updateColumns(final T bean, final String... columns) {
|
||||
final EntityInfo<T> info = loadEntityInfo((Class<T>) bean.getClass());
|
||||
if (info.isVirtualEntity()) {
|
||||
updateColumns(null, info, value, columns);
|
||||
updateColumns(null, info, bean, columns);
|
||||
return;
|
||||
}
|
||||
Connection conn = createWriteSQLConnection();
|
||||
try {
|
||||
updateColumns(conn, info, value, columns);
|
||||
updateColumns(conn, info, bean, columns);
|
||||
} finally {
|
||||
closeSQLConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumns(final CompletionHandler<Void, T> handler, final T value, final String... columns) {
|
||||
updateColumns(value, columns);
|
||||
if (handler != null) handler.completed(null, value);
|
||||
public <T> void updateColumns(final CompletionHandler<Void, T> handler, final T bean, final String... columns) {
|
||||
updateColumns(bean, columns);
|
||||
if (handler != null) handler.completed(null, bean);
|
||||
}
|
||||
|
||||
private <T> void updateColumns(final Connection conn, final EntityInfo<T> info, final T value, final String... columns) {
|
||||
if (value == null || columns.length < 1) return;
|
||||
private <T> void updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final String... columns) {
|
||||
if (bean == null || columns.length < 1) return;
|
||||
try {
|
||||
final Class<T> clazz = (Class<T>) value.getClass();
|
||||
final Class<T> clazz = (Class<T>) bean.getClass();
|
||||
StringBuilder setsql = new StringBuilder();
|
||||
final Serializable id = info.getPrimary().get(value);
|
||||
final Serializable id = info.getPrimary().get(bean);
|
||||
final List<Attribute<T, Serializable>> attrs = new ArrayList<>();
|
||||
final boolean virtual = info.isVirtualEntity();
|
||||
for (String col : columns) {
|
||||
@@ -972,12 +1016,12 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
attrs.add(attr);
|
||||
if (!virtual) {
|
||||
if (setsql.length() > 0) setsql.append(", ");
|
||||
setsql.append(info.getSQLColumn(null, col)).append(" = ").append(formatToString(attr.get(value)));
|
||||
setsql.append(info.getSQLColumn(null, col)).append(" = ").append(FilterNode.formatToString(attr.get(bean)));
|
||||
}
|
||||
}
|
||||
if (!virtual) {
|
||||
String sql = "UPDATE " + info.getTable() + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + formatToString(id);
|
||||
if (debug.get()) logger.finest(value.getClass().getSimpleName() + ": " + sql);
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get()) logger.finest(bean.getClass().getSimpleName() + ": " + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
stmt.close();
|
||||
@@ -986,7 +1030,76 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
if (cache == null) return;
|
||||
T rs = cache.update(value, attrs);
|
||||
T rs = cache.update(bean, attrs);
|
||||
if (cacheListener != null) cacheListener.updateCache(clazz, rs);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对象指定的一些字段, 必须是Entity对象
|
||||
*
|
||||
* @param <T> Entity类的泛型
|
||||
* @param bean Entity对象
|
||||
* @param node 过滤node 不能为null
|
||||
* @param columns 需要更新的字段
|
||||
*/
|
||||
@Override
|
||||
public <T> void updateColumns(final T bean, final FilterNode node, final String... columns) {
|
||||
final EntityInfo<T> info = loadEntityInfo((Class<T>) bean.getClass());
|
||||
if (info.isVirtualEntity()) {
|
||||
updateColumns(null, info, bean, node, columns);
|
||||
return;
|
||||
}
|
||||
Connection conn = createWriteSQLConnection();
|
||||
try {
|
||||
updateColumns(conn, info, bean, node, columns);
|
||||
} finally {
|
||||
closeSQLConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateColumns(final CompletionHandler<Void, FilterNode> handler, final T bean, final FilterNode node, final String... columns) {
|
||||
updateColumns(bean, node, columns);
|
||||
if (handler != null) handler.completed(null, node);
|
||||
}
|
||||
|
||||
private <T> void updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final FilterNode node, final String... columns) {
|
||||
if (bean == null || node == null || columns.length < 1) return;
|
||||
try {
|
||||
final Class<T> clazz = (Class<T>) bean.getClass();
|
||||
StringBuilder setsql = new StringBuilder();
|
||||
final Serializable id = info.getPrimary().get(bean);
|
||||
final List<Attribute<T, Serializable>> attrs = new ArrayList<>();
|
||||
final boolean virtual = info.isVirtualEntity();
|
||||
for (String col : columns) {
|
||||
Attribute<T, Serializable> attr = info.getUpdateAttribute(col);
|
||||
if (attr == null) continue;
|
||||
attrs.add(attr);
|
||||
if (!virtual) {
|
||||
if (setsql.length() > 0) setsql.append(", ");
|
||||
setsql.append(info.getSQLColumn("a", col)).append(" = ").append(FilterNode.formatToString(attr.get(bean)));
|
||||
}
|
||||
}
|
||||
if (!virtual) {
|
||||
Map<Class, String> joinTabalis = node.getJoinTabalis();
|
||||
CharSequence join = node.createSQLJoin(this, joinTabalis, info);
|
||||
CharSequence where = node.createSQLExpress(info, joinTabalis);
|
||||
|
||||
String sql = "UPDATE " + info.getTable(node) + " a SET " + setsql
|
||||
+ (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get()) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
stmt.close();
|
||||
if (writeListener != null) writeListener.update(sql);
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
if (cache == null) return;
|
||||
T[] rs = cache.update(bean, attrs, node);
|
||||
if (cacheListener != null) cacheListener.updateCache(clazz, rs);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -1051,7 +1164,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, joinTabalis, info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : ("a." + column))) + " FROM " + info.getTable() + " a"
|
||||
final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : ("a." + column))) + " FROM " + info.getTable(node) + " a"
|
||||
+ (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(sql);
|
||||
@@ -1114,7 +1227,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, joinTabalis, info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT a." + sqlkey + ", " + func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : ("a." + funcColumn)))
|
||||
+ " FROM " + info.getTable() + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + sqlkey;
|
||||
+ " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + sqlkey;
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(sql);
|
||||
Map<K, N> rs = new LinkedHashMap<>();
|
||||
@@ -1147,6 +1260,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
* @param <T> Entity类的泛型
|
||||
* @param clazz Entity类
|
||||
* @param pk 主键值
|
||||
*
|
||||
* @return Entity对象
|
||||
*/
|
||||
@Override
|
||||
@@ -1172,7 +1286,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final Connection conn = createReadSQLConnection();
|
||||
try {
|
||||
final SelectColumn sels = selects;
|
||||
final String sql = "SELECT * FROM " + info.getTable() + " WHERE " + info.getPrimarySQLColumn() + " = " + formatToString(pk);
|
||||
final String sql = "SELECT * FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
@@ -1251,7 +1365,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, joinTabalis, info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT a.* FROM " + info.getTable() + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
final String sql = "SELECT a.* FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
@@ -1283,7 +1397,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
|
||||
final Connection conn = createReadSQLConnection();
|
||||
try {
|
||||
final String sql = "SELECT COUNT(*) FROM " + info.getTable() + " WHERE " + info.getPrimarySQLColumn() + " = " + formatToString(pk);
|
||||
final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " exists sql=" + sql);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
@@ -1327,7 +1441,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, joinTabalis, info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT COUNT(" + info.getPrimarySQLColumn("a") + ") FROM " + info.getTable() + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
final String sql = "SELECT COUNT(" + info.getPrimarySQLColumn("a") + ") FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " exists sql=" + sql);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
@@ -1426,6 +1540,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤Bean
|
||||
*
|
||||
* @return 字段集合
|
||||
*/
|
||||
@Override
|
||||
@@ -1479,6 +1594,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
* @param clazz Entity类
|
||||
* @param column 过滤字段名
|
||||
* @param key 过滤字段值
|
||||
*
|
||||
* @return Entity对象的集合
|
||||
*/
|
||||
@Override
|
||||
@@ -1498,6 +1614,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
* @param <T> Entity类的泛型
|
||||
* @param clazz Entity类
|
||||
* @param bean 过滤Bean
|
||||
*
|
||||
* @return Entity对象集合
|
||||
*/
|
||||
@Override
|
||||
@@ -1530,6 +1647,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
* @param clazz Entity类
|
||||
* @param selects 收集的字段
|
||||
* @param bean 过滤Bean
|
||||
*
|
||||
* @return Entity对象的集合
|
||||
*/
|
||||
@Override
|
||||
@@ -1620,6 +1738,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤Bean
|
||||
*
|
||||
* @return Entity对象的集合
|
||||
*/
|
||||
@Override
|
||||
@@ -1653,6 +1772,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
* @param selects 收集的字段集合
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤Bean
|
||||
*
|
||||
* @return Entity对象的集合
|
||||
*/
|
||||
@Override
|
||||
@@ -1694,14 +1814,14 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, joinTabalis, info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT a.* FROM " + info.getTable() + " a" + (join == null ? "" : join)
|
||||
final String sql = "SELECT a.* FROM " + info.getTable(node) + " a" + (join == null ? "" : join)
|
||||
+ ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + info.createSQLOrderby(flipper);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST))
|
||||
logger.finest(clazz.getSimpleName() + " query sql=" + sql + (flipper == null ? "" : (" LIMIT " + flipper.index() + "," + flipper.getSize())));
|
||||
logger.finest(clazz.getSimpleName() + " query sql=" + sql + (flipper == null ? "" : (" LIMIT " + flipper.getOffset() + "," + flipper.getLimit())));
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
if (flipper != null && flipper.index() > 0) set.absolute(flipper.index());
|
||||
final int limit = flipper == null ? Integer.MAX_VALUE : flipper.getSize();
|
||||
if (flipper != null && flipper.getOffset() > 0) set.absolute(flipper.getOffset());
|
||||
final int limit = flipper == null ? Integer.MAX_VALUE : flipper.getLimit();
|
||||
int i = 0;
|
||||
while (set.next()) {
|
||||
i++;
|
||||
|
||||
@@ -13,6 +13,8 @@ import java.util.function.Consumer;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* DataSource 为数据库或内存数据库的数据源,提供类似JPA、Hibernate的接口与功能。
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
@@ -73,29 +75,35 @@ public interface DataSource {
|
||||
|
||||
public <T> void updateColumn(final Class<T> clazz, final Serializable id, final String column, final Serializable value);
|
||||
|
||||
public <T> void updateColumn(final Class<T> clazz, final String column, final Serializable value, final FilterNode node);
|
||||
|
||||
public <T> void updateColumnIncrement(final Class<T> clazz, final Serializable id, final String column, long incvalue);
|
||||
|
||||
public <T> void updateColumnAnd(final Class<T> clazz, final Serializable id, final String column, long incvalue);
|
||||
|
||||
public <T> void updateColumnOr(final Class<T> clazz, final Serializable id, final String column, long incvalue);
|
||||
|
||||
public <T> void updateColumns(final T value, final String... columns);
|
||||
public <T> void updateColumns(final T bean, final String... columns);
|
||||
|
||||
public <T> void updateColumns(final T bean, final FilterNode node, final String... columns);
|
||||
|
||||
//----------------------异步版---------------------------------
|
||||
public <T> void update(final CompletionHandler<Void, T[]> handler, final T... values);
|
||||
|
||||
public <T> void updateColumn(final CompletionHandler<Void, Serializable> handler, final Class<T> clazz, final Serializable id, final String column, final Serializable value);
|
||||
|
||||
public <T> void updateColumn(final CompletionHandler<Void, FilterNode> handler, final Class<T> clazz, final String column, final Serializable value, final FilterNode node);
|
||||
|
||||
public <T> void updateColumnIncrement(final CompletionHandler<Void, Serializable> handler, final Class<T> clazz, final Serializable id, final String column, long incvalue);
|
||||
|
||||
public <T> void updateColumnAnd(final CompletionHandler<Void, Serializable> handler, final Class<T> clazz, final Serializable id, final String column, long incvalue);
|
||||
|
||||
public <T> void updateColumnOr(final CompletionHandler<Void, Serializable> handler, final Class<T> clazz, final Serializable id, final String column, long incvalue);
|
||||
|
||||
public <T> void updateColumns(final CompletionHandler<Void, T> handler, final T value, final String... columns);
|
||||
public <T> void updateColumns(final CompletionHandler<Void, T> handler, final T bean, final String... columns);
|
||||
|
||||
public <T> void updateColumns(final CompletionHandler<Void, FilterNode> handler, final T bean, final FilterNode node, final String... columns);
|
||||
|
||||
|
||||
|
||||
//############################################# 查询接口 #############################################
|
||||
//-----------------------getXXXXResult-----------------------------
|
||||
public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column);
|
||||
@@ -130,6 +138,7 @@ public interface DataSource {
|
||||
* @param <T> 泛型
|
||||
* @param clazz Entity类
|
||||
* @param pk 主键值
|
||||
*
|
||||
* @return Entity对象
|
||||
*/
|
||||
public <T> T find(final Class<T> clazz, final Serializable pk);
|
||||
@@ -183,6 +192,7 @@ public interface DataSource {
|
||||
* @param clazz Entity类
|
||||
* @param column 过滤字段名
|
||||
* @param key 过滤字段值
|
||||
*
|
||||
* @return 字段值的集合
|
||||
*/
|
||||
public <T, V extends Serializable> HashSet<V> queryColumnSet(final String selectedColumn, final Class<T> clazz, final String column, final Serializable key);
|
||||
@@ -219,6 +229,7 @@ public interface DataSource {
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤Bean
|
||||
*
|
||||
* @return 结果集合
|
||||
*/
|
||||
public <T, V extends Serializable> Sheet<V> queryColumnSheet(final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterBean bean);
|
||||
@@ -237,6 +248,7 @@ public interface DataSource {
|
||||
* @param clazz Entity类
|
||||
* @param column 过滤字段名
|
||||
* @param key 过滤字段值
|
||||
*
|
||||
* @return Entity的List
|
||||
*/
|
||||
public <T> List<T> queryList(final Class<T> clazz, final String column, final Serializable key);
|
||||
@@ -288,6 +300,7 @@ public interface DataSource {
|
||||
* @param clazz Entity类
|
||||
* @param flipper 翻页对象
|
||||
* @param bean 过滤Bean
|
||||
*
|
||||
* @return Entity的Sheet
|
||||
*/
|
||||
public <T> Sheet<T> querySheet(final Class<T> clazz, final Flipper flipper, final FilterBean bean);
|
||||
@@ -322,6 +335,7 @@ public interface DataSource {
|
||||
* 通常用于复杂的更新操作
|
||||
*
|
||||
* @param sqls SQL语句
|
||||
*
|
||||
* @return 结果数组
|
||||
*/
|
||||
public int[] directExecute(String... sqls);
|
||||
|
||||
@@ -11,7 +11,23 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p> 详情见: http://redkale.org
|
||||
* <pre>
|
||||
* int 10万-100万 (36进制 4位) 255t - lflr 长度4 rewrite "^/dir/(\w+)/((\w{2})(\w{2})\..*)$" /$1/$3/$2 last;
|
||||
* int 1000万-6000万 (36进制 5位) 5yc1t - zq0an 长度5-6 rewrite "^/dir/(\w+)/((\w{2})(\w{2})(\w\w?)\..*)$" /$1/$3/$4/$2 last;
|
||||
* int 2亿-20亿 (36进制 6位) 3b2ozl - x2qxvk
|
||||
* long 30亿-770亿 (36进制 7位) 1dm4etd - zdft88v 长度7-8 rewrite "^/dir/(\w+)/((\w{2})(\w{2})(\w{2})(\w\w?)\..*)$" /$1/$3/$4/$5/$2 last;
|
||||
* long 1000亿-2万亿 (36进制 8位) 19xtf1tt - piscd0jj
|
||||
* 随机文件名: (32进制 26位) 26-27长度
|
||||
* #文件名 长度: 26 (1)
|
||||
* rewrite "^/dir/(\w+)/((\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{14})\..*)$" /dir/$1/$3/$4/$5/$6/$7/$8/$2;
|
||||
* #文件名 长度: 26 (2)
|
||||
* rewrite "^/dir/(\w+)/(\w\w/\w\w/\w\w/\w\w/\w\w/\w\w)/(\w{12}(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})\..*)$" /$1/$2/$4/$5/$6/$7/$8/$9/$3 last;
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Target({FIELD})
|
||||
@@ -22,7 +38,7 @@ public @interface DistributeGenerator {
|
||||
|
||||
/**
|
||||
* 如果allocationSize的值小于或等于1,则主键不会加上nodeid
|
||||
*
|
||||
*
|
||||
* @return allocationSize
|
||||
*/
|
||||
int allocationSize() default 1000;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.source;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 当使用DistributeGenerator控制主键值时, 如果表A与表AHistory使用同一主键时, 就需要将表A的class标记:
|
||||
* <blockquote><pre>
|
||||
* @DistributeTables({AHistory.class})
|
||||
* public class A {
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* 这样DistributeGenerator将从A、B表中取最大值来初始化主键值。 常见场景就是表B是数据表A对应的历史表
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Target({TYPE})
|
||||
@Retention(RUNTIME)
|
||||
public @interface DistributeTables {
|
||||
|
||||
Class[] value();
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.redkale.source;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
@@ -30,7 +31,8 @@ public final class EntityCache<T> {
|
||||
|
||||
private final ConcurrentHashMap<Serializable, T> map = new ConcurrentHashMap();
|
||||
|
||||
private final Collection<T> list = new ConcurrentLinkedQueue(); // CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢;10w数据查询需要 0.062秒, 查询慢40%;
|
||||
// CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢;10w数据查询需要 0.062秒, 查询慢40%;
|
||||
private final Collection<T> list = new ConcurrentLinkedQueue();
|
||||
|
||||
private final Map<String, Comparator<T>> sortComparators = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -43,7 +45,7 @@ public final class EntityCache<T> {
|
||||
private final Attribute<T, Serializable> primary;
|
||||
|
||||
private final Reproduce<T, T> newReproduce;
|
||||
|
||||
|
||||
private final Reproduce<T, T> chgReproduce;
|
||||
|
||||
private volatile boolean fullloaded;
|
||||
@@ -55,7 +57,8 @@ public final class EntityCache<T> {
|
||||
this.type = info.getType();
|
||||
this.creator = info.getCreator();
|
||||
this.primary = info.primary;
|
||||
this.needcopy = true;
|
||||
VirtualEntity ve = info.getType().getAnnotation(VirtualEntity.class);
|
||||
this.needcopy = ve == null || !ve.direct();
|
||||
this.newReproduce = Reproduce.create(type, type, (m) -> {
|
||||
try {
|
||||
return type.getDeclaredField(m).getAnnotation(Transient.class) == null;
|
||||
@@ -66,9 +69,9 @@ public final class EntityCache<T> {
|
||||
this.chgReproduce = Reproduce.create(type, type, (m) -> {
|
||||
try {
|
||||
java.lang.reflect.Field field = type.getDeclaredField(m);
|
||||
if(field.getAnnotation(Transient.class) != null) return false;
|
||||
if (field.getAnnotation(Transient.class) != null) return false;
|
||||
Column column = field.getAnnotation(Column.class);
|
||||
if(column != null && !column.updatable()) return false;
|
||||
if (column != null && !column.updatable()) return false;
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return true;
|
||||
@@ -79,11 +82,13 @@ public final class EntityCache<T> {
|
||||
public void fullLoad() {
|
||||
if (info.fullloader == null) return;
|
||||
clear();
|
||||
List<T> all = info.fullloader.apply(type);
|
||||
all.stream().filter(x -> x != null).forEach(x -> {
|
||||
this.map.put(this.primary.get(x), x);
|
||||
});
|
||||
this.list.addAll(all);
|
||||
List<T> all = info.fullloader.apply(info.source, type);
|
||||
if (all != null) {
|
||||
all.stream().filter(x -> x != null).forEach(x -> {
|
||||
this.map.put(this.primary.get(x), x);
|
||||
});
|
||||
this.list.addAll(all);
|
||||
}
|
||||
this.fullloaded = true;
|
||||
}
|
||||
|
||||
@@ -302,7 +307,7 @@ public final class EntityCache<T> {
|
||||
Stream<T> stream = this.list.stream();
|
||||
if (filter != null) stream = stream.filter(filter);
|
||||
if (comparator != null) stream = stream.sorted(comparator);
|
||||
if (flipper != null) stream = stream.skip(flipper.index()).limit(flipper.getSize());
|
||||
if (flipper != null) stream = stream.skip(flipper.getOffset()).limit(flipper.getLimit());
|
||||
final List<T> rs = new ArrayList<>();
|
||||
if (selects == null) {
|
||||
Consumer<? super T> action = x -> rs.add(needcopy ? newReproduce.copy(creator.create(), x) : x);
|
||||
@@ -340,7 +345,7 @@ public final class EntityCache<T> {
|
||||
if (old == null) {
|
||||
this.list.add(rs);
|
||||
} else {
|
||||
logger.log(Level.WARNING, "cache repeat insert data: " + value);
|
||||
logger.log(Level.WARNING, this.type + " cache repeat insert data: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,6 +386,17 @@ public final class EntityCache<T> {
|
||||
return rs;
|
||||
}
|
||||
|
||||
public T[] update(final T value, final Collection<Attribute<T, Serializable>> attrs, final FilterNode node) {
|
||||
if (value == null || node == null) return (T[]) Array.newInstance(type, 0);
|
||||
T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len));
|
||||
for (T rs : rms) {
|
||||
for (Attribute attr : attrs) {
|
||||
attr.set(rs, attr.get(value));
|
||||
}
|
||||
}
|
||||
return rms;
|
||||
}
|
||||
|
||||
public <V> T update(final Serializable id, Attribute<T, V> attr, final V fieldValue) {
|
||||
if (id == null) return null;
|
||||
T rs = this.map.get(id);
|
||||
@@ -388,6 +404,15 @@ public final class EntityCache<T> {
|
||||
return rs;
|
||||
}
|
||||
|
||||
public <V> T[] update(Attribute<T, V> attr, final V fieldValue, final FilterNode node) {
|
||||
if (attr == null || node == null) return (T[]) Array.newInstance(type, 0);
|
||||
T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len));
|
||||
for (T rs : rms) {
|
||||
attr.set(rs, fieldValue);
|
||||
}
|
||||
return rms;
|
||||
}
|
||||
|
||||
public <V> T updateColumnOr(final Serializable id, Attribute<T, V> attr, final long orvalue) {
|
||||
if (id == null) return null;
|
||||
T rs = this.map.get(id);
|
||||
@@ -437,7 +462,7 @@ public final class EntityCache<T> {
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
protected Comparator<T> createComparator(Flipper flipper) {
|
||||
if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty() || flipper.getSort().indexOf(';') >= 0 || flipper.getSort().indexOf('\n') >= 0) return null;
|
||||
if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty() || flipper.getSort().indexOf(';') >= 0 || flipper.getSort().indexOf('\n') >= 0) return null;
|
||||
final String sort = flipper.getSort();
|
||||
Comparator<T> comparator = this.sortComparators.get(sort);
|
||||
if (comparator != null) return comparator;
|
||||
@@ -453,121 +478,19 @@ public final class EntityCache<T> {
|
||||
final Attribute<T, Serializable> pattr = getAttribute(sub[0].substring(pos + 1, pos2));
|
||||
final String func = sub[0].substring(0, pos);
|
||||
if ("ABS".equalsIgnoreCase(func)) {
|
||||
Function getter = null;
|
||||
if (pattr.type() == int.class || pattr.type() == Integer.class) {
|
||||
attr = new Attribute<T, Serializable>() {
|
||||
|
||||
@Override
|
||||
public Class type() {
|
||||
return pattr.type();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class declaringClass() {
|
||||
return pattr.declaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String field() {
|
||||
return pattr.field();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable get(T obj) {
|
||||
return Math.abs(((Number) pattr.get(obj)).intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T obj, Serializable value) {
|
||||
pattr.set(obj, value);
|
||||
}
|
||||
};
|
||||
getter = x -> Math.abs(((Number) pattr.get((T) x)).intValue());
|
||||
} else if (pattr.type() == long.class || pattr.type() == Long.class) {
|
||||
attr = new Attribute<T, Serializable>() {
|
||||
|
||||
@Override
|
||||
public Class type() {
|
||||
return pattr.type();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class declaringClass() {
|
||||
return pattr.declaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String field() {
|
||||
return pattr.field();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable get(T obj) {
|
||||
return Math.abs(((Number) pattr.get(obj)).longValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T obj, Serializable value) {
|
||||
pattr.set(obj, value);
|
||||
}
|
||||
};
|
||||
getter = x -> Math.abs(((Number) pattr.get((T) x)).longValue());
|
||||
} else if (pattr.type() == float.class || pattr.type() == Float.class) {
|
||||
attr = new Attribute<T, Serializable>() {
|
||||
|
||||
@Override
|
||||
public Class type() {
|
||||
return pattr.type();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class declaringClass() {
|
||||
return pattr.declaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String field() {
|
||||
return pattr.field();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable get(T obj) {
|
||||
return Math.abs(((Number) pattr.get(obj)).floatValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T obj, Serializable value) {
|
||||
pattr.set(obj, value);
|
||||
}
|
||||
};
|
||||
getter = x -> Math.abs(((Number) pattr.get((T) x)).floatValue());
|
||||
} else if (pattr.type() == double.class || pattr.type() == Double.class) {
|
||||
attr = new Attribute<T, Serializable>() {
|
||||
|
||||
@Override
|
||||
public Class type() {
|
||||
return pattr.type();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class declaringClass() {
|
||||
return pattr.declaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String field() {
|
||||
return pattr.field();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable get(T obj) {
|
||||
return Math.abs(((Number) pattr.get(obj)).doubleValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T obj, Serializable value) {
|
||||
pattr.set(obj, value);
|
||||
}
|
||||
};
|
||||
getter = x -> Math.abs(((Number) pattr.get((T) x)).doubleValue());
|
||||
} else {
|
||||
throw new RuntimeException("Flipper not supported sort illegal type by ABS (" + flipper.getSort() + ")");
|
||||
}
|
||||
attr = (Attribute<T, Serializable>) Attribute.create(pattr.declaringClass(), pattr.field(), pattr.type(), getter, (o, v) -> pattr.set(o, v));
|
||||
} else if (func.isEmpty()) {
|
||||
attr = pattr;
|
||||
} else {
|
||||
|
||||
@@ -83,8 +83,6 @@ public final class EntityInfo<T> {
|
||||
//---------------------计算主键值----------------------------
|
||||
private final int nodeid;
|
||||
|
||||
final Class[] distributeTables;
|
||||
|
||||
final boolean autoGenerated;
|
||||
|
||||
final boolean distributed;
|
||||
@@ -95,18 +93,20 @@ public final class EntityInfo<T> {
|
||||
|
||||
final int allocationSize;
|
||||
|
||||
final Function<Class, List> fullloader;
|
||||
final DataSource source;
|
||||
|
||||
final BiFunction<DataSource, Class, List> fullloader;
|
||||
//------------------------------------------------------------
|
||||
|
||||
public static <T> EntityInfo<T> load(Class<T> clazz, final int nodeid, final boolean cacheForbidden, final Properties conf,
|
||||
Function<Class, List> fullloader) {
|
||||
DataSource source, BiFunction<DataSource, Class, List> fullloader) {
|
||||
EntityInfo rs = entityInfos.get(clazz);
|
||||
if (rs != null) return rs;
|
||||
synchronized (entityInfos) {
|
||||
rs = entityInfos.get(clazz);
|
||||
if (rs == null) {
|
||||
if (nodeid < 0) throw new IllegalArgumentException("nodeid(" + nodeid + ") is illegal");
|
||||
rs = new EntityInfo(clazz, nodeid, cacheForbidden, conf, fullloader);
|
||||
rs = new EntityInfo(clazz, nodeid, cacheForbidden, conf, source, fullloader);
|
||||
entityInfos.put(clazz, rs);
|
||||
if (rs.cache != null) {
|
||||
if (fullloader == null) throw new IllegalArgumentException(clazz.getName() + " auto loader is illegal");
|
||||
@@ -121,13 +121,12 @@ public final class EntityInfo<T> {
|
||||
return entityInfos.get(clazz);
|
||||
}
|
||||
|
||||
private EntityInfo(Class<T> type, int nodeid, final boolean cacheForbidden, Properties conf, Function<Class, List> fullloader) {
|
||||
private EntityInfo(Class<T> type, int nodeid, final boolean cacheForbidden,
|
||||
Properties conf, DataSource source, BiFunction<DataSource, Class, List> fullloader) {
|
||||
this.type = type;
|
||||
this.fullloader = fullloader;
|
||||
this.source = source;
|
||||
//---------------------------------------------
|
||||
this.nodeid = nodeid >= 0 ? nodeid : 0;
|
||||
DistributeTables dt = type.getAnnotation(DistributeTables.class);
|
||||
this.distributeTables = dt == null ? null : dt.value();
|
||||
|
||||
LogLevel ll = type.getAnnotation(LogLevel.class);
|
||||
this.logLevel = ll == null ? Integer.MIN_VALUE : Level.parse(ll.value()).intValue();
|
||||
@@ -135,7 +134,15 @@ public final class EntityInfo<T> {
|
||||
Table t = type.getAnnotation(Table.class);
|
||||
if (type.getAnnotation(VirtualEntity.class) != null) {
|
||||
this.table = null;
|
||||
BiFunction<DataSource, Class, List> loader = null;
|
||||
try {
|
||||
loader = type.getAnnotation(VirtualEntity.class).loader().newInstance();
|
||||
} catch (Exception e) {
|
||||
logger.severe(type + " init @VirtualEntity.loader error", e);
|
||||
}
|
||||
this.fullloader = loader;
|
||||
} else {
|
||||
this.fullloader = fullloader;
|
||||
this.table = (t == null) ? type.getSimpleName().toLowerCase() : (t.catalog().isEmpty()) ? t.name() : (t.catalog() + '.' + t.name());
|
||||
}
|
||||
this.creator = Creator.create(type);
|
||||
@@ -180,8 +187,10 @@ public final class EntityInfo<T> {
|
||||
// }
|
||||
DistributeGenerator dg = field.getAnnotation(DistributeGenerator.class);
|
||||
if (dg != null) {
|
||||
if (!field.getType().isPrimitive())
|
||||
throw new RuntimeException(cltmp.getName() + "'s @" + DistributeGenerator.class.getSimpleName() + " primary must be primitive class type field");
|
||||
if (!field.getType().isPrimitive()) {
|
||||
throw new RuntimeException(cltmp.getName() + "'s @"
|
||||
+ DistributeGenerator.class.getSimpleName() + " primary must be primitive class type field");
|
||||
}
|
||||
sqldistribute = true;
|
||||
auto = false;
|
||||
allocationSize0 = dg.allocationSize();
|
||||
@@ -286,7 +295,15 @@ public final class EntityInfo<T> {
|
||||
return table == null;
|
||||
}
|
||||
|
||||
public String getTable() {
|
||||
public String getTable(Serializable primary) {
|
||||
return table;
|
||||
}
|
||||
|
||||
public String getTable(FilterNode node) {
|
||||
return table;
|
||||
}
|
||||
|
||||
public String getTable(T bean) {
|
||||
return table;
|
||||
}
|
||||
|
||||
@@ -312,7 +329,7 @@ public final class EntityInfo<T> {
|
||||
}
|
||||
|
||||
protected String createSQLOrderby(Flipper flipper) {
|
||||
if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty() || flipper.getSort().indexOf(';') >= 0 || flipper.getSort().indexOf('\n') >= 0 ) return "";
|
||||
if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty() || flipper.getSort().indexOf(';') >= 0 || flipper.getSort().indexOf('\n') >= 0) return "";
|
||||
final String sort = flipper.getSort();
|
||||
String sql = this.sortOrderbySqls.get(sort);
|
||||
if (sql != null) return sql;
|
||||
@@ -369,12 +386,18 @@ public final class EntityInfo<T> {
|
||||
Serializable o = (Serializable) set.getObject(this.getSQLColumn(null, attr.field()));
|
||||
if (o != null) {
|
||||
Class t = attr.type();
|
||||
if (t == short.class) {
|
||||
if (t == int.class) {
|
||||
o = ((Number) o).intValue();
|
||||
} else if (t == short.class) {
|
||||
o = ((Number) o).shortValue();
|
||||
} else if (t == long.class) {
|
||||
o = ((Number) o).longValue();
|
||||
} else if (t == int.class) {
|
||||
o = ((Number) o).intValue();
|
||||
} else if (t == float.class) {
|
||||
o = ((Number) o).floatValue();
|
||||
} else if (t == double.class) {
|
||||
o = ((Number) o).doubleValue();
|
||||
} else if (t == byte.class) {
|
||||
o = ((Number) o).byteValue();
|
||||
}
|
||||
}
|
||||
attr.set(obj, o);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
package org.redkale.source;
|
||||
|
||||
/**
|
||||
*
|
||||
* 不被标记为@javax.persistence.Transient 的字段均视为过滤条件
|
||||
*
|
||||
* <p> 详情见: http://redkale.org
|
||||
* @author zhangjx
|
||||
|
||||
@@ -10,6 +10,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 过滤字段标记
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.redkale.source;
|
||||
|
||||
/**
|
||||
* 函数表达式, 均与SQL定义中的表达式相同
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
@@ -15,17 +16,17 @@ package org.redkale.source;
|
||||
public enum FilterExpress {
|
||||
|
||||
EQUAL("="),
|
||||
IGNORECASEEQUAL("="),//不区分大小写的 =
|
||||
NOTEQUAL("<>"),
|
||||
IGNORECASENOTEQUAL("="),//不区分大小写的 <>
|
||||
GREATERTHAN(">"),
|
||||
LESSTHAN("<"),
|
||||
GREATERTHANOREQUALTO(">="),
|
||||
LESSTHANOREQUALTO("<="),
|
||||
|
||||
STARTSWITH("LIKE"),
|
||||
NOTSTARTSWITH("NOT LIKE"),
|
||||
ENDSWITH("LIKE"),
|
||||
NOTENDSWITH("NOT LIKE"),
|
||||
|
||||
LIKE("LIKE"),
|
||||
NOTLIKE("NOT LIKE"),
|
||||
IGNORECASELIKE("LIKE"), //不区分大小写的 LIKE
|
||||
|
||||
@@ -6,17 +6,20 @@
|
||||
package org.redkale.source;
|
||||
|
||||
/**
|
||||
* 常见的SQL聚合函数
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* <p> 详情见: http://redkale.org
|
||||
* @author zhangjx
|
||||
*/
|
||||
public enum FilterFunc {
|
||||
AVG,
|
||||
COUNT,
|
||||
DISTINCTCOUNT,
|
||||
MAX,
|
||||
MIN,
|
||||
SUM;
|
||||
AVG, //平均值
|
||||
COUNT, //总数
|
||||
DISTINCTCOUNT, //去重总数
|
||||
MAX, //最大值
|
||||
MIN, //最小值
|
||||
SUM; //求和
|
||||
|
||||
public String getColumn(String col) {
|
||||
if (this == DISTINCTCOUNT) return "COUNT(DISTINCT " + col + ")";
|
||||
|
||||
@@ -51,7 +51,7 @@ import java.lang.annotation.*;
|
||||
* </pre></blockquote>
|
||||
* 转换的SQL语句为: WHERE id = ? AND ((desc LIKE ? AND name LIKE ?) OR (age = ? OR birthday = ?))
|
||||
* 因为默认是AND关系, @FilterGroup("") 等价于 @FilterGroup("[AND]")
|
||||
* 所以示例二的@FilterGroup("[OR]g1.[AND]subg1") 可以简化为 @FilterGroup("[OR]g1")
|
||||
* 所以示例二的@FilterGroup("[OR]g1.[AND]subg1") 可以简化为 @FilterGroup("[OR]g1.subg1")
|
||||
*/
|
||||
/**
|
||||
* <p>
|
||||
|
||||
@@ -277,7 +277,7 @@ public class FilterJoinNode extends FilterNode {
|
||||
if (node.joinClass == null) return null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String[] joinColumns = node.joinColumns;
|
||||
sb.append(" INNER JOIN ").append(node.joinEntity.getTable()).append(" ").append(joinTabalis.get(node.joinClass))
|
||||
sb.append(" INNER JOIN ").append(node.joinEntity.getTable(node)).append(" ").append(joinTabalis.get(node.joinClass))
|
||||
.append(" ON ").append(info.getSQLColumn("a", joinColumns[0])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), joinColumns[0]));
|
||||
for (int i = 1; i < joinColumns.length; i++) {
|
||||
sb.append(" AND ").append(info.getSQLColumn("a", joinColumns[i])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), joinColumns[i]));
|
||||
|
||||
@@ -13,9 +13,11 @@ import static org.redkale.source.FilterExpress.*;
|
||||
import org.redkale.util.Attribute;
|
||||
|
||||
/**
|
||||
* 注意: 在调用 createSQLExpress 之前必须先调用 createSQLJoin 在调用 createPredicate 之前必须先调用 isCacheUseable
|
||||
* <p>
|
||||
* <p>
|
||||
* 注意: <br>
|
||||
* 在调用 createSQLExpress 之前必须先调用 createSQLJoin <br>
|
||||
* 在调用 createPredicate 之前必须先调用 isCacheUseable
|
||||
*
|
||||
*
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -64,7 +66,7 @@ public class FilterNode {
|
||||
Class comp = val.getClass().getComponentType();
|
||||
if (Range.class.isAssignableFrom(comp)) {
|
||||
exp = FilterExpress.BETWEEN;
|
||||
} else if (comp.isArray() || Collection.class.isAssignableFrom(comp)) {
|
||||
} else {
|
||||
exp = FilterExpress.IN;
|
||||
}
|
||||
}
|
||||
@@ -75,6 +77,17 @@ public class FilterNode {
|
||||
this.value = val;
|
||||
}
|
||||
|
||||
public Serializable findValue(final String col) {
|
||||
if (this.column.equals(col)) return this.value;
|
||||
if (this.nodes == null) return null;
|
||||
for (FilterNode n : this.nodes) {
|
||||
if (n == null) continue;
|
||||
Serializable val = n.findValue(col);
|
||||
if (val != null) return val;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final FilterNode and(FilterNode node) {
|
||||
return any(node, false);
|
||||
}
|
||||
@@ -143,10 +156,11 @@ public class FilterNode {
|
||||
/**
|
||||
* 该方法需要重载
|
||||
*
|
||||
* @param <T> Entity类的泛型
|
||||
* @param func EntityInfo的加载器
|
||||
* @param <T> Entity类的泛型
|
||||
* @param func EntityInfo的加载器
|
||||
* @param joinTabalis 关联表集合
|
||||
* @param info Entity类的EntityInfo
|
||||
* @param info Entity类的EntityInfo
|
||||
*
|
||||
* @return SQL的join语句 不存在返回null
|
||||
*/
|
||||
protected <T> CharSequence createSQLJoin(final Function<Class, EntityInfo> func, final Map<Class, String> joinTabalis, final EntityInfo<T> info) {
|
||||
@@ -192,6 +206,7 @@ public class FilterNode {
|
||||
* 该方法需要重载
|
||||
*
|
||||
* @param entityApplyer EntityInfo的加载器
|
||||
*
|
||||
* @return 是否可以使用缓存
|
||||
*/
|
||||
protected boolean isCacheUseable(final Function<Class, EntityInfo> entityApplyer) {
|
||||
@@ -205,9 +220,10 @@ public class FilterNode {
|
||||
/**
|
||||
* 该方法需要重载
|
||||
*
|
||||
* @param <T> Entity类的泛型
|
||||
* @param <T> Entity类的泛型
|
||||
* @param joinTabalis 关联表的集合
|
||||
* @param info EntityInfo
|
||||
* @param info EntityInfo
|
||||
*
|
||||
* @return JOIN的SQL语句
|
||||
*/
|
||||
protected <T> CharSequence createSQLExpress(final EntityInfo<T> info, final Map<Class, String> joinTabalis) {
|
||||
@@ -327,7 +343,7 @@ public class FilterNode {
|
||||
if (express == NOTCONTAIN) return info.notcontainSQL.replace("${column}", info.getSQLColumn(talis, column)).replace("${keystr}", val);
|
||||
if (express == IGNORECASENOTCONTAIN) return info.notcontainSQL.replace("${column}", "LOWER(" + info.getSQLColumn(talis, column) + ")").replace("${keystr}", val);
|
||||
|
||||
if (express == IGNORECASELIKE || express == IGNORECASENOTLIKE) {
|
||||
if (express == IGNORECASEEQUAL || express == IGNORECASENOTEQUAL || express == IGNORECASELIKE || express == IGNORECASENOTLIKE) {
|
||||
sb.append("LOWER(").append(info.getSQLColumn(talis, column)).append(')');
|
||||
if (fk) val = "LOWER(" + info.getSQLColumn(talis, ((FilterKey) val0).getColumn()) + ')';
|
||||
} else {
|
||||
@@ -553,7 +569,6 @@ public class FilterNode {
|
||||
return field + " != null";
|
||||
}
|
||||
};
|
||||
if (attr == null) return null;
|
||||
if (val0 == null) return null;
|
||||
|
||||
final Class atype = attr.type();
|
||||
@@ -697,6 +712,36 @@ public class FilterNode {
|
||||
return field + ' ' + express.value() + ' ' + formatToString(val);
|
||||
}
|
||||
};
|
||||
case IGNORECASEEQUAL:
|
||||
return fk ? new Predicate<T>() {
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
Object rs = attr.get(t);
|
||||
Object rs2 = fkattr.get(t);
|
||||
if (rs == null && rs2 == null) return true;
|
||||
if (rs == null || rs2 == null) return false;
|
||||
return Objects.equals(rs.toString().toLowerCase(), rs2.toString().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')';
|
||||
}
|
||||
} : new Predicate<T>() {
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return val.toString().equalsIgnoreCase(rs.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(val);
|
||||
}
|
||||
};
|
||||
case NOTEQUAL:
|
||||
return fk ? new Predicate<T>() {
|
||||
|
||||
@@ -721,6 +766,36 @@ public class FilterNode {
|
||||
return field + ' ' + express.value() + ' ' + formatToString(val);
|
||||
}
|
||||
};
|
||||
case IGNORECASENOTEQUAL:
|
||||
return fk ? new Predicate<T>() {
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
Object rs = attr.get(t);
|
||||
Object rs2 = fkattr.get(t);
|
||||
if (rs == null && rs2 == null) return false;
|
||||
if (rs == null || rs2 == null) return true;
|
||||
return !Objects.equals(rs.toString().toLowerCase(), rs2.toString().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')';
|
||||
}
|
||||
} : new Predicate<T>() {
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return true;
|
||||
return !val.toString().equalsIgnoreCase(rs.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(val);
|
||||
}
|
||||
};
|
||||
case GREATERTHAN:
|
||||
return fk ? new Predicate<T>() {
|
||||
|
||||
@@ -1671,7 +1746,8 @@ public class FilterNode {
|
||||
value = "%" + value;
|
||||
} else if (express == IGNORECASELIKE || express == IGNORECASENOTLIKE) {
|
||||
value = "%" + value.toString().toLowerCase() + '%';
|
||||
} else if (express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN) {
|
||||
} else if (express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN
|
||||
|| express == IGNORECASEEQUAL || express == IGNORECASENOTEQUAL) {
|
||||
value = value.toString().toLowerCase();
|
||||
}
|
||||
return new StringBuilder().append('\'').append(value.toString().replace("'", "\\'")).append('\'');
|
||||
|
||||
@@ -354,7 +354,8 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
|
||||
if (express == ISNULL || express == ISNOTNULL) {
|
||||
sb.append(col).append(' ').append(express.value());
|
||||
} else {
|
||||
boolean lower = (express == IGNORECASELIKE || express == IGNORECASENOTLIKE || express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN);
|
||||
boolean lower = (express == IGNORECASEEQUAL || express == IGNORECASENOTEQUAL || express == IGNORECASELIKE
|
||||
|| express == IGNORECASENOTLIKE || express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN);
|
||||
sb.append(lower ? ("LOWER(" + col + ')') : col).append(' ').append(express.value()).append(" ?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
package org.redkale.source;
|
||||
|
||||
/**
|
||||
* FilterValue主要用于复杂的表达式, 例如: col / 10 = 3 、MOD(col, 8) > 0 这些都不是单独一个数值能表达的,因此需要FilterValue 才构建 8 、 > 、0 组合值.
|
||||
* FilterValue主要用于复杂的表达式。<br>
|
||||
* 例如: col / 10 = 3 、MOD(col, 8) > 0 这些都不是单独一个数值能表达的,因此需要FilterValue 才构建 8 、 > 、0 组合值.
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
|
||||
@@ -16,105 +16,121 @@ import java.io.Serializable;
|
||||
*/
|
||||
public final class Flipper implements Serializable, Cloneable {
|
||||
|
||||
public static int DEFAULT_PAGESIZE = 20;
|
||||
public static int DEFAULT_LIMIT = 20;
|
||||
|
||||
private int size = DEFAULT_PAGESIZE;
|
||||
private int limit = DEFAULT_LIMIT;
|
||||
|
||||
private int page = 1;
|
||||
private int offset = 0;
|
||||
|
||||
private String sort = "";
|
||||
|
||||
public Flipper() {
|
||||
}
|
||||
|
||||
public Flipper(int pageSize) {
|
||||
this.size = pageSize;
|
||||
public Flipper(int limit) {
|
||||
this.limit = limit > 0 ? limit : DEFAULT_LIMIT;
|
||||
}
|
||||
|
||||
public Flipper(String sortColumn) {
|
||||
this.sort = sortColumn;
|
||||
}
|
||||
|
||||
public Flipper(int pageSize, int pageNo) {
|
||||
this.size = pageSize > 0 ? pageSize : DEFAULT_PAGESIZE;
|
||||
this.page = pageNo > 0 ? pageNo : 1;
|
||||
public Flipper(int limit, int offset) {
|
||||
this.limit = limit > 0 ? limit : DEFAULT_LIMIT;
|
||||
this.offset = offset < 0 ? 0 : offset;
|
||||
}
|
||||
|
||||
public Flipper(int pageSize, int pageNo, String sortColumn) {
|
||||
this.size = pageSize > 0 ? pageSize : DEFAULT_PAGESIZE;
|
||||
this.page = pageNo > 0 ? pageNo : 1;
|
||||
public Flipper(int limit, int offset, String sortColumn) {
|
||||
this.limit = limit > 0 ? limit : DEFAULT_LIMIT;
|
||||
this.offset = offset < 0 ? 0 : offset;
|
||||
this.sort = sortColumn;
|
||||
}
|
||||
|
||||
public void copyTo(Flipper copy) {
|
||||
if (copy == null) return;
|
||||
copy.page = this.page;
|
||||
copy.size = this.size;
|
||||
public Flipper copyTo(Flipper copy) {
|
||||
if (copy == null) return copy;
|
||||
copy.offset = this.offset;
|
||||
copy.limit = this.limit;
|
||||
copy.sort = this.sort;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void copyFrom(Flipper copy) {
|
||||
if (copy == null) return;
|
||||
this.page = copy.page;
|
||||
this.size = copy.size;
|
||||
public Flipper copyFrom(Flipper copy) {
|
||||
if (copy == null) return this;
|
||||
this.offset = copy.offset;
|
||||
this.limit = copy.limit;
|
||||
this.sort = copy.sort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Flipper next() {
|
||||
this.page++;
|
||||
this.offset = getOffset() + this.limit;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("CloneDoesntCallSuperClone")
|
||||
public Flipper clone() {
|
||||
return new Flipper(this.size, this.page, this.sort);
|
||||
}
|
||||
|
||||
public int index() {
|
||||
return (getPage() - 1) * getSize();
|
||||
return this.copyTo(new Flipper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "{page:" + this.page + ", size=" + this.size + ", sort=" + this.sort + "}";
|
||||
return this.getClass().getSimpleName() + "{offset:" + this.offset + ", limit:" + this.limit + ", sort:" + this.sort + "}";
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
if (size > 0) {
|
||||
this.size = size;
|
||||
public void setLimit(int limit) {
|
||||
if (limit > 0) {
|
||||
this.limit = limit;
|
||||
}
|
||||
}
|
||||
|
||||
public int getPage() {
|
||||
return page;
|
||||
public Flipper limit(int limit) {
|
||||
setLimit(limit);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setPage(int page) {
|
||||
if (page >= 0) {
|
||||
this.page = page;
|
||||
}
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void setOffset(int offset) {
|
||||
this.offset = offset < 0 ? 0 : offset;
|
||||
}
|
||||
|
||||
public Flipper offset(int offset) {
|
||||
setOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public Flipper sortIfEmpty(String sort) {
|
||||
if (this.sort == null || this.sort.isEmpty()) {
|
||||
this.sort = sort;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setSort(String sort) {
|
||||
if (sort != null) {
|
||||
this.sort = sort.trim();
|
||||
}
|
||||
}
|
||||
|
||||
public Flipper sort(String sort) {
|
||||
setSort(sort);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static Flipper sortIfAbsent(Flipper flipper, String sort) {
|
||||
if (flipper != null) return flipper.sortIfAbsent(sort);
|
||||
return flipper;
|
||||
}
|
||||
|
||||
public Flipper sortIfAbsent(String sort) {
|
||||
if (this.sort == null || this.sort.isEmpty()) {
|
||||
this.sort = sort;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ public class JDBCPoolSource {
|
||||
watchThread.setName("DataSource-Watch-" + maps.size() + "-Thread");
|
||||
watchThread.setDaemon(true);
|
||||
watchThread.start();
|
||||
dataSource.logger.log(Level.FINER, watchThread.getName() + " start watching " + file);
|
||||
dataSource.logger.log(Level.INFO, watchThread.getName() + " start watching " + file);
|
||||
//-----------------------------------------------------------
|
||||
list.add(new WeakReference<>(this));
|
||||
maps.put(file, new AbstractMap.SimpleEntry<>(watcher, list));
|
||||
|
||||
@@ -8,6 +8,8 @@ package org.redkale.source;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import java.lang.annotation.*;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
|
||||
/**
|
||||
* VirtualEntity表示虚拟的数据实体类, 通常Entity都会映射到数据库中的某个表,而标记为VirtualEntity的Entity类只存在DataCache中
|
||||
@@ -22,4 +24,17 @@ import java.lang.annotation.*;
|
||||
@Retention(RUNTIME)
|
||||
public @interface VirtualEntity {
|
||||
|
||||
//DataSource是否直接返回对象的真实引用, 而不是copy一份
|
||||
boolean direct() default false;
|
||||
|
||||
//初始化时数据的加载器
|
||||
Class<? extends BiFunction<DataSource, Class, List>> loader() default DefaultFunctionLoader.class;
|
||||
|
||||
public static class DefaultFunctionLoader implements BiFunction<DataSource, Class, List> {
|
||||
|
||||
@Override
|
||||
public List apply(DataSource source, Class type) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import java.util.function.BiPredicate;
|
||||
/**
|
||||
* 该类提供类似JSONObject的数据结构,主要用于读取xml配置文件和http-header存储
|
||||
*
|
||||
* <p> 详情见: http://redkale.org
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -21,25 +23,17 @@ public abstract class AnyValue {
|
||||
/**
|
||||
* 可读写的AnyValue默认实现类
|
||||
*
|
||||
* <p> 详情见: http://redkale.org
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final class DefaultAnyValue extends AnyValue {
|
||||
|
||||
public static final BiPredicate<String, String> EQUALS = new BiPredicate<String, String>() { //为了兼容Android
|
||||
@Override
|
||||
public boolean test(String name1, String name2) {
|
||||
return name1.equals(name2);
|
||||
}
|
||||
};
|
||||
public static final BiPredicate<String, String> EQUALS = (name1, name2) -> name1.equals(name2);
|
||||
|
||||
public static final BiPredicate<String, String> EQUALSIGNORE = new BiPredicate<String, String>() { //为了兼容Android
|
||||
@Override
|
||||
public boolean test(String name1, String name2) {
|
||||
return name1.equalsIgnoreCase(name2);
|
||||
}
|
||||
};
|
||||
public static final BiPredicate<String, String> EQUALSIGNORE = (name1, name2) -> name1.equalsIgnoreCase(name2);
|
||||
|
||||
private final BiPredicate<String, String> predicate;
|
||||
|
||||
@@ -331,7 +325,7 @@ public abstract class AnyValue {
|
||||
return new DefaultAnyValue();
|
||||
}
|
||||
|
||||
protected String toString(int len) {
|
||||
public String toString(int len) {
|
||||
if (len < 0) len = 0;
|
||||
char[] chars = new char[len];
|
||||
Arrays.fill(chars, ' ');
|
||||
|
||||
@@ -10,8 +10,9 @@ import jdk.internal.org.objectweb.asm.*;
|
||||
|
||||
/**
|
||||
* MethodVisitor 的调试类
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* <p> 详情见: http://redkale.org
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class AsmMethodVisitor {
|
||||
@@ -25,6 +26,13 @@ public class AsmMethodVisitor {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void debugLine() {
|
||||
if (!debug) return;
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private final Map<Label, Integer> labels = new LinkedHashMap();
|
||||
|
||||
private static final String[] opcodes = new String[200]; //0 -18
|
||||
@@ -137,7 +145,15 @@ public class AsmMethodVisitor {
|
||||
|
||||
public void visitLdcInsn(Object o) {
|
||||
visitor.visitLdcInsn(o);
|
||||
if (debug) System.out.println("mv.visitLdcInsn(" + o + ");");
|
||||
if (debug) {
|
||||
if (o instanceof CharSequence) {
|
||||
System.out.println("mv.visitLdcInsn(\"" + o + "\");");
|
||||
} else if (o instanceof jdk.internal.org.objectweb.asm.Type) {
|
||||
System.out.println("mv.visitLdcInsn(Type.getType(\"" + o + "\"));");
|
||||
} else {
|
||||
System.out.println("mv.visitLdcInsn(" + o + ");");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
|
||||
@@ -8,19 +8,20 @@ package org.redkale.util;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
* 该类实现动态映射一个JavaBean类中成员对应的getter、setter方法; 代替低效的反射实现方式。
|
||||
* <blockquote><pre>
|
||||
* public class Record {
|
||||
* <p>
|
||||
*
|
||||
* private String name;
|
||||
* <p>
|
||||
*
|
||||
* public String getName() {
|
||||
* return name;
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* public void setName(String name) {
|
||||
* this.name = name;
|
||||
* }
|
||||
@@ -33,27 +34,27 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
* 等价于:
|
||||
* <blockquote><pre>
|
||||
* Attribute<Record, String> nameAction = new Attribute<Record, String>() {
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public String field() {
|
||||
* return "name";
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public String get(Record obj) {
|
||||
* return obj.getName();
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public void set(Record obj, String value) {
|
||||
* obj.setName(value);
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public Class type() {
|
||||
* return String.class;
|
||||
* }
|
||||
* <p>
|
||||
*
|
||||
* @Override
|
||||
* public Class declaringClass() {
|
||||
* return Record.class;
|
||||
@@ -67,7 +68,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
* 当不存在getter方法时,get操作固定返回null <br>
|
||||
* 当不存在setter方法时,set操作为空方法 <br>
|
||||
* <p>
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -339,20 +339,20 @@ public interface Attribute<T, F> {
|
||||
/**
|
||||
* 根据Class、字段别名、字段类型生成虚构的 Attribute 对象,get、set方法为空方法。
|
||||
*
|
||||
* @param <T>
|
||||
* @param <F>
|
||||
* @param clazz
|
||||
* @param fieldalias
|
||||
* @param fieldtype
|
||||
* @param <T> 依附类的类型
|
||||
* @param <F> 字段类型
|
||||
* @param clazz 指定依附的类
|
||||
* @param fieldalias 字段别名
|
||||
* @param fieldtype 字段的类
|
||||
*
|
||||
* @return
|
||||
* @return Attribute对象
|
||||
*/
|
||||
public static <T, F> Attribute<T, F> create(final Class<T> clazz, String fieldalias, final Class<F> fieldtype) {
|
||||
return create(clazz, fieldalias, fieldtype, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Class、字段别名、字段类型、Field、getter和setter方法生成 Attribute 对象。 fieldalias&fieldtype、Field、tgetter、setter不能同时为null
|
||||
* 根据Class、字段别名、字段类型、Field、getter和setter方法生成 Attribute 对象。 fieldalias/fieldtype、Field、tgetter、setter不能同时为null.
|
||||
*
|
||||
* @param <T> 依附类的类型
|
||||
* @param <F> 字段类型
|
||||
@@ -443,7 +443,7 @@ public interface Attribute<T, F> {
|
||||
} catch (Throwable ex) {
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final ClassWriter cw = new ClassWriter(0);
|
||||
final ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||
MethodVisitor mv;
|
||||
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + columnDesc + ">;", "java/lang/Object", new String[]{supDynName});
|
||||
|
||||
@@ -11,8 +11,8 @@ import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 自动加载。 使用场景:
|
||||
* 1、被标记为@AutoLoad(false)的Service类不会被自动加载
|
||||
* 2、被标记为@AutoLoad(false)的Servlet类不会被自动加载
|
||||
* 1、被标记为@AutoLoad(false)的Service类不会被自动加载, 当被依赖时才会被加载
|
||||
* 2、被标记为@AutoLoad(false)的Servlet类不会被自动加载
|
||||
*
|
||||
* <p> 详情见: http://redkale.org
|
||||
* @author zhangjx
|
||||
|
||||
@@ -31,14 +31,20 @@ public final class ByteArray {
|
||||
content = new byte[Math.max(128, size)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空数据,将count置为0,并不清掉byte[]的内容
|
||||
*/
|
||||
public void clear() {
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
public int find(byte value) {
|
||||
return find(0, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较内容是否相同
|
||||
*
|
||||
* @param bytes 待比较内容
|
||||
*
|
||||
* @return 是否相同
|
||||
*/
|
||||
public boolean equal(final byte[] bytes) {
|
||||
if (bytes == null || count != bytes.length) return false;
|
||||
for (int i = 0; i < count; i++) {
|
||||
@@ -47,26 +53,58 @@ public final class ByteArray {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断内容是否为空
|
||||
*
|
||||
* @return 是否为空
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字节长度
|
||||
*
|
||||
* @return 长度
|
||||
*/
|
||||
public int size() {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定位置的byte值,须确保0 <= index < size
|
||||
*
|
||||
* @param index 位置
|
||||
*
|
||||
* @return byte值
|
||||
*/
|
||||
public byte get(int index) {
|
||||
return content[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后一个字节值,调用前须保证count大于0
|
||||
*
|
||||
* @return byte值
|
||||
*/
|
||||
public byte getLastByte() {
|
||||
return content[count - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将buf内容覆盖到本对象内容中
|
||||
*
|
||||
* @param buf
|
||||
*/
|
||||
public void copyTo(byte[] buf) {
|
||||
System.arraycopy(this.content, 0, buf, 0, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将array的内容引用复制给本对象
|
||||
*
|
||||
* @param array
|
||||
*/
|
||||
public void directFrom(ByteArray array) {
|
||||
if (array != null) {
|
||||
this.content = array.content;
|
||||
@@ -74,6 +112,11 @@ public final class ByteArray {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将本对象的内容引用复制给array
|
||||
*
|
||||
* @param array
|
||||
*/
|
||||
public void directTo(ByteArray array) {
|
||||
if (array != null) {
|
||||
array.content = this.content;
|
||||
@@ -81,32 +124,92 @@ public final class ByteArray {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接获取全部数据, 实际数据需要根据size长度来截取
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public byte[] directBytes() {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取byte[]
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return Arrays.copyOf(content, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取byte[]并清空
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public byte[] getBytesAndClear() {
|
||||
byte[] bs = Arrays.copyOf(content, count);
|
||||
clear();
|
||||
return bs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定值第一次出现的位置,没有返回-1
|
||||
*
|
||||
* @param value 查询值
|
||||
*
|
||||
* @return 所在位置
|
||||
*/
|
||||
public int find(byte value) {
|
||||
return find(0, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定的起始位置查询value值出现的位置,没有返回-1
|
||||
*
|
||||
* @param offset 起始位置
|
||||
* @param value 查询值
|
||||
*
|
||||
* @return 所在位置
|
||||
*/
|
||||
public int find(int offset, char value) {
|
||||
return find(offset, (byte) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定的起始位置查询value值出现的位置,没有返回-1
|
||||
*
|
||||
* @param offset 起始位置
|
||||
* @param value 查询值
|
||||
*
|
||||
* @return 所在位置
|
||||
*/
|
||||
public int find(int offset, byte value) {
|
||||
return find(offset, -1, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定的起始位置和长度查询value值出现的位置,没有返回-1
|
||||
*
|
||||
* @param offset 起始位置
|
||||
* @param limit 长度限制
|
||||
* @param value 查询值
|
||||
*
|
||||
* @return 所在位置
|
||||
*/
|
||||
public int find(int offset, int limit, char value) {
|
||||
return find(offset, limit, (byte) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定的起始位置和长度查询value值出现的位置,没有返回-1
|
||||
*
|
||||
* @param offset 起始位置
|
||||
* @param limit 长度限制
|
||||
* @param value 查询值
|
||||
*
|
||||
* @return 所在位置
|
||||
*/
|
||||
public int find(int offset, int limit, byte value) {
|
||||
byte[] bytes = this.content;
|
||||
int end = limit > 0 ? limit : count;
|
||||
@@ -116,14 +219,30 @@ public final class ByteArray {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除最后一个字节
|
||||
*/
|
||||
public void removeLastByte() {
|
||||
if (count > 0) count--;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入一个int值
|
||||
*
|
||||
* @param value int值
|
||||
*/
|
||||
public void writeInt(int value) {
|
||||
write((byte) (value >> 24 & 0xFF), (byte) (value >> 16 & 0xFF), (byte) (value >> 8 & 0xFF), (byte) (value & 0xFF));
|
||||
write((byte) (value >> 24 & 0xFF),
|
||||
(byte) (value >> 16 & 0xFF),
|
||||
(byte) (value >> 8 & 0xFF),
|
||||
(byte) (value & 0xFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入一个byte值
|
||||
*
|
||||
* @param value byte值
|
||||
*/
|
||||
public void write(byte value) {
|
||||
if (count >= content.length - 1) {
|
||||
byte[] ns = new byte[content.length + 8];
|
||||
@@ -133,6 +252,11 @@ public final class ByteArray {
|
||||
content[count++] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入一组byte值
|
||||
*
|
||||
* @param values 一组byte值
|
||||
*/
|
||||
public void write(byte... values) {
|
||||
if (count >= content.length - values.length) {
|
||||
byte[] ns = new byte[content.length + values.length];
|
||||
@@ -143,6 +267,12 @@ public final class ByteArray {
|
||||
count += values.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入ByteBuffer指定长度的数据
|
||||
*
|
||||
* @param buffer
|
||||
* @param len
|
||||
*/
|
||||
public void write(ByteBuffer buffer, int len) {
|
||||
if (len < 1) return;
|
||||
if (count >= content.length - len) {
|
||||
@@ -159,21 +289,53 @@ public final class ByteArray {
|
||||
return new String(content, 0, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定字符集转成字符串
|
||||
*
|
||||
* @param charset 字符集
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String toString(final Charset charset) {
|
||||
return toString(0, count, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定字符集转成字符串并清空数据
|
||||
*
|
||||
* @param charset 字符集
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String toStringAndClear(final Charset charset) {
|
||||
String str = toString(0, count, charset);
|
||||
clear();
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定的起始位置和长度按指定字符集转成字符串
|
||||
*
|
||||
* @param offset 起始位置
|
||||
* @param len 长度
|
||||
* @param charset 字符集
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String toString(final int offset, int len, final Charset charset) {
|
||||
if (charset == null) return new String(Utility.decodeUTF8(content, offset, len));
|
||||
return new String(content, offset, len, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定的起始位置和长度按指定字符集并转义后转成字符串
|
||||
*
|
||||
* @param offset 起始位置
|
||||
* @param len 长度
|
||||
* @param charset 字符集
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String toDecodeString(final int offset, int len, final Charset charset) {
|
||||
int index = offset;
|
||||
for (int i = offset; i < (offset + len); i++) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.function.Predicate;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -41,7 +42,7 @@ public interface Reproduce<D, S> {
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||
FieldVisitor fv;
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
@@ -16,7 +16,6 @@ import javax.annotation.Resource;
|
||||
/**
|
||||
* 如果Resource(name = "$") 表示资源name采用所属对象的name
|
||||
* <p>
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -176,7 +175,7 @@ public final class ResourceFactory {
|
||||
if (re == null) {
|
||||
map.put(name, new ResourceEntry(rs));
|
||||
} else {
|
||||
map.put(name, new ResourceEntry(rs, re.elements, autoSync));
|
||||
map.put(name, new ResourceEntry(rs, name, re.elements, autoSync));
|
||||
}
|
||||
return re == null ? null : (A) re.value;
|
||||
}
|
||||
@@ -373,16 +372,26 @@ public final class ResourceFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceLoader findLoader(Type ft, Field field) {
|
||||
private ResourceLoader findMatchLoader(Type ft, Field field) {
|
||||
ResourceLoader it = this.loadermap.get(ft);
|
||||
if (it == null) it = this.loadermap.get(field.getType());
|
||||
if (it != null) return it;
|
||||
return parent == null ? null : parent.findMatchLoader(ft, field);
|
||||
}
|
||||
|
||||
private ResourceLoader findRegxLoader(Type ft, Field field) {
|
||||
Class c = field.getType();
|
||||
for (Map.Entry<Type, ResourceLoader> en : this.loadermap.entrySet()) {
|
||||
Type t = en.getKey();
|
||||
if (t == ft) return en.getValue();
|
||||
if (t instanceof Class && (((Class) t)).isAssignableFrom(c)) return en.getValue();
|
||||
}
|
||||
return parent == null ? null : parent.findLoader(ft, field);
|
||||
return parent == null ? null : parent.findRegxLoader(ft, field);
|
||||
}
|
||||
|
||||
private ResourceLoader findLoader(Type ft, Field field) {
|
||||
ResourceLoader it = this.findMatchLoader(ft, field);
|
||||
return it == null ? findRegxLoader(ft, field) : it;
|
||||
}
|
||||
|
||||
private static class ResourceEntry<T> {
|
||||
@@ -396,7 +405,7 @@ public final class ResourceFactory {
|
||||
this.elements = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
public ResourceEntry(T value, final List<ResourceElement> elements, boolean sync) {
|
||||
public ResourceEntry(T value, final String name, final List<ResourceElement> elements, boolean sync) {
|
||||
this.value = value;
|
||||
this.elements = elements == null ? new CopyOnWriteArrayList<>() : elements;
|
||||
if (sync && elements != null && !elements.isEmpty()) {
|
||||
@@ -424,11 +433,26 @@ public final class ResourceFactory {
|
||||
}
|
||||
}
|
||||
if (rs == null && classtype.isPrimitive()) rs = Array.get(Array.newInstance(classtype, 1), 0);
|
||||
Object oldVal = null;
|
||||
if (element.listener != null) {
|
||||
try {
|
||||
oldVal = element.field.get(dest);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
try {
|
||||
element.field.set(dest, rs);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (element.listener != null) {
|
||||
try {
|
||||
element.listener.invoke(dest, name, rs, oldVal);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,16 +460,41 @@ public final class ResourceFactory {
|
||||
|
||||
private static class ResourceElement<T> {
|
||||
|
||||
private static final HashMap<Class, Method> listenerMethods = new HashMap<>(); //不使用ConcurrentHashMap是因为value不能存null
|
||||
|
||||
public final WeakReference<T> dest;
|
||||
|
||||
public final Field field;
|
||||
public final Field field; //Resource 字段
|
||||
|
||||
public final Class fieldType;
|
||||
|
||||
public final Method listener;
|
||||
|
||||
public ResourceElement(T dest, Field field) {
|
||||
this.dest = new WeakReference(dest);
|
||||
this.field = field;
|
||||
this.fieldType = field.getType();
|
||||
Class t = dest.getClass();
|
||||
String tn = t.getName();
|
||||
this.listener = tn.startsWith("java.") || tn.startsWith("javax.") ? null : findListener(t);
|
||||
}
|
||||
|
||||
private static synchronized Method findListener(Class clazz) {
|
||||
Class loop = clazz;
|
||||
Method m = null;
|
||||
do {
|
||||
for (Method method : loop.getDeclaredMethods()) {
|
||||
if (method.getAnnotation(ResourceListener.class) != null
|
||||
&& method.getParameterCount() == 3
|
||||
&& String.class.isAssignableFrom(method.getParameterTypes()[0])) {
|
||||
m = method;
|
||||
m.setAccessible(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while ((loop = loop.getSuperclass()) != Object.class);
|
||||
listenerMethods.put(clazz, m);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
52
src/org/redkale/util/ResourceListener.java
Normal file
52
src/org/redkale/util/ResourceListener.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.util;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* @Resource资源被更新时的监听事件。本注解只能标记在方法参数为(String name, T newVal, T oldVal)上。
|
||||
* 方法在资源被更新以后调用。
|
||||
*
|
||||
* <blockquote><pre>
|
||||
* public class Record {
|
||||
*
|
||||
* @Resource(name = "record.id")
|
||||
* private int id;
|
||||
*
|
||||
* @Resource(name = "record.name")
|
||||
* private String name;
|
||||
*
|
||||
* @ResourceListener
|
||||
* private void changeResource(String name, Object newVal, Object oldVal) {
|
||||
* System.out.println("@Resource = " + name + " 资源变更: newVal = " + newVal + ", oldVal = " + oldVal);
|
||||
* }
|
||||
*
|
||||
* public static void main(String[] args) throws Exception {
|
||||
* ResourceFactory factory = ResourceFactory.root();
|
||||
* factory.register("record.id", "2345");
|
||||
* factory.register("record.name", "my old name");
|
||||
* Record record = new Record();
|
||||
* factory.inject(record);
|
||||
* factory.register("record.name", "my new name");
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
public @interface ResourceListener {
|
||||
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import java.util.stream.*;
|
||||
/**
|
||||
* 页集合。 结构由一个total总数和一个List列表组合而成。
|
||||
* <p>
|
||||
* <p>
|
||||
* 详情见: http://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
@@ -42,6 +41,10 @@ public class Sheet<T> implements java.io.Serializable, Iterable<T> {
|
||||
return data == null ? new Sheet() : new Sheet(data.size(), data);
|
||||
}
|
||||
|
||||
public static <E> Sheet<E> empty() {
|
||||
return new Sheet<>();
|
||||
}
|
||||
|
||||
public Sheet<T> copyTo(Sheet<T> copy) {
|
||||
if (copy == null) return copy;
|
||||
copy.total = this.total;
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.lang.reflect.Type;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
@@ -139,7 +140,7 @@ public abstract class TypeToken<T> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||
FieldVisitor fv;
|
||||
MethodVisitor mv;
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, "java/lang/Object", null);
|
||||
|
||||
@@ -38,6 +38,8 @@ public final class Utility {
|
||||
|
||||
private static final javax.net.ssl.SSLContext DEFAULTSSL_CONTEXT;
|
||||
|
||||
private static final javax.net.ssl.HostnameVerifier defaultVerifier = (s, ss) -> true;
|
||||
|
||||
static {
|
||||
sun.misc.Unsafe usafe = null;
|
||||
long fd1 = 0L;
|
||||
@@ -103,7 +105,7 @@ public final class Utility {
|
||||
int pos = buffer.position();
|
||||
int limit = buffer.limit();
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
buffer.get(bytes);
|
||||
buffer.get(bytes);
|
||||
buffer.position(pos);
|
||||
buffer.limit(limit);
|
||||
println(string, bytes);
|
||||
@@ -164,6 +166,7 @@ public final class Utility {
|
||||
* 获取指定时间当天凌晨零点的格林时间
|
||||
*
|
||||
* @param time 指定时间
|
||||
*
|
||||
* @return 毫秒数
|
||||
*/
|
||||
public static long midnight(long time) {
|
||||
@@ -184,6 +187,7 @@ public final class Utility {
|
||||
* 获取时间点所在星期的周一
|
||||
*
|
||||
* @param time 指定时间
|
||||
*
|
||||
* @return 毫秒数
|
||||
*/
|
||||
public static long monday(long time) {
|
||||
@@ -198,6 +202,7 @@ public final class Utility {
|
||||
* 获取时间点所在星期的周日
|
||||
*
|
||||
* @param time 指定时间
|
||||
*
|
||||
* @return 毫秒数
|
||||
*/
|
||||
public static long sunday(long time) {
|
||||
@@ -212,6 +217,7 @@ public final class Utility {
|
||||
* 获取时间点所在月份的1号
|
||||
*
|
||||
* @param time 指定时间
|
||||
*
|
||||
* @return 毫秒数
|
||||
*/
|
||||
public static long monthFirstDay(long time) {
|
||||
@@ -418,7 +424,8 @@ public final class Utility {
|
||||
* 将两个数字组装成一个long
|
||||
*
|
||||
* @param high 高位值
|
||||
* @param low 低位值
|
||||
* @param low 低位值
|
||||
*
|
||||
* @return long值
|
||||
*/
|
||||
public static long merge(int high, int low) {
|
||||
@@ -495,6 +502,30 @@ public final class Utility {
|
||||
return remoteHttpContent(ctx, "POST", url, headers, body).toString("UTF-8");
|
||||
}
|
||||
|
||||
public static String postHttpContent(String url, Charset charset) throws IOException {
|
||||
return remoteHttpContent(null, "POST", url, null, null).toString(charset.name());
|
||||
}
|
||||
|
||||
public static String postHttpContent(String url, Charset charset, String body) throws IOException {
|
||||
return remoteHttpContent(null, "POST", url, null, body).toString(charset.name());
|
||||
}
|
||||
|
||||
public static String postHttpContent(String url, Charset charset, Map<String, String> headers, String body) throws IOException {
|
||||
return remoteHttpContent(null, "POST", url, headers, body).toString(charset.name());
|
||||
}
|
||||
|
||||
public static String postHttpContent(SSLContext ctx, String url, Charset charset) throws IOException {
|
||||
return remoteHttpContent(ctx, "POST", url, null, null).toString(charset.name());
|
||||
}
|
||||
|
||||
public static String postHttpContent(SSLContext ctx, String url, Charset charset, String body) throws IOException {
|
||||
return remoteHttpContent(ctx, "POST", url, null, body).toString(charset.name());
|
||||
}
|
||||
|
||||
public static String postHttpContent(SSLContext ctx, String url, Charset charset, Map<String, String> headers, String body) throws IOException {
|
||||
return remoteHttpContent(ctx, "POST", url, headers, body).toString(charset.name());
|
||||
}
|
||||
|
||||
public static byte[] postHttpBytesContent(String url) throws IOException {
|
||||
return remoteHttpContent(null, "POST", url, null, null).toByteArray();
|
||||
}
|
||||
@@ -527,6 +558,22 @@ public final class Utility {
|
||||
return remoteHttpContent(null, "GET", url, headers, body).toString("UTF-8");
|
||||
}
|
||||
|
||||
public static String getHttpContent(String url, Charset charset) throws IOException {
|
||||
return remoteHttpContent(null, "GET", url, null, null).toString(charset.name());
|
||||
}
|
||||
|
||||
public static String getHttpContent(SSLContext ctx, String url, Charset charset) throws IOException {
|
||||
return remoteHttpContent(ctx, "GET", url, null, null).toString(charset.name());
|
||||
}
|
||||
|
||||
public static String getHttpContent(SSLContext ctx, String url, Charset charset, Map<String, String> headers, String body) throws IOException {
|
||||
return remoteHttpContent(ctx, "GET", url, headers, body).toString(charset.name());
|
||||
}
|
||||
|
||||
public static String getHttpContent(String url, Charset charset, Map<String, String> headers, String body) throws IOException {
|
||||
return remoteHttpContent(null, "GET", url, headers, body).toString(charset.name());
|
||||
}
|
||||
|
||||
public static byte[] getHttpBytesContent(String url) throws IOException {
|
||||
return remoteHttpContent(null, "GET", url, null, null).toByteArray();
|
||||
}
|
||||
@@ -543,11 +590,19 @@ public final class Utility {
|
||||
return remoteHttpContent(ctx, "GET", url, headers, body).toByteArray();
|
||||
}
|
||||
|
||||
protected static ByteArrayOutputStream remoteHttpContent(SSLContext ctx, String method, String url, Map<String, String> headers, String body) throws IOException {
|
||||
public static ByteArrayOutputStream remoteHttpContent(String method, String url, Map<String, String> headers, String body) throws IOException {
|
||||
return remoteHttpContent(null, method, url, headers, body);
|
||||
}
|
||||
|
||||
public static ByteArrayOutputStream remoteHttpContent(SSLContext ctx, String method, String url, Map<String, String> headers, String body) throws IOException {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setConnectTimeout(3000);
|
||||
conn.setReadTimeout(3000);
|
||||
if (conn instanceof HttpsURLConnection) ((HttpsURLConnection) conn).setSSLSocketFactory((ctx == null ? DEFAULTSSL_CONTEXT : ctx).getSocketFactory());
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
HttpsURLConnection httpsconn = ((HttpsURLConnection) conn);
|
||||
httpsconn.setSSLSocketFactory((ctx == null ? DEFAULTSSL_CONTEXT : ctx).getSocketFactory());
|
||||
httpsconn.setHostnameVerifier(defaultVerifier);
|
||||
}
|
||||
conn.setRequestMethod(method);
|
||||
if (headers != null) {
|
||||
for (Map.Entry<String, String> en : headers.entrySet()) { //不用forEach是为了兼容JDK 6
|
||||
@@ -566,7 +621,7 @@ public final class Utility {
|
||||
conn.disconnect();
|
||||
return remoteHttpContent(ctx, method, newurl, headers, body);
|
||||
}
|
||||
InputStream in = conn.getInputStream();
|
||||
InputStream in = rs < 400 ? conn.getInputStream() : conn.getErrorStream();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
|
||||
byte[] bytes = new byte[1024];
|
||||
int pos;
|
||||
|
||||
@@ -20,11 +20,11 @@ public class FilterNodeTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final Properties props = new Properties();
|
||||
final Function<Class, List> fullloader = (Class t) -> new ArrayList();
|
||||
final Function<Class, EntityInfo> func = (Class t) -> EntityInfo.load(t, 0, false, props, fullloader);
|
||||
final EntityInfo<CarTestTable> carEntity = EntityInfo.load(CarTestTable.class, 0, false, props, (t) -> CarTestTable.createList());
|
||||
final EntityInfo<UserTestTable> userEntity = EntityInfo.load(UserTestTable.class, 0, false, props, (t) -> UserTestTable.createList());
|
||||
final EntityInfo<CarTypeTestTable> typeEntity = EntityInfo.load(CarTypeTestTable.class, 0, false, props, (t) -> CarTypeTestTable.createList());
|
||||
final BiFunction<DataSource, Class, List> fullloader = (s, t) -> new ArrayList();
|
||||
final Function<Class, EntityInfo> func = (Class t) -> EntityInfo.load(t, 0, false, props, null, fullloader);
|
||||
final EntityInfo<CarTestTable> carEntity = EntityInfo.load(CarTestTable.class, 0, false, props, null, (s, t) -> CarTestTable.createList());
|
||||
final EntityInfo<UserTestTable> userEntity = EntityInfo.load(UserTestTable.class, 0, false, props, null, (s, t) -> UserTestTable.createList());
|
||||
final EntityInfo<CarTypeTestTable> typeEntity = EntityInfo.load(CarTypeTestTable.class, 0, false, props, null, (s, t) -> CarTypeTestTable.createList());
|
||||
|
||||
final CarTestBean bean = new CarTestBean();
|
||||
bean.carid = 70002;
|
||||
@@ -32,7 +32,7 @@ public class FilterNodeTest {
|
||||
bean.createtime = 500;
|
||||
bean.typename = "法拉利";
|
||||
FilterNode joinNode1 = FilterJoinNode.create(UserTestTable.class, new String[]{"userid", "username"}, "username", LIKE, bean.username)
|
||||
.or(FilterJoinNode.create(UserTestTable.class, new String[]{"userid", "username"}, "createtime", GREATERTHAN, bean.createtime));
|
||||
.or(FilterJoinNode.create(UserTestTable.class, new String[]{"userid", "username"}, "createtime", GREATERTHAN, bean.createtime));
|
||||
FilterNode joinNode2 = FilterJoinNode.create(CarTypeTestTable.class, "cartype", "typename", LIKE, bean.typename);
|
||||
FilterNode node = CarTestBean.caridTransient() ? (joinNode2.or(joinNode1)) : FilterNode.create("carid", GREATERTHAN, bean.carid).and(joinNode1).or(joinNode2);
|
||||
FilterNode beanNode = FilterNodeBean.createFilterNode(bean);
|
||||
|
||||
@@ -88,9 +88,9 @@ public interface HttpRequestDesc {
|
||||
public long getRequstURIPath(String prefix, long defaultValue);
|
||||
|
||||
//获取请求URL分段中含prefix段的int值
|
||||
// 例如请求URL /pipes/record/query/page:2/size:50
|
||||
// 获取page参数: int page = request.getRequstURIPath("page:", 1);
|
||||
// 获取size参数: int size = request.getRequstURIPath("size:", 20);
|
||||
// 例如请求URL /pipes/record/query/offset:2/limit:50
|
||||
// 获取page参数: int offset = request.getRequstURIPath("offset:", 1);
|
||||
// 获取size参数: int limit = request.getRequstURIPath("limit:", 20);
|
||||
public int getRequstURIPath(String prefix, int defaultValue);
|
||||
|
||||
//获取请求URL分段中含prefix段的值
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
package org.redkale.test.source;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.BiFunction;
|
||||
import javax.persistence.Id;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.source.*;
|
||||
import org.redkale.util.Attribute;
|
||||
|
||||
@@ -33,8 +34,8 @@ public class CacheTestBean {
|
||||
Attribute idattr = Attribute.create(CacheTestBean.class, "pkgid");
|
||||
Attribute nameattr = Attribute.create(CacheTestBean.class, "name");
|
||||
Attribute priceattr = Attribute.create(CacheTestBean.class, "price");
|
||||
Function<Class, List> fullloader = (z) -> list;
|
||||
EntityCache<CacheTestBean> cache = new EntityCache(EntityInfo.load(CacheTestBean.class, 0, true,new Properties(), fullloader));
|
||||
BiFunction<DataSource, Class, List> fullloader = (s, z) -> list;
|
||||
EntityCache<CacheTestBean> cache = new EntityCache(EntityInfo.load(CacheTestBean.class, 0, true, new Properties(), null, fullloader));
|
||||
cache.fullLoad();
|
||||
|
||||
System.out.println(cache.queryColumnMap("pkgid", FilterFunc.COUNT, "name", null));
|
||||
@@ -43,6 +44,10 @@ public class CacheTestBean {
|
||||
System.out.println(cache.queryColumnMap("pkgid", FilterFunc.SUM, "price", null));
|
||||
System.out.println(cache.queryColumnMap("pkgid", FilterFunc.MAX, "price", null));
|
||||
System.out.println(cache.queryColumnMap("pkgid", FilterFunc.MIN, "price", null));
|
||||
|
||||
System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.EQUAL, "BB")));
|
||||
System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.IGNORECASEEQUAL, "BB")));
|
||||
System.out.println(cache.querySheet(null, null, FilterNode.create("name", FilterExpress.IGNORECASENOTLIKE, "B")));
|
||||
}
|
||||
|
||||
public CacheTestBean() {
|
||||
@@ -78,4 +83,9 @@ public class CacheTestBean {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class TestSourceCache {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final EntityInfo<TestEntity> info = EntityInfo.load(TestEntity.class, 0, false,new Properties(), null);
|
||||
final EntityInfo<TestEntity> info = EntityInfo.load(TestEntity.class, 0, false, new Properties(), null, null);
|
||||
TestEntity[] entitys = new TestEntity[10_0000];
|
||||
for (int i = 0; i < entitys.length; i++) {
|
||||
entitys[i] = new TestEntity(i + 1, "用户_" + (i + 1));
|
||||
|
||||
@@ -15,35 +15,35 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public class ResourceTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ResourceFactory factory = ResourceFactory.root();
|
||||
factory.register("property.id", "2345"); //注入String类型的property.id
|
||||
AService aservice = new AService();
|
||||
BService bservice = new BService("eeeee");
|
||||
public static void main(String[] args) throws Exception {
|
||||
ResourceFactory factory = ResourceFactory.root();
|
||||
factory.register("property.id", "2345"); //注入String类型的property.id
|
||||
AService aservice = new AService();
|
||||
BService bservice = new BService("eeeee");
|
||||
|
||||
factory.register(aservice); //放进Resource池内,默认的资源名name为""
|
||||
factory.register(bservice); //放进Resource池内,默认的资源名name为""
|
||||
factory.register(aservice); //放进Resource池内,默认的资源名name为""
|
||||
factory.register(bservice); //放进Resource池内,默认的资源名name为""
|
||||
|
||||
factory.inject(aservice); //给aservice注入id、bservice,bigint没有资源,所以为null
|
||||
factory.inject(bservice); //给bservice注入id、aservice
|
||||
System.out.println(aservice); //输出结果为:{id:"2345", intid: 2345, bigint:null, bservice:{name:eeeee}}
|
||||
System.out.println(bservice); //输出结果为:{name:"eeeee", id: 2345, aserivce:{id:"2345", intid: 2345, bigint:null, bservice:{name:eeeee}}}
|
||||
factory.inject(aservice); //给aservice注入id、bservice,bigint没有资源,所以为null
|
||||
factory.inject(bservice); //给bservice注入id、aservice
|
||||
System.out.println(aservice); //输出结果为:{id:"2345", intid: 2345, bigint:null, bservice:{name:eeeee}}
|
||||
System.out.println(bservice); //输出结果为:{name:"eeeee", id: 2345, aserivce:{id:"2345", intid: 2345, bigint:null, bservice:{name:eeeee}}}
|
||||
|
||||
factory.register("seqid", 200); //放进Resource池内, 同时ResourceFactory会自动更新aservice的seqid值
|
||||
System.out.println(factory.find("seqid", int.class)); //输出结果为:200
|
||||
factory.register("bigint", new BigInteger("666666666666666")); //放进Resource池内, 同时ResourceFactory会自动更新aservice对象的bigint值
|
||||
System.out.println(aservice); //输出结果为:{id:"2345", intid: 2345, bigint:666666666666666, bservice:{name:eeeee}} 可以看出seqid与bigint值都已自动更新
|
||||
factory.register("seqid", 200); //放进Resource池内, 同时ResourceFactory会自动更新aservice的seqid值
|
||||
System.out.println(factory.find("seqid", int.class)); //输出结果为:200
|
||||
factory.register("bigint", new BigInteger("666666666666666")); //放进Resource池内, 同时ResourceFactory会自动更新aservice对象的bigint值
|
||||
System.out.println(aservice); //输出结果为:{id:"2345", intid: 2345, bigint:666666666666666, bservice:{name:eeeee}} 可以看出seqid与bigint值都已自动更新
|
||||
|
||||
factory.register("property.id", "6789"); //更新Resource池内的id资源值, 同时ResourceFactory会自动更新aservice、bservice的id值
|
||||
System.out.println(aservice); //输出结果为:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:eeeee}}
|
||||
System.out.println(bservice); //输出结果为:{name:"eeeee", id: 6789, aserivce:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:eeeee}}}
|
||||
factory.register("property.id", "6789"); //更新Resource池内的id资源值, 同时ResourceFactory会自动更新aservice、bservice的id值
|
||||
System.out.println(aservice); //输出结果为:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:eeeee}}
|
||||
System.out.println(bservice); //输出结果为:{name:"eeeee", id: 6789, aserivce:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:eeeee}}}
|
||||
|
||||
bservice = new BService("ffff");
|
||||
factory.register(bservice); //更新Resource池内name=""的BService资源, 同时ResourceFactory会自动更新aservice的bservice对象
|
||||
factory.inject(bservice);
|
||||
System.out.println(aservice); //输出结果为:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:ffff}}
|
||||
bservice = new BService("ffff");
|
||||
factory.register(bservice); //更新Resource池内name=""的BService资源, 同时ResourceFactory会自动更新aservice的bservice对象
|
||||
factory.inject(bservice);
|
||||
System.out.println(aservice); //输出结果为:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:ffff}}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,6 +57,11 @@ class BService {
|
||||
|
||||
private String name = "";
|
||||
|
||||
@ResourceListener
|
||||
private void changeResource(String name, Object newVal, Object oldVal) {
|
||||
System.out.println("@Resource = " + name + " 资源变更: newVal = " + newVal + ", oldVal = " + oldVal);
|
||||
}
|
||||
|
||||
@java.beans.ConstructorProperties({"name"})
|
||||
public BService(String name) {
|
||||
this.name = name;
|
||||
|
||||
Reference in New Issue
Block a user