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

com.joe.easysocket.server.backserver.mvc.impl.MvcControllerImpl Maven / Gradle / Ivy

The newest version!
package com.joe.easysocket.server.backserver.mvc.impl;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import javax.validation.ValidationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.joe.easysocket.server.backserver.impl.MvcDataworker;
import com.joe.easysocket.server.backserver.mvc.MvcController;
import com.joe.easysocket.server.backserver.mvc.coder.DataReader;
import com.joe.easysocket.server.backserver.mvc.coder.DataReaderContainer;
import com.joe.easysocket.server.backserver.mvc.coder.DataWriter;
import com.joe.easysocket.server.backserver.mvc.coder.DataWriterContainer;
import com.joe.easysocket.server.backserver.mvc.container.BeanContainer;
import com.joe.easysocket.server.backserver.mvc.context.RequestContext;
import com.joe.easysocket.server.backserver.mvc.context.SessionManager;
import com.joe.easysocket.server.backserver.mvc.data.InterfaceData;
import com.joe.easysocket.server.backserver.mvc.impl.context.HttpRequestContext;
import com.joe.easysocket.server.backserver.mvc.impl.context.HttpResponseContext;
import com.joe.easysocket.server.backserver.mvc.impl.exception.*;
import com.joe.easysocket.server.backserver.mvc.impl.exceptionmapper.ExceptionMapper;
import com.joe.easysocket.server.backserver.mvc.impl.exceptionmapper.ExceptionMapperContainer;
import com.joe.easysocket.server.backserver.mvc.impl.filter.FilterContainer;
import com.joe.easysocket.server.backserver.mvc.impl.param.ParamParserContainer;
import com.joe.easysocket.server.backserver.mvc.impl.resource.Resource;
import com.joe.easysocket.server.backserver.mvc.impl.resource.ResourceContainer;
import com.joe.utils.common.StringUtils;
import com.joe.utils.data.BaseDTO;
import com.joe.utils.parse.json.JsonParser;
import com.joe.utils.protocol.Datagram;

/**
 * MVC控制器
 *
 * @author joe
 * @version 2018.03.06 10:34
 */
public class MvcControllerImpl implements MvcController {
    private static final Logger      LOGGER      = LoggerFactory.getLogger(MvcDataworker.class);
    private static final JsonParser  JSON_PARSER = JsonParser.getInstance();
    private ResourceContainer        resourceContainer;
    private FilterContainer          filterContainer;
    private DataWriterContainer      dataWriterContainer;
    private DataReaderContainer      dataReaderContainer;
    private ExceptionMapperContainer exceptionMapperContainer;
    private SessionManager           sessionManager;
    private BeanContainer            beanContainer;
    private ParamParserContainer     paramParserContainer;
    //执行任务的线程池
    private ExecutorService          service;
    //关闭标志,为true时表示关闭
    private volatile boolean         shutdown    = true;
    /**
     * 数据处理线程名的格式
     */
    private String                   threadName;
    /**
     * 最大线程数
     */
    private int                      maxThread;
    /**
     * 最小线程数
     */
    private int                      minThread;
    /**
     * 空闲线程存活时间,单位为秒
     */
    private long                     threadAliveTime;

    /**
     * MVC控制器
     *
     * @param sessionManager  session管理器
     * @param beanContainer   bean容器,不能为null
     * @param maxThread       MVC处理器线程池的最大线程数
     * @param minThread       MVC处理器线程池的最小线程数
     * @param threadAliveTime MVC处理器线程池空闲线程存活时间,单位为秒
     */
    public MvcControllerImpl(SessionManager sessionManager, BeanContainer beanContainer,
                             int maxThread, int minThread, long threadAliveTime) {
        if (sessionManager == null || beanContainer == null) {
            throw new NullPointerException("sessionManager or beanContainer must not be null");
        }
        this.sessionManager = sessionManager;
        this.beanContainer = beanContainer;
        this.maxThread = maxThread <= 0 ? Runtime.getRuntime().availableProcessors() * 150
            : maxThread;
        this.minThread = minThread <= 0 ? Runtime.getRuntime().availableProcessors() * 50
            : minThread;
        this.threadAliveTime = threadAliveTime <= 0 ? 30 : threadAliveTime;
        this.threadName = "mvc处理线程%d";
    }

