设计模式实战 - 工厂类获取数据同步器 OpenDataSynchronizerFactory

上篇博客实现了通过工厂类获取设备连接器: Java设计模式实战 - 02 工厂类获取设备连接器 DeviceConnectorFactory 。这篇博客会在上篇博客的基础上通过工厂类获取数据同步器;

1. 项目背景

SIR平台上,从XDR平台接入的安全告警数据,为了保证两个平台安全告警数据的处置状态一致,SIR平台需要将已经处置闭环的安全告警数据的处置状态同步给XDR平台,从而更新XDR平台上安全告警数据的处置状态。

2. 设备类型枚举 SourceDeviceEnum

考虑到将来可能会接入SIP或其他平台的数据,这里设计的时候做了扩展,将来如果想继续接入新的第三方连接设备,只需要在设备类型枚举 SourceDeviceEnum 中加入一个设备即可。

public enum SourceDeviceEnum {
    /**
     * XDR设备
     */
    XDR;

    public static SourceDeviceEnum build(String srcDevice) {
        for (SourceDeviceEnum sourceDeviceEnum : values()) {
            if (sourceDeviceEnum.name().equals(srcDevice)) {
                return sourceDeviceEnum;
            }
        }
        return null;
    }
}

3. 同步数据类型枚举 SyncDataTypeEnum

考虑到将来可能会接入更多类型的数据,比如资产数据,脆弱性数据,这里设计的时候做了扩展,将来如果想同步其他类型数据,只需要在数据类型枚举 SyncDataTypeEnum 中加入一个类型即可。

/**
 * 同步的数据类型
 */
public enum SyncDataTypeEnum {

    /**
     * 事件处置状态
     */
    INCIDENT_DEAL_STATUS,

    /**
     * 告警处置状态
     */
    ALERT_DEAL_STATUS;
}

4. 自定义注解用于标识向哪个设备同步哪种数据 @SupportType

/**
 * 用于
 * {@link OpenDataSynchronizer} 标注支持的同步类型
 * {@link DeviceConnector} 标注支持的设备连接器
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SupportType {

    /**
     * @return 支持的同步数据类型
     */
    SyncDataTypeEnum[] syncDataType() default {};

    /**
     * @return 支持的设备类型
     */
    SourceDeviceEnum[] sourceDevice() default {};
}

5. 数据同步器接口 OpenDataSynchronizer

public interface OpenDataSynchronizer<DATA, RETURN> {

    /**
     * 同步数据
     * 
     * @param connection 设备连接
     * @param data       同步数据内容
     * @return 同步结果
     */
    RETURN sync(DeviceConnection connection, DATA data);
}

6. Xdr安全告警处置状态同步器类 XdrAlertDealStatusSynchronizer

@Slf4j
@Service
// 注解标识设备连接为XDR,数据状态为ALERT_DEAL_STATUS
@SupportType(sourceDevice = SourceDeviceEnum.XDR, syncDataType = SyncDataTypeEnum.ALERT_DEAL_STATUS)
public class XdrAlertDealStatusSynchronizer implements OpenDataSynchronizer<List<KafkaDealStatusSyncDto>, ApiResponse<?>> {

    /**
     * XDR 接口单次上限100
     */
    final private static int BATCH_SIZE = 100;

    @Setter(onMethod_ = @Autowired)
    private LinkageBusClient linkageBusClient;

    @Override
    public ApiResponse<?> sync(DeviceConnection connection, List<KafkaDealStatusSyncDto> dataList) {
        if (Objects.nonNull(connection) && !CollectionUtils.isEmpty(dataList)) {
            XdrDeviceConnection xdrDeviceConnection = (XdrDeviceConnection) connection;
            // feign调用之前设置本地线程AKSK信息
            AkSkHolder.set(xdrDeviceConnection.getAkSk());
            // 分批次推送
            ArrayList<KafkaDealStatusSyncDto> batchList = new ArrayList<>();
            int size = dataList.size();
 for (int i = 0; i < dataList.size(); i++) {
                KafkaDealStatusSyncDto data = dataList.get(i);
                XdrDisposalSceneEnum disposalScene = XdrDisposalSceneEnum.buildByName(data.getModifyType());
                assert disposalScene != null;
                data.setModifyType(disposalScene.getXdrScene());
                batchList.add(data);
                if (batchList.size() == BATCH_SIZE || i == size - 1) {
                    // 转换数据为XDR状态同步接口请求入参
                    XdrSyncIncidentStatusQo xdrSyncIncidentStatusQo = convertToXdrSyncIncidentStatusQo(batchList);
                    XdrApiResponse<SyncDealStatusResponse> res = linkageBusClient.syncAlertStatus(
                            URI.create(xdrDeviceConnection.getLinkageBusUrl()), xdrDeviceConnection.getCompanyId(), xdrSyncIncidentStatusQo
                    );
                    log.info("feign call response: {}, {} data pushed", res, batchList.size());
// 一个出错就失败,todo: 和XDR确认批量部分成功返回码
                    if (!XdrApiResponse.CODE_SUCCESS.equals(res.getCode()) || res.getData().getCode() == ApiResponse.CODE_ERR_COMMON) {
                        throw new DataSyncException("exception.xdr.sync.data.failed");
                    }
                    batchList.clear();
                }
            }
            return ApiResponse.newInstance(ApiResponse.CODE_OK, null);
        }
        throw new DataSyncException("exception.xdr.sync.data.failed");
    }

