Extensible Use of COLA and Source Code Research

Keywords: Programming Java Lombok Apache

A Preliminary Study on the Use and Design of cola Extension Points

Packaging changes provide flexibility to respond to changes in program requirements.

Extension point use

Steps:

Defines an extension point interface, which can be a validator, converter, entity; must end with ExtPt to represent an extension point.

For example, I define an extension point interface for the organizational structure of a cloud hub, a messaging extension point, an open extension point, and a rest interface extension point for a web api.

Define Extension Point Interface

package com.authine.web.cola.domain.customer;

import com.alibaba.cola.extension.ExtensionPointI;
import com.authine.web.cola.dto.domainmodel.Department;

import java.util.List;

/**
 * @author carter
 * create_date  2020/5/25 14:25
 * description     Define Extension Point Interfaces, some methods for organizations.
 */

public interface OrganizationExtPt extends ExtensionPointI {

    /**
     * Query all departments under the enterprise based on corpId
     *
     * @param corpId        Enterprise Number
     * @param includeDelete Does it contain deleted departments
     * @return department
     */
    List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete);


}

For example, business expansion is pinned down by WeChat:

This is based on the extended theory (x,y);

That is, the key of the extension point is derived from the business, use case, and scenario, and then the extension class is the business processing code for the actual business scenario.

Pin Scene Extension Point Implementation

package com.authine.web.cola.domain.customer.extpt;

import com.alibaba.cola.extension.Extension;
import com.authine.web.cola.dto.domainmodel.Department;
import com.authine.web.cola.domain.customer.OrganizationExtPt;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.List;

/**
 * @author carter
 * create_date  2020/5/25 14:32
 * description     Enterprise Departments Nail Expansion in the Scenario of Getting Departments List via corpId
 */
@Extension(bizId = "organize",useCase = "getByCorpId",scenario = "dingTalk")
@Slf4j
public class DingTalkOrganizationExt implements OrganizationExtPt {

    @Override
    public List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete) {

        log.info("In organizational business, use cases for getting Department lists from enterprise numbers, how business is handled in pinned scenarios");

        log.info("Configuration information via pins API Obtain organizational information and assemble departmental information for cloud hub identification");

        Department department = new Department();

        department.setName("dingTalk");
        department.setCorpId(corpId);

        return Collections.singletonList(department);
    }
}

Enterprise WeChat Extension Point Implementation

package com.authine.web.cola.domain.customer.extpt;

import com.alibaba.cola.extension.Extension;
import com.authine.web.cola.dto.domainmodel.Department;
import com.authine.web.cola.domain.customer.OrganizationExtPt;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.List;

/**
 * @author carter
 * create_date  2020/5/25 15:05
 * description     Extension Point Implementation of Enterprise WeChat
 */
@Extension(bizId = "organize",useCase = "getByCorpId",scenario = "wechat")
@Slf4j
public class WechatOrganizationExt  implements OrganizationExtPt {
    @Override
    public List<Department> getDepartmentsByCorpId(String corpId, Boolean includeDelete) {

        log.info("Business: Organizational, Use Case: Getting Departments by Enterprise Number , Scenario: Enterprise WeChat");

        log.info("Through Enterprise WeChat API Get departmental information for your organization and wrap it in the desired Department list");

        Department department = new Department();

        department.setName("wechat");
        department.setCorpId(corpId);

        return Collections.singletonList(department);
    }
}

Extension point use

Used in the command executor.

package com.authine.web.cola.executor.query;

import com.alibaba.cola.command.Command;
import com.alibaba.cola.command.CommandExecutorI;
import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.extension.ExtensionExecutor;
import com.authine.web.cola.dto.domainmodel.Department;
import com.authine.web.cola.domain.customer.OrganizationExtPt;
import com.authine.web.cola.dto.OrgnizationQry;

import java.util.List;


/**
 * @author carter
 * create_date  2020/5/25 15:09
 * description     Query organization for instructions execution
 */
@Command
public class OrgazationQueryExe implements CommandExecutorI<MultiResponse, OrgnizationQry> {