    @Override
    public synchronized void start() {
        if (!shutdown) {
            LOGGER.debug("服务器已经启动,请勿重复启动");
            return;
        }
        init();
        LOGGER.debug("服务器启动成功");
        shutdown = false;
    }

    @Override
    public synchronized void shutdown() {
        if (shutdown) {
            LOGGER.debug("服务器已经关闭,请勿重复关闭");
            return;
        }

        LOGGER.info("销毁MVC数据处理器");
        LOGGER.debug("关闭正在运行的数据处理,等待当前数据处理的完成");
        service.shutdown();
        LOGGER.debug("数据处理完成,关闭数据处理服务");

        beanContainer.shutdown();
        sessionManager.shutdown();

        resourceContainer.shutdown();
        filterContainer.shutdown();
        dataWriterContainer.shutdown();
        dataReaderContainer.shutdown();
        exceptionMapperContainer.shutdown();
        paramParserContainer.shutdown();
        LOGGER.info("MVC数据处理器销毁成功");
        shutdown = true;
    }

    @Override
    public  void deal(Datagram datagram, R requestContext,
                                                Consumer consumer) throws NullPointerException {
        LOGGER.debug("读取到数据为:{}", datagram);
        if (datagram == null) {
            LOGGER.warn("接收到的数据为空,不处理");
            throw new NullPointerException("接收到的数据为空");
        }

        if (requestContext == null) {
            LOGGER.warn("RequestContext为空,不处理");
            throw new NullPointerException("RequestContext为空");
        }

        if (consumer == null) {
            LOGGER.warn("Consumer为空");
            throw new NullPointerException("Consumer为空");
        }

        if (worker(datagram.getType())) {
            LOGGER.debug("该数据可以处理,提交到线程池开始处理");
            service.submit(() -> {
                InterfaceData result = accept(datagram.getBody(), requestContext);
                LOGGER.debug("MVC数据处理器处理{}的结果为:{}", datagram, result);
                consumer.accept(result);
            });
        }
    }

    /**
     * 初始化
     */
    private void init() {
        LOGGER.info("开始初始化MVC数据处理器");

        //构建数据处理ThreadFactory
        ThreadFactory factory = new ThreadFactory() {
            AtomicInteger counter = new AtomicInteger(0);

            public Thread newThread(Runnable r) {
                return new Thread(r, String.format(threadName, this.counter.getAndAdd(1)));
            }
        };

        //构建线程池
        LOGGER.debug("构建线程池,参数为{}:{}:{}:{}", minThread, maxThread, threadAliveTime, threadName);
        service = new ThreadPoolExecutor(minThread, maxThread, threadAliveTime, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(), factory);
        LOGGER.debug("线程池构建完成");

        //不加该行service中没有提交过任务时当程序运行完毕会自动关闭,只有这样该线程组才不会自动关闭,后端项目才不
        // 会启动后就立即自动关闭
        service.submit(() -> LOGGER.debug("线程组启动成功"));

        LOGGER.info("初始化容器");
        beanContainer.start();
        sessionManager.start();
        resourceContainer = new ResourceContainer(beanContainer);
        filterContainer = new FilterContainer(beanContainer);
        dataWriterContainer = new DataWriterContainer(beanContainer);
        paramParserContainer = new ParamParserContainer(beanContainer);
        dataReaderContainer = new DataReaderContainer(beanContainer, paramParserContainer);
        exceptionMapperContainer = new ExceptionMapperContainer(beanContainer);

        resourceContainer.start();
        filterContainer.start();
        dataWriterContainer.start();
        dataReaderContainer.start();
        exceptionMapperContainer.start();
        paramParserContainer.start();
        LOGGER.info("容器初始化完毕");
        LOGGER.info("MVC数据处理器初始化完毕");
    }