    /**
     * 将同步的数据转换为请求入参
     * 
     * @param dataList 同步数据
     * @return 请求入参
     */
    private XdrSyncIncidentStatusQo convertToXdrSyncIncidentStatusQo(List<KafkaDealStatusSyncDto> dataList) {
        return new XdrSyncIncidentStatusQo(dataList);
    }
}               

7. Xdr安全事件处置状态同步器类 XdrIncidentDealStatusSynchronizer

@Slf4j
@Service
@SupportType(sourceDevice = SourceDeviceEnum.XDR, syncDataType = SyncDataTypeEnum.INCIDENT_DEAL_STATUS)
public class XdrIncidentDealStatusSynchronizer implements OpenDataSynchronizer<List<KafkaDealStatusSyncDto>, ApiResponse<?>> {

    /**
     * XDR 接口单次上限100
     */
    final private static int BATCH_SIZE = 100;

    @Setter(onMethod_ = @Autowired)
    private LinkageBusClient linkageBusClient;

    @Override
    public ApiResponse<?> sync(DeviceConnection connection, List<KafkaDealStatusSyncDto> dataList) {
        if (Objects.nonNull(connection) && !CollectionUtils.isEmpty(dataList)) {
            XdrDeviceConnection xdrDeviceConnection = (XdrDeviceConnection) connection;
            // feign调用之前设置本地线程AKSK信息
            AkSkHolder.set(xdrDeviceConnection.getAkSk());
            // 分批次推送
            ArrayList<KafkaDealStatusSyncDto> batchList = new ArrayList<>();
 int size = dataList.size();
            for (int i = 0; i < dataList.size(); i++) {
                KafkaDealStatusSyncDto data = dataList.get(i);
                XdrDisposalSceneEnum disposalScene = XdrDisposalSceneEnum.buildByName(data.getModifyType());
                assert disposalScene != null;
                data.setModifyType(disposalScene.getXdrScene());
                batchList.add(data);
                if (batchList.size() == BATCH_SIZE || i == size - 1) {
                    // 转换数据为XDR状态同步接口请求入参
                    XdrSyncIncidentStatusQo xdrSyncIncidentStatusQo = convertToXdrSyncIncidentStatusQo(batchList);
                    XdrApiResponse<SyncDealStatusResponse> res = linkageBusClient.syncIncidentStatus(
                            URI.create(xdrDeviceConnection.getLinkageBusUrl()), xdrDeviceConnection.getCompanyId(), xdrSyncIncidentStatusQo
                    );
                    log.info("feign call response: {}, {} data pushed", res, batchList.size());
   // 一个出错就失败,todo: 和XDR确认批量部分成功返回码
                    if (!XdrApiResponse.CODE_SUCCESS.equals(res.getCode()) || res.getData().getCode() == ApiResponse.CODE_ERR_COMMON) {
                        throw new DataSyncException("exception.xdr.sync.data.failed");
                    }
                    batchList.clear();
                }
            }
            return ApiResponse.newInstance(ApiResponse.CODE_OK, null);
        }
        throw new DataSyncException("exception.xdr.sync.data.failed");
    }

    /**
     * 将同步的数据转换为请求入参
     * 
     * @param dataList 同步数据
     * @return 请求入参
     */
    private XdrSyncIncidentStatusQo convertToXdrSyncIncidentStatusQo(List<KafkaDealStatusSyncDto> dataList) {
        return new XdrSyncIncidentStatusQo(dataList);
    }
}              

