split bounded contexts into separate maven modules

This commit is contained in:
Luc Weinbrecht
2022-05-04 06:25:58 +02:00
parent 2f857a79e5
commit 49119644a5
108 changed files with 921 additions and 400 deletions

17
recommendation/Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM maven:3.8.5-openjdk-17 as build
COPY ../pom.xml .
RUN mvn -B dependency:go-offline
COPY ../src src
RUN mvn -B package
FROM openjdk:11-jre-slim-buster
COPY --from=build ../target/recommendation.jar .
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "recommendation.jar"]

140
recommendation/pom.xml Normal file
View File

@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.weinbrecht.luc.bpm.architecture.recommendation</groupId>
<artifactId>recommendation</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version.springboot>2.6.4</version.springboot>
<version.camunda>7.17.0</version.camunda>
<version.junit5>5.8.2</version.junit5>
<version.lombok>1.18.24</version.lombok>
<version.domainprimitives>0.1.0</version.domainprimitives>
<version.bpmAssert>1.1.0</version.bpmAssert>
<version.camundaMockito>6.17.0</version.camundaMockito>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${version.springboot}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-bom</artifactId>
<version>${version.camunda}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
</dependency>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>io.github.domain-primitives</groupId>
<artifactId>domainprimitives-java</artifactId>
<version>${version.domainprimitives}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${version.lombok}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${version.junit5}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${version.junit5}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${version.junit5}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.camunda.bpm.extension</groupId>
<artifactId>camunda-bpm-junit5</artifactId>
<version>${version.bpmAssert}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.camunda.community.mockito</groupId>
<artifactId>camunda-platform-7-mockito</artifactId>
<version>${version.camundaMockito}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${version.springboot}</version>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,13 @@
package de.weinbrecht.luc.bpm.architecture.recommendation;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,8 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common;
public class ProcessConstants {
public static final String START_EVENT_MESSAGE_REF = "crossSellingPotentialDiscoveredMessage";
public static final String CUSTOMER_NUMBER = "customerNumber";
public static final String CONTENT_NUMBER = "contentNumber";
}

View File

@@ -0,0 +1,23 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.in.process;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.in.RecommendationPicker;
import lombok.RequiredArgsConstructor;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.CONTENT_NUMBER;
@RequiredArgsConstructor
@Component
public class PickContent implements JavaDelegate {
private final RecommendationPicker recommendationPicker;
@Override
public void execute(DelegateExecution execution) {
ContentId contentId = recommendationPicker.pickContent();
execution.setVariable(CONTENT_NUMBER, contentId.getValue());
}
}

View File

@@ -0,0 +1,36 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.in.process;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Recommendation;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Customer;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.RecommendationQuery;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.SendNotification;
import lombok.RequiredArgsConstructor;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.CONTENT_NUMBER;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.CUSTOMER_NUMBER;
@RequiredArgsConstructor
@Component
public class SendRecommendation implements JavaDelegate {
private final SendNotification sendNotification;
private final RecommendationQuery recommendationQuery;
@Override
public void execute(DelegateExecution execution) {
Long contentId = (Long) execution.getVariable(CONTENT_NUMBER);
String customerId = (String) execution.getVariable(CUSTOMER_NUMBER);
Content content = recommendationQuery.findContentById(new ContentId(contentId));
Customer customer = recommendationQuery.findCustomerById(new CustomerId(customerId));
Recommendation recommendation = new Recommendation(customer, content);
sendNotification.send(recommendation);
}
}

View File

@@ -0,0 +1,24 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.in.web;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.in.RecommendationCreation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RequiredArgsConstructor
@RestController
@RequestMapping("recommendation")
public class RecommendationController {
private final RecommendationCreation recommendationCreation;
@PostMapping("/{caseId}")
public void create(@RequestBody RecommendationResource recommendationResource,
@PathVariable String caseId) {
recommendationCreation.create(
caseId,
recommendationResource.getLoanAgreementNumber(),
new CustomerId(recommendationResource.getCustomerNumber())
);
}
}