    /**
     * 处理数据
     *
     * @param body 待处理数据
     * @param r    RequestContext
     * @param   RequestContext的类型
     * @return 处理结果
     * @throws NullPointerException 此处有可能抛出NullPointException,在message解析失败时会抛出{@link RequestDataError RequestDataError}
     *                              异常,该异常系统不会自动处理,最后会交给用户异常处理器处理,而当用户异常处理器没有正确处理该异常时最终会抛
     *                              出{@link NullPointerException NullPointerException},主要是由于构建异常消息时需要从message中获取ID,而此时
     *                              message是null,所以会抛出@link NullPointerException NullPointerException}
     */
    private  InterfaceData accept(byte[] body,
                                                            R r) throws NullPointerException {
        LOGGER.debug("接收到数据,开始处理。[{}]", body);
        HttpResponseContext responseContext;
        InterfaceData resultData;
        InterfaceData message = null;
        HttpRequestContext requestContext = (HttpRequestContext) r;

        if (body == null || body.length == 0) {
            //请求必须有请求体,否则最基本的invoke信息都没有
            LOGGER.warn("该请求没有请求体,请求内容为null");
            return null;
        }
        // 构建响应上下文,必须在此处初始化,否则当发生异常的时候异常中获取到的responseContext是空,无法获取到信息
        responseContext = new HttpResponseContext();
        // MVC数据处理器只有这一种请求data,直接读取
        LOGGER.debug("开始解析请求数据");

        try {
            message = JSON_PARSER.readAsObject(body, InterfaceData.class);
            if (message == null) {
                String msg = Arrays.toString(body);
                LOGGER.debug("数据解析异常,用户数据为:[{}],要解析的类型为:[{}]", msg, InterfaceData.class);
                throw new RequestDataError("请求数据异常,数据为:" + msg);
            }
            LOGGER.debug("请求数据解析完毕,请求数据为:{}", message);

            // 搜索指定的resource
            Resource resource = findResource(message.getInvoke());
            // 放进请求上下文
            requestContext.setResource(resource);
            LOGGER.debug("请求的资源查找完成,请求资源为:{}", resource);

            // 开始查找数据编码器
            LOGGER.debug("开始查找数据编码器");
            // 找到请求数据编码处理器
            DataReader dataReader = findReaderInterceptor(resource.getConsume());
            requestContext.setReader(dataReader);
            LOGGER.debug("数据编码器为:{}", dataReader);

            LOGGER.debug("开始解码参数");
            // 开始解码数据
            Object[] param = dataReader.read(resource.getParams(), requestContext,
                message.getData());
            requestContext.setParams(param);
            LOGGER.debug("参数解码完毕,参数为:{}", param);

            LOGGER.debug("开始验证参数");
            resource.check(param);
            LOGGER.debug("参数验证完毕");

            LOGGER.debug("开始请求filter");
            // 请求filter
            filterContainer.requestFilter(requestContext.getRequest());
            LOGGER.debug("filter完毕,开始调用资源");

            // 调用资源
            Object result = resource.invoke(requestContext);
            responseContext.getResponse().setResult(result);
            LOGGER.debug("资源调用完毕,请求结果为:{}", result);

            // 响应
            LOGGER.debug("开始处理响应");
            resultData = response(requestContext, responseContext, message);
            LOGGER.debug("响应处理完毕");
        } catch (ResourceNotFoundException e) {
            LOGGER.error("用户请求的资源不存在", e);
            resultData = buildResult(new BaseDTO<>("404"), message.getId(), message.getInvoke(),
                findWriterInterceptor(null));
        } catch (MediaTypeNoSupportException e) {
            LOGGER.error("找不到对应的参数解析器", e);
            resultData = buildResult(new BaseDTO<>("504"), message.getId(), message.getInvoke(),
                resolveDataInterceptor(requestContext, responseContext));
        } catch (ParamParserException e) {
            LOGGER.error("参数解析失败", e);
            resultData = buildResult(new BaseDTO<>("505"), message.getId(), message.getInvoke(),
                resolveDataInterceptor(requestContext, responseContext));
        } catch (ValidationException e) {
            LOGGER.error("参数验证失败", e);
            resultData = buildResult(new BaseDTO<>("506"), message.getId(), message.getInvoke(),
                resolveDataInterceptor(requestContext, responseContext));
        } catch (FilterException e) {
            LOGGER.error("filter异常");
            resultData = buildResult(new BaseDTO<>("507"), message.getId(), message.getInvoke(),
                resolveDataInterceptor(requestContext, responseContext));
        } catch (Throwable e) {
            // 请求过程中发生了异常
            LOGGER.error("请求过程中发生了异常,开始查找相应的异常处理器处理异常", e);

            // 查找异常处理器
            List exceptionMappers = exceptionMapperContainer
                .select(mapper -> mapper.mapper(e));

            LOGGER.info("异常处理器查找完毕");
            if (exceptionMappers.isEmpty()) {
                LOGGER.error("异常没有找到相应的处理器", e);
                resultData = buildResult(new BaseDTO<>("508"), message.getId(), message.getInvoke(),
                    resolveDataInterceptor(requestContext, responseContext));
            } else {
                LOGGER.info("找到异常处理器,由相应的异常处理器处理");
                try {
                    HttpResponseContext.Response response = exceptionMappers.get(0).toResponse(e);
                    resultData = new InterfaceData(message.getId(), message.getInvoke(),
                        resolveDataInterceptor(requestContext, responseContext)
                            .write(response.getResult()));
                } catch (Throwable throwable) {
                    LOGGER.error("用户异常处理器[{}]处理异常[{}]的过程中又发生了异常", exceptionMappers.get(0), e,
                        throwable);
                    resultData = buildResult(new BaseDTO<>("509"), message.getId(),
                        message.getInvoke(),
                        resolveDataInterceptor(requestContext, responseContext));
                }
            }
        }
        return resultData;
    }