8. 数据同步器工厂类 OpenDataSynchronizerFactory

@Service
public class OpenDataSynchronizerFactory<DATA, RETURN> {

    // <设备类型,<数据类型,数据同步器>>
    private final ConcurrentHashMap<SourceDeviceEnum, ConcurrentHashMap<SyncDataTypeEnum, OpenDataSynchronizer<DATA, RETURN>>> SYNCHRONIZERS = new ConcurrentHashMap<>();

    public OpenDataSynchronizerFactory(List<OpenDataSynchronizer<DATA, RETURN>> synchronizes/*Spring容器中的数据同步器*/) {
        for (OpenDataSynchronizer<DATA, RETURN> synchronizer : synchronizes) {
            // 获取数据同步器上的 @SupportType 注解
            SupportType supportTypeAnno = AnnotationUtils.findAnnotation(synchronizer.getClass(), SupportType.class);
            if (Objects.nonNull(supportTypeAnno)) {
                // 获取注解的属性值
                // 设备类型
                SourceDeviceEnum[] sourceDevices = supportTypeAnno.sourceDevice();
                // 数据类型
                SyncDataTypeEnum[] syncDataTypes = supportTypeAnno.syncDataType();
                // 缓存所有所有数据同步器
for (SourceDeviceEnum sourceDevice : sourceDevices) {
                    final ConcurrentHashMap<SyncDataTypeEnum, OpenDataSynchronizer<DATA, RETURN>> synchronizesWithDevice = SYNCHRONIZERS.getOrDefault(sourceDevice, new ConcurrentHashMap<>());
                    for (SyncDataTypeEnum syncDataType : syncDataTypes) {
                        synchronizesWithDevice.put(syncDataType, synchronizer);
                    }
                    SYNCHRONIZERS.put(sourceDevice, synchronizesWithDevice);
                }
            }
        }
    }

    // 获取一个数据同步器
    public OpenDataSynchronizer<DATA, RETURN> get(SourceDeviceEnum sourceDevice, SyncDataTypeEnum syncDataType) {
        if (Objects.nonNull(sourceDevice) && Objects.nonNull(syncDataType)) {
            final ConcurrentHashMap<SyncDataTypeEnum, OpenDataSynchronizer<DATA, RETURN>> synchronizesWithDevice = SYNCHRONIZERS.get(sourceDevice);
            if (!CollectionUtils.isEmpty(synchronizesWithDevice)) {
                OpenDataSynchronizer<DATA, RETURN> synchronizer = synchronizesWithDevice.get(syncDataType);
                if (Objects.nonNull(synchronizer)) {
                    return synchronizer;
                }
            }
        }
        throw new DataSyncException("exception.sync.data.type.not.support");
    }
}

9. 数据同步分发 OpenDataSyncDispatchServiceImpl

@Service
public class OpenDataSyncDispatchServiceImpl<DATA, RETURN> implements OpenDataSyncDispatchService<DATA, RETURN> {

    @Setter(onMethod_ = @Autowired)
    private DeviceConnectorFactory deviceConnectorFactory;

    @Setter(onMethod_ = @Autowired)
    private OpenDataSynchronizerFactory<DATA, RETURN> openDataSynchronizerFactory;

    @Setter(onMethod_ = @Autowired)
    private ThreadPoolTaskExecutor executor;

    @Override
    public RETURN dispatch(SyncDataWrapper<DATA> syncDataWrapper) {
        // 设备类型
        SourceDeviceEnum sourceDevice = syncDataWrapper.getDeviceInfo().getSourceDevice();
        // 同步的数据类型
        SyncDataTypeEnum syncDataType = syncDataWrapper.getSyncDataType();
        // 通过日志来源设备和同步数据类型获取一个同步器
        OpenDataSynchronizer<DATA, RETURN> synchronizer = openDataSynchronizerFactory.get(sourceDevice, syncDataType);
        // 通过日志来源设备获取一个设备连接器
        DeviceConnector connector = deviceConnectorFactory.get(sourceDevice);
        // 通过设备连接器打开一个连接
        DeviceConnection connection = connector.connect(syncDataWrapper.getDeviceInfo());
        // 同步数据
        return synchronizer.sync(connection, syncDataWrapper.getData());
    }

    @Override
    public Future<RETURN> dispatchAsync(SyncDataWrapper<DATA> syncDataWrapper) {
        return executor.submit(() -> dispatch(syncDataWrapper));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我一直在流浪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值