org.redkale.boot.NodeHttpServer Maven / Gradle / Ivy
/*
* 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.boot;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import javax.annotation.*;
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.net.*;
import org.redkale.net.http.*;
import org.redkale.net.sncp.Sncp;
import org.redkale.service.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.*;
import org.redkale.watch.*;
/**
* HTTP Server节点的配置Server
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@NodeProtocol({"HTTP"})
public class NodeHttpServer extends NodeServer {
protected final boolean rest; //是否加载REST服务, 为true加载rest节点信息并将所有可REST化的Service生成RestServlet
protected final HttpServer httpServer;
public NodeHttpServer(Application application, AnyValue serconf) {
super(application, createServer(application, serconf));
this.httpServer = (HttpServer) server;
this.rest = serconf == null ? false : serconf.getAnyValue("rest") != null;
}
private static Server createServer(Application application, AnyValue serconf) {
return new HttpServer(application.getStartTime(), application.getResourceFactory().createChild());
}
public HttpServer getHttpServer() {
return httpServer;
}
@Override
public InetSocketAddress getSocketAddress() {
return httpServer == null ? null : httpServer.getSocketAddress();
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter createServiceClassFilter() {
return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service");
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter createFilterClassFilter() {
return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter");
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter createServletClassFilter() {
return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet");
}
@Override
protected ClassFilter createOtherClassFilter() {
return createClassFilter(null, RestWebSocket.class, WebSocket.class, null, null, "rest", "websocket");
}
@Override
protected void loadService(ClassFilter serviceFilter, ClassFilter otherFilter) throws Exception {
super.loadService(serviceFilter, otherFilter);
initWebSocketService();
}
@Override
protected void loadFilter(ClassFilter filterFilter, ClassFilter otherFilter) throws Exception {
if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter);
}
@Override
@SuppressWarnings("unchecked")
protected void loadServlet(ClassFilter servletFilter, ClassFilter otherFilter) throws Exception {
if (httpServer != null) loadHttpServlet(servletFilter, otherFilter);
}
private void initWebSocketService() {
final NodeServer self = this;
final ResourceFactory regFactory = application.getResourceFactory();
resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务
try {
if (field.getAnnotation(Resource.class) == null) return;
if (!(src instanceof WebSocketServlet)) return;
ResourceFactory.ResourceLoader loader = null;
ResourceFactory sncpResFactory = null;
for (NodeServer ns : application.servers) {
if (!ns.isSNCP()) continue;
sncpResFactory = ns.resourceFactory;
loader = sncpResFactory.findLoader(WebSocketNode.class, field);
if (loader != null) break;
}
if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment);
synchronized (regFactory) {
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) {
resourceFactory.register(RESNAME_SNCP_ADDR, InetSocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, InetSocketAddress.class));
resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, SocketAddress.class));
resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class));
}
if (nodeService == null) {
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set) null, (AnyValue) null);
regFactory.register(resourceName, WebSocketNode.class, nodeService);
}
resourceFactory.inject(nodeService, self);
field.set(src, nodeService);
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
}
}, WebSocketNode.class);
}
@SuppressWarnings("unchecked")
protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter classFilter) throws Exception {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
List> list = new ArrayList(classFilter.getFilterEntrys());
for (FilterEntry en : list) {
Class clazz = (Class) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
final HttpFilter filter = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.httpServer.addHttpFilter(filter, filterConf);
if (sb != null) sb.append(threadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
}
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
}
@SuppressWarnings("unchecked")
protected void loadHttpServlet(final ClassFilter servletFilter, ClassFilter webSocketFilter) throws Exception {
final AnyValue servletsConf = this.serverConf.getAnyValue("servlets");
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
String prefix0 = servletsConf == null ? "" : servletsConf.getValue("path", "");
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
final String prefix = prefix0;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
List> list = new ArrayList(servletFilter.getFilterEntrys());
list.sort((FilterEntry o1, FilterEntry o2) -> { //必须保证WebSocketServlet优先加载, 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode
boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType());
boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType());
if (ws1 == ws2) {
Priority p1 = o1.getType().getAnnotation(Priority.class);
Priority p2 = o2.getType().getAnnotation(Priority.class);
int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
return v == 0 ? o1.getType().getName().compareTo(o2.getType().getName()) : 0;
}
return ws1 ? -1 : 1;
});
final List> ss = sb == null ? null : new ArrayList<>();
for (FilterEntry en : list) {
Class clazz = (Class) 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.getDeclaredConstructor().newInstance();
resourceFactory.inject(servlet, this);
final String[] mappings = ws.value();
String pref = ws.repair() ? prefix : "";
DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty();
this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings);
if (ss != null) {
for (int i = 0; i < mappings.length; i++) {
mappings[i] = pref + mappings[i];
}
ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings));
}
}
int max = 0;
if (ss != null && sb != null) {
Collections.sort(ss, (AbstractMap.SimpleEntry o1, AbstractMap.SimpleEntry o2) -> o1.getKey().compareTo(o2.getKey()));
for (AbstractMap.SimpleEntry as : ss) {
if (as.getKey().length() > max) max = as.getKey().length();
}
for (AbstractMap.SimpleEntry as : ss) {
sb.append(threadName).append(" Load ").append(as.getKey());
for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' ');
}
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
}
}
if (rest && serverConf != null) {
final List