> getFilterEntrys() {
+ return entrys;
+ }
+
+ /**
+ * 自动扫描地过滤指定的class
+ *
+ * @param property
+ * @param clazzname
+ */
+ @SuppressWarnings("unchecked")
+ public final void filter(AnyValue property, String clazzname) {
+ filter(property, clazzname, true);
+ }
+
+ /**
+ * 过滤指定的class
+ *
+ * @param property application.xml中对应class节点下的property属性项
+ * @param clazzname class名称
+ * @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略
+ */
+ public final void filter(AnyValue property, String clazzname, boolean autoscan) {
+ if (!accept(property, clazzname)) return;
+ try {
+ Class clazz = Class.forName(clazzname);
+ if (accept(property, clazz, autoscan)) {
+ FilterEntry en = new FilterEntry(clazz, property);
+ if (!entrys.contains(en)) entrys.add(en);
+ }
+ } catch (Throwable cfe) {
+ }
+ }
+
+ private static Pattern[] toPattern(String[] regs) {
+ if (regs == null) return null;
+ int i = 0;
+ Pattern[] rs = new Pattern[regs.length];
+ for (String reg : regs) {
+ if (reg == null || reg.trim().isEmpty()) continue;
+ rs[i++] = Pattern.compile(reg.trim());
+ }
+ if (i == 0) return null;
+ if (i == rs.length) return rs;
+ Pattern[] ps = new Pattern[i];
+ System.arraycopy(rs, 0, ps, 0, i);
+ return ps;
+ }
+
+ /**
+ * 判断class是否有效
+ *
+ * @param property
+ * @param classname
+ * @return
+ */
+ public boolean accept(AnyValue property, String classname) {
+ if (this.refused) return false;
+ if (classname.startsWith("java.") || classname.startsWith("javax.")) return false;
+ if (excludePatterns != null) {
+ for (Pattern reg : excludePatterns) {
+ if (reg.matcher(classname).matches()) return false;
+ }
+ }
+ if (includePatterns != null) {
+ for (Pattern reg : includePatterns) {
+ if (reg.matcher(classname).matches()) return true;
+ }
+ }
+ return includePatterns == null;
+ }
+
+ /**
+ * 判断class是否有效
+ *
+ * @param property
+ * @param clazz
+ * @param autoscan
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public boolean accept(AnyValue property, Class clazz, boolean autoscan) {
+ if (this.refused || !Modifier.isPublic(clazz.getModifiers())) return false;
+ if (clazz.getAnnotation(Ignore.class) != null) 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));
+ }
+
+ public void setSuperClass(Class superClass) {
+ this.superClass = superClass;
+ }
+
+ public void setAnnotationClass(Class extends Annotation> annotationClass) {
+ this.annotationClass = annotationClass;
+ }
+
+ public Pattern[] getIncludePatterns() {
+ return includePatterns;
+ }
+
+ public void setIncludePatterns(String[] includePatterns) {
+ this.includePatterns = toPattern(includePatterns);
+ }
+
+ public Pattern[] getExcludePatterns() {
+ return excludePatterns;
+ }
+
+ public void setExcludePatterns(String[] excludePatterns) {
+ this.excludePatterns = toPattern(excludePatterns);
+ }
+
+ public Class extends Annotation> getAnnotationClass() {
+ return annotationClass;
+ }
+
+ public Class getSuperClass() {
+ return superClass;
+ }
+
+ public boolean isRefused() {
+ return refused;
+ }
+
+ public void setRefused(boolean refused) {
+ this.refused = refused;
+ }
+
+ /**
+ * 存放符合条件的class与class指定的属性项
+ *
+ * @param
+ */
+ public static final class FilterEntry {
+
+ private final String name;
+
+ private final Class type;
+
+ private final AnyValue property;
+
+ protected Object attachment;
+
+ public FilterEntry(Class type, AnyValue property) {
+ this.type = type;
+ this.property = property;
+ this.name = property == null ? "" : property.getValue("name", "");
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName()
+ + ", type=" + this.type.getSimpleName() + ", name=" + name
+ + ", remote=" + (property == null ? "null" : property.getValue("remote")) + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return this.type.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ return (this.type == ((FilterEntry>) obj).type && this.name.equals(((FilterEntry>) obj).name));
+ }
+
+ public Class getType() {
+ return type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public AnyValue getProperty() {
+ return property;
+ }
+
+ public Object getAttachment() {
+ return attachment;
+ }
+
+ public void setAttachment(Object attachment) {
+ this.attachment = attachment;
+ }
+
+ }
+
+ /**
+ * class加载类
+ */
+ public static class Loader {
+
+ protected static final Logger logger = Logger.getLogger(Loader.class.getName());
+
+ /**
+ * 加载当前线程的classpath扫描所有class进行过滤
+ *
+ * @param exclude 不需要扫描的文件夹, 可以为null
+ * @param filters
+ * @throws IOException
+ */
+ public static void load(final File exclude, final ClassFilter... filters) throws IOException {
+ URLClassLoader loader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
+ List urlfiles = new ArrayList<>(2);
+ List urljares = new ArrayList<>(2);
+ final URL exurl = exclude != null ? exclude.toURI().toURL() : null;
+ for (URL url : loader.getURLs()) {
+ if (exurl != null && exurl.sameFile(url)) continue;
+ if (url.getPath().endsWith(".jar")) {
+ urljares.add(url);
+ } else {
+ urlfiles.add(url);
+ }
+ }
+
+ List files = new ArrayList<>();
+ boolean debug = logger.isLoggable(Level.FINEST);
+ StringBuilder debugstr = new StringBuilder();
+ for (URL url : urljares) {
+ try (JarFile jar = new JarFile(URLDecoder.decode(url.getFile(), "UTF-8"))) {
+ Enumeration it = jar.entries();
+ while (it.hasMoreElements()) {
+ String entryname = it.nextElement().getName().replace('/', '.');
+ if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) {
+ String classname = entryname.substring(0, entryname.length() - 6);
+ if (classname.startsWith("javax.") || classname.startsWith("org.") || classname.startsWith("com.mysql.")) continue;
+ if (debug) debugstr.append(classname).append("\r\n");
+ for (final ClassFilter filter : filters) {
+ if (filter != null) filter.filter(null, classname);
+ }
+ }
+ }
+ }
+ }
+ for (URL url : urlfiles) {
+ files.clear();
+ File root = new File(url.getFile());
+ String rootpath = root.getPath();
+ loadClassFiles(exclude, root, files);
+ for (File f : files) {
+ String classname = f.getPath().substring(rootpath.length() + 1, f.getPath().length() - 6).replace(File.separatorChar, '.');
+ if (classname.startsWith("javax.") || classname.startsWith("org.") || classname.startsWith("com.mysql.")) continue;
+ if (debug) debugstr.append(classname).append("\r\n");
+ for (final ClassFilter filter : filters) {
+ if (filter != null) filter.filter(null, classname);
+ }
+ }
+ }
+ //if (debug) logger.log(Level.INFO, "scan classes: \r\n{0}", debugstr);
+ }
+
+ private static void loadClassFiles(File exclude, File root, List files) {
+ if (root.isFile() && root.getName().endsWith(".class")) {
+ files.add(root);
+ } else if (root.isDirectory()) {
+ if (exclude != null && exclude.equals(root)) return;
+ for (File f : root.listFiles()) {
+ loadClassFiles(exclude, f, files);
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/wentch/redkale/boot/LogFileHandler.java b/src/com/wentch/redkale/boot/LogFileHandler.java
new file mode 100644
index 000000000..e21921c1f
--- /dev/null
+++ b/src/com/wentch/redkale/boot/LogFileHandler.java
@@ -0,0 +1,230 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.boot;
+
+import java.io.*;
+import java.nio.file.*;
+import static java.nio.file.StandardCopyOption.*;
+import java.time.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+import java.util.logging.*;
+import java.util.logging.Formatter;
+
+/**
+ * 自定义的日志存储类
+ *
+ * @author zhangjx
+ */
+public class LogFileHandler extends Handler {
+
+ public static class LoggingFormater extends Formatter {
+
+ private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n";
+
+ @Override
+ public String format(LogRecord record) {
+ String source;
+ if (record.getSourceClassName() != null) {
+ source = record.getSourceClassName();
+ if (record.getSourceMethodName() != null) {
+ source += " " + record.getSourceMethodName();
+ }
+ } else {
+ source = record.getLoggerName();
+ }
+ String message = formatMessage(record);
+ String throwable = "";
+ if (record.getThrown() != null) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw) {
+ @Override
+ public void println() {
+ super.print("\r\n");
+ }
+ };
+ pw.println();
+ record.getThrown().printStackTrace(pw);
+ pw.close();
+ throwable = sw.toString();
+ }
+ return String.format(format,
+ System.currentTimeMillis(),
+ source,
+ record.getLoggerName(),
+ record.getLevel().getName(),
+ message,
+ throwable);
+ }
+
+ }
+
+ protected final LinkedBlockingQueue records = new LinkedBlockingQueue();
+
+ private String pattern;
+
+ private int limit; //文件大小限制
+
+ private final AtomicInteger index = new AtomicInteger();
+
+ private int count = 1; //文件限制
+
+ private long tomorrow;
+
+ private boolean append;
+
+ private final AtomicLong length = new AtomicLong();
+
+ private File logfile;
+
+ private OutputStream stream;
+
+ public LogFileHandler() {
+ updateTomorrow();
+ configure();
+ open();
+ }
+
+ private void updateTomorrow() {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.add(Calendar.DAY_OF_YEAR, 1);
+ long t = cal.getTimeInMillis();
+ if (this.tomorrow != t) index.set(0);
+ this.tomorrow = t;
+ }
+
+ private void open() {
+ new Thread() {
+ {
+ setName("Logging-FileHandler-Thread");
+ setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ LogRecord record = records.take();
+ if ((limit > 0 && limit <= length.get()) || tomorrow <= record.getMillis()) {
+ updateTomorrow();
+ if (stream != null) {
+ stream.close();
+ for (int i = Math.min(count - 2, index.get() - 1); i > 0; i--) {
+ File greater = new File(logfile.getPath() + "." + i);
+ if (greater.exists()) Files.move(greater.toPath(), new File(logfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
+ }
+ Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
+ stream = null;
+ }
+ }
+ if (stream == null) {
+ index.incrementAndGet();
+ java.time.LocalDate date = LocalDate.now();
+ logfile = new File(pattern.replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
+ logfile.getParentFile().mkdirs();
+ length.set(logfile.length());
+ stream = new FileOutputStream(logfile, append);
+ }
+ //----------------------写日志-------------------------
+ String message = getFormatter().format(record);
+ String encoding = getEncoding();
+ byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding);
+ stream.write(bytes);
+ length.addAndGet(bytes.length);
+ } catch (Exception e) {
+ ErrorManager err = getErrorManager();
+ if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE);
+ }
+ }
+
+ }
+ }.start();
+ }
+
+ private void configure() {
+ LogManager manager = LogManager.getLogManager();
+ String cname = getClass().getName();
+ pattern = manager.getProperty(cname + ".pattern");
+ if (pattern == null) pattern = "logs/log-%d.log";
+ String limitstr = manager.getProperty(cname + ".limit");
+ try {
+ if (limitstr != null) limit = Math.abs(Integer.decode(limitstr));
+ } catch (Exception e) {
+ }
+ String countstr = manager.getProperty(cname + ".count");
+ try {
+ if (countstr != null) count = Math.max(1, Math.abs(Integer.decode(countstr)));
+ } catch (Exception e) {
+ }
+ String appendstr = manager.getProperty(cname + ".append");
+ try {
+ if (appendstr != null) append = "true".equalsIgnoreCase(appendstr) || "1".equals(appendstr);
+ } catch (Exception e) {
+ }
+ String levelstr = manager.getProperty(cname + ".level");
+ try {
+ if (levelstr != null) {
+ Level l = Level.parse(levelstr);
+ setLevel(l != null ? l : Level.ALL);
+ }
+ } catch (Exception e) {
+ }
+ String filterstr = manager.getProperty(cname + ".filter");
+ try {
+ if (filterstr != null) {
+ Class> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr);
+ setFilter((Filter) clz.newInstance());
+ }
+ } catch (Exception e) {
+ }
+ String formatterstr = manager.getProperty(cname + ".formatter");
+ try {
+ if (formatterstr != null) {
+ Class> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr);
+ setFormatter((Formatter) clz.newInstance());
+ }
+ } catch (Exception e) {
+ }
+ if (getFormatter() == null) setFormatter(new SimpleFormatter());
+
+ String encodingstr = manager.getProperty(cname + ".encoding");
+ try {
+ if (encodingstr != null) setEncoding(encodingstr);
+ } catch (Exception e) {
+ }
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ records.offer(record);
+ }
+
+ @Override
+ public void flush() {
+ try {
+ if (stream != null) stream.flush();
+ } catch (Exception e) {
+ ErrorManager err = getErrorManager();
+ if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE);
+ }
+ }
+
+ @Override
+ public void close() throws SecurityException {
+ try {
+ if (stream != null) stream.close();
+ } catch (Exception e) {
+ ErrorManager err = getErrorManager();
+ if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE);
+ }
+ }
+
+}
diff --git a/src/com/wentch/redkale/boot/NodeHttpServer.java b/src/com/wentch/redkale/boot/NodeHttpServer.java
new file mode 100644
index 000000000..6fc78c3bc
--- /dev/null
+++ b/src/com/wentch/redkale/boot/NodeHttpServer.java
@@ -0,0 +1,83 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.boot;
+
+import com.wentch.redkale.net.http.WebServlet;
+import com.wentch.redkale.net.http.HttpServer;
+import com.wentch.redkale.net.http.HttpServlet;
+import com.wentch.redkale.util.AnyValue;
+import com.wentch.redkale.boot.ClassFilter.FilterEntry;
+import com.wentch.redkale.service.Service;
+import java.lang.reflect.Modifier;
+import java.net.InetSocketAddress;
+import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.logging.*;
+
+/**
+ *
+ * @author zhangjx
+ */
+public final class NodeHttpServer extends NodeServer {
+
+ private final HttpServer server;
+
+ public NodeHttpServer(Application application, CountDownLatch servicecdl, HttpServer server) {
+ super(application, servicecdl, server);
+ this.server = server;
+ }
+
+ @Override
+ public void init(AnyValue config) throws Exception {
+ server.init(config);
+ super.init(config);
+ }
+
+ @Override
+ public InetSocketAddress getSocketAddress() {
+ return server == null ? null : server.getSocketAddress();
+ }
+
+ @Override
+ public void load(AnyValue config) throws Exception {
+ super.load(config);
+ ClassFilter httpFilter = createHttpServletClassFilter(application.nodeName, config);
+ ClassFilter serviceFilter = createServiceClassFilter(application.nodeName, config);
+ long s = System.currentTimeMillis();
+ ClassFilter.Loader.load(application.getHome(), serviceFilter, httpFilter);
+ long e = System.currentTimeMillis() - s;
+ logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
+ loadService(config.getAnyValue("services"), serviceFilter); //必须在servlet之前
+ if (server != null) initHttpServlet(config.getAnyValue("servlets"), httpFilter);
+ }
+
+ protected static ClassFilter createHttpServletClassFilter(final String node, final AnyValue config) {
+ return createClassFilter(node, config, WebServlet.class, HttpServlet.class, null, "servlets", "servlet");
+ }
+
+ protected void initHttpServlet(final AnyValue conf, final ClassFilter filter) throws Exception {
+ final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
+ final String prefix = conf == null ? "" : conf.getValue("prefix", "");
+ final String threadName = "[" + Thread.currentThread().getName() + "] ";
+ for (FilterEntry en : filter.getFilterEntrys()) {
+ Class clazz = en.getType();
+ if (Modifier.isAbstract(clazz.getModifiers())) continue;
+ WebServlet ws = clazz.getAnnotation(WebServlet.class);
+ if (ws == null || ws.value().length == 0) continue;
+ final HttpServlet servlet = clazz.newInstance();
+ application.factory.inject(servlet);
+ String[] mappings = ws.value();
+ if (ws.fillurl() && !prefix.isEmpty()) {
+ for (int i = 0; i < mappings.length; i++) {
+ mappings[i] = prefix + mappings[i];
+ }
+ }
+ this.server.addHttpServlet(servlet, en.getProperty(), mappings);
+ if (sb != null) sb.append(threadName).append(" Loaded ").append(clazz.getName()).append(" --> ").append(Arrays.toString(mappings)).append(LINE_SEPARATOR);
+ }
+ if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
+ }
+}
diff --git a/src/com/wentch/redkale/boot/NodeServer.java b/src/com/wentch/redkale/boot/NodeServer.java
new file mode 100644
index 000000000..b5325e5b3
--- /dev/null
+++ b/src/com/wentch/redkale/boot/NodeServer.java
@@ -0,0 +1,250 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.boot;
+
+import com.wentch.redkale.net.sncp.ServiceEntry;
+import com.wentch.redkale.net.Server;
+import com.wentch.redkale.net.sncp.Sncp;
+import com.wentch.redkale.service.Service;
+import com.wentch.redkale.service.MultiService;
+import com.wentch.redkale.util.AnyValue;
+import com.wentch.redkale.util.Ignore;
+import com.wentch.redkale.boot.ClassFilter.FilterEntry;
+import com.wentch.redkale.util.AnyValue.DefaultAnyValue;
+import java.io.*;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.*;
+import java.net.InetSocketAddress;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.function.Consumer;
+import java.util.logging.*;
+
+/**
+ *
+ * @author zhangjx
+ */
+public abstract class NodeServer {
+
+ public static final String LINE_SEPARATOR = "\r\n";
+
+ protected final Logger logger;
+
+ protected final Application application;
+
+ private final CountDownLatch servicecdl;
+
+ private final Server server;
+
+ protected Consumer consumer;
+
+ public NodeServer(Application application, CountDownLatch servicecdl, Server server) {
+ this.application = application;
+ this.servicecdl = servicecdl;
+ this.server = server;
+ this.logger = Logger.getLogger(this.getClass().getSimpleName());
+ }
+
+ public void load(final AnyValue config) throws Exception {
+ }
+
+ public void init(AnyValue config) throws Exception {
+ //设置root文件夹
+ String webroot = config.getValue("root", "root");
+ File myroot = new File(webroot);
+ if (!webroot.contains(":") && !webroot.startsWith("/")) {
+ myroot = new File(System.getProperty(Application.RESNAME_HOME), webroot);
+ }
+ final String homepath = myroot.getCanonicalPath();
+ Server.loadLib(logger, config.getValue("lib", "") + ";" + homepath + "/lib/*;" + homepath + "/classes");
+ }
+
+ public abstract InetSocketAddress getSocketAddress();
+
+ public void start() throws IOException {
+ server.start();
+ }
+
+ public void shutdown() throws IOException {
+ server.shutdown();
+ }
+
+ protected void loadLocalService(final Set> entrys) throws Exception {
+ final HashMap localServices = application.localServices;
+ for (final FilterEntry entry : entrys) {
+ Class extends Service> serviceClass = entry.getType();
+ if (serviceClass.getAnnotation(Ignore.class) != null) continue;
+ final boolean multi = MultiService.class.isAssignableFrom(serviceClass);
+ if (!multi) { //单例模式
+ synchronized (application.localServices) {
+ ServiceEntry old = localServices.get(serviceClass);
+ if (old == null) {
+ old = new ServiceEntry(serviceClass, (Service) serviceClass.newInstance(), entry.getProperty(), "");
+ localServices.put(serviceClass, old);
+ }
+ if (consumer != null) consumer.accept(old);
+ continue;
+ }
+ }
+ String name = multi ? entry.getName() : "";
+ synchronized (application.localServices) {
+ ServiceEntry old = localServices.get(serviceClass);
+ if (old != null && old.containsName(name)) {
+ if (consumer != null) consumer.accept(old);
+ continue;
+ }
+ final Service service = (Service) serviceClass.newInstance();
+ if (old == null) {
+ old = new ServiceEntry(serviceClass, service, entry.getProperty(), name);
+ localServices.put(serviceClass, old);
+ } else {
+ old.addName(name);
+ }
+ if (consumer != null) consumer.accept(old);
+ }
+ }
+ }
+
+ protected void loadRemoteService(final Set> entrys) throws Exception {
+ for (FilterEntry entry : entrys) {
+ Class serviceClass = entry.getType();
+ if (serviceClass.getAnnotation(Ignore.class) != null) continue;
+ String remote = entry.getAttachment().toString();
+ Service service = Sncp.createRemoteService(entry.getName(), serviceClass, remote);
+ synchronized (application.remoteServices) {
+ application.remoteServices.add(new ServiceEntry(serviceClass, service, entry.getProperty(), entry.getName()));
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void loadService(final AnyValue servicesConf, ClassFilter serviceFilter) throws Exception {
+ if (serviceFilter == null) return;
+ final String threadName = "[" + Thread.currentThread().getName() + "] ";
+ final Set> entrys = serviceFilter.getFilterEntrys();
+ final Set> localentrys = new HashSet<>(entrys.size());
+ final Set> remotentrys = new HashSet<>(entrys.size());
+ final String defremote = getRemoteName(servicesConf, "LOCAL");
+ for (FilterEntry entry : entrys) { //service实现类
+ final Class type = entry.getType();
+ if (type.isInterface()) continue;
+ if (Modifier.isFinal(type.getModifiers())) continue;
+ if (!Modifier.isPublic(type.getModifiers())) continue;
+ if (Modifier.isAbstract(type.getModifiers())) continue;
+ if (type.getAnnotation(Ignore.class) != null) continue;
+ final String remote = getRemoteName(entry.getProperty(), defremote);
+ if ("LOCAL".equals(remote)) { //本地模式
+ localentrys.add(entry);
+ } else { //远程模式
+ entry.setAttachment(remote);
+ remotentrys.add(entry);
+ }
+ }
+ loadLocalService(localentrys);
+ loadRemoteService(remotentrys);
+ servicecdl.countDown();
+ servicecdl.await();
+
+ final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
+ synchronized (application) {
+ if (!application.serviceInited) {
+ application.serviceInited = true;
+ //--------------- register ---------------
+ application.localServices.forEach((x, y) -> {
+ y.getNames().forEach(n -> application.factory.register(n, y.getServiceClass(), y.getService()));
+ });
+ application.remoteServices.forEach(y -> {
+ y.getNames().forEach(n -> application.factory.register(n, y.getServiceClass(), y.getService()));
+ });
+ //---------------- inject ----------------
+ application.localServices.forEach((x, y) -> {
+ application.factory.inject(y.getService());
+ });
+ application.remoteServices.forEach(y -> {
+ application.factory.inject(y.getService());
+ });
+ //----------------- init -----------------
+ application.localServices.entrySet().parallelStream().forEach(k -> {
+ Class x = k.getKey();
+ ServiceEntry y = k.getValue();
+ long s = System.currentTimeMillis();
+ y.getService().init(y.getServiceConf());
+ long e = System.currentTimeMillis() - s;
+ if (e > 2 && sb != null) {
+ sb.append(threadName).append("LocalService(").append(y.getNames()).append("|").append(y.getServiceClass()).append(") init ").append(e).append("ms").append(LINE_SEPARATOR);
+ }
+ });
+ if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
+ }
+ }
+ }
+
+ private String getRemoteName(AnyValue av, String remote) {
+ remote = (remote == null || remote.trim().isEmpty()) ? "LOCAL" : remote.trim();
+ if (av == null) return remote;
+ String r = av.getValue("remote");
+ if ("LOCAL".equalsIgnoreCase(r)) return "LOCAL";
+ if (r != null && !r.trim().isEmpty()) return r.trim();
+ return remote;
+ }
+
+ protected static ClassFilter createServiceClassFilter(final String localNode, final AnyValue config) {
+ return createClassFilter(localNode, config, null, Service.class, Annotation.class, "services", "service");
+ }
+
+ protected static ClassFilter createClassFilter(final String localNode, final AnyValue config, Class extends Annotation> ref,
+ Class inter, Class extends Annotation> ref2, String properties, String property) {
+ ClassFilter filter = new ClassFilter(ref, inter);
+ if (properties == null && properties == null) return filter;
+ AnyValue list = config == null ? null : config.getAnyValue(properties);
+ if (list == null) return filter;
+ if ("services".equals(properties)) {
+ for (AnyValue group : list.getAnyValues("group")) {
+ String remotenames = group.getValue("remotenames");
+ for (AnyValue propnode : group.getAnyValues(property)) {
+ boolean hasremote = false;
+ if (remotenames != null) {
+ for (String rn : remotenames.split(";")) {
+ rn = rn.trim();
+ if (rn.isEmpty() || localNode.equals(rn)) continue;
+ DefaultAnyValue s = new DefaultAnyValue();
+ s.addValue("value", propnode.getValue("value"));
+ s.addValue("name", rn);
+ s.addValue("remote", rn);
+ ((DefaultAnyValue) list).addValue(property, s);
+ hasremote = true;
+ }
+ }
+ if (hasremote) {
+ ((DefaultAnyValue) propnode).setValue("name", localNode);
+ ((DefaultAnyValue) propnode).setValue("remote", "");
+ ((DefaultAnyValue) list).addValue(property, propnode);
+ }
+ }
+ }
+ }
+ for (AnyValue av : list.getAnyValues(property)) {
+ for (AnyValue prop : av.getAnyValues("property")) {
+ ((DefaultAnyValue) av).addValue(prop.getValue("name"), prop.getValue("value"));
+ }
+ filter.filter(av, av.getValue("value"), false);
+ }
+ if (list.getBoolValue("autoload", true)) {
+ String includes = list.getValue("includes", "");
+ String excludes = list.getValue("excludes", "");
+ filter.setIncludePatterns(includes.split(";"));
+ filter.setExcludePatterns(excludes.split(";"));
+ } else {
+ if (ref2 == null || ref2 == Annotation.class) {
+ filter.setRefused(true);
+ } else if (ref2 != Annotation.class) {
+ filter.setAnnotationClass(ref2);
+ }
+ }
+ return filter;
+ }
+
+}
diff --git a/src/com/wentch/redkale/boot/NodeSncpServer.java b/src/com/wentch/redkale/boot/NodeSncpServer.java
new file mode 100644
index 000000000..8f6e74899
--- /dev/null
+++ b/src/com/wentch/redkale/boot/NodeSncpServer.java
@@ -0,0 +1,50 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.boot;
+
+import com.wentch.redkale.net.sncp.SncpServer;
+import com.wentch.redkale.util.AnyValue;
+import com.wentch.redkale.service.Service;
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ *
+ * @author zhangjx
+ */
+public final class NodeSncpServer extends NodeServer {
+
+ private final SncpServer server;
+
+ public NodeSncpServer(Application application, CountDownLatch regcdl, SncpServer server) {
+ super(application, regcdl, server);
+ this.server = server;
+ this.consumer = x -> server.addService(x);
+ }
+
+ @Override
+ public void init(AnyValue config) throws Exception {
+ server.init(config);
+ super.init(config);
+ }
+
+ @Override
+ public InetSocketAddress getSocketAddress() {
+ return server == null ? null : server.getSocketAddress();
+ }
+
+ @Override
+ public void load(AnyValue config) throws Exception {
+ super.load(config);
+ ClassFilter serviceFilter = createServiceClassFilter(application.nodeName, config);
+ long s = System.currentTimeMillis();
+ ClassFilter.Loader.load(application.getHome(), serviceFilter);
+ long e = System.currentTimeMillis() - s;
+ logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
+ loadService(config.getAnyValue("services"), serviceFilter); //必须在servlet之前
+ }
+
+}
diff --git a/src/com/wentch/redkale/convert/AnyEncoder.java b/src/com/wentch/redkale/convert/AnyEncoder.java
new file mode 100644
index 000000000..6522a3b31
--- /dev/null
+++ b/src/com/wentch/redkale/convert/AnyEncoder.java
@@ -0,0 +1,40 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import java.lang.reflect.Type;
+
+/**
+ * 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入Writer,JSON则不写入。
+ *
+ * @author zhangjx
+ * @param
+ */
+public final class AnyEncoder implements Encodeable {
+
+ final Factory factory;
+
+ AnyEncoder(Factory factory) {
+ this.factory = factory;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void convertTo(final Writer out, final T value) {
+ if (value == null) {
+ out.writeNull();
+ } else {
+ out.wirteClassName(value.getClass());
+ factory.loadEncoder(value.getClass()).convertTo(out, value);
+ }
+ }
+
+ @Override
+ public Type getType() {
+ return Object.class;
+ }
+
+}
diff --git a/src/com/wentch/redkale/convert/ArrayDecoder.java b/src/com/wentch/redkale/convert/ArrayDecoder.java
new file mode 100644
index 000000000..fa41b8b3a
--- /dev/null
+++ b/src/com/wentch/redkale/convert/ArrayDecoder.java
@@ -0,0 +1,79 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+/**
+ * 对象数组的序列化,不包含int[]、long[]这样的primitive class数组.
+ * 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有采用int存储。
+ * 支持一定程度的泛型。
+ *
+ * @author zhangjx
+ * @param
+ */
+@SuppressWarnings("unchecked")
+public final class ArrayDecoder implements Decodeable {
+
+ private final Type type;
+
+ private final Type componentType;
+
+ private final Class componentClass;
+
+ private final Decodeable decoder;
+
+ public ArrayDecoder(final Factory factory, final Type type) {
+ this.type = type;
+ if (type instanceof GenericArrayType) {
+ Type t = ((GenericArrayType) type).getGenericComponentType();
+ this.componentType = t instanceof TypeVariable ? Object.class : t;
+ } else if ((type instanceof Class) && ((Class) type).isArray()) {
+ this.componentType = ((Class) type).getComponentType();
+ } else {
+ throw new ConvertException("(" + type + ") is not a array type");
+ }
+ if (this.componentType instanceof ParameterizedType) {
+ this.componentClass = (Class) ((ParameterizedType) this.componentType).getRawType();
+ } else {
+ this.componentClass = (Class) this.componentType;
+ }
+ factory.register(type, this);
+ this.decoder = factory.loadDecoder(this.componentType);
+ }
+
+ @Override
+ public T[] convertFrom(Reader in) {
+ final int len = in.readArrayB();
+ if (len == Reader.SIGN_NULL) return null;
+ final Decodeable localdecoder = this.decoder;
+ final List result = new ArrayList();
+ if (len == Reader.SIGN_NOLENGTH) {
+ while (in.hasNext()) {
+ result.add(localdecoder.convertFrom(in));
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ result.add(localdecoder.convertFrom(in));
+ }
+ }
+ in.readArrayE();
+ T[] rs = (T[]) Array.newInstance((Class) this.componentClass, result.size());
+ return result.toArray(rs);
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", decoder:" + this.decoder + "}";
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+}
diff --git a/src/com/wentch/redkale/convert/ArrayEncoder.java b/src/com/wentch/redkale/convert/ArrayEncoder.java
new file mode 100644
index 000000000..f85744375
--- /dev/null
+++ b/src/com/wentch/redkale/convert/ArrayEncoder.java
@@ -0,0 +1,75 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import java.lang.reflect.*;
+
+/**
+ * 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组.
+ * 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有采用int存储。
+ * 支持一定程度的泛型。
+ *
+ * @author zhangjx
+ * @param
+ */
+@SuppressWarnings("unchecked")
+public final class ArrayEncoder implements Encodeable {
+
+ private final Type type;
+
+ private final Type componentType;
+
+ private final Encodeable anyEncoder;
+
+ private final Encodeable encoder;
+
+ public ArrayEncoder(final Factory factory, final Type type) {
+ this.type = type;
+ if (type instanceof GenericArrayType) {
+ Type t = ((GenericArrayType) type).getGenericComponentType();
+ this.componentType = t instanceof TypeVariable ? Object.class : t;
+ } else if ((type instanceof Class) && ((Class) type).isArray()) {
+ this.componentType = ((Class) type).getComponentType();
+ } else {
+ throw new ConvertException("(" + type + ") is not a array type");
+ }
+ factory.register(type, this);
+ this.encoder = factory.loadEncoder(this.componentType);
+ this.anyEncoder = factory.getAnyEncoder();
+ }
+
+ @Override
+ public void convertTo(Writer out, T[] value) {
+ if (value == null) {
+ out.writeNull();
+ return;
+ }
+ if (value.length == 0) {
+ out.writeArrayB(0);
+ out.writeArrayE();
+ return;
+ }
+ out.writeArrayB(value.length);
+ final Type comp = this.componentType;
+ boolean first = true;
+ for (Object v : value) {
+ if (!first) out.writeArrayMark();
+ ((v.getClass() == comp) ? encoder : anyEncoder).convertTo(out, v);
+ if (first) first = false;
+ }
+ out.writeArrayE();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", encoder:" + this.encoder + "}";
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+}
diff --git a/src/com/wentch/redkale/convert/CollectionDecoder.java b/src/com/wentch/redkale/convert/CollectionDecoder.java
new file mode 100644
index 000000000..df87cfd99
--- /dev/null
+++ b/src/com/wentch/redkale/convert/CollectionDecoder.java
@@ -0,0 +1,69 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import com.wentch.redkale.util.Creator;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * 对象集合的反序列化.
+ * 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
+ * 支持一定程度的泛型。
+ *
+ * @author zhangjx
+ * @param
+ */
+@SuppressWarnings("unchecked")
+public final class CollectionDecoder implements Decodeable> {
+
+ private final Type type;
+
+ private final Type componentType;
+
+ protected Creator> creator;
+
+ private final Decodeable decoder;
+
+ public CollectionDecoder(final Factory factory, final Type type) {
+ this.type = type;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pt = (ParameterizedType) type;
+ this.componentType = pt.getActualTypeArguments()[0];
+ this.creator = factory.loadCreator((Class) pt.getRawType());
+ factory.register(type, this);
+ this.decoder = factory.loadDecoder(this.componentType);
+ } else {
+ throw new ConvertException("collectiondecoder not support the type (" + type + ")");
+ }
+ }
+
+ @Override
+ public Collection convertFrom(Reader in) {
+ final int len = in.readArrayB();
+ if (len == Reader.SIGN_NULL) return null;
+ final Decodeable localdecoder = this.decoder;
+ final Collection result = this.creator.create();
+ if (len == Reader.SIGN_NOLENGTH) {
+ while (in.hasNext()) {
+ result.add(localdecoder.convertFrom(in));
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ result.add(localdecoder.convertFrom(in));
+ }
+ }
+ in.readArrayE();
+ return result;
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+}
diff --git a/src/com/wentch/redkale/convert/CollectionEncoder.java b/src/com/wentch/redkale/convert/CollectionEncoder.java
new file mode 100644
index 000000000..e823a5775
--- /dev/null
+++ b/src/com/wentch/redkale/convert/CollectionEncoder.java
@@ -0,0 +1,65 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import java.lang.reflect.*;
+import java.util.Collection;
+
+/**
+ * 对象集合的序列化.
+ * 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
+ * 支持一定程度的泛型。
+ *
+ * @author zhangjx
+ * @param
+ */
+@SuppressWarnings("unchecked")
+public final class CollectionEncoder implements Encodeable> {
+
+ private final Type type;
+
+ private final Encodeable encoder;
+
+ public CollectionEncoder(final Factory factory, final Type type) {
+ this.type = type;
+ if (type instanceof ParameterizedType) {
+ Type t = ((ParameterizedType) type).getActualTypeArguments()[0];
+ if (t instanceof TypeVariable) {
+ this.encoder = factory.getAnyEncoder();
+ } else {
+ this.encoder = factory.loadEncoder(t);
+ }
+ } else {
+ this.encoder = factory.getAnyEncoder();
+ }
+ }
+
+ @Override
+ public void convertTo(Writer out, Collection value) {
+ if (value == null) {
+ out.writeNull();
+ return;
+ }
+ if (value.isEmpty()) {
+ out.writeArrayB(0);
+ out.writeArrayE();
+ return;
+ }
+ out.writeArrayB(value.size());
+ boolean first = true;
+ for (Object v : value) {
+ if (!first) out.writeArrayMark();
+ encoder.convertTo(out, v);
+ if (first) first = false;
+ }
+ out.writeArrayE();
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+}
diff --git a/src/com/wentch/redkale/convert/Convert.java b/src/com/wentch/redkale/convert/Convert.java
new file mode 100644
index 000000000..61b42ff63
--- /dev/null
+++ b/src/com/wentch/redkale/convert/Convert.java
@@ -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 com.wentch.redkale.convert;
+
+/**
+ * 序列化操作类
+ *
+ * @author zhangjx
+ * @param
+ * @param
+ */
+public abstract class Convert {
+
+ protected final Factory factory;
+
+ protected Convert(Factory factory) {
+ this.factory = factory;
+ }
+
+ public Factory getFactory() {
+ return this.factory;
+ }
+}
diff --git a/src/com/wentch/redkale/convert/ConvertColumn.java b/src/com/wentch/redkale/convert/ConvertColumn.java
new file mode 100644
index 000000000..1a868f0c4
--- /dev/null
+++ b/src/com/wentch/redkale/convert/ConvertColumn.java
@@ -0,0 +1,44 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import java.lang.annotation.*;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+/**
+ * 依附在setter、getter方法、字段进行简单的配置
+ *
+ * @author zhangjx
+ */
+@Inherited
+@Documented
+@Target({METHOD, FIELD})
+@Retention(RUNTIME)
+@Repeatable(ConvertColumns.class)
+public @interface ConvertColumn {
+
+ /**
+ * 给字段取个别名, 只对JSON有效
+ *
+ * @return
+ */
+ String name() default "";
+
+ /**
+ * 解析/序列化时是否屏蔽该字段
+ *
+ * @return
+ */
+ boolean ignore() default false;
+
+ /**
+ * 解析/序列化定制化的TYPE
+ *
+ * @return
+ */
+ ConvertType type() default ConvertType.ALL;
+}
diff --git a/src/com/wentch/redkale/convert/ConvertColumnEntry.java b/src/com/wentch/redkale/convert/ConvertColumnEntry.java
new file mode 100644
index 000000000..c5339b17a
--- /dev/null
+++ b/src/com/wentch/redkale/convert/ConvertColumnEntry.java
@@ -0,0 +1,67 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+/**
+ * ConvertColumn 对应的实体类
+ *
+ * @author zhangjx
+ */
+public final class ConvertColumnEntry {
+
+ private String name = "";
+
+ private boolean ignore;
+
+ private ConvertType convertType;
+
+ public ConvertColumnEntry() {
+ }
+
+ public ConvertColumnEntry(ConvertColumn column) {
+ if (column == null) return;
+ this.name = column.name();
+ this.ignore = column.ignore();
+ this.convertType = column.type();
+ }
+
+ public ConvertColumnEntry(String name, boolean ignore) {
+ this.name = name;
+ this.ignore = ignore;
+ this.convertType = ConvertType.ALL;
+ }
+
+ public ConvertColumnEntry(String name, boolean ignore, ConvertType convertType) {
+ this.name = name;
+ this.ignore = ignore;
+ this.convertType = convertType;
+ }
+
+ public String name() {
+ return name == null ? "" : name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean ignore() {
+ return ignore;
+ }
+
+ public void setIgnore(boolean ignore) {
+ this.ignore = ignore;
+ }
+
+ public ConvertType type() {
+ return convertType == null ? ConvertType.ALL : convertType;
+ }
+
+ public void setConvertType(ConvertType convertType) {
+ this.convertType = convertType;
+ }
+
+}
diff --git a/src/com/wentch/redkale/convert/ConvertColumns.java b/src/com/wentch/redkale/convert/ConvertColumns.java
new file mode 100644
index 000000000..a5e613ad0
--- /dev/null
+++ b/src/com/wentch/redkale/convert/ConvertColumns.java
@@ -0,0 +1,24 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import java.lang.annotation.*;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+/**
+ * ConvertColumn 的多用类
+ *
+ * @author zhangjx
+ */
+@Inherited
+@Documented
+@Target({METHOD, FIELD})
+@Retention(RUNTIME)
+public @interface ConvertColumns {
+
+ ConvertColumn[] value();
+}
diff --git a/src/com/wentch/redkale/convert/ConvertException.java b/src/com/wentch/redkale/convert/ConvertException.java
new file mode 100644
index 000000000..002c6946e
--- /dev/null
+++ b/src/com/wentch/redkale/convert/ConvertException.java
@@ -0,0 +1,28 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+/**
+ *
+ * @author zhangjx
+ */
+public class ConvertException extends RuntimeException {
+
+ public ConvertException() {
+ super();
+ }
+
+ public ConvertException(String s) {
+ super(s);
+ }
+
+ public ConvertException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConvertException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/com/wentch/redkale/convert/ConvertType.java b/src/com/wentch/redkale/convert/ConvertType.java
new file mode 100644
index 000000000..7871a01d8
--- /dev/null
+++ b/src/com/wentch/redkale/convert/ConvertType.java
@@ -0,0 +1,28 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+/**
+ *
+ * @author zhangjx
+ */
+public enum ConvertType {
+
+ JSON(1),
+ BSON(2),
+ ALL(127);
+
+ private final int value;
+
+ private ConvertType(int v) {
+ this.value = v;
+ }
+
+ public boolean contains(ConvertType type) {
+ if (type == null) return false;
+ return this.value >= type.value && (this.value & type.value) > 0;
+ }
+}
diff --git a/src/com/wentch/redkale/convert/DeMember.java b/src/com/wentch/redkale/convert/DeMember.java
new file mode 100644
index 000000000..2897e7d23
--- /dev/null
+++ b/src/com/wentch/redkale/convert/DeMember.java
@@ -0,0 +1,64 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import com.wentch.redkale.util.Attribute;
+
+/**
+ *
+ * @author zhangjx
+ * @param
+ * @param
+ * @param
+ */
+@SuppressWarnings("unchecked")
+public final class DeMember implements Comparable> {
+
+ protected final Attribute attribute;
+
+ protected Decodeable decoder;
+
+ public DeMember(final Attribute attribute) {
+ this.attribute = attribute;
+ }
+
+ public DeMember(Attribute attribute, Decodeable decoder) {
+ this(attribute);
+ this.decoder = decoder;
+ }
+
+ public final void read(R in, T obj) {
+ this.attribute.set(obj, decoder.convertFrom(in));
+ }
+
+ public Attribute getAttribute() {
+ return this.attribute;
+ }
+
+ @Override
+ public final int compareTo(DeMember o) {
+ if (o == null) return 1;
+ return this.attribute.field().compareTo(o.attribute.field());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof DeMember)) return false;
+ DeMember other = (DeMember) obj;
+ return compareTo(other) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.attribute.field().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "DeMember{" + "attribute=" + attribute.field() + ", decoder=" + decoder + '}';
+ }
+}
diff --git a/src/com/wentch/redkale/convert/Decodeable.java b/src/com/wentch/redkale/convert/Decodeable.java
new file mode 100644
index 000000000..c6b58899a
--- /dev/null
+++ b/src/com/wentch/redkale/convert/Decodeable.java
@@ -0,0 +1,27 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import java.lang.reflect.Type;
+
+/**
+ *
+ * @author zhangjx
+ * @param
+ * @param
+ */
+public interface Decodeable {
+
+ public T convertFrom(final R in);
+
+ /**
+ * 泛型映射接口
+ *
+ * @return
+ */
+ public Type getType();
+
+}
diff --git a/src/com/wentch/redkale/convert/EnMember.java b/src/com/wentch/redkale/convert/EnMember.java
new file mode 100644
index 000000000..85fb0fe4f
--- /dev/null
+++ b/src/com/wentch/redkale/convert/EnMember.java
@@ -0,0 +1,60 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import com.wentch.redkale.util.Attribute;
+
+/**
+ *
+ * @author zhangjx
+ * @param
+ * @param
+ * @param
+ */
+@SuppressWarnings("unchecked")
+public final class EnMember implements Comparable> {
+
+ private final Attribute attribute;
+
+ final Encodeable encoder;
+
+ public EnMember(Attribute attribute, Encodeable encoder) {
+ this.attribute = attribute;
+ this.encoder = encoder;
+ }
+
+ public boolean write(final W out, final boolean comma, final T obj) {
+ F value = attribute.get(obj);
+ if (value == null) return comma;
+ out.writeField(comma, attribute);
+ encoder.convertTo(out, value);
+ return true;
+ }
+
+ @Override
+ public final int compareTo(EnMember o) {
+ if (o == null) return 1;
+ return this.attribute.field().compareTo(o.attribute.field());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof EnMember)) return false;
+ EnMember other = (EnMember) obj;
+ return compareTo(other) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.attribute.field().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "EnMember{" + "attribute=" + attribute.field() + ", encoder=" + encoder + '}';
+ }
+}
diff --git a/src/com/wentch/redkale/convert/Encodeable.java b/src/com/wentch/redkale/convert/Encodeable.java
new file mode 100644
index 000000000..595383445
--- /dev/null
+++ b/src/com/wentch/redkale/convert/Encodeable.java
@@ -0,0 +1,27 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import java.lang.reflect.Type;
+
+/**
+ *
+ * @author zhangjx
+ * @param
+ * @param
+ */
+public interface Encodeable {
+
+ public void convertTo(final W out, T value);
+
+ /**
+ * 泛型映射接口
+ *
+ * @return
+ */
+ public Type getType();
+
+}
diff --git a/src/com/wentch/redkale/convert/Factory.java b/src/com/wentch/redkale/convert/Factory.java
new file mode 100644
index 000000000..41da5a10b
--- /dev/null
+++ b/src/com/wentch/redkale/convert/Factory.java
@@ -0,0 +1,342 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import com.wentch.redkale.convert.ext.StringArraySimpledCoder;
+import com.wentch.redkale.convert.ext.DoubleArraySimpledCoder;
+import com.wentch.redkale.convert.ext.LongSimpledCoder;
+import com.wentch.redkale.convert.ext.ByteArraySimpledCoder;
+import com.wentch.redkale.convert.ext.IntArraySimpledCoder;
+import com.wentch.redkale.convert.ext.DoubleSimpledCoder;
+import com.wentch.redkale.convert.ext.TwoLongSimpledCoder;
+import com.wentch.redkale.convert.ext.CharSimpledCoder;
+import com.wentch.redkale.convert.ext.IntSimpledCoder;
+import com.wentch.redkale.convert.ext.InetAddressSimpledCoder;
+import com.wentch.redkale.convert.ext.LongArraySimpledCoder;
+import com.wentch.redkale.convert.ext.DateSimpledCoder;
+import com.wentch.redkale.convert.ext.BoolSimpledCoder;
+import com.wentch.redkale.convert.ext.CharArraySimpledCoder;
+import com.wentch.redkale.convert.ext.EnumSimpledCoder;
+import com.wentch.redkale.convert.ext.BigIntegerSimpledCoder;
+import com.wentch.redkale.convert.ext.ByteSimpledCoder;
+import com.wentch.redkale.convert.ext.StringSimpledCoder;
+import com.wentch.redkale.convert.ext.NumberSimpledCoder;
+import com.wentch.redkale.convert.ext.TypeSimpledCoder;
+import com.wentch.redkale.convert.ext.ShortArraySimpledCoder;
+import com.wentch.redkale.convert.ext.BoolArraySimpledCoder;
+import com.wentch.redkale.convert.ext.ShortSimpledCoder;
+import com.wentch.redkale.convert.ext.FloatArraySimpledCoder;
+import com.wentch.redkale.convert.ext.FloatSimpledCoder;
+import com.wentch.redkale.util.TwoLong;
+import com.wentch.redkale.util.Creator;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Map;
+import java.lang.reflect.*;
+import java.math.BigInteger;
+import java.net.*;
+import static com.wentch.redkale.convert.ext.InetAddressSimpledCoder.*;
+import java.util.*;
+
+/**
+ *
+ * @author zhangjx
+ * @param
+ * @param
+ */
+@SuppressWarnings("unchecked")
+public abstract class Factory {
+
+ private final Factory parent;
+
+ protected Convert convert;
+
+ private final Encodeable anyEncoder = new AnyEncoder(this);
+
+ //-----------------------------------------------------------------------------------
+ private final HashedMap creators = new HashedMap();
+
+ private final HashedMap> decoders = new HashedMap();
+
+ private final HashedMap> encoders = new HashedMap();
+
+ private final HashMap columnEntrys = new HashMap();
+
+ protected Factory(Factory parent) {
+ this.parent = parent;
+ if (parent == null) {
+ //---------------------------------------------------------
+ this.register(boolean.class, BoolSimpledCoder.instance);
+ this.register(Boolean.class, BoolSimpledCoder.instance);
+
+ this.register(byte.class, ByteSimpledCoder.instance);
+ this.register(Byte.class, ByteSimpledCoder.instance);
+
+ this.register(short.class, ShortSimpledCoder.instance);
+ this.register(Short.class, ShortSimpledCoder.instance);
+
+ this.register(char.class, CharSimpledCoder.instance);
+ this.register(Character.class, CharSimpledCoder.instance);
+
+ this.register(int.class, IntSimpledCoder.instance);
+ this.register(Integer.class, IntSimpledCoder.instance);
+
+ this.register(long.class, LongSimpledCoder.instance);
+ this.register(Long.class, LongSimpledCoder.instance);
+
+ this.register(float.class, FloatSimpledCoder.instance);
+ this.register(Float.class, FloatSimpledCoder.instance);
+
+ this.register(double.class, DoubleSimpledCoder.instance);
+ this.register(Double.class, DoubleSimpledCoder.instance);
+
+ this.register(Number.class, NumberSimpledCoder.instance);
+ this.register(String.class, StringSimpledCoder.instance);
+ this.register(java.util.Date.class, DateSimpledCoder.instance);
+ this.register(BigInteger.class, BigIntegerSimpledCoder.instance);
+ this.register(InetAddress.class, InetAddressSimpledCoder.instance);
+ this.register(TwoLong.class, TwoLongSimpledCoder.instance);
+ this.register(Class.class, TypeSimpledCoder.instance);
+ this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
+ this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
+ //---------------------------------------------------------
+ this.register(boolean[].class, BoolArraySimpledCoder.instance);
+ this.register(byte[].class, ByteArraySimpledCoder.instance);
+ this.register(short[].class, ShortArraySimpledCoder.instance);
+ this.register(char[].class, CharArraySimpledCoder.instance);
+ this.register(int[].class, IntArraySimpledCoder.instance);
+ this.register(long[].class, LongArraySimpledCoder.instance);
+ this.register(float[].class, FloatArraySimpledCoder.instance);
+ this.register(double[].class, DoubleArraySimpledCoder.instance);
+ this.register(String[].class, StringArraySimpledCoder.instance);
+ //---------------------------------------------------------
+ }
+ }
+
+ public Factory parent() {
+ return this.parent;
+ }
+
+ public abstract ConvertType getConvertType();
+
+ public abstract boolean isReversible();
+
+ public abstract Factory createChild();
+
+ public Convert getConvert() {
+ return convert;
+ }
+
+ public ConvertColumnEntry findRef(AccessibleObject field) {
+ if (field == null) return null;
+ ConvertColumnEntry en = this.columnEntrys.get(field);
+ if (en != null) return en;
+ final ConvertType ct = this.getConvertType();
+ for (ConvertColumn ref : field.getAnnotationsByType(ConvertColumn.class)) {
+ if (ref.type().contains(ct)) return new ConvertColumnEntry(ref);
+ }
+ return null;
+ }
+
+ public final boolean register(final Class type, String column, ConvertColumnEntry entry) {
+ if (type == null || column == null || entry == null) return false;
+ try {
+ final Field field = type.getDeclaredField(column);
+ String get = "get";
+ if (field.getType() == boolean.class || field.getType() == Boolean.class) get = "is";
+ char[] cols = column.toCharArray();
+ cols[0] = Character.toUpperCase(cols[0]);
+ String col2 = new String(cols);
+ try {
+ register(type.getMethod(get + col2), entry);
+ } catch (Exception ex) {
+ }
+ try {
+ register(type.getMethod("set" + col2, field.getType()), entry);
+ } catch (Exception ex) {
+ }
+ return register(field, entry);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public final boolean register(final AccessibleObject field, final ConvertColumnEntry entry) {
+ if (field == null || entry == null) return false;
+ this.columnEntrys.put(field, entry);
+ return true;
+ }
+
+ public final void register(final Class clazz, final Creator extends E> creator) {
+ creators.put(clazz, creator);
+ }
+
+ public final Creator findCreator(Class type) {
+ Creator creator = creators.get(type);
+ if (creator != null) return creator;
+ return this.parent == null ? null : this.parent.findCreator(type);
+ }
+
+ public final Creator loadCreator(Class type) {
+ Creator result = findCreator(type);
+ if (result == null) {
+ result = Creator.create(type);
+ creators.put(type, result);
+ }
+ return result;
+ }
+
+ //----------------------------------------------------------------------
+ public final Encodeable getAnyEncoder() {
+ return (Encodeable) anyEncoder;
+ }
+
+ public final void register(final Type clazz, final SimpledCoder coder) {
+ decoders.put(clazz, coder);
+ encoders.put(clazz, coder);
+ }
+
+ public final void register(final Type clazz, final Decodeable decoder) {
+ decoders.put(clazz, decoder);
+ }
+
+ public final void register(final Type clazz, final Encodeable printer) {
+ encoders.put(clazz, printer);
+ }
+
+ public final Decodeable findDecoder(final Type type) {
+ Decodeable rs = (Decodeable) decoders.get(type);
+ if (rs != null) return rs;
+ return this.parent == null ? null : this.parent.findDecoder(type);
+ }
+
+ public final Encodeable findEncoder(final Type type) {
+ Encodeable rs = (Encodeable) encoders.get(type);
+ if (rs != null) return rs;
+ return this.parent == null ? null : this.parent.findEncoder(type);
+ }
+
+ public final Decodeable loadDecoder(final Type type) {
+ Decodeable decoder = findDecoder(type);
+ if (decoder != null) return decoder;
+ if (type instanceof GenericArrayType) return new ArrayDecoder(this, type);
+ Class clazz;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pts = (ParameterizedType) type;
+ clazz = (Class) (pts).getRawType();
+ } else if (type instanceof Class) {
+ clazz = (Class) type;
+ } else {
+ throw new ConvertException("not support the type (" + type + ")");
+ }
+ decoder = findDecoder(clazz);
+ if (decoder != null) return decoder;
+ return createDecoder(type, clazz);
+ }
+
+ public final Decodeable createDecoder(final Type type) {
+ Class clazz;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pts = (ParameterizedType) type;
+ clazz = (Class) (pts).getRawType();
+ } else if (type instanceof Class) {
+ clazz = (Class) type;
+ } else {
+ throw new ConvertException("not support the type (" + type + ")");
+ }
+ return createDecoder(type, clazz);
+ }
+
+ private Decodeable createDecoder(final Type type, final Class clazz) {
+ Decodeable decoder = null;
+ ObjectDecoder od = null;
+ if (clazz.isEnum()) {
+ decoder = new EnumSimpledCoder(clazz);
+ } else if (clazz.isArray()) {
+ decoder = new ArrayDecoder(this, type);
+ } else if (Collection.class.isAssignableFrom(clazz)) {
+ decoder = new CollectionDecoder(this, type);
+ } else if (Map.class.isAssignableFrom(clazz)) {
+ decoder = new MapDecoder(this, type);
+ } else if (clazz == Object.class) {
+ od = new ObjectDecoder(type);
+ decoder = od;
+ } else if (!clazz.getName().startsWith("java.")) {
+ od = new ObjectDecoder(type);
+ decoder = od;
+ }
+ if (decoder == null) throw new ConvertException("not support the type (" + type + ")");
+ register(type, decoder);
+ if (od != null) od.init(this);
+ return decoder;
+ }
+
+ public final Encodeable loadEncoder(final Type type) {
+ Encodeable encoder = findEncoder(type);
+ if (encoder != null) return encoder;
+ if (type instanceof GenericArrayType) return new ArrayEncoder(this, type);
+ Class clazz;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pts = (ParameterizedType) type;
+ clazz = (Class) (pts).getRawType();
+ } else if (type instanceof TypeVariable) {
+ TypeVariable tv = (TypeVariable) type;
+ Type t = Object.class;
+ if (tv.getBounds().length == 1) {
+ t = tv.getBounds()[0];
+ }
+ if (!(t instanceof Class)) t = Object.class;
+ clazz = (Class) t;
+ } else if (type instanceof Class) {
+ clazz = (Class) type;
+ } else {
+ throw new ConvertException("not support the type (" + type + ")");
+ }
+ encoder = findEncoder(clazz);
+ if (encoder != null) return encoder;
+ return createEncoder(type, clazz);
+ }
+
+ public final Encodeable createEncoder(final Type type) {
+ Class clazz;
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType pts = (ParameterizedType) type;
+ clazz = (Class) (pts).getRawType();
+ } else if (type instanceof Class) {
+ clazz = (Class) type;
+ } else {
+ throw new ConvertException("not support the type (" + type + ")");
+ }
+ return createEncoder(type, clazz);
+ }
+
+ private Encodeable createEncoder(final Type type, final Class clazz) {
+ Encodeable encoder = null;
+ ObjectEncoder oe = null;
+ if (clazz.isEnum()) {
+ encoder = new EnumSimpledCoder(clazz);
+ } else if (clazz.isArray()) {
+ encoder = new ArrayEncoder(this, type);
+ } else if (Collection.class.isAssignableFrom(clazz)) {
+ encoder = new CollectionEncoder(this, type);
+ } else if (Map.class.isAssignableFrom(clazz)) {
+ encoder = new MapEncoder(this, type);
+ } else if (clazz == Object.class) {
+ return (Encodeable) this.anyEncoder;
+ } else if (!clazz.getName().startsWith("java.")) {
+ oe = new ObjectEncoder(type);
+ encoder = oe;
+ }
+ if (encoder == null) throw new ConvertException("not support the type (" + type + ")");
+ register(type, encoder);
+ if (oe != null) oe.init(this);
+ return encoder;
+
+ }
+
+}
diff --git a/src/com/wentch/redkale/convert/HashedMap.java b/src/com/wentch/redkale/convert/HashedMap.java
new file mode 100644
index 000000000..acd7fbbec
--- /dev/null
+++ b/src/com/wentch/redkale/convert/HashedMap.java
@@ -0,0 +1,68 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+/**
+ * 只增不减的伪Map类
+ *
+ * @author zhangjx
+ * @param
+ * @param
+ */
+@SuppressWarnings("unchecked")
+public final class HashedMap {
+
+ protected final transient Entry[] table;
+
+ public HashedMap() {
+ this(128);
+ }
+
+ public HashedMap(int initCapacity) {
+ this.table = new Entry[Math.max(initCapacity, 16)];
+ }
+
+ public final V get(final K key) {
+ final K k = key;
+ final Entry[] data = this.table;
+ Entry entry = data[k.hashCode() & (data.length - 1)];
+ while (entry != null) {
+ if (k == entry.key) return entry.value;
+ entry = entry.next;
+ }
+ return null;
+ }
+
+ public final V put(K key, V value) {
+ final K k = key;
+ final Entry[] data = this.table;
+ final int index = k.hashCode() & (data.length - 1);
+ Entry entry = data[index];
+ while (entry != null) {
+ if (k == entry.key) {
+ entry.value = value;
+ return entry.value;
+ }
+ entry = entry.next;
+ }
+ data[index] = new Entry<>(key, value, data[index]);
+ return null;
+ }
+
+ protected static final class Entry {
+
+ protected V value;
+
+ protected final K key;
+
+ protected final Entry next;
+
+ protected Entry(K key, V value, Entry next) {
+ this.key = key;
+ this.value = value;
+ this.next = next;
+ }
+ }
+}
diff --git a/src/com/wentch/redkale/convert/MapDecoder.java b/src/com/wentch/redkale/convert/MapDecoder.java
new file mode 100644
index 000000000..dfd7c2c1e
--- /dev/null
+++ b/src/com/wentch/redkale/convert/MapDecoder.java
@@ -0,0 +1,78 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.wentch.redkale.convert;
+
+import com.wentch.redkale.util.Creator;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+/**
+ *
+ * @author zhangjx
+ * @param
+ * @param
+ */
+@SuppressWarnings("unchecked")
+public final class MapDecoder implements Decodeable> {
+
+ private final Type type;
+
+ private final Type keyType;
+
+ private final Type valueType;
+
+ protected Creator