    /**
     * 响应处理
     *
     * @param requestContext  请求上下文
     * @param responseContext 响应上下文
     * @param userData        用户发来的请求数据
     * @return 响应数据
     * @throws MediaTypeNoSupportException 找不到相应数据的编码器
     * @throws FilterException             filter异常
     */
    private InterfaceData response(HttpRequestContext requestContext,
                                   HttpResponseContext responseContext,
                                   InterfaceData userData) throws MediaTypeNoSupportException,
                                                           FilterException {
        LOGGER.debug("开始构建响应");
        HttpRequestContext.RequestWrapper request = requestContext.getRequest();
        Resource resource = requestContext.getResource();
        HttpResponseContext.Response response = responseContext.getResponse();

        // 请求过程中没有发生异常
        // 响应filter
        filterContainer.responseFilter(request, response);

        // 找到处理响应的编码器
        DataWriter dataWriter = findWriterInterceptor(resource.getProduce());
        responseContext.setWriter(dataWriter);

        Object result = response.getResult();

        LOGGER.debug("请求结果为:{}", result);

        // 根据不同的结果分别处理,资源有可能没有返回,有可能返回聊天消息,也有可能返回正常的数据
        if (result == null) {
            LOGGER.debug("请求的接口{}没有响应消息,返回一个BaseDTO", userData.getInvoke());
            // 如果该接口没有响应消息,那么返回一个基本的请求成功
            return buildResult(BaseDTO.buildSuccess(), userData.getId(), userData.getInvoke(),
                dataWriter);
        } else if (result instanceof InterfaceData) {
            LOGGER.debug("用户响应的信息是InterfaceData对象");
            return (InterfaceData) result;
        } else {
            LOGGER.debug("请求接口{}的响应信息为:{}", userData.getInvoke(), result);
            // 该接口有响应消息并且不是聊天类型消息,那么直接将该消息返回
            return buildResult(result, userData.getId(), userData.getInvoke(), dataWriter);
        }
    }

    /**
     * 构建响应结果
     *
     * @param result     响应的结果
     * @param id         对应的请求消息的ID
     * @param invoke     对应的请求消息请求的接口名
     * @param dataWriter 对应的数据处理器
     * @return 响应结果
     */
    private InterfaceData buildResult(Object result, String id, String invoke,
                                      DataWriter dataWriter) {
        invoke = invoke.startsWith("/") ? "/back" + invoke : "/back/" + invoke;
        if (dataWriter == null) {
            return null;
        }
        return new InterfaceData(id, invoke, dataWriter.write(result));
    }

    /**
     * 查找数据解码器
     *
     * @param consume 待读取内容的类型
     * @return 指定类型对应的数据解码器
     * @throws MediaTypeNoSupportException 找不到指定类型数据解码器时抛出该异常
     */
    private DataReader findReaderInterceptor(String consume) throws MediaTypeNoSupportException {
        LOGGER.debug("要查找的数据解码器类型为:{}", consume);
        final String realConsume = StringUtils.isEmpty(consume) ? "json" : consume;
        List dataReaders = dataReaderContainer
            .select(reader -> reader.isReadable(realConsume));

        LOGGER.debug("数据编码器为:{}", dataReaders);
        if (dataReaders.isEmpty()) {
            // 找不到支持
            throw new MediaTypeNoSupportException(consume);
        }

        return dataReaders.get(0);
    }

    /**
     * 查找resource
     *
     * @param path resource的名字
     * @return 要查找的resource
     * @throws ResourceNotFoundException 找不到指定resource时抛出该异常
     */
    private Resource findResource(String path) throws ResourceNotFoundException {
        if (StringUtils.isEmpty(path)) {
            LOGGER.error("请求信息中没有请求资源的名字");
            throw new ResourceNotFoundException(String.valueOf(path));
        }
        Resource resource = resourceContainer.findResource(path);
        LOGGER.debug("请求的资源为:{}", resource);

        if (resource == null) {
            // 找不到要请求的resource
            LOGGER.error("没有找到{}对应的资源,请检查请求地址是否有误", path);
            throw new ResourceNotFoundException(path);
        }
        return resource;
    }

    /**
     * 查找数据编码器
     *
     * @param produce 要处理的数据的类型
     * @return 指定类型对应的数据编码器
     * @throws MediaTypeNoSupportException 找不到指定编码器时抛出该异常
     */
    private DataWriter findWriterInterceptor(String produce) throws MediaTypeNoSupportException {
        LOGGER.debug("查找{}格式的数据编码器", produce);
        final String dataProduce = StringUtils.isEmpty(produce) ? "json" : produce;

        List dataWriters = dataWriterContainer
            .select(dataInterceptor -> dataInterceptor.isWriteable(dataProduce));

        if (dataWriters.isEmpty()) {
            // 找不到支持
            throw new MediaTypeNoSupportException(String.valueOf(dataProduce));
        }
        return dataWriters.get(0);
    }

    /**
     * 确定一个响应数据处理器
     *
     * @param requestContext  请求上下文
     * @param responseContext 响应上下文
     * @return 响应数据处理器,该方法肯定会返回一个响应数据处理器
     */
    private DataWriter resolveDataInterceptor(HttpRequestContext requestContext,
                                              HttpResponseContext responseContext) {
        LOGGER.debug("根据上下文确定一个响应数据处理器");

        DataWriter writer = responseContext.getWriter();

        if (writer != null) {
            LOGGER.debug("响应过程中已经确定了响应数据处理器,直接返回");
            return writer;
        } else {
            Resource resource = requestContext.getResource();
            try {
                return findWriterInterceptor(resource.getProduce());
            } catch (Exception e) {
                return findWriterInterceptor(null);
            }
        }
    }

    /**
     * 该MVC处理器是否可以处理指定类型的数据
     *
     * @param type 数据类型
     * @return 返回true表示可以处理
     */
    private boolean worker(int type) {
        return type == 1;
    }

    static {
        // 参数错误
        BaseDTO.addStatus("400", "Fail Param");
        // 找不到参数解析器
        BaseDTO.addStatus("505", "Data Parser Not Found");
        // 找不到指定资源
        BaseDTO.addStatus("404", "NotFoundResource");
        // 数据非法
        BaseDTO.addStatus("501", "DataError");
        // 已经有其他用户登录
        BaseDTO.addStatus("502", "Other User Login");
        // 密码修改,请重新登录
        BaseDTO.addStatus("503", "Password Has Changed");
        // 心跳超时
        BaseDTO.addStatus("504", "Timeout");
        // 参数验证失败
        BaseDTO.addStatus("505", "validation fail");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy