All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.jiashunx.masker.rest.framework.MRestServer Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
package io.github.jiashunx.masker.rest.framework;

import io.github.jiashunx.masker.rest.framework.cons.Constants;
import io.github.jiashunx.masker.rest.framework.exception.MRestServerInitializeException;
import io.github.jiashunx.masker.rest.framework.filter.MRestFilter;
import io.github.jiashunx.masker.rest.framework.filter.MRestFilterChain;
import io.github.jiashunx.masker.rest.framework.filter.MRestDispatchFilter;
import io.github.jiashunx.masker.rest.framework.filter.StaticResourceFilter;
import io.github.jiashunx.masker.rest.framework.handler.*;
import io.github.jiashunx.masker.rest.framework.model.MRestHandlerConfig;
import io.github.jiashunx.masker.rest.framework.type.MRestNettyThreadType;
import io.github.jiashunx.masker.rest.framework.util.MRestHeaderBuilder;
import io.github.jiashunx.masker.rest.framework.util.MRestThreadFactory;
import io.github.jiashunx.masker.rest.framework.util.MRestUtils;
import io.github.jiashunx.masker.rest.framework.util.StringUtils;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.*;

/**
 * @author jiashunx
 */
public class MRestServer {

    private static final Logger logger = LoggerFactory.getLogger(MRestServer.class);

    private volatile boolean started = false;

    private int listenPort;
    private String serverName;
    private int bossThreadNum = 0;
    private int workerThreadNum = 0;
    private boolean connectionKeepAlive;
    private String contextPath = Constants.DEFAULT_CONTEXT_PATH;

    public MRestServer() {
        this(MRestUtils.getDefaultServerPort(), MRestUtils.getDefaultServerName());
    }

    public MRestServer(String serverName) {
        this(MRestUtils.getDefaultServerPort(), serverName);
    }

    public MRestServer(int listenPort) {
        this(listenPort, MRestUtils.getDefaultServerName());
    }

    public MRestServer(int listenPort, String serverName) {
        listenPort(listenPort);
        serverName(serverName);
    }

    public MRestServer listenPort(int listenPort) {
        if (listenPort <= 0 || listenPort > 65535) {
            throw new IllegalArgumentException("listenPort -> " + listenPort);
        }
        this.listenPort = listenPort;
        return this;
    }

    public MRestServer serverName(String serverName) {
        if (StringUtils.isBlank(serverName)) {
            throw new IllegalArgumentException("serverName -> " + serverName);
        }
        this.serverName = serverName;
        return this;
    }

    public MRestServer bossThreadNum(int bossThreadNum) {
        if (bossThreadNum < 0) {
            throw new IllegalArgumentException("bossThreadNum -> " + bossThreadNum);
        }
        this.bossThreadNum = bossThreadNum;
        return this;
    }

    public MRestServer workerThreadNum(int workerThreadNum) {
        if (workerThreadNum < 0) {
            throw new IllegalArgumentException("workThreadNum -> " + workerThreadNum);
        }
        this.workerThreadNum = workerThreadNum;
        return this;
    }

    public MRestServer connectionKeepAlive(boolean connectionKeepAlive) {
        this.connectionKeepAlive = connectionKeepAlive;
        return this;
    }

    public boolean isConnectionKeepAlive() {
        return this.connectionKeepAlive;
    }

    public MRestServer contextPath(String contextPath) {
        if (StringUtils.isBlank(contextPath)) {
            throw new IllegalArgumentException("contextPath can't be empty");
        }
        String _ctxPath = contextPath.trim();
        while (_ctxPath.endsWith(Constants.URL_PATH_SEP)) {
            if (_ctxPath.length() == 1) {
                break;
            }
            _ctxPath = _ctxPath.substring(0, _ctxPath.length() - 1);
        }
        if (!_ctxPath.startsWith(Constants.URL_PATH_SEP)) {
            _ctxPath += Constants.URL_PATH_SEP;
        }
        this.contextPath = _ctxPath;
        return this;
    }

    public String getContextPath() {
        return this.contextPath;
    }

    /**
     * 检查server是否已启动
     * @throws MRestServerInitializeException MRestServerInitializeException
     */
    private void checkServerState() throws MRestServerInitializeException {
        if (started) {
            throw new MRestServerInitializeException(String.format("server: %s has already been initialized", serverName));
        }
    }

