96 Commits
1.0.0 ... 1.1.0

Author SHA1 Message Date
Redkale
bb77681445 2016-08-24 16:08:12 +08:00
Redkale
d2a3291f13 删除@DistributeTables功能 2016-08-24 15:46:29 +08:00
Redkale
116e82115b 2016-08-24 14:00:57 +08:00
Redkale
e0b5067949 2016-08-24 13:58:31 +08:00
Redkale
8e639e32a3 2016-08-24 13:55:44 +08:00
Redkale
5a1586bc6a 2016-08-24 13:48:52 +08:00
Redkale
30b6ddc4f4 2016-08-24 08:00:32 +08:00
Redkale
0667c6a12e 2016-08-24 07:11:30 +08:00
Redkale
6eb9429b68 2016-08-23 14:06:25 +08:00
Redkale
91f32684ef 2016-08-23 13:28:01 +08:00
Redkale
fc475d6253 2016-08-20 17:38:23 +08:00
Redkale
3df13a3b17 2016-08-20 12:34:55 +08:00
Redkale
3d0bd3a5c2 2016-08-20 12:33:23 +08:00
Redkale
42c188867a 2016-08-18 14:21:23 +08:00
Redkale
a3647f79ac 2016-08-16 20:11:40 +08:00
Redkale
f83fded877 2016-08-16 20:00:34 +08:00
Redkale
df42be86d6 2016-08-16 19:42:17 +08:00
Redkale
d22b44f1f9 2016-08-16 19:32:07 +08:00
Redkale
1592938d99 2016-08-16 19:04:02 +08:00
Redkale
a3681ca698 2016-08-15 11:32:12 +08:00
Redkale
5f1556e5cb 2016-08-15 11:20:33 +08:00
Redkale
64bc543df2 2016-08-14 16:14:51 +08:00
Redkale
36f03344bd 2016-08-10 15:17:50 +08:00
Redkale
d764b741d4 2016-08-08 11:54:22 +08:00
Redkale
e5a83c51f3 2016-08-08 11:01:37 +08:00
Redkale
42e7ac298e 2016-08-08 10:48:46 +08:00
Redkale
bf9098df86 2016-08-01 21:58:27 +08:00
Redkale
d44e45166e 2016-08-01 20:39:17 +08:00
Redkale
07304e61d9 2016-07-31 22:08:11 +08:00
Redkale
c69fcc25db 2016-07-30 22:06:36 +08:00
Redkale
72e25d653b 2016-07-27 16:51:46 +08:00
Redkale
c3fde23d77 2016-07-26 11:21:03 +08:00
Redkale
1369bd3e9e 2016-07-26 10:47:47 +08:00
Redkale
2c70162798 2016-07-26 10:38:14 +08:00
Redkale
5db8426553 2016-07-26 10:36:22 +08:00
Redkale
3c667d88aa 2016-07-26 09:58:37 +08:00
Redkale
7a11b7887c 2016-07-26 09:43:25 +08:00
Redkale
bfbc48a597 2016-07-25 19:21:18 +08:00
Redkale
ba14c59baa 2016-07-25 17:56:40 +08:00
Redkale
63d7e85fb4 2016-07-25 17:48:58 +08:00
Redkale
66c2f0970f 2016-07-25 17:38:00 +08:00
Redkale
e78d590fc2 2016-07-25 17:36:00 +08:00
Redkale
8f7ca376a9 2016-07-25 17:07:26 +08:00
Redkale
82b041e6d5 2016-07-24 12:15:44 +08:00
Redkale
5ae6afa20e 2016-07-24 12:05:01 +08:00
Redkale
1aeae47a89 2016-07-24 11:57:16 +08:00
Redkale
7bf73b60b5 2016-07-24 11:47:36 +08:00
Redkale
7e26ddddd5 2016-07-23 10:28:13 +08:00
Redkale
593ed3e7e0 2016-07-23 10:14:20 +08:00
Redkale
c0ff67df73 2016-07-23 09:55:57 +08:00
Redkale
5489e01987 2016-07-23 09:44:58 +08:00
Redkale
65910f8b66 2016-07-23 09:26:20 +08:00
Redkale
ea277f4ea3 2016-07-22 16:15:50 +08:00
Redkale
02fc248fcf 2016-07-22 11:41:59 +08:00
Redkale
1b42a4241a 2016-07-21 15:39:32 +08:00
Redkale
7ed0020b1d 2016-07-18 21:15:55 +08:00
Redkale
94bb11329d 2016-07-18 21:14:56 +08:00
Redkale
fccf314282 2016-07-18 21:09:47 +08:00
Redkale
35029ad796 2016-07-18 21:06:19 +08:00
Redkale
ea059594f0 2016-07-18 21:05:48 +08:00
Redkale
839c742423 2016-07-12 23:44:41 +08:00
Redkale
00b6910986 2016-07-12 14:08:14 +08:00
Redkale
01da67dbf8 2016-07-11 14:13:27 +08:00
Redkale
73a7864e09 2016-07-08 21:41:15 +08:00
Redkale
a996ab5eb5 2016-07-07 09:46:33 +08:00
Redkale
0d6a778d9c 2016-07-06 22:45:11 +08:00
Redkale
34ddd0d65e 2016-07-06 22:40:24 +08:00
Redkale
4f950d5874 2016-07-06 22:05:39 +08:00
Redkale
6b228f1fc9 2016-07-06 22:04:32 +08:00
Redkale
521a0d4cbb Update README.md 2016-07-05 22:31:11 +08:00
Redkale
9ba2571ed1 2016-07-05 13:57:32 +08:00
Redkale
cfcdb093e7 2016-07-05 09:03:41 +08:00
Redkale
9c3c7e414a 2016-07-03 14:02:42 +08:00
Redkale
a7781322c7 2016-07-03 12:27:05 +08:00
Redkale
34a0fef035 2016-07-03 12:04:31 +08:00
Redkale
4dfda3ec51 2016-07-03 12:01:13 +08:00
Redkale
fd0a6b94e9 2016-07-03 09:40:24 +08:00
Redkale
1af2457cc7 2016-07-03 09:33:26 +08:00
Redkale
f4bd6074f1 2016-07-02 21:27:52 +08:00
Redkale
5b6e01ecb8 2016-07-02 20:48:59 +08:00
Redkale
85844276ed 2016-07-02 19:15:33 +08:00
Redkale
af74bad593 2016-07-02 17:47:43 +08:00
Redkale
f82becfe62 2016-07-02 16:58:19 +08:00
Redkale
a98d5f32a1 2016-07-02 16:41:58 +08:00
Redkale
a2d4b6ec37 2016-07-02 10:49:11 +08:00
RedKale
29a80e95c7 2016-06-29 17:43:26 +08:00
RedKale
41c1ea32d1 将Flipper中的page页号换成start记录号,将影响到HttpServlet中findFlipper的实现 2016-06-28 16:24:35 +08:00
RedKale
c100207302 2016-06-27 16:48:15 +08:00
RedKale
5b4b08b6ce 2016-06-27 16:30:19 +08:00
RedKale
a6a3890e70 2016-06-27 16:08:51 +08:00
RedKale
d3d24529a6 2016-06-27 09:28:35 +08:00
RedKale
5593a518fc 2016-06-26 22:25:24 +08:00
RedKale
511a31bf95 2016-06-26 14:08:35 +08:00
RedKale
999f55222b 2016-06-26 11:15:55 +08:00
RedKale
56cd881a99 2016-06-26 10:24:46 +08:00
RedKale
a0439518f3 2016-06-26 10:18:32 +08:00
73 changed files with 1750 additions and 662 deletions

View File

@@ -21,4 +21,4 @@
&nbsp;&nbsp;&nbsp;由于RedKale使用了JDK 8 内置的ASM包所以需要在源码工程中的编译器选项中加入 <b>-XDignore.symbol.file=true</b>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>详情请访问:&nbsp;&nbsp;&nbsp;&nbsp;<a href='http://www.redkale.org' target='_blank'>http://www.redkale.org</a></h5>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>详情请访问:&nbsp;&nbsp;&nbsp;&nbsp;<a href='http://redkale.org' target='_blank'>http://redkale.org</a></h5>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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&lt;FilterEntry&lt;T&gt;&gt;
*/
public final Set<FilterEntry<T>> getFilterExpectEntrys() {
return expectEntrys;
}
/**
* 获取所有的class集合
*
* @return Set&lt;FilterEntry&lt;T&gt;&gt;
*/
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 {

View File

@@ -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());
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -10,7 +10,6 @@ import java.lang.reflect.Type;
/**
* 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入WriterJSON则不写入。
* <p>
* <p>
* 详情见: http://redkale.org
*
* @author zhangjx

View File

@@ -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;

View File

@@ -161,6 +161,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
* 对象格式: [0x1][short字段个数][字段名][字段值]...[0x2]
*
* @param in 输入流
*
* @return 反解析后的对象结果
*/
@Override

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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> {

View File

@@ -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> {

View File

@@ -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;
}

View File

@@ -13,7 +13,6 @@ import org.redkale.util.*;
*
* writeTo系列的方法输出的字符不能含特殊字符
* <p>
* <p>
* 详情见: http://redkale.org
*
* @author zhangjx

View File

@@ -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> {

View File

@@ -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) {

View File

@@ -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 使用。
* 当标记为 &#64;AuthIgnore 的方法不会再调用之前调用authenticate 方法。
* 当标记为 &#64;AuthIgnore 的方法在执行execute之前不会调用authenticate 方法。
*
* <p>
* 详情见: http://redkale.org
@@ -47,7 +51,7 @@ public abstract class BasedHttpServlet extends HttpServlet {
/**
* 配合 BasedHttpServlet 使用。
* 用于对&#64;WebServlet对应的url进行细分。 其 url
* 用于对&#64;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;

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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");

View File

@@ -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()) { //没有上传

View File

@@ -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");

View File

@@ -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();
}

View File

@@ -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 ");

View File

@@ -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>
*
* &#64;MultiRun(selfrun = false)
* public void createSomeThing(TestBean bean){
* //do something
* }
* <p>
*
* &#64;MultiRun
* public String updateSomeThing(String id){
* return "hello" + id;
* }
* }
* </pre></blockquote>
* <p>
*
* <blockquote><pre>
* &#64;Resource(name = "")
* &#64;SncpDyn(remote = false)
* &#64;ResourceType({TestService.class})
* public final class _DynLocalTestService extends TestService{
* <p>
*
* &#64;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>
*
* &#64;Override
* public String toString() {
* return _redkale_selfstring == null ? super.toString() : _redkale_selfstring;
* }
* <p>
*
* &#64;Override
* public void createSomeThing(TestBean bean){
* this._redkale_createSomeThing(false, true, true, bean);
* }
* <p>
*
* &#64;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>
*
* &#64;Override
* public String updateSomeThing(String id){
* return this._redkale_updateSomeThing(true, true, true, id);
* }
* <p>
*
* &#64;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 {
* &#64;SncpDyn(remote = true)
* &#64;ResourceType({TestService.class})
* public final class _DynRemoteTestService extends TestService{
* <p>
*
* &#64;Resource
* private BsonConvert _redkale_convert;
* <p>
*
* private Transport _redkale_transport;
* <p>
*
* private SncpClient _redkale_client;
* <p>
*
* private String _redkale_selfstring;
* <p>
*
* &#64;Override
* public String toString() {
* return _redkale_selfstring == null ? super.toString() : _redkale_selfstring;
* }
* <p>
*
* &#64;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>
*
* &#64;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>
*
* &#64;Override
* public void createSomeThing(TestBean bean){
* _redkale_client.remote(_redkale_convert, _redkale_transport, 2, bean);
* }
* <p>
*
* &#64;Override
* public String findSomeThing(){
* return _redkale_client.remote(_redkale_convert, _redkale_transport, 3);
* }
* <p>
*
* &#64;Override
* public String updateSomeThing(String id){
* return _redkale_client.remote(_redkale_convert, _redkale_transport, 4, id);
* }
* }
* </pre></blockquote>
* <p>
*
* 创建远程模式的Service实例
*
* @param <T> Service泛型

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -11,7 +11,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 本地模式注解。
* 声明为LocalService的Service将不会变成远程模式,只能以本地模式存在, 无论配置文件中是否配置成远程模式都会被忽略。
* 声明为LocalService的Service只能以本地模式存在 即使配置文件中配置成远程模式也将被忽略。
*
* <p> 详情见: http://redkale.org
* @author zhangjx

View File

@@ -0,0 +1,26 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.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();
}

View File

@@ -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);

View File

@@ -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++;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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>
* &#64;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();
}

View File

@@ -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 {

View File

@@ -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);

View File

@@ -7,6 +7,8 @@
package org.redkale.source;
/**
*
* 不被标记为&#64;javax.persistence.Transient 的字段均视为过滤条件
*
* <p> 详情见: http://redkale.org
* @author zhangjx

View File

@@ -10,6 +10,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.*;
/**
* 过滤字段标记
*
* <p>
* 详情见: http://redkale.org

View File

@@ -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

View File

@@ -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 + ")";

View File

@@ -51,7 +51,7 @@ import java.lang.annotation.*;
* </pre></blockquote>
* 转换的SQL语句为: WHERE id = ? AND ((desc LIKE ? AND name LIKE ?) OR (age = ? OR birthday = ?))
* 因为默认是AND关系 &#64;FilterGroup("") 等价于 &#64;FilterGroup("[AND]")
* 所以示例二的&#64;FilterGroup("[OR]g1.[AND]subg1") 可以简化为 &#64;FilterGroup("[OR]g1")
* 所以示例二的&#64;FilterGroup("[OR]g1.[AND]subg1") 可以简化为 &#64;FilterGroup("[OR]g1.subg1")
*/
/**
* <p>

View File

@@ -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]));

View File

@@ -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('\'');

View File

@@ -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(" ?");
}
}

View File

@@ -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) &gt; 0 这些都不是单独一个数值能表达的因此需要FilterValue 才构建 8 、 &gt; 、0 组合值.
*
* <p>
* 详情见: http://redkale.org

View File

@@ -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;
}
}

View File

@@ -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));

View File

@@ -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;
}
}
}

View File

@@ -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, ' ');

View File

@@ -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) {

View File

@@ -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&lt;Record, String&gt; nameAction = new Attribute&lt;Record, String&gt;() {
* <p>
*
* &#64;Override
* public String field() {
* return "name";
* }
* <p>
*
* &#64;Override
* public String get(Record obj) {
* return obj.getName();
* }
* <p>
*
* &#64;Override
* public void set(Record obj, String value) {
* obj.setName(value);
* }
* <p>
*
* &#64;Override
* public Class type() {
* return String.class;
* }
* <p>
*
* &#64;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});

View File

@@ -11,8 +11,8 @@ import java.lang.annotation.*;
/**
* 自动加载。 使用场景:
* 1、被标记为@AutoLoad(false)的Service类不会被自动加载
* 2、被标记为@AutoLoad(false)的Servlet类不会被自动加载
* 1、被标记为&#64;AutoLoad(false)的Service类不会被自动加载, 当被依赖时才会被加载
* 2、被标记为&#64;AutoLoad(false)的Servlet类不会被自动加载
*
* <p> 详情见: http://redkale.org
* @author zhangjx

View File

@@ -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 &lt;= index &lt; 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++) {

View File

@@ -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;

View File

@@ -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;
}
}

View 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;
/**
* &#64;Resource资源被更新时的监听事件。本注解只能标记在方法参数为(String name, T newVal, T oldVal)上。
* 方法在资源被更新以后调用。
*
* <blockquote><pre>
* public class Record {
*
* &#64;Resource(name = "record.id")
* private int id;
*
* &#64;Resource(name = "record.name")
* private String name;
*
* &#64;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 {
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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段的值

View File

@@ -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);
}
}

View File

@@ -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));

View File

@@ -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、bservicebigint没有资源所以为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、bservicebigint没有资源所以为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;