    private final ExtensionExecutor extensionExecutor;

    public OrgazationQueryExe(ExtensionExecutor extensionExecutor) {
        this.extensionExecutor = extensionExecutor;
    }


    @Override
    public MultiResponse execute(OrgnizationQry cmd) {

        String corpId = cmd.getCorpId();

        boolean includeDelete = cmd.isIncludeDelete();

        List<Department> departmentList = extensionExecutor.execute(OrganizationExtPt.class, cmd.getBizScenario(),
                ex -> ex.getDepartmentsByCorpId(corpId, includeDelete));


        return MultiResponse.ofWithoutTotal(departmentList);
    }
}

Test the use of extension points

Encapsulates an http interface to invoke.

package com.authine.web.cola.controller;

import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.extension.BizScenario;
import com.authine.web.cola.api.OrganizationServiceI;
import com.authine.web.cola.dto.OrgnizationQry;
import com.authine.web.cola.dto.domainmodel.Department;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrganizationController {

    private final OrganizationServiceI organizationServiceI;

    public OrganizationController(OrganizationServiceI organizationServiceI) {
        this.organizationServiceI = organizationServiceI;
    }

    @GetMapping(value = "/organization/getDepartmentsByCorpId/{corpId}/{scenario}")
    public MultiResponse<Department> listCustomerByName(@PathVariable("corpId") String corpId,@PathVariable("scenario") String scenario){

        OrgnizationQry qry = new OrgnizationQry();
        qry.setCorpId(corpId);
        qry.setIncludeDelete(true);
        qry.setBizScenario(BizScenario.valueOf("organize","getByCorpId",scenario));

        return organizationServiceI.getDepartmentsByCorpId(qry);
    }


}

Below are the results of testing using interfaces.

Summary

The metadata-based extension point design provides flexibility to cope with the diversity of business scenarios and to support version upgrades. Other extension points (checkers, converters) and others can also be easily extended. Use examples are in the framework's unit test cases.

Extension point design

Design Essence

Design concept.Is a data-based configuration extension.That is, based on the configuration data on the notes.

The @Extension source is as follows:

package com.alibaba.cola.extension;

import com.alibaba.cola.common.ColaConstant;
import org.springframework.stereotype.Component;

import java.lang.annotation.*;


@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Component
public @interface Extension {
    String bizId()  default BizScenario.DEFAULT_BIZ_ID;
    String useCase() default BizScenario.DEFAULT_USE_CASE;
    String scenario() default BizScenario.DEFAULT_SCENARIO;
}

The illustration is as follows:

The following is a further study of the source code.Start with the source code you are using.

ExtensionExecutor

The class diagram is shown below.

First, the Component is labeled, so you can get instances in ioc by type.

Finally, the execution function is placed in the parent AbstractComponentExecutor;

Focus on the analysis of the functions it achieves: namely, expanding instances through coordinates;

 /**
     * if the bizScenarioUniqueIdentity is "ali.tmall.supermarket"
     *
     * the search path is as below:
     * 1,first try to get extension by "ali.tmall.supermarket", if get, return it.
     * 2,loop try to get extension by "ali.tmall", if get, return it.
     * 3,loop try to get extension by "ali", if get, return it.
     * 4,if not found, try the default extension
     * @param targetClz
     */
    protected <Ext> Ext locateExtension(Class<Ext> targetClz, BizScenario bizScenario) {
        checkNull(bizScenario);

        Ext extension;
        String bizScenarioUniqueIdentity = bizScenario.getUniqueIdentity();
        logger.debug("BizScenario in locateExtension is : " + bizScenarioUniqueIdentity);

        // first try
        extension = firstTry(targetClz, bizScenarioUniqueIdentity);
        if (extension != null) {
            return extension;
        }

        // loop try
        extension = loopTry(targetClz, bizScenarioUniqueIdentity);
        if (extension != null) {
            return extension;
        }

        throw new ColaException("Can not find extension with ExtensionPoint: "+targetClz+" BizScenario:"+bizScenarioUniqueIdentity);
    }

The steps are as follows:

ExtensionRepository

package com.alibaba.cola.extension;

import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Component;

import lombok.Getter;

/**
 * ExtensionRepository 
 * @author fulan.zjf 2017-11-05
 */
@Component
public class ExtensionRepository {

    @Getter
    private Map<ExtensionCoordinate, ExtensionPointI> extensionRepo = new HashMap<>();

}

Inside is an empty map, mainly the assembly process.Look at the Extension Register below;

ExtensionRegister

Look at the name, that is, register the components of the extension.

/*
 * Copyright 2017 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.cola.boot;

import com.alibaba.cola.common.ApplicationContextHelper;
import com.alibaba.cola.common.ColaConstant;
import com.alibaba.cola.exception.framework.ColaException;
import com.alibaba.cola.extension.*;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * ExtensionRegister 
 * @author fulan.zjf 2017-11-05
 */
@Component
public class ExtensionRegister implements RegisterI{

    @Autowired
    private ExtensionRepository extensionRepository;
    

    @Override
    public void doRegistration(Class<?> targetClz) {
        ExtensionPointI extension = (ExtensionPointI) ApplicationContextHelper.getBean(targetClz);
        Extension extensionAnn = targetClz.getDeclaredAnnotation(Extension.class);
        String extPtClassName = calculateExtensionPoint(targetClz);
        BizScenario bizScenario = BizScenario.valueOf(extensionAnn.bizId(), extensionAnn.useCase(), extensionAnn.scenario());
        ExtensionCoordinate extensionCoordinate = new ExtensionCoordinate(extPtClassName, bizScenario.getUniqueIdentity());
        ExtensionPointI preVal = extensionRepository.getExtensionRepo().put(extensionCoordinate, extension);
        if (preVal != null) {
            throw new ColaException("Duplicate registration is not allowed for :" + extensionCoordinate);
        }
    }

    /**
     * @param targetClz
     * @return
     */
    private String calculateExtensionPoint(Class<?> targetClz) {
        Class[] interfaces = targetClz.getInterfaces();
        if (ArrayUtils.isEmpty(interfaces))
            throw new ColaException("Please assign a extension point interface for "+targetClz);
        for (Class intf : interfaces) {
            String extensionPoint = intf.getSimpleName();
            if (StringUtils.contains(extensionPoint, ColaConstant.EXTENSION_EXTPT_NAMING))
                return intf.getName();
        }
        throw new ColaException("Your name of ExtensionPoint for "+targetClz+" is not valid, must be end of "+ ColaConstant.EXTENSION_EXTPT_NAMING);
    }

}

The registration process is as follows:

This is the process of registering extension classes with the extended warehouse.

Registration timing.The start time is registered with the package scan.

RegisterFactory

Place various registers in ioc and return them in a unified way.

/*
 * Copyright 2017 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.cola.boot;

import com.alibaba.cola.command.Command;
import com.alibaba.cola.command.PostInterceptor;
import com.alibaba.cola.command.PreInterceptor;
import com.alibaba.cola.event.EventHandler;
import com.alibaba.cola.extension.Extension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * RegisterFactory
 *
 * @author fulan.zjf 2017-11-04
 */
@Component
public class RegisterFactory{

    @Autowired
    private PreInterceptorRegister preInterceptorRegister;
    @Autowired
    private PostInterceptorRegister postInterceptorRegister;
    @Autowired
    private CommandRegister commandRegister;
    @Autowired
    private ExtensionRegister extensionRegister;
    @Autowired
    private EventRegister eventRegister;


    public RegisterI getRegister(Class<?> targetClz) {
        PreInterceptor preInterceptorAnn = targetClz.getDeclaredAnnotation(PreInterceptor.class);
        if (preInterceptorAnn != null) {
            return preInterceptorRegister;
        }
        PostInterceptor postInterceptorAnn = targetClz.getDeclaredAnnotation(PostInterceptor.class);
        if (postInterceptorAnn != null) {
            return postInterceptorRegister;
        }
        Command commandAnn = targetClz.getDeclaredAnnotation(Command.class);
        if (commandAnn != null) {
            return commandRegister;
        }
        Extension extensionAnn = targetClz.getDeclaredAnnotation(Extension.class);
        if (extensionAnn != null) {
            return extensionRegister;
        }
        EventHandler eventHandlerAnn = targetClz.getDeclaredAnnotation(EventHandler.class);
        if (eventHandlerAnn != null) {
            return eventRegister;
        }
        return null;
    }
}