View File

@@ -0,0 +1,9 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.in.web;
import lombok.Data;
@Data
public class RecommendationResource {
private Long loanAgreementNumber;
private String customerNumber;
}

View File

@@ -0,0 +1,16 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.console;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Recommendation;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.SendNotification;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class SendNotificationClient implements SendNotification {
@Override
public void send(Recommendation recommendation) {
log.info("Sending recommendation '{}' to customer {}",
recommendation.getContent().getDescription().getValue(), recommendation.getCustomer().getName());
}
}

View File

@@ -0,0 +1,40 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db;
import de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content.ContentCRUDRepository;
import de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content.ContentMapper;
import de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content.ContentNotFoundException;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Customer;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.MailAddress;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Name;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.RecommendationQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@Component
public class RecommendationRepository implements RecommendationQuery {
private final ContentCRUDRepository contentCRUDRepository;
private final ContentMapper mapper;
@Override
public Content findContentById(ContentId contentId) {
return contentCRUDRepository.findById(contentId.getValue())
.map(mapper::mapToDomain)
.orElseThrow(() -> new ContentNotFoundException(
String.format("Could not find recommendation content with id %s", contentId.getValue())));
}
@Override
public Customer findCustomerById(CustomerId customerId) {
// Here would be a own CRUDRepository for the customer information we need during recommendation
return new Customer(
customerId,
new Name("Max Mustermann"),
new MailAddress("max@mustermann.de")
);
}
}

View File

@@ -0,0 +1,8 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ContentCRUDRepository extends CrudRepository<ContentEntity, Long> {
}

View File

@@ -0,0 +1,19 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import static javax.persistence.GenerationType.AUTO;
@Data
@Entity
public class ContentEntity {
@Id
@GeneratedValue(strategy= AUTO)
private Long id;
private String content;
}

View File

@@ -0,0 +1,17 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Description;
import org.springframework.stereotype.Component;
@Component
public class ContentMapper {
public Content mapToDomain(ContentEntity contentEntity) {
return new Content(
new ContentId(contentEntity.getId()),
new Description(contentEntity.getContent())
);
}
}

View File

@@ -0,0 +1,7 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content;
public class ContentNotFoundException extends RuntimeException {
public ContentNotFoundException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,20 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@Component
public class ExampleContentRunner implements CommandLineRunner {
private final ContentCRUDRepository contentCRUDRepository;
@Override
public void run(String... args) throws Exception {
ContentEntity contentEntity = new ContentEntity();
contentEntity.setId(1L);
contentEntity.setContent("Here we do some cross-selling");
contentCRUDRepository.save(contentEntity);
}
}

View File

@@ -0,0 +1,27 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.process;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.StartRecommendation;
import lombok.RequiredArgsConstructor;
import org.camunda.bpm.engine.RuntimeService;
import org.springframework.stereotype.Component;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.CUSTOMER_NUMBER;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.START_EVENT_MESSAGE_REF;
import static java.util.Collections.singletonMap;
@RequiredArgsConstructor
@Component
public class ProcessEngineClient implements StartRecommendation {
private final RuntimeService runtimeService;
@Override
public void start(String caseId, CustomerId customerId) {
runtimeService.startProcessInstanceByMessage(
START_EVENT_MESSAGE_REF,
caseId,
singletonMap(CUSTOMER_NUMBER, customerId.getValue())
);
}
}

View File

@@ -0,0 +1,26 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model;
import io.github.domainprimitives.object.Entity;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@EqualsAndHashCode(callSuper = false)
public class Content extends Entity {
private final ContentId contentId;
private final Description description;
public Content(ContentId contentId, Description description) {
this.contentId = contentId;
this.description = description;
validate();
}
@Override
protected void validate() {
validateNotNull(contentId, "ContentId");
validateNotNull(description, "Description");
evaluateValidations();
}
}

View File

@@ -0,0 +1,11 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model;
import io.github.domainprimitives.type.ValueObject;
import static io.github.domainprimitives.validation.Constraints.isNotNullLong;
public class ContentId extends ValueObject<Long> {
public ContentId(Long value) {
super(value, isNotNullLong());
}
}

View File

@@ -0,0 +1,11 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model;
import io.github.domainprimitives.type.ValueObject;
import static io.github.domainprimitives.validation.Constraints.isNotBlank;
public class Description extends ValueObject<String> {
public Description(String value) {
super(value, isNotBlank());
}
}

View File

@@ -0,0 +1,27 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Customer;
import io.github.domainprimitives.object.Aggregate;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@EqualsAndHashCode(callSuper = false)
public class Recommendation extends Aggregate {
private final Customer customer;
private final Content content;
public Recommendation(Customer customer, Content content) {
this.customer = customer;
this.content = content;
this.validate();
}
@Override
protected void validate() {
validateNotNull(customer, "Customer");
validateNotNull(content, "Content");
evaluateValidations();
}
}

View File

@@ -0,0 +1,29 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer;
import io.github.domainprimitives.object.ComposedValueObject;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@Getter
@EqualsAndHashCode(callSuper = false)
public class Customer extends ComposedValueObject {
private final CustomerId customerId;
private final Name name;
private final MailAddress mailAddress;
public Customer(CustomerId customerId, Name name, MailAddress mailAddress) {
this.customerId = customerId;
this.name = name;
this.mailAddress = mailAddress;
this.validate();
}
@Override
protected void validate() {
validateNotNull(customerId, "Customer Number");
validateNotNull(name, "Name");
validateNotNull(mailAddress, "Mail Address");
evaluateValidations();
}
}

View File

@@ -0,0 +1,11 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer;
import io.github.domainprimitives.type.ValueObject;
import static io.github.domainprimitives.validation.Constraints.isNotBlank;
public class CustomerId extends ValueObject<String> {
public CustomerId(String value) {
super(value, isNotBlank());
}
}

View File

@@ -0,0 +1,19 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer;
import io.github.domainprimitives.type.ValueObject;
import java.util.regex.Pattern;
import static io.github.domainprimitives.validation.Constraints.isPattern;
public class MailAddress extends ValueObject<String> {
public static final String VALID_EMAIL_ADDRESS_REGEX =
Pattern.compile("^[a-zA-Z0-9_!#$%&*+=?`{|}~^.-]+@[a-zA-Z0-9.-]+$",
Pattern.CASE_INSENSITIVE).pattern();
public MailAddress(String value) {
super(value, isPattern(VALID_EMAIL_ADDRESS_REGEX));
}
}

View File

@@ -0,0 +1,11 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer;
import io.github.domainprimitives.type.ValueObject;
import static io.github.domainprimitives.validation.Constraints.hasMinLength;
public class Name extends ValueObject<String> {
public Name(String value) {
super(value, hasMinLength(3));
}
}

View File

@@ -0,0 +1,30 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.service;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.in.RecommendationCreation;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.in.RecommendationPicker;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.RecommendationQuery;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.StartRecommendation;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@Component
class RecommendationService implements RecommendationPicker, RecommendationCreation {
private final RecommendationQuery recommendationQuery;
private final StartRecommendation startRecommendation;
@Override
public ContentId pickContent() {
// Magically decide which content is the right one for the customer
return recommendationQuery.findContentById(new ContentId(1L)).getContentId();
}
@Override
public void create(String loanCaseId, Long loanAgreementNumber, CustomerId customerId) {
// Fetch customer her and save subset of needed values to recommendation
startRecommendation.start(loanCaseId + "-" + loanAgreementNumber, customerId);
}
}

View File

@@ -0,0 +1,7 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.usecase.in;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
public interface RecommendationCreation {
void create(String loanCaseId, Long loanAgreementNumber, CustomerId customerId);
}

View File

@@ -0,0 +1,7 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.usecase.in;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
public interface RecommendationPicker {
ContentId pickContent();
}

View File

@@ -0,0 +1,11 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Customer;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
public interface RecommendationQuery {
Content findContentById(ContentId contentId);
Customer findCustomerById(CustomerId customerId);
}

View File

@@ -0,0 +1,7 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Recommendation;
public interface SendNotification {
void send(Recommendation recommendation);
}

View File

@@ -0,0 +1,7 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
public interface StartRecommendation {
void start(String caseId, CustomerId customerId);
}

View File

@@ -0,0 +1,16 @@
spring:
datasource:
url: jdbc:h2:file:./camunda-recommendation-h2-database
jpa:
hibernate:
ddl-auto: create-drop
camunda.bpm.admin-user:
id: admin
password: pw
generic-properties:
properties:
initializeTelemetry: false
server:
port: 8081

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_02tppq7" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.0.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.17.0">
<bpmn:collaboration id="Collaboration_197td5k">
<bpmn:participant id="Participant_1y5ozpf" name="Recommendation example" processRef="Cross_Selling_Recommendation" />
</bpmn:collaboration>
<bpmn:process id="Cross_Selling_Recommendation" name="Cross Selling Recommendation" isExecutable="true">
<bpmn:serviceTask id="PickContentServiceTask" name="Pick Content" camunda:delegateExpression="${pickContent}">
<bpmn:incoming>Flow_14xjzhy</bpmn:incoming>
<bpmn:outgoing>Flow_0klv1t5</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:startEvent id="CrossSellingStartEvent" name="Cross-Selling potential discovered">
<bpmn:outgoing>Flow_14xjzhy</bpmn:outgoing>
<bpmn:messageEventDefinition id="MessageEventDefinition_1ibjd8g" messageRef="Message_0hftsl2" />
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_14xjzhy" sourceRef="CrossSellingStartEvent" targetRef="PickContentServiceTask" />
<bpmn:endEvent id="CrossSellingRecommendationEndEvent" name="Cross-Selling  recommendation sent">
<bpmn:incoming>Flow_1bnyw18</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0klv1t5" sourceRef="PickContentServiceTask" targetRef="SendRecommendationServiceTask" />
<bpmn:sequenceFlow id="Flow_1bnyw18" sourceRef="SendRecommendationServiceTask" targetRef="CrossSellingRecommendationEndEvent" />
<bpmn:serviceTask id="SendRecommendationServiceTask" name="Send Recommendation" camunda:delegateExpression="${sendRecommendation}">
<bpmn:incoming>Flow_0klv1t5</bpmn:incoming>
<bpmn:outgoing>Flow_1bnyw18</bpmn:outgoing>
</bpmn:serviceTask>
</bpmn:process>
<bpmn:message id="Message_0hftsl2" name="crossSellingPotentialDiscoveredMessage" />
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_197td5k">
<bpmndi:BPMNShape id="Participant_1y5ozpf_di" bpmnElement="Participant_1y5ozpf" isHorizontal="true">
<dc:Bounds x="160" y="80" width="610" height="210" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_14xjzhy_di" bpmnElement="Flow_14xjzhy">
<di:waypoint x="288" y="160" />
<di:waypoint x="340" y="160" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0klv1t5_di" bpmnElement="Flow_0klv1t5">
<di:waypoint x="440" y="160" />
<di:waypoint x="500" y="160" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1bnyw18_di" bpmnElement="Flow_1bnyw18">
<di:waypoint x="600" y="160" />
<di:waypoint x="662" y="160" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Activity_0bsm7s4_di" bpmnElement="PickContentServiceTask">
<dc:Bounds x="340" y="120" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1ct00fd_di" bpmnElement="CrossSellingStartEvent">
<dc:Bounds x="252" y="142" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="237" y="185" width="66" height="40" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_18vk8pm_di" bpmnElement="CrossSellingRecommendationEndEvent">
<dc:Bounds x="662" y="142" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="640" y="185" width="82" height="40" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BPMNShape_1shn36r" bpmnElement="SendRecommendationServiceTask">
<dc:Bounds x="500" y="120" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@@ -0,0 +1,52 @@
package de.weinbrecht.luc.bpm.architecture.recommendation;
import de.weinbrecht.luc.bpm.architecture.recommendation.adapter.in.process.PickContent;
import de.weinbrecht.luc.bpm.architecture.recommendation.adapter.in.process.SendRecommendation;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.test.Deployment;
import org.camunda.bpm.extension.junit5.test.ProcessEngineExtension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.assertThat;
import static org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.runtimeService;
import static org.camunda.community.mockito.DelegateExpressions.registerJavaDelegateMock;
import static org.camunda.community.mockito.DelegateExpressions.verifyJavaDelegateMock;
@ExtendWith(ProcessEngineExtension.class)
class ProcessTest {
public static final String PROCESS_DEFINITION = "Cross_Selling_Recommendation";
private static final String START_EVENT = "CrossSellingStartEvent";
private static final String PICK_CONTENT_SERVICE_TASK = "PickContentServiceTask";
private static final String SEND_RECOMMENDATION_SERVICE_TASK = "SendRecommendationServiceTask";
private static final String END_EVENT = "CrossSellingRecommendationEndEvent";
@BeforeEach
void setUp() {
registerJavaDelegateMock(PickContent.class);
registerJavaDelegateMock(SendRecommendation.class);
}
@Test
@Deployment(resources = "cross_selling_recommendation.bpmn")
void shouldExecuteProcess_happy_path() {
ProcessInstance processInstance = runtimeService().startProcessInstanceByKey(
PROCESS_DEFINITION
);
assertThat(processInstance)
.hasPassedInOrder(
START_EVENT,
PICK_CONTENT_SERVICE_TASK,
SEND_RECOMMENDATION_SERVICE_TASK,
END_EVENT);
verifyJavaDelegateMock(PickContent.class).executed();
verifyJavaDelegateMock(SendRecommendation.class).executed();
assertThat(processInstance).isEnded();
}
}

View File

@@ -0,0 +1,33 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.in.process;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.in.RecommendationPicker;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoSettings;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.CONTENT_NUMBER;
import static org.mockito.Mockito.*;
@MockitoSettings
class PickContentTest {
@InjectMocks
private PickContent classUnderTest;
@Mock
private RecommendationPicker recommendationPicker;
@Test
void should_call_service_and_set_variable() {
ContentId contentId = new ContentId(1L);
when(recommendationPicker.pickContent()).thenReturn(contentId);
DelegateExecution delegateExecution = mock(DelegateExecution.class);
classUnderTest.execute(delegateExecution);
verify(delegateExecution).setVariable(CONTENT_NUMBER, contentId.getValue());
}
}

View File

@@ -0,0 +1,59 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.in.process;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Description;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Recommendation;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Customer;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.MailAddress;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Name;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.RecommendationQuery;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.SendNotification;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoSettings;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.CONTENT_NUMBER;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.CUSTOMER_NUMBER;
import static org.mockito.Mockito.*;
@MockitoSettings
class SendRecommendationTest {
@InjectMocks
private SendRecommendation classUnderTest;
@Mock
private SendNotification sendNotification;
@Mock
RecommendationQuery recommendationQuery;
@Test
void should_load_data_and_call_service_to_send_notification() {
ContentId contentId = new ContentId(1L);
CustomerId customerId = new CustomerId("A1");
DelegateExecution delegateExecution = mock(DelegateExecution.class);
when(delegateExecution.getVariable(CONTENT_NUMBER)).thenReturn(contentId.getValue());
when(delegateExecution.getVariable(CUSTOMER_NUMBER)).thenReturn(customerId.getValue());
Content content = new Content(contentId, new Description("Foo"));
when(recommendationQuery.findContentById(contentId)).thenReturn(content);
Customer customer = createCustomer(customerId);
when(recommendationQuery.findCustomerById(customerId)).thenReturn(customer);
classUnderTest.execute(delegateExecution);
verify(sendNotification).send(new Recommendation(customer, content));
}
private Customer createCustomer(CustomerId customerId) {
return new Customer(
customerId,
new Name("Tester"),
new MailAddress("tester@ewb.io")
);
}
}

View File

@@ -0,0 +1,42 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.in.web;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.in.RecommendationCreation;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
@WebMvcTest(RecommendationController.class)
class RecommendationControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private RecommendationCreation recommendationCreation;
@Test
void should_class_creation_on_post() throws Exception {
String caseId = "A-11";
Long loanAgreementNumber = 1L;
CustomerId customerId = new CustomerId("C-8");
String requestJson = "{\"loanAgreementNumber\": \"" + loanAgreementNumber + "\",\"customerNumber\": \"" + customerId.getValue() + "\"}";
mockMvc.perform(
post("/recommendation/" + caseId)
.contentType(APPLICATION_JSON_VALUE)
.content(requestJson)
)
.andDo(print())
.andExpect(status().isOk());
verify(recommendationCreation).create(caseId, loanAgreementNumber, customerId);
}
}

View File

@@ -0,0 +1,72 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db;
import de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content.ContentCRUDRepository;
import de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content.ContentEntity;
import de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content.ContentMapper;
import de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content.ContentNotFoundException;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Description;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Customer;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.MailAddress;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Name;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoSettings;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Answers.CALLS_REAL_METHODS;
import static org.mockito.Mockito.when;
@MockitoSettings
class RecommendationRepositoryTest {
@InjectMocks
private RecommendationRepository classUnderTest;
@Mock
private ContentCRUDRepository contentCRUDRepository;
@Mock(answer = CALLS_REAL_METHODS)
private ContentMapper contentMapper;
@Test
void should_find_content() {
Content content = new Content(new ContentId(1L), new Description("Test"));
ContentEntity contentEntity = new ContentEntity();
contentEntity.setId(content.getContentId().getValue());
contentEntity.setContent(content.getDescription().getValue());
when(contentCRUDRepository.findById(content.getContentId().getValue())).thenReturn(of(contentEntity));
Content result = classUnderTest.findContentById(content.getContentId());
assertThat(result).isEqualTo(content);
}
@Test
void should_find_content_throw_custom_exception() {
ContentId contentId = new ContentId(1L);
when(contentCRUDRepository.findById(contentId.getValue())).thenReturn(empty());
assertThrows(ContentNotFoundException.class,
() -> classUnderTest.findContentById(contentId));
}
@Test
void should_find_customer() {
Customer customer = new Customer(
new CustomerId("A-11"),
new Name("Max Mustermann"),
new MailAddress("max@mustermann.de")
);
Customer result = classUnderTest.findCustomerById(customer.getCustomerId());
assertThat(result).isEqualTo(customer);
}
}

View File

@@ -0,0 +1,21 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@Import(ExampleContentRunner.class)
class ContentCRUDRepositoryTest {
@Autowired
private ContentCRUDRepository contentCRUDRepository;
@Test
void should_find_content() {
assertThat(contentCRUDRepository.findById(1L)).isNotNull();
}
}

View File

@@ -0,0 +1,25 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.db.content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Description;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class ContentMapperTest {
private ContentMapper classUnderTest = new ContentMapper();
@Test
void should_map_all_fields() {
Content content = new Content(new ContentId(1L), new Description("Test"));
ContentEntity contentEntity = new ContentEntity();
contentEntity.setId(content.getContentId().getValue());
contentEntity.setContent(content.getDescription().getValue());
Content result = classUnderTest.mapToDomain(contentEntity);
assertThat(result).isEqualTo(content);
}
}

View File

@@ -0,0 +1,37 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.adapter.out.process;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import org.camunda.bpm.engine.RuntimeService;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoSettings;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.CUSTOMER_NUMBER;
import static de.weinbrecht.luc.bpm.architecture.recommendation.adapter.common.ProcessConstants.START_EVENT_MESSAGE_REF;
import static java.util.Collections.singletonMap;
import static org.mockito.Mockito.verify;
@MockitoSettings
class ProcessEngineClientTest {
@InjectMocks
private ProcessEngineClient classUnderTest;
@Mock
private RuntimeService runtimeService;
@Test
void should_class_runtime_service_to_start() {
String caseId = "super-test";
CustomerId customerId = new CustomerId("Test");
classUnderTest.start(caseId, customerId);
verify(runtimeService).startProcessInstanceByMessage(
START_EVENT_MESSAGE_REF,
caseId,
singletonMap(CUSTOMER_NUMBER, customerId.getValue())
);
}
}

View File

@@ -0,0 +1,39 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model;
import io.github.domainprimitives.validation.InvariantException;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ContentTest {
@Test
void should_create_valid_object() {
ContentId contentId = new ContentId(1L);
Description description = new Description("Testing");
Content content = new Content(contentId, description);
assertThat(content).isNotNull();
assertThat(content.getContentId()).isEqualTo(contentId);
assertThat(content.getDescription()).isEqualTo(description);
}
@Nested
class InvariantTest {
@Test
void should_throw_invariant_exception_if_id_is_null() {
Description description = new Description("Testing");
assertThrows(InvariantException.class, () -> new Content(null, description));
}
@Test
void should_throw_invariant_exception_if_description_is_null() {
ContentId contentId = new ContentId(1L);
assertThrows(InvariantException.class, () -> new Content(contentId, null));
}
}
}

View File

@@ -0,0 +1,43 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Customer;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.MailAddress;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.Name;
import io.github.domainprimitives.validation.InvariantException;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class RecommendationTest {
@Test
void should_create_valid_object() {
Content content = new Content(new ContentId(1L), new Description("Test"));
Customer customer = new Customer(new CustomerId("A"), new Name("Tester"), new MailAddress("tester@web.io"));
Recommendation recommendation = new Recommendation(customer, content);
assertThat(recommendation).isNotNull();
assertThat(recommendation.getContent()).isEqualTo(content);
assertThat(recommendation.getCustomer()).isEqualTo(customer);
}
@Nested
class InvariantTest {
@Test
void should_throw_invariant_exception_if_customer_is_null() {
Content content = new Content(new ContentId(1L), new Description("Test"));
assertThrows(InvariantException.class, () -> new Recommendation(null, content));
}
@Test
void should_throw_invariant_exception_if_content_is_null() {
Customer customer = new Customer(new CustomerId("A"), new Name("Tester"), new MailAddress("tester@web.io"));
assertThrows(InvariantException.class, () -> new Recommendation(customer, null));
}
}
}

View File

@@ -0,0 +1,51 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer;
import io.github.domainprimitives.validation.InvariantException;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class CustomerTest {
@Test
void should_create_valid_object() {
Name name = new Name("Tester");
MailAddress mailAddress = new MailAddress("tester@web.io");
CustomerId customerId = new CustomerId("A-1");
Customer customer = new Customer(customerId, name, mailAddress);
assertThat(customer).isNotNull();
assertThat(customer.getCustomerId()).isEqualTo(customerId);
assertThat(customer.getName()).isEqualTo(name);
assertThat(customer.getMailAddress()).isEqualTo(mailAddress);
}
@Nested
class InvariantTest {
@Test
void should_throw_invariant_exception_if_customer_number_is_null() {
Name name = new Name("Tester");
MailAddress mailAddress = new MailAddress("tester@web.io");
assertThrows(InvariantException.class, () -> new Customer(null, name, mailAddress));
}
@Test
void should_throw_invariant_exception_if_name_is_null() {
MailAddress mailAddress = new MailAddress("tester@web.io");
CustomerId customerId = new CustomerId("A-1");
assertThrows(InvariantException.class, () -> new Customer(customerId, null, mailAddress));
}
@Test
void should_throw_invariant_exception_if_mail_is_null() {
Name name = new Name("Tester");
CustomerId customerId = new CustomerId("A-1");
assertThrows(InvariantException.class, () -> new Customer(customerId, name, null));
}
}
}

View File

@@ -0,0 +1,51 @@
package de.weinbrecht.luc.bpm.architecture.recommendation.domain.service;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Content;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.ContentId;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.Description;
import de.weinbrecht.luc.bpm.architecture.recommendation.domain.model.customer.CustomerId;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.RecommendationQuery;
import de.weinbrecht.luc.bpm.architecture.recommendation.usecase.out.StartRecommendation;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoSettings;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@MockitoSettings
class RecommendationServiceTest {
@InjectMocks
private RecommendationService classUnderTest;
@Mock
private RecommendationQuery recommendationQuery;
@Mock
private StartRecommendation startRecommendation;
@Test
void should_magically_pick_the_content() {
ContentId contentId = new ContentId(1L);
Content content = new Content(contentId, new Description("Test"));
when(recommendationQuery.findContentById(contentId)).thenReturn(content);
ContentId result = classUnderTest.pickContent();
assertThat(result).isEqualTo(contentId);
}
@Test
void should_start_recommendation() {
CustomerId customerId = new CustomerId("Test");
String loanCaseId = "11";
Long loanAgreementNumber = 1L;
classUnderTest.create(loanCaseId, loanAgreementNumber, customerId);
verify(startRecommendation).start(loanCaseId + "-" + loanAgreementNumber, customerId);
}
}

View File

@@ -0,0 +1,5 @@
spring.datasource.url: jdbc:h2:file:./camunda-h2-database
camunda.bpm.admin-user:
id: admin
password: pw

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.camunda.bpm.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:h2:mem:camunda;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />
<!-- Database configurations -->
<property name="databaseSchemaUpdate" value="true" />
<!-- job executor configurations -->
<property name="jobExecutorActivate" value="false" />
<property name="history" value="full" />
</bean>
</beans>

View File

@@ -0,0 +1,63 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.apache.ibatis" level="info" />
<!--
<logger name="org.apache.ibatis" level="DEBUG"/>
-->
<logger name="javax.activation" level="info" />
<logger name="org.springframework" level="info" />
<logger name="org.camunda" level="info" />
<!--
<logger name="org.camunda" level="DEBUG"/>
-->
<logger name="org.camunda.bpm.engine.test" level="debug" />
<!--
<logger name="org.camunda.bpm.engine.bpmn.parser" level="debug" />
<logger name="org.camunda.bpm.engine.bpmn.behavior" level="debug" />
<logger name="org.camunda.bpm.engine.cmmn.transformer" level="debug" />
<logger name="org.camunda.bpm.engine.cmmn.behavior" level="debug" />
<logger name="org.camunda.bpm.engine.cmmn.operation" level="debug" />
<logger name="org.camunda.bpm.engine.cmd" level="debug" />
<logger name="org.camunda.bpm.engine.persistence" level="debug" />
<logger name="org.camunda.bpm.engine.impl.persistence.entity" level="debug" />
<logger name="org.camunda.bpm.engine.impl.history.event" level="debug" />
<logger name="org.camunda.bpm.engine.impl.batch.history" level="debug" />
<logger name="org.camunda.bpm.engine.impl.batch" level="debug" />
<logger name="org.camunda.bpm.engine.history" level="debug" />
<logger name="org.camunda.bpm.engine.impl.cmmn.entity.repository" level="debug" />
<logger name="org.camunda.bpm.engine.impl.cmmn.entity.runtime" level="debug" />
<logger name="org.camunda.bpm.engine.impl.dmn.entity.repository" level="debug" />
<logger name="org.camunda.bpm.engine.tx" level="debug" />
<logger name="org.camunda.bpm.engine.cfg" level="debug" />
<logger name="org.camunda.bpm.engine.jobexecutor" level="debug" />
<logger name="org.camunda.bpm.engine.context" level="debug" />
<logger name="org.camunda.bpm.engine.core" level="debug" />
<logger name="org.camunda.bpm.engine.pvm" level="debug" />
<logger name="org.camunda.bpm.engine.metrics" level="debug" />
<logger name="org.camunda.bpm.engine.util" level="debug" />
<logger name="org.camunda.bpm.application" level="debug" />
<logger name="org.camunda.bpm.container" level="debug" />
-->
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>