    /**
     * 启动server
     * @throws MRestServerInitializeException MRestServerInitializeException
     */
    public synchronized void start() throws MRestServerInitializeException {
        checkServerState();
        if (logger.isInfoEnabled()) {
            logger.info("start server: {}[contextPath={}], listening on port: {}", serverName, contextPath, listenPort);
        }
        try {
            // mapping处理
            for (Runnable mappingTask: mappingTaskList) {
                mappingTask.run();
            }
            // filter处理
            for (Runnable filterTask: filterTaskList) {
                filterTask.run();
            }
            EventLoopGroup bossGroup = new NioEventLoopGroup(bossThreadNum, new MRestThreadFactory(MRestNettyThreadType.BOSS, listenPort));
            EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreadNum, new MRestThreadFactory(MRestNettyThreadType.WORKER, listenPort));
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            bootstrap.option(ChannelOption.TCP_NODELAY, true);
            bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new MRestServerChannelInitializer(this));
            Channel channel = bootstrap.bind(listenPort).sync().channel();
            if (logger.isInfoEnabled()) {
                logger.info("start server: {} success, listening on port: {}", serverName, listenPort);
            }
            final Thread syncThread = new Thread(() -> {
                try {
                    channel.closeFuture().syncUninterruptibly();
                } catch (Throwable throwable) {
                    if (logger.isErrorEnabled()) {
                        logger.error("server: {} channel close future synchronized failed", serverName, throwable);
                    }
                }
            });
            syncThread.setName(serverName + "-closeFuture.Sync");
            syncThread.setDaemon(true);
            syncThread.start();
            started = true;
        } catch (Throwable throwable) {
            throw new MRestServerInitializeException(String.format("start server: %s failed", serverName), throwable);
        }
    }


    /**************************************************** SEP ****************************************************/
    /**************************************************** SEP ****************************************************/

    private final Map> urlMappingHandler = new HashMap<>();

    /**
     * 添加url映射处理的任务(在服务启动时统一添加).
     */
    private final List mappingTaskList = new ArrayList<>();

    /**
     * 指定url是否是已指定映射处理.
     * @param requestURL requestURL
     * @param methods methods
     * @return boolean
     */
    public boolean isMappingURL(String requestURL, HttpMethod... methods) {
        if (urlMappingHandler.containsKey(requestURL)) {
            Map handlerMap = urlMappingHandler.get(requestURL);
            for (HttpMethod method: methods) {
                if (handlerMap.get(method) != null) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 检查映射url正确性.
     * @param url url
     * @param methods methods
     */
    private void checkMappingUrl(String url, HttpMethod... methods) {
        if (StringUtils.isBlank(url)) {
            throw new IllegalArgumentException(String.format("illegal mapping url: %s", url));
        }
        if (isMappingURL(url, methods)) {
            throw new MRestServerInitializeException(String.format("url mapping conflict: %s", url));
        }
    }

    private void checkMappingHandler(MRestHandler handler) {
        if (handler == null || handler.getHandler() == null) {
            throw new NullPointerException();
        }
    }

    public MRestServer mapping(String url, Supplier handler, HttpMethod... methods) {
        return mapping(url, handler, MRestHeaderBuilder.Build(), methods);
    }

    public MRestServer mapping(String url, Supplier handler, Map headers, HttpMethod... methods) {
        return mapping(url, handler, MRestHandlerConfig.newInstance(headers), methods);
    }

    public MRestServer mapping(String url, Supplier handler, MRestHandlerConfig config, HttpMethod... methods) {
        return mapping(url, new MRestHandlerSupplier<>(url, handler, config, methods), methods);
    }

    public MRestServer mapping(String url, Runnable handler, HttpMethod... methods) {
        return mapping(url, handler, MRestHeaderBuilder.Build(), methods);
    }

    public MRestServer mapping(String url, Runnable handler, Map headers, HttpMethod... methods) {
        return mapping(url, handler, MRestHandlerConfig.newInstance(headers), methods);
    }

    public MRestServer mapping(String url, Runnable handler, MRestHandlerConfig config, HttpMethod... methods) {
        return mapping(url, new MRestHandlerConsumerVoid(url, handler, config, methods), methods);
    }

    public MRestServer mapping(String url, Consumer handler, HttpMethod... methods) {
        return mapping(url, handler, MRestHeaderBuilder.Build(), methods);
    }

    public MRestServer mapping(String url, Consumer handler, Map headers, HttpMethod... methods) {
        return mapping(url, handler, MRestHandlerConfig.newInstance(headers), methods);
    }

    public MRestServer mapping(String url, Consumer handler, MRestHandlerConfig config, HttpMethod... methods) {
        return mapping(url, new MRestHandlerConsumerReq<>(url, handler, config, methods), methods);
    }

    public MRestServer mapping(String url, BiConsumer handler, HttpMethod... methods) {
        return mapping(url, handler, MRestHeaderBuilder.Build(), methods);
    }

    public MRestServer mapping(String url, BiConsumer handler, Map headers, HttpMethod... methods) {
        return mapping(url, handler, MRestHandlerConfig.newInstance(headers), methods);
    }

    public MRestServer mapping(String url, BiConsumer handler, MRestHandlerConfig config, HttpMethod... methods) {
        return mapping(url, new MRestHandlerConsumerReqResp<>(url, handler, config, methods), methods);
    }

    public  MRestServer mapping(String url, Function handler, HttpMethod... methods) {
        return mapping(url, handler, MRestHeaderBuilder.Build(), methods);
    }

    public  MRestServer mapping(String url, Function handler, Map headers, HttpMethod... methods) {
        return mapping(url, handler, MRestHandlerConfig.newInstance(headers), methods);
    }

    public  MRestServer mapping(String url, Function handler, MRestHandlerConfig config, HttpMethod... methods) {
        return mapping(url, new MRestHandlerFunction<>(url, handler, config, methods), methods);
    }

    public  MRestServer mapping(String url, BiFunction handler, HttpMethod... methods) {
        return mapping(url, handler, MRestHeaderBuilder.Build(), methods);
    }

    public  MRestServer mapping(String url, BiFunction handler, Map headers, HttpMethod... methods) {
        return mapping(url, handler, MRestHandlerConfig.newInstance(headers), methods);
    }

    public  MRestServer mapping(String url, BiFunction handler, MRestHandlerConfig config, HttpMethod... methods) {
        return mapping(url, new MRestHandlerBiFunction<>(url, handler, config, methods), methods);
    }

    private synchronized MRestServer mapping(String url, MRestHandler handler, HttpMethod... methods) {
        checkServerState();
        mappingTaskList.add(() -> {
            checkMappingUrl(url, methods);
            checkMappingHandler(handler);
            Map handlerMap = urlMappingHandler.computeIfAbsent(url, k -> new HashMap<>());
            for (HttpMethod method: methods) {
                handlerMap.put(method, handler);
            }
            if (logger.isInfoEnabled()) {
                logger.info("server: {} register url handler success, {}, {}", serverName, methods, url);
            }
        });
        return this;
    }

    public MRestHandler getUrlMappingHandler(String requestURL, HttpMethod method) {
        Map handlerMap = urlMappingHandler.get(requestURL);
        if (handlerMap != null) {
            return handlerMap.get(method);
        }
        return null;
    }

    public  MRestServer get(String url, Supplier handler) {
        return mapping(url, handler, HttpMethod.GET);
    }

    public  MRestServer get(String url, Supplier handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.GET);
    }

    public  MRestServer get(String url, Supplier handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.GET);
    }

    public MRestServer get(String url, Runnable handler) {
        return mapping(url, handler, HttpMethod.GET);
    }

    public MRestServer get(String url, Runnable handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.GET);
    }

    public MRestServer get(String url, Runnable handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.GET);
    }

    public MRestServer get(String url, Consumer handler) {
        return mapping(url, handler, HttpMethod.GET);
    }

    public MRestServer get(String url, Consumer handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.GET);
    }

    public MRestServer get(String url, Consumer handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.GET);
    }

    public  MRestServer get(String url, Function handler) {
        return mapping(url, handler, HttpMethod.GET);
    }

    public  MRestServer get(String url, Function handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.GET);
    }

    public  MRestServer get(String url, Function handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.GET);
    }

    public  MRestServer get(String url, BiFunction handler) {
        return mapping(url, handler, HttpMethod.GET);
    }

    public  MRestServer get(String url, BiFunction handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.GET);
    }

    public  MRestServer get(String url, BiFunction handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.GET);
    }

    public MRestServer get(String url, BiConsumer handler) {
        return mapping(url, handler, HttpMethod.GET);
    }

    public MRestServer get(String url, BiConsumer handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.GET);
    }

    public MRestServer get(String url, BiConsumer handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.GET);
    }

    public MRestServer post(String url, Consumer handler) {
        return mapping(url, handler, HttpMethod.POST);
    }

    public MRestServer post(String url, Consumer handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.POST);
    }

    public MRestServer post(String url, Consumer handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.POST);
    }

    public  MRestServer post(String url, Function handler) {
        return mapping(url, handler, HttpMethod.POST);
    }

    public  MRestServer post(String url, Function handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.POST);
    }

    public  MRestServer post(String url, Function handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.POST);
    }

    public  MRestServer post(String url, BiFunction handler) {
        return mapping(url, handler, HttpMethod.POST);
    }

    public  MRestServer post(String url, BiFunction handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.POST);
    }

    public  MRestServer post(String url, BiFunction handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.POST);
    }

    public MRestServer post(String url, BiConsumer handler) {
        return mapping(url, handler, HttpMethod.POST);
    }

    public MRestServer post(String url, BiConsumer handler, Map headers) {
        return mapping(url, handler, headers, HttpMethod.POST);
    }

    public MRestServer post(String url, BiConsumer handler, MRestHandlerConfig config) {
        return mapping(url, handler, config, HttpMethod.POST);
    }

    public MRestServer fileupload(String url, Consumer handler) {
        return post(url, handler);
    }

    public  MRestServer fileupload(String url, Function handler) {
        return post(url, handler);
    }

    public  MRestServer fileupload(String url, BiFunction handler) {
        return post(url, handler);
    }

    public MRestServer fileupload(String url, BiConsumer handler) {
        return post(url, handler);
    }

    public MRestServer filedownload(String url, BiConsumer handler) {
        return get(url, handler);
    }


    /**************************************************** SEP ****************************************************/
    /**************************************************** SEP ****************************************************/


    /**
     * filter映射处理.
     */
    private final Map> filterMap = new HashMap<>();
    /**
     * 请求分发处理.
     */
    private final MRestFilter requestFilter = new MRestDispatchFilter();
    /**
     * 静态资源处理.
     */
    private final MRestFilter staticResourceFilter = new StaticResourceFilter();
    /**
     * 添加filter的任务(在服务启动时统一添加).
     */
    private final List filterTaskList = new ArrayList<>();

    public MRestFilterChain getCommonStaticResourceFilterChain(String requestURL) {
        List filterList = new LinkedList<>();
        filterList.add(staticResourceFilter);
        filterList.add(requestFilter);
        return new MRestFilterChain(this, filterList.toArray(new MRestFilter[0]));
    }

    public MRestFilterChain getFilterChain(String requestURL) {
        Set filterSet = new HashSet<>();
        filterMap.forEach((urlPattern, filterList) -> {
            String pattern = "^" + urlPattern.replace("*", "\\S*") + "$";
            if (requestURL.matches(pattern)) {
                filterSet.addAll(filterList);
            }
        });
        // 对filter进行排序, 按照order小到大进行顺序排序.
        LinkedList filterList = new LinkedList<>(filterSet);
        filterList.sort((filter0, filter1) -> {
            int order0 = filter0.order();
            int order1 = filter1.order();
            return order0 - order1;
        });
        filterList.addLast(staticResourceFilter);
        filterList.addLast(requestFilter);
        return new MRestFilterChain(this, filterList.toArray(new MRestFilter[0]));
    }

    /**
     * 添加filter, 自动扫描filter注解, 获取urlPattern.
     * @param filterArr filterArr
     * @return MRestServer
     */
    public synchronized MRestServer filter(MRestFilter... filterArr) {
        for (MRestFilter filter: filterArr) {
            String[] urlPatterns = filter.urlPatterns();
            if (urlPatterns == null || urlPatterns.length == 0) {
                urlPatterns = Constants.DEFAULT_FILTER_URLPATTERNS;
            }
            filter0(filter, urlPatterns);
        }
        return this;
    }

    /**
     * 添加filter, 根据传入urlPattern来进行匹配.
     * @param urlPattern urlPattern
     * @param filterArr filterArr
     * @return MRestServer
     */
    public synchronized MRestServer filter(String urlPattern, MRestFilter... filterArr) {
        for (MRestFilter filter: filterArr) {
            filter0(filter, urlPattern);
        }
        return this;
    }

    /**
     * 添加filter, 一次指定多个urlPattern.
     * @param filter filter
     * @param urlPatterns urlPattern array.
     * @return MRestServer
     */
    public synchronized MRestServer filter(MRestFilter filter, String... urlPatterns) {
        return filter0(filter, urlPatterns);
    }

    private synchronized MRestServer filter0(MRestFilter filter, String... urlPatterns) {
        checkServerState();
        filterTaskList.add(() -> {
            MRestFilter restFilter = Objects.requireNonNull(filter);
            if (urlPatterns.length == 0) {
                throw new IllegalArgumentException("can't assign empty urlPatterns to filter: " + filter.filterName());
            }
            for (String urlPattern: urlPatterns) {
                filterMap.computeIfAbsent(urlPattern, k -> new ArrayList<>()).add(restFilter);
                if (logger.isInfoEnabled()) {
                    logger.info("server: {} register filter success, {} -> {}", serverName, urlPattern, filter.filterName());
                }
            }
        });
        return this;
    }


    /**************************************************** SEP ****************************************************/
    /**************************************************** SEP ****************************************************/


    private BiConsumer defaultErrorHandler = null;

    public BiConsumer getDefaultErrorHandler() {
        return defaultErrorHandler;
    }

    public MRestServer defaultErrorHandler(BiConsumer defaultErrorHandler) {
        this.defaultErrorHandler = Objects.requireNonNull(defaultErrorHandler);
        return this;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy