Why learn source code?

benefits

  • Through the source code can understand the structure of the code relationship experience the details of code design, reading the source code can quickly improve the level of development technology
  • Reading source code is a required course for junior engineers to move to senior engineers

Activiti6.0 source Overview

Summary of the source code

The way GitHub works

  • GitHub source: github.com/Activiti/Ac…

  • Git checkout -b study6 activiti-6.0.0

  • Compile using MAVNE: MVN clean test-compile

The Activiti module is introduced

  • Project engineering structure

  • Engineering submodules (compile design from top to bottom)

  • Each package under the project is explained accordingly

The core module
  • Module /activiti-engine Core engine
  • Module/Activiti-Spring Spring integration module
  • Module /activiti-spring-boot SpringBoot integration module
  • Module/ativiti-REST Provides restful API modules externally
  • Module /activiti-form-engine Form engine module
  • Module/Activiti-LDAP Integrated LDAP user module (integrated with corporate/enterprise users)
Acitviti-engine relies directly on modules
  • Bpmn-converter is mainly for model conversion
  • Process-validation validation of the mileage model
  • Dmp-api new decision standard
  • Image-generate XML file how to draw flow chart in stream file
  • Form-api corresponds to the form
  • Form-model Corresponds to the form

Run activiti-app based on the source code

Start the activiti – app

1 $ cd modules/activiti-ui/activiti-app

2 $ cd mvn clean tomcat7:run

3 $ open http://locahot:9999/activiti-app

activiti-ui

  • Activiti-app integrates published WAR packages
  • Activiti-app-conf UI is independent of configuration outside of the business
  • Activiti-logic UI business logic
  • Activiti-app-rest provides the rest API for the interface

web.xml

  • Activiti-app is an aggregation of the entire Activiti-UI project, so start with web.xml.

  • Web.xml position.. /activiti-ui/activiti-app/src/main/webapp/WEB-INF/web.xml

  • View the Listener configuration in web. XML

  • WebConfigurer members

WebConfigurer

contextInitialized

@Override
    public void contextInitialized(ServletContextEvent sce) {
        log.debug("Configuring Spring root application context");

        ServletContext servletContext = sce.getServletContext();

        AnnotationConfigWebApplicationContext rootContext = null;
        
        if (context == null) {
            rootContext = new AnnotationConfigWebApplicationContext(); // Rebuild the container if the top and bottom ask you if the container is empty
            rootContext.register(ApplicationConfiguration.class);      // Define a root container to construct the bean inside the container
            
            if (rootContext.getServletContext() == null) {
              rootContext.setServletContext(servletContext);
            }
            
            rootContext.refresh();
            context = rootContext;
            
        } else {
            rootContext = context;
            if (rootContext.getServletContext() == null) {
              rootContext.setServletContext(servletContext);  // Put the servletContext container in the Spring container}}// Make a bidirectional binding between the Spring and Tomcat containers. First, put the Sping container in the Servlet container as a property
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, rootContext);

        EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);

        initSpring(servletContext, rootContext);    // Initialize the spring-related class capacity
        initSpringSecurity(servletContext, disps);  // Initialize the springSecurity-related class capacity

        log.debug("Web application fully configured");
    }

Copy the code

initSpring

private void initSpring(ServletContext servletContext, AnnotationConfigWebApplicationContext rootContext) {
        log.debug("Configuring Spring Web application context");
        AnnotationConfigWebApplicationContext appDispatcherServletConfiguration = new AnnotationConfigWebApplicationContext();
        appDispatcherServletConfiguration.setParent(rootContext); // The context passed above
        After initialization, register two child containers (one is appDispatcher, the other is apiDispatcher)
        appDispatcherServletConfiguration.register(AppDispatcherServletConfiguration.class);

        log.debug("Registering Spring MVC Servlet");
        ServletRegistration.Dynamic appDispatcherServlet = servletContext.addServlet("appDispatcher".new DispatcherServlet(appDispatcherServletConfiguration));
        appDispatcherServlet.addMapping("/app/*");
        appDispatcherServlet.setLoadOnStartup(1);
        appDispatcherServlet.setAsyncSupported(true);

        log.debug("Registering Activiti public REST API");
        AnnotationConfigWebApplicationContext apiDispatcherServletConfiguration = new AnnotationConfigWebApplicationContext();
        apiDispatcherServletConfiguration.setParent(rootContext);
        After initialization, register two child containers (one is appDispatcher, the other is apiDispatcher)
        apiDispatcherServletConfiguration.register(ApiDispatcherServletConfiguration.class);

        ServletRegistration.Dynamic apiDispatcherServlet = servletContext.addServlet("apiDispatcher".new DispatcherServlet(apiDispatcherServletConfiguration));
        apiDispatcherServlet.addMapping("/api/*");
        apiDispatcherServlet.setLoadOnStartup(1);
        apiDispatcherServlet.setAsyncSupported(true);
    }

Copy the code

WebConfigurer

The WebConfigurer Listtener refers to the LoadContextListener corresponding to SpringMVC. The corresponding container is ApplicationConfiguration

AppContextDispatcherServlet is corresponding to the entire business (internal), corresponding container for AppDispatcherServletConfiguration

ApiContextDispatcherServlet is available API, corresponding container for ApiDispatcherServletConfiguration

ApplicationConfiguration (Root container)

The root container is primarily an annotation configured on a class

@Configuration
@PropertySources({ @PropertySource("classpath:/META-INF/activiti-app/activiti-app.properties"), @PropertySource(value = "classpath:activiti-app.properties", ignoreResourceNotFound = true), // ignoreResourceNotFound = true @propertysource (value = "file:activiti-app.properties", ignoreResourceNotFound = true), })
// Some configurations related to postnatal processing are assumed to be unique
@ComponentScan(basePackages = { "org.activiti.app.conf", "org.activiti.app.repository", "org.activiti.app.service", "org.activiti.app.security", "org.activiti.app.model.component"})
@EnableJpaRepositories({ "org.activiti.app.repository" })
@EntityScan({"org.activiti.app.domain"})
@EnableTransactionManagement
public class ApplicationConfiguration {
	
	/**
	 * This is needed to make property resolving work on annotations ...
	 * (see http://stackoverflow.com/questions/11925952/custom-spring-property-source-does-not-resolve-placeholders-in-value) 
	 * 
	 * @Scheduled(cron="${someProperty}")
	 */
	@Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(a) {
        return newPropertySourcesPlaceholderConfigurer(); }}Copy the code

AppDispatcherServletConfiguration

@Configuration
@ComponentScan(value = {"org.activiti.app.rest"})
@EnableAsync
public class AppDispatcherServletConfiguration extends WebMvcConfigurationSupport {
     // A lot of the configuration in the class is related to front-end configuration
    private final Logger log = LoggerFactory.getLogger(AppDispatcherServletConfiguration.class);

    @Inject
    private ObjectMapper objectMapper;  // To handle JSON serialization
    
    @Inject
    private Environment environment; // Environment information

    @Bean // Check for local information or persistent information
    public SessionLocaleResolver localeResolver(a) {
        return new SessionLocaleResolver();
    }

    @Bean // Local information gets language information
    public LocaleChangeInterceptor localeChangeInterceptor(a) {
        log.debug("Configuring localeChangeInterceptor");
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("language");
        return localeChangeInterceptor;
    }

    @Bean // Maximum number of file uploads
    public MultipartResolver multipartResolver(a) {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
        multipartResolver.setMaxUploadSize(environment.getProperty("file.upload.max.size", Long.class));
        return multipartResolver;
    }

    @Bean // The most important thing in SpringMVC is HandlerMapping, which maps each URL and method
    public RequestMappingHandlerMapping requestMappingHandlerMapping(a) {
        log.debug("Creating requestMappingHandlerMapping");
        RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
        requestMappingHandlerMapping.setUseSuffixPatternMatch(false); Struts (.do or.xml suffix)
        requestMappingHandlerMapping.setRemoveSemicolonContent(false);// Whether to keep the original URL separated by semicolons
        Object[] interceptors = {localeChangeInterceptor()};
        requestMappingHandlerMapping.setInterceptors(interceptors);
        return requestMappingHandlerMapping;
    }
    
    @Override // Configure some methods for JSON conversion
    public void configureMessageConverters(List
       
        > converters)
       > {
        addDefaultHttpMessageConverters(converters);
        for(HttpMessageConverter<? > converter: converters) {if (converter instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = (MappingJackson2HttpMessageConverter) converter;
                jackson2HttpMessageConverter.setObjectMapper(objectMapper);
                break; }}}}Copy the code

The HelloWorld Activiti 6.0

Secondary approval process

Eclipse (Design Flowchart)

Eclipse installs the Activiti6.0 plug-in
  • The Eclipse blog.csdn.net/jenyzhang/a Activiti6.0 plug-in installation address and their operating document…

  • The working flow chart based on Eclipse is drawn as follows

IDEA Creates the Maven project

Maven Engineering Basics

Pom dependency description

Package based on SpringBoot
Add dependencies to POM files
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>. 2.0.0 RELEASE</version>
    </parent>
    
     <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
     </dependency>
    
     <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
     </build>

Copy the code
Maven package command
mvn package

Copy the code

Workflow applets based on command line interaction

    public static void main(String[] args) throws ParseException {
        LOGGER.info("Start our program.");
        /* Create a process engine based on in-memory database storage */
        ProcessEngine processEngine = getProcessEngine();

        // Deploy the process definition file
        ProcessDefinition processDefinition = getProcessDefinition(processEngine);

        // Start the run stream
        ProcessInstance processInstance = getProcessInstance(processEngine, processDefinition);

        // Process process tasks
        processTask(processEngine, processInstance);
        LOGGER.info("End our program.");

    }
Copy the code
Create a process engine
   private static ProcessEngine getProcessEngine(a) {
        ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
        ProcessEngine processEngine = cfg.buildProcessEngine(); // Create the process
        String name = processEngine.getName();
        String version = ProcessEngine.VERSION;
        LOGGER.info("Name of process engine [{}], version of process engine [{}]", name, version);
        return processEngine;
    }
Copy the code
Deploy the process definition file
    private static ProcessDefinition getProcessDefinition(ProcessEngine processEngine) {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        deploymentBuilder.addClasspathResource("second_approve.bpmn20.xml");
        Deployment deployment = deploymentBuilder.deploy();
        String deploymentId = deployment.getId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId)
                .singleResult();
        LOGGER.info("Process definition file [{}], process ID[{}]", processDefinition.getName(), processDefinition.getId());
        return processDefinition;
    }
Copy the code
Start running flow
    private static ProcessInstance getProcessInstance(ProcessEngine processEngine, ProcessDefinition processDefinition) {
        RuntimeService runtimeService = processEngine.getRuntimeService();
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());
        LOGGER.info("Start process [{}]", processInstance.getProcessDefinitionKey());
        return processInstance;
    }
Copy the code
Process tasks
private static void processTask(ProcessEngine processEngine, ProcessInstance processInstance) throws ParseException {
        Scanner scanner = new Scanner(System.in);
        while(processInstance ! =null && !processInstance.isEnded()) {
            // Process process tasks
            TaskService taskService = processEngine.getTaskService();
            List<Task> list = taskService.createTaskQuery().list();
            LOGGER.info("Number of pending tasks [{}]", list.size());
            for (Task task : list) {
                LOGGER.info("Pending tasks [{}]", task.getName());
                FormService formService = processEngine.getFormService();
                TaskFormData taskFormData = formService.getTaskFormData(task.getId());
                List<FormProperty> formProperties = taskFormData.getFormProperties();
                HashMap<String, Object> variables = Maps.newHashMap();
                String line = null;
                for (FormProperty formProperty : formProperties) {
                    if (StringFormType.class.isInstance(formProperty.getType())) {
                        LOGGER.info("Please enter [{}]?", formProperty.getName());
                        line = scanner.nextLine();
                        variables.put(formProperty.getId(), line);
                    } else if (DateFormType.class.isInstance(formProperty.getType())) {
                        LOGGER.info("Please enter [{}]? Format (YYYY-MM-DD)", formProperty.getName());
                        line = scanner.nextLine();
                        SimpleDateFormat dateFormType = new SimpleDateFormat("yyyy-MM-dd");
                        Date date = dateFormType.parse(line);
                        variables.put(formProperty.getId(), date);
                    } else {
                        LOGGER.info("This content is not currently supported [{}]", formProperty.getType());
                    }
                    LOGGER.info("The class capacity you entered is [{}]", line);
                }
                taskService.complete(task.getId(), variables);
                processInstance = processEngine.getRuntimeService().createProcessInstanceQuery()
                        .processInstanceId(processInstance.getId()).singleResult();

            }
        }
        scanner.close();
    }
Copy the code