BootStrap

Scan java's class es for ioc assembly;

/*
 * Copyright 2017 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.cola.boot;

import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.cola.exception.framework.ColaException;

import lombok.Getter;
import lombok.Setter;

/**
 * <B>Core boot startup class for application </B>
 * <p>
 * Responsible for scanning inApplicationContext.xmlGet CommandExecutors, intercepters, extensions, validators, etc.
 * Deliver to each registrar for registration.
 *
 * @author fulan.zjf 2017-11-04
 */
public class Bootstrap {
    @Getter
    @Setter
    private List<String> packages;
    private ClassPathScanHandler handler;

    @Autowired
    private RegisterFactory registerFactory;


    public void init() {
        Set<Class<?>> classSet = scanConfiguredPackages();
        registerBeans(classSet);
    }

    /**
     * @param classSet
     */
    private void registerBeans(Set<Class<?>> classSet) {
        for (Class<?> targetClz : classSet) {
            RegisterI register = registerFactory.getRegister(targetClz);
            if (null != register) {
                register.doRegistration(targetClz);
            }
        }

    }



Registration of other core components is also included in the code.

AbstractComponentExecutor

An abstract component executor whose main function is to locate an extension class and then execute the interface's methods.

The source code is as follows:

package com.alibaba.cola.boot;

import com.alibaba.cola.extension.BizScenario;
import com.alibaba.cola.extension.ExtensionCoordinate;

import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author fulan.zjf
 * @date 2017/12/21
 */
public abstract class AbstractComponentExecutor {

    /**
     * Execute extension with Response
     *
     * @param targetClz
     * @param bizScenario
     * @param exeFunction
     * @param <R> Response Type
     * @param <T> Parameter Type
     * @return
     */
    public <R, T> R execute(Class<T> targetClz, BizScenario bizScenario, Function<T, R> exeFunction) {
        T component = locateComponent(targetClz, bizScenario);
        return exeFunction.apply(component);
    }

    public <R, T> R execute(ExtensionCoordinate extensionCoordinate, Function<T, R> exeFunction){
        return execute((Class<T>) extensionCoordinate.getExtensionPointClass(), extensionCoordinate.getBizScenario(), exeFunction);
    }

    /**
     * Execute extension without Response
     *
     * @param targetClz
     * @param context
     * @param exeFunction
     * @param <T> Parameter Type
     */
    public <T> void executeVoid(Class<T> targetClz, BizScenario context, Consumer<T> exeFunction) {
        T component = locateComponent(targetClz, context);
        exeFunction.accept(component);
    }

    public <T> void executeVoid(ExtensionCoordinate extensionCoordinate, Consumer<T> exeFunction){
        executeVoid(extensionCoordinate.getExtensionPointClass(), extensionCoordinate.getBizScenario(), exeFunction);
    }

    protected abstract <C> C locateComponent(Class<C> targetClz, BizScenario context);
}

Function <T, R> is the main functional interface for java8. T: An instance of an extension class registered in the system; R is the method that calls T to use the class and the return value after execution.

The choice of which method to execute is given to the business logic code.

Four different overload methods are provided.

Summary

Extend by key,value.

Code

Code point I get! 

Originality is not easy, attention is valuable, forwarding price is higher!Please indicate the source of the reprint so that we can communicate with each other, make common progress and welcome communication. I will continue to share my knowledge of Java software programming and the way programmers develop their careers. Welcome to my attention. I have sorted out various resources of programming learning over the years, paid attention to the public number'Li Fuchun Continuous Output', and sent'Learning Materials'to share with you!

Posted by web_noob on Mon, 25 May 2020 10:01:16 -0700