Feature: remove eip and cloud patterns (#2354)

* feat: remove EIP patterns

* feat: remove cloud patterns

* feat: remove serverless pattern
This commit is contained in:
Robert Volkmann
2022-11-29 18:43:40 +01:00
committed by GitHub
parent 1aa1196c19
commit 6a5b26e1b8
104 changed files with 0 additions and 6083 deletions
-38
View File
@@ -1,38 +0,0 @@
# Build output
target/
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# IDE
.idea/
*.iml
.settings/
.project
.classpath
# macOS
.DS_Store
# Azure Functions
local.settings.json
bin/
obj/
-80
View File
@@ -1,80 +0,0 @@
---
title: Claim Check Pattern
category: Cloud
language: en
tag:
- Cloud distributed
- Microservices
---
## Name
[Claim Check Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/claim-check)
## Also known as
[Reference-Based Messaging](https://www.enterpriseintegrationpatterns.com/patterns/messaging/StoreInLibrary.html)
## Intent
- Reduce the load of data transfer through the Internet. Instead of sending actual data directly, just send the message reference.
- Improve data security. As only message reference is shared, no data is exposed to the Internet.
## Explanation
Real-World Example
> Suppose if you want to build a photo processing system. A photo processing system takes an image as input, processes it, and outputs a different set of images. Consider system has one persistent storage, one input component, ten processing components, messaging platform. Once a photo is given to the input component, it stores that image on persistent storage. It then creates ten different messages/events with the same image location and publishes them to the messaging platform. The messaging platform triggers ten different processing components. The ten processing components extract information about image location from the received event and then read an image from persistent storage. They generate ten different images from the original image and drop these images again to persistent storage.
In Plain words
> Split a large message into a claim check and a payload. Send the claim check to the messaging platform and store the payload to an external service. This pattern allows large messages to be processed while protecting the message bus and the client from being overwhelmed or slowed down. This pattern also helps to reduce costs, as storage is usually cheaper than resource units used by the messaging platform.([ref](https://docs.microsoft.com/en-us/azure/architecture/patterns/claim-check))
## Architecture Diagram
![alt text](./etc/Claim-Check-Pattern.png "Claim Check Pattern")
## Applicability
Use the Claim Check Pattern when
- Huge processing data causes a lot of bandwidth consumption to transfer data through the Internet.
- To secure your data transfer by storing in common persistent storage.
- Using a cloud platform - Azure Functions or AWS Lambda, Azure EventGrid or AWS Event Bridge, Azure Blob Storage or AWS S3 Bucket.
- Each service must be independent and idempotent. Output data is dropped to persistent storage by the service.
- Publish-subscribe messaging pattern needs to be used.
## Consequences
- This pattern is stateless. Any compute API will not store any data.
- You must have persistent storage and a reliable messaging platform.
## Tutorials
### Workflow
Suppose a telecom company wants to build call cost calculator system which generate the call cost daily. At the end of each day, details of the calls made by the consumers are stored somewhere. The call calculator system will read this data and generate call cost data for each user. Consumers will be billed using this generated data in case of postpaid service.
Producer class( `UsageDetailPublisherFunction` Azure Function) will generate call usage details (here we are generating call data in producer class itself. In real world scenario, it will read from storage). `UsageDetailPublisherFunction` creates a message. Message consists of message header and message body. Message header is basically an event grid event or claim or message reference. Message body contains actual data. `UsageDetailPublisherFunction` sends a message header to Event Grid topic `usage-detail` and drops an entire message to the blob storage. Event Grid then sent this message header to the `UsageCostProcessorFunction` Azure function. It will read the entire message from blob storage with the help of the header, will calculate call cost and drop the result to the blob storage.
### Class Diagrams
![alt text](./etc/class-diagram.png "Claim-Check-Class-Diagram")
### Setup
- Any operating system can be used macOS, Windows, Linux as everything is deployed on Azure.
- Install Java JDK 11 and set up Java environmental variables.
- Install Git.
- Install Visual Studio Code.
- Install [ Azure Functions extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions) to be able to deploy using Visual studio.
### Storage Data
The data is stored in the Azure blob storage in the container `callusageapp`. For every trigger, one GUID is created. Under the `GUID folder`, 2 files will be created `input.json` and `output.json`.
`Input.json` is dropped `producer` azure function which contains call usage details.` Output.json` contains call cost details which are dropped by the `consumer` azure function.
## Credits
- [Messaging Pattern - Claim Check](https://www.enterpriseintegrationpatterns.com/patterns/messaging/StoreInLibrary.html)
- [Azure Architecture Pattern - Claim Check Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/claim-check)
@@ -1,38 +0,0 @@
# Build output
target/
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# IDE
.idea/
*.iml
.settings/
.project
.classpath
# macOS
.DS_Store
# Azure Functions
local.settings.json
bin/
obj/
@@ -1,117 +0,0 @@
@startuml
package com.iluwatar.claimcheckpattern.producer.calldetails.functions {
class UsageDetailPublisherFunction {
- eventHandlerUtility : EventHandlerUtility<MessageHeader>
- messageHandlerUtility : MessageHandlerUtility<UsageDetail>
+ UsageDetailPublisherFunction()
+ UsageDetailPublisherFunction(messageHandlerUtility : MessageHandlerUtility<UsageDetail>, eventHandlerUtility : EventHandlerUtility<MessageHeader>)
+ run(request : HttpRequestMessage<Optional<String>>, context : ExecutionContext) : HttpResponseMessage
}
}
package com.iluwatar.claimcheckpattern.domain {
class Message<T> {
- messageBody : MessageBody<T>
- messageHeader : MessageHeader
+ Message<T>()
+ getMessageBody() : MessageBody<T>
+ getMessageHeader() : MessageHeader
+ setMessageBody(messageBody : MessageBody<T>)
+ setMessageHeader(messageHeader : MessageHeader)
}
class MessageBody<T> {
- data : List<T>
+ MessageBody<T>()
+ getData() : List<T>
+ setData(data : List<T>)
}
class MessageHeader {
- data : Object
- dataVersion : String
- eventTime : String
- eventType : String
- id : String
- subject : String
- topic : String
+ MessageHeader()
+ getData() : Object
+ getDataVersion() : String
+ getEventTime() : String
+ getEventType() : String
+ getId() : String
+ getSubject() : String
+ getTopic() : String
+ setData(data : Object)
+ setDataVersion(dataVersion : String)
+ setEventTime(eventTime : String)
+ setEventType(eventType : String)
+ setId(id : String)
+ setSubject(subject : String)
+ setTopic(topic : String)
}
class MessageReference {
- dataFileName : String
- dataLocation : String
+ MessageReference()
+ MessageReference(dataLocation : String, dataFileName : String)
+ getDataFileName() : String
+ getDataLocation() : String
+ setDataFileName(dataFileName : String)
+ setDataLocation(dataLocation : String)
}
class UsageCostDetail {
- callCost : double
- dataCost : double
- userId : String
+ UsageCostDetail()
+ getCallCost() : double
+ getDataCost() : double
+ getUserId() : String
+ setCallCost(callCost : double)
+ setDataCost(dataCost : double)
+ setUserId(userId : String)
}
class UsageDetail {
- data : int
- duration : int
- userId : String
+ UsageDetail()
+ getData() : int
+ getDuration() : int
+ getUserId() : String
+ setData(data : int)
+ setDuration(duration : int)
+ setUserId(userId : String)
}
}
package com.iluwatar.claimcheckpattern.utility {
class EventHandlerUtility<T> {
- customEventClient : EventGridPublisherClient<BinaryData>
+ EventHandlerUtility<T>()
+ EventHandlerUtility<T>(customEventClient : EventGridPublisherClient<BinaryData>)
+ publishEvent(customEvent : T, logger : Logger)
}
class MessageHandlerUtility<T> {
- blobServiceClient : BlobServiceClient
+ MessageHandlerUtility<T>()
+ MessageHandlerUtility<T>(blobServiceClient : BlobServiceClient)
+ dropToPersistantStorage(message : Message<T>, logger : Logger)
+ readFromPersistantStorage(messageReference : MessageReference, logger : Logger) : Message<T>
}
}
package com.iluwatar.claimcheckpattern.consumer.callcostprocessor.functions {
class UsageCostProcessorFunction {
- messageHandlerUtilityForUsageCostDetail : MessageHandlerUtility<UsageCostDetail>
- messageHandlerUtilityForUsageDetail : MessageHandlerUtility<UsageDetail>
+ UsageCostProcessorFunction()
+ UsageCostProcessorFunction(messageHandlerUtilityForUsageDetail : MessageHandlerUtility<UsageDetail>, messageHandlerUtilityForUsageCostDetail : MessageHandlerUtility<UsageCostDetail>)
- calculateUsageCostDetails(usageDetailsList : List<UsageDetail>) : List<UsageCostDetail>
+ run(request : HttpRequestMessage<Optional<String>>, context : ExecutionContext) : HttpResponseMessage
}
}
UsageCostProcessorFunction --> "-messageHandlerUtilityForUsageDetail" MessageHandlerUtility
Message --> "-messageBody" MessageBody
UsageDetailPublisherFunction --> "-eventHandlerUtility" EventHandlerUtility
Builder ..+ HttpResponseMessage
UsageDetailPublisherFunction --> "-messageHandlerUtility" MessageHandlerUtility
Message --> "-messageHeader" MessageHeader
@enduml
@@ -1,7 +0,0 @@
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
}
}
@@ -1,152 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License
Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<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>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>claim-check-pattern</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>call-usage-app</artifactId>
<name>call-usage-app</name>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<azure.functions.maven.plugin.version>1.14.0</azure.functions.maven.plugin.version>
<azure.functions.java.library.version>1.4.2</azure.functions.java.library.version>
<functionAppName>CallUsageApp</functionAppName>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-sdk-bom</artifactId>
<version>1.0.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.microsoft.azure.functions</groupId>
<artifactId>azure-functions-java-library</artifactId>
<version>${azure.functions.java.library.version}</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-messaging-eventgrid</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.13.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId>
<version>${azure.functions.maven.plugin.version}</version>
<configuration>
<!-- function app name -->
<appName>${functionAppName}</appName>
<!-- function app resource group -->
<resourceGroup>java-functions-group</resourceGroup>
<!-- function app service plan name -->
<appServicePlanName>java-functions-app-service-plan</appServicePlanName>
<!-- function app region-->
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-regions for all valid values -->
<region>westus</region>
<!-- function pricingTier, default to be consumption if not specified -->
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-pricing-tiers for all valid values -->
<!-- <pricingTier></pricingTier> -->
<!-- Whether to disable application insights, default is false -->
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details for all valid configurations for application insights-->
<!-- <disableAppInsights></disableAppInsights> -->
<runtime>
<!-- runtime os, could be windows, linux or docker-->
<os>windows</os>
<javaVersion>11</javaVersion>
</runtime>
<appSettings>
<property>
<name>FUNCTIONS_EXTENSION_VERSION</name>
<value>~3</value>
</property>
</appSettings>
</configuration>
<executions>
<execution>
<id>package-functions</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
<!--Remove obj folder generated by .NET SDK in maven clean-->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<filesets>
<fileset>
<directory>obj</directory>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,169 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.consumer.callcostprocessor.functions;
import com.azure.core.util.BinaryData;
import com.azure.core.util.serializer.TypeReference;
import com.azure.messaging.eventgrid.EventGridEvent;
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationEventData;
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationResponse;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.MessageBody;
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
import com.iluwatar.claimcheckpattern.domain.MessageReference;
import com.iluwatar.claimcheckpattern.domain.UsageCostDetail;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Azure Functions with HTTP Trigger.
* This is Consumer class.
*/
public class UsageCostProcessorFunction {
private MessageHandlerUtility<UsageDetail> messageHandlerUtilityForUsageDetail;
private MessageHandlerUtility<UsageCostDetail> messageHandlerUtilityForUsageCostDetail;
public UsageCostProcessorFunction() {
this.messageHandlerUtilityForUsageDetail = new MessageHandlerUtility<>();
this.messageHandlerUtilityForUsageCostDetail = new MessageHandlerUtility<>();
}
public UsageCostProcessorFunction(
MessageHandlerUtility<UsageDetail> messageHandlerUtilityForUsageDetail,
MessageHandlerUtility<UsageCostDetail> messageHandlerUtilityForUsageCostDetail) {
this.messageHandlerUtilityForUsageDetail = messageHandlerUtilityForUsageDetail;
this.messageHandlerUtilityForUsageCostDetail = messageHandlerUtilityForUsageCostDetail;
}
/**
* Azure function which gets triggered when event grid event send event to it.
* After receiving event, it read input file from blob storage, calculate call cost details.
* It creates new message with cost details and drop message to blob storage.
* @param request represents HttpRequestMessage
* @param context represents ExecutionContext
* @return HttpResponseMessage
*/
@FunctionName("UsageCostProcessorFunction")
public HttpResponseMessage run(@HttpTrigger(name = "req", methods = { HttpMethod.GET,
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)
HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) {
try {
var eventGridEvents = EventGridEvent.fromString(request.getBody().get());
for (var eventGridEvent : eventGridEvents) {
// Handle system events
if (eventGridEvent.getEventType()
.equals("Microsoft.EventGrid.SubscriptionValidationEvent")) {
SubscriptionValidationEventData subscriptionValidationEventData = eventGridEvent.getData()
.toObject(SubscriptionValidationEventData.class);
// Handle the subscription validation event
var responseData = new SubscriptionValidationResponse();
responseData.setValidationResponse(subscriptionValidationEventData.getValidationCode());
return request.createResponseBuilder(HttpStatus.OK).body(responseData).build();
} else if (eventGridEvent.getEventType().equals("UsageDetail")) {
// Get message header and reference
var messageReference = eventGridEvent.getData()
.toObject(MessageReference.class);
// Read message from persistent storage
var message = this.messageHandlerUtilityForUsageDetail
.readFromPersistantStorage(messageReference, context.getLogger());
// Get Data and generate cost details
List<UsageDetail> usageDetailsList = BinaryData.fromObject(
message.getMessageBody().getData())
.toObject(new TypeReference<>() {
});
var usageCostDetailsList = calculateUsageCostDetails(usageDetailsList);
// Create message body
var newMessageBody = new MessageBody<UsageCostDetail>();
newMessageBody.setData(usageCostDetailsList);
// Create message header
var newMessageReference = new MessageReference("callusageapp",
eventGridEvent.getId() + "/output.json");
var newMessageHeader = new MessageHeader();
newMessageHeader.setId(eventGridEvent.getId());
newMessageHeader.setSubject("UsageCostProcessor");
newMessageHeader.setTopic("");
newMessageHeader.setEventType("UsageCostDetail");
newMessageHeader.setEventTime(OffsetDateTime.now().toString());
newMessageHeader.setData(newMessageReference);
newMessageHeader.setDataVersion("v1.0");
// Create entire message
var newMessage = new Message<UsageCostDetail>();
newMessage.setMessageHeader(newMessageHeader);
newMessage.setMessageBody(newMessageBody);
// Drop data to persistent storage
this.messageHandlerUtilityForUsageCostDetail.dropToPersistantStorage(newMessage,
context.getLogger());
context.getLogger().info("Message is dropped successfully");
return request.createResponseBuilder(HttpStatus.OK)
.body("Message is dropped successfully").build();
}
}
} catch (Exception e) {
context.getLogger().warning(e.getMessage());
}
return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR).body(null).build();
}
private List<UsageCostDetail> calculateUsageCostDetails(List<UsageDetail> usageDetailsList) {
if (usageDetailsList == null) {
return null;
}
var usageCostDetailsList = new ArrayList<UsageCostDetail>();
usageDetailsList.forEach(usageDetail -> {
var usageCostDetail = new UsageCostDetail();
usageCostDetail.setUserId(usageDetail.getUserId());
usageCostDetail.setCallCost(usageDetail.getDuration() * 0.30); // 0.30₹ per minute
usageCostDetail.setDataCost(usageDetail.getData() * 0.20); // 0.20₹ per MB
usageCostDetailsList.add(usageCostDetail);
});
return usageCostDetailsList;
}
}
@@ -1,44 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* It is the message which gets dropped or read by Producer or Consumer Azure functions.
* It is stored in the json format.
* @param <T> represents UsageDetail or UsageCostDetail
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Message<T> {
private MessageHeader messageHeader;
private MessageBody<T> messageBody;
}
@@ -1,44 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* It is message body of the message.
* It stores actual data in our case UsageCostDetail or UsageDetail.
* @param <T> represents UsageDetail or UsageCostDetail
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageBody<T> {
private List<T> data;
}
@@ -1,48 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* This is message header or event which is sent to Event Grid.
* Its structure is same as Azure Event Grid Event Class.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageHeader {
private String id;
private String subject;
private String topic;
private String eventType;
private String eventTime;
private Object data;
private String dataVersion;
}
@@ -1,46 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* This is claim/message reference class.
* It contains the information about data where it is stored in persistent storage
* and file name.
* dataLocation is blob storage container name.
* dataFileName is file name in above container.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageReference {
private String dataLocation;
private String dataFileName;
}
@@ -1,44 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* This is call cost details class.
* It stores userId of the caller, call duration cost and data cost.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UsageCostDetail {
private String userId;
private double callCost;
private double dataCost;
}
@@ -1,45 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* This is call usage detail calls.
* It stores userId of the caller, call duration and data used.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UsageDetail {
private String userId;
private int duration;
private int data;
}
@@ -1,147 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.producer.calldetails.functions;
import com.azure.messaging.eventgrid.EventGridEvent;
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationEventData;
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationResponse;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.MessageBody;
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
import com.iluwatar.claimcheckpattern.domain.MessageReference;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import com.iluwatar.claimcheckpattern.utility.EventHandlerUtility;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
/**
* Azure Functions with HTTP Trigger.
* This is Producer class.
*/
public class UsageDetailPublisherFunction {
private MessageHandlerUtility<UsageDetail> messageHandlerUtility;
private EventHandlerUtility<MessageHeader> eventHandlerUtility;
public UsageDetailPublisherFunction() {
this.messageHandlerUtility = new MessageHandlerUtility<>();
this.eventHandlerUtility = new EventHandlerUtility<>();
}
public UsageDetailPublisherFunction(MessageHandlerUtility<UsageDetail> messageHandlerUtility,
EventHandlerUtility<MessageHeader> eventHandlerUtility) {
this.messageHandlerUtility = messageHandlerUtility;
this.eventHandlerUtility = eventHandlerUtility;
}
/**
* Azure function which create message, drop it in persistent storage
* and publish the event to Event Grid topic.
* @param request represents HttpRequestMessage
* @param context represents ExecutionContext
* @return HttpResponseMessage
*/
@FunctionName("UsageDetailPublisherFunction")
public HttpResponseMessage run(@HttpTrigger(name = "req", methods = {
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)
HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) {
try {
var eventGridEvents = EventGridEvent.fromString(request.getBody().get());
for (EventGridEvent eventGridEvent : eventGridEvents) {
// Handle system events
if (eventGridEvent.getEventType()
.equals("Microsoft.EventGrid.SubscriptionValidationEvent")) {
SubscriptionValidationEventData subscriptionValidationEventData = eventGridEvent.getData()
.toObject(SubscriptionValidationEventData.class);
// Handle the subscription validation event
var responseData = new SubscriptionValidationResponse();
responseData.setValidationResponse(subscriptionValidationEventData.getValidationCode());
return request.createResponseBuilder(HttpStatus.OK).body(responseData).build();
} else if (eventGridEvent.getEventType().equals("UsageDetail")) {
// Create message body
var messageBody = new MessageBody<UsageDetail>();
var usageDetailsList = new ArrayList<UsageDetail>();
var random = new Random();
for (int i = 0; i < 51; i++) {
var usageDetail = new UsageDetail();
usageDetail.setUserId("userId" + i);
usageDetail.setData(random.nextInt(500));
usageDetail.setDuration(random.nextInt(500));
usageDetailsList.add(usageDetail);
}
messageBody.setData(usageDetailsList);
// Create message header
var messageHeader = new MessageHeader();
messageHeader.setId(UUID.randomUUID().toString());
messageHeader.setSubject("UsageDetailPublisher");
messageHeader.setTopic("usagecostprocessorfunction-topic");
messageHeader.setEventType("UsageDetail");
messageHeader.setEventTime(OffsetDateTime.now().toString());
var messageReference = new MessageReference("callusageapp",
messageHeader.getId() + "/input.json");
messageHeader.setData(messageReference);
messageHeader.setDataVersion("v1.0");
// Create entire message
var message = new Message<UsageDetail>();
message.setMessageHeader(messageHeader);
message.setMessageBody(messageBody);
// Drop data to persistent storage
this.messageHandlerUtility.dropToPersistantStorage(message, context.getLogger());
// Publish event to event grid topic
eventHandlerUtility.publishEvent(messageHeader, context.getLogger());
context.getLogger().info("Message is dropped and event is published successfully");
return request.createResponseBuilder(HttpStatus.OK).body(message).build();
}
}
} catch (Exception e) {
context.getLogger().warning(e.getMessage());
}
return request.createResponseBuilder(HttpStatus.OK).body(null).build();
}
}
@@ -1,67 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.utility;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.util.BinaryData;
import com.azure.messaging.eventgrid.EventGridPublisherClient;
import com.azure.messaging.eventgrid.EventGridPublisherClientBuilder;
import java.util.logging.Logger;
/**
* This class is event publisher utility which published message header to Event Grid topic.
* @param <T> represents UsageDetail or UsageCostDetail
*/
public class EventHandlerUtility<T> {
private EventGridPublisherClient<BinaryData> customEventClient;
/** Default constructor.
*/
public EventHandlerUtility() {
this.customEventClient = new EventGridPublisherClientBuilder()
.endpoint(System.getenv("EventGridURL"))
.credential(new AzureKeyCredential(System.getenv("EventGridKey")))
.buildCustomEventPublisherClient();
}
/**
Parameterized constructor.
*/
public EventHandlerUtility(EventGridPublisherClient<BinaryData> customEventClient) {
this.customEventClient = customEventClient;
}
/**
Method for publishing event to Event Grid Topic.
*/
public void publishEvent(T customEvent, Logger logger) {
try {
customEventClient.sendEvent(BinaryData.fromObject(customEvent));
} catch (Exception e) {
logger.info(e.getMessage());
}
}
}
@@ -1,128 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.utility;
import com.azure.core.util.BinaryData;
import com.azure.core.util.serializer.TypeReference;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.MessageReference;
import java.util.logging.Logger;
/**
* This class read and drop message from Azure blob storage.
* @param <T> represents UsageDetail or UsageCostDetail
*/
public class MessageHandlerUtility<T> {
private BlobServiceClient blobServiceClient;
/**
* Parameterized constructor.
* @param blobServiceClient represents BlobServiceClient
*/
public MessageHandlerUtility(BlobServiceClient blobServiceClient) {
this.blobServiceClient = blobServiceClient;
}
/**
* Default constructor.
*/
public MessageHandlerUtility() {
// Create a BlobServiceClient object which will be used to create a container
// client
this.blobServiceClient = new BlobServiceClientBuilder()
.connectionString(System.getenv("BlobStorageConnectionString")).buildClient();
}
/**
* Read message from blob storage.
* @param messageReference represents MessageReference
* @param logger represents Logger
* @return Message
*/
public Message<T> readFromPersistantStorage(MessageReference messageReference, Logger logger) {
Message<T> message = null;
try {
// Get container name from message reference
String containerName = messageReference.getDataLocation();
// Get blob name from message reference
String blobName = messageReference.getDataFileName();
// Get container client
BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient(containerName);
// Get a reference to a blob
BlobClient blobClient = containerClient.getBlobClient(blobName);
// download the blob
message = blobClient.downloadContent().toObject(new TypeReference<Message<T>>() {
});
} catch (Exception e) {
logger.info(e.getMessage());
}
return message;
}
/**
* Drop message to blob storage.
* @param message represents Message
* @param logger represents Logger
*/
public void dropToPersistantStorage(Message<T> message, Logger logger) {
try {
// Get message reference
MessageReference messageReference = (MessageReference) message.getMessageHeader().getData();
// Create a unique name for the container
String containerName = messageReference.getDataLocation();
// Create the container and return a container client object
BlobContainerClient containerClient = this.blobServiceClient
.getBlobContainerClient(containerName);
if (!containerClient.exists()) {
containerClient.create();
}
// Get a reference to a blob
BlobClient blobClient = containerClient.getBlobClient(messageReference.getDataFileName());
// Upload the blob
blobClient.upload(BinaryData.fromObject(message));
} catch (Exception e) {
logger.info(e.getMessage());
}
}
}
@@ -1,106 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.HttpStatusType;
import java.util.HashMap;
import java.util.Map;
/**
* The mock for HttpResponseMessage, can be used in unit tests to verify if the
* returned response by HTTP trigger function is correct or not.
*/
public class HttpResponseMessageMock implements HttpResponseMessage {
private int httpStatusCode;
private HttpStatusType httpStatus;
private Object body;
private Map<String, String> headers;
public HttpResponseMessageMock(HttpStatusType status, Map<String, String> headers, Object body) {
this.httpStatus = status;
this.httpStatusCode = status.value();
this.headers = headers;
this.body = body;
}
@Override
public HttpStatusType getStatus() {
return this.httpStatus;
}
@Override
public int getStatusCode() {
return httpStatusCode;
}
@Override
public String getHeader(String key) {
return this.headers.get(key);
}
@Override
public Object getBody() {
return this.body;
}
public static class HttpResponseMessageBuilderMock implements HttpResponseMessage.Builder {
private Object body;
private int httpStatusCode;
private Map<String, String> headers = new HashMap<>();
private HttpStatusType httpStatus;
public Builder status(HttpStatus status) {
this.httpStatusCode = status.value();
this.httpStatus = status;
return this;
}
@Override
public Builder status(HttpStatusType httpStatusType) {
this.httpStatusCode = httpStatusType.value();
this.httpStatus = httpStatusType;
return this;
}
@Override
public HttpResponseMessage.Builder header(String key, String value) {
this.headers.put(key, value);
return this;
}
@Override
public HttpResponseMessage.Builder body(Object body) {
this.body = body;
return this;
}
@Override
public HttpResponseMessage build() {
return new HttpResponseMessageMock(this.httpStatus, this.headers, this.body);
}
}
}
@@ -1,182 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.consumer.callcostprocessor.functions;
import com.iluwatar.claimcheckpattern.HttpResponseMessageMock;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.MessageBody;
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
import com.iluwatar.claimcheckpattern.domain.MessageReference;
import com.iluwatar.claimcheckpattern.domain.UsageCostDetail;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* Unit test for Function class.
*/
@ExtendWith(MockitoExtension.class)
class UsageCostProcessorFunctionTest {
@Mock
MessageHandlerUtility<UsageDetail> mockMessageHandlerUtilityForUsageADetail;
@Mock
MessageHandlerUtility<UsageCostDetail> mockMessageHandlerUtilityForUsageCostDetail;
@Mock
ExecutionContext context;
Message<UsageCostDetail> messageToDrop;
Message<UsageDetail> messageToRead;
MessageReference messageReference;
@InjectMocks
UsageCostProcessorFunction usageCostProcessorFunction;
@BeforeEach
void setUp() {
var messageBodyUsageDetail = new MessageBody<UsageDetail>();
var usageDetailsList = new ArrayList<UsageDetail>();
var messageBodyUsageCostDetail = new MessageBody<UsageCostDetail>();
var usageCostDetailsList = new ArrayList<UsageCostDetail>();
for (int i = 0; i < 2; i++) {
var usageDetail = new UsageDetail();
usageDetail.setUserId("userId" + i);
usageDetail.setData(i + 1);
usageDetail.setDuration(i + 1);
usageDetailsList.add(usageDetail);
var usageCostDetail = new UsageCostDetail();
usageCostDetail.setUserId(usageDetail.getUserId());
usageCostDetail.setDataCost(usageDetail.getData() * 0.20);
usageCostDetail.setCallCost(usageDetail.getDuration() * 0.30);
usageCostDetailsList.add(usageCostDetail);
}
messageBodyUsageDetail.setData(usageDetailsList);
messageBodyUsageCostDetail.setData(usageCostDetailsList);
// Create message header
var messageHeader = new MessageHeader();
messageHeader.setId(UUID.randomUUID().toString());
messageHeader.setSubject("UsageDetailPublisher");
messageHeader.setTopic("usagecostprocessorfunction-topic");
messageHeader.setEventType("UsageDetail");
messageHeader.setEventTime(OffsetDateTime.now().toString());
this.messageReference = new MessageReference("callusageapp", "d8284456-dfff-4bd4-9cef-ea99f70f4835/input.json");
messageHeader.setData(messageReference);
messageHeader.setDataVersion("v1.0");
// Create entire message
messageToRead = new Message<>();
messageToRead.setMessageHeader(messageHeader);
messageToRead.setMessageBody(messageBodyUsageDetail);
messageToDrop = new Message<>();
messageToDrop.setMessageHeader(messageHeader);
messageToDrop.setMessageBody(messageBodyUsageCostDetail);
}
/**
* Unit test for HttpTriggerJava method.
*/
@Test
void shouldTriggerHttpAzureFunctionJavaWithSubscriptionValidationEventType() throws Exception {
// Setup
@SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
String fileAbsolutePath = getClass().getResource("/subscriptionValidationEvent.json").getPath()
.replaceAll("%20", " "), jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
doReturn(Optional.of(jsonBody)).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
@Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
// Invoke
final HttpResponseMessage ret = this.usageCostProcessorFunction.run(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.OK);
}
@Test
void shouldTriggerHttpAzureFunctionJavaWithUsageDetailEventType() throws Exception {
// Setup
@SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
String fileAbsolutePath = getClass().getResource("/usageDetailEvent.json").getPath().replaceAll("%20", " "),
jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
doReturn(Optional.of(jsonBody)).when(req).getBody();
doReturn(Logger.getGlobal()).when(context).getLogger();
when(this.mockMessageHandlerUtilityForUsageADetail.readFromPersistantStorage(any(MessageReference.class),
eq(Logger.getGlobal()))).thenReturn(messageToRead);
doAnswer(new Answer<HttpResponseMessage.Builder>() {
@Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
assertNotNull(this.mockMessageHandlerUtilityForUsageADetail);
assertEquals(this.messageToRead, this.mockMessageHandlerUtilityForUsageADetail
.readFromPersistantStorage(this.messageReference, Logger.getGlobal()));
// Invoke
final HttpResponseMessage ret = this.usageCostProcessorFunction.run(req, context);
// Verify
assertEquals(HttpStatus.OK, ret.getStatus());
assertEquals("Message is dropped successfully", ret.getBody());
}
}
@@ -1,120 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.producer.calldetails.functions;
import com.iluwatar.claimcheckpattern.HttpResponseMessageMock;
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import com.iluwatar.claimcheckpattern.utility.EventHandlerUtility;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* Unit test for Function class.
*/
@ExtendWith(MockitoExtension.class)
class UsageDetailPublisherFunctionTest {
@Mock
MessageHandlerUtility<UsageDetail> mockMessageHandlerUtility;
@Mock
EventHandlerUtility<MessageHeader> mockEventHandlerUtility;
@InjectMocks
UsageDetailPublisherFunction usageDetailPublisherFunction;
/**
* Unit test for HttpTriggerJava method.
*/
@Test
void shouldTriggerHttpAzureFunctionJavaWithSubscriptionValidationEventType() throws Exception {
// Setup
@SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
String fileAbsolutePath = getClass().getResource("/subscriptionValidationEvent.json").getPath()
.replaceAll("%20", " "), jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
doReturn(Optional.of(jsonBody)).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
@Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
// Invoke
final HttpResponseMessage ret = this.usageDetailPublisherFunction.run(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.OK);
}
@Test
void shouldTriggerHttpAzureFunctionJavaWithUsageDetailEventType() throws Exception {
// Setup
@SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
String fileAbsolutePath = getClass().getResource("/usageDetailEvent.json").getPath().replaceAll("%20", " "),
jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
doReturn(Optional.of(jsonBody)).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
@Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
doReturn(Logger.getGlobal()).when(context).getLogger();
// Invoke
final HttpResponseMessage ret = this.usageDetailPublisherFunction.run(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.OK);
}
}
@@ -1,71 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.utility;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import com.azure.core.util.BinaryData;
import com.azure.messaging.eventgrid.EventGridPublisherClient;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import java.util.logging.Logger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class EventHandlerUtilityTest {
@Mock
EventGridPublisherClient<BinaryData> mockCustomEventClient;
@InjectMocks
EventHandlerUtility<Message<UsageDetail>> eventHandlerUtility;
@BeforeEach
void setUp() {
System.setProperty("EventGridURL", "https://www.dummyEndpoint.com/api/events");
System.setProperty("EventGridKey", "EventGridURL");
}
@Test
void shouldPublishEvent() {
doNothing().when(mockCustomEventClient).sendEvent(any(BinaryData.class));
eventHandlerUtility.publishEvent(null, Logger.getLogger("logger"));
verify(mockCustomEventClient, times(1)).sendEvent(any(BinaryData.class));
}
@Test
void shouldPublishEventWithNullLogger() {
eventHandlerUtility.publishEvent(null, null);
verify(mockCustomEventClient, times(1)).sendEvent(any(BinaryData.class));
}
}
@@ -1,115 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.utility;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.iluwatar.claimcheckpattern.domain.*;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Logger;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class MessageHandlerUtilityTest {
@Mock
private BlobClient mockBlobClient;
@Mock
private BlobContainerClient mockContainerClient;
@Mock
private BlobServiceClient mockBlobServiceClient;
@InjectMocks
private MessageHandlerUtility<UsageDetail> messageHandlerUtility;
private Message<UsageDetail> messageToPublish;
private MessageReference messageReference;
@BeforeEach
void setUp() {
System.setProperty("BlobStorageConnectionString", "https://www.dummyEndpoint.com/api/blobs");
var messageBody = new MessageBody<UsageDetail>();
var usageDetailsList = new ArrayList<UsageDetail>();
var random = new Random();
for (int i = 0; i < 51; i++) {
var usageDetail = new UsageDetail();
usageDetail.setUserId("userId" + i);
usageDetail.setData(random.nextInt(500));
usageDetail.setDuration(random.nextInt(500));
usageDetailsList.add(usageDetail);
}
messageBody.setData(usageDetailsList);
// Create message header
var messageHeader = new MessageHeader();
messageHeader.setId(UUID.randomUUID().toString());
messageHeader.setSubject("UsageDetailPublisher");
messageHeader.setTopic("usagecostprocessorfunction-topic");
messageHeader.setEventType("UsageDetail");
messageHeader.setEventTime(OffsetDateTime.now().toString());
this.messageReference = new MessageReference("callusageapp", messageHeader.getId() + "/input.json");
messageHeader.setData(messageReference);
messageHeader.setDataVersion("v1.0");
// Create entire message
this.messageToPublish = new Message<>();
this.messageToPublish.setMessageHeader(messageHeader);
this.messageToPublish.setMessageBody(messageBody);
when(mockContainerClient.getBlobClient(anyString())).thenReturn(mockBlobClient);
when(mockBlobServiceClient.getBlobContainerClient(anyString())).thenReturn(mockContainerClient);
}
@Test
void shouldDropMessageToPersistantStorage() {
messageHandlerUtility.dropToPersistantStorage(messageToPublish, Logger.getLogger("logger"));
verify(mockBlobServiceClient, times(1)).getBlobContainerClient(anyString());
// verify(mockContainerClient, times(0)).exists();
}
@Test
void shouldReadMessageFromPersistantStorage() {
messageHandlerUtility.readFromPersistantStorage(messageReference, Logger.getLogger("logger"));
verify(mockBlobServiceClient, times(1)).getBlobContainerClient(anyString());
}
}
@@ -1,15 +0,0 @@
[
{
"data": {
"validationCode": "C12F266E-79D9-4C0A-9922-5EF6201A34C2",
"validationUrl": "https://rp-centralindia.eventgrid.azure.net:553/eventsubscriptions/usagedetailpublisherfunction-subscription/validate?idu003dC12F266E-79D9-4C0A-9922-5EF6201A34C2u0026tu003d2021-10-26T08:10:52.4999377Zu0026apiVersionu003d2020-10-15-previewu0026tokenu003d30kEVoL8rAOWzQv0buurhrKnbP%2bGMtHObbA%2bax6wb4Y%3d"
},
"dataVersion": "2",
"eventTime": "2021-10-26T08:10:52.4999377Z",
"eventType": "Microsoft.EventGrid.SubscriptionValidationEvent",
"id": "e2a8466b-3dc0-46b7-bb7d-b999e51a2848",
"metadataVersion": "1",
"subject": "",
"topic": "/subscriptions/0fef643d-a6b1-48f9-a256-53fbd0d22f48/resourceGroups/resource-group-ccp/providers/Microsoft.EventGrid/domains/event-grid-domains-ccp/topics/usagedetailpublisherfunction-topic"
}
]
@@ -1,15 +0,0 @@
[
{
"data": {
"dataFileName": "d8284456-dfff-4bd4-9cef-ea99f70f4835/input.json",
"dataLocation": "callusageapp"
},
"dataVersion": "v1.0",
"eventTime": "2021-10-25T19:17:15.7468501Z",
"eventType": "UsageDetail",
"id": "d8284456-dfff-4bd4-9cef-ea99f70f4835",
"metadataVersion": "1",
"subject": "UsageDetailPublisher",
"topic": "/subscriptions/0fef643d-a6b1-48f9-a256-ea99f70f4835/resourceGroups/resource-group-ccp/providers/Microsoft.EventGrid/domains/event-grid-domains-ccp/topics/usagecostprocessorfunction-topic"
}
]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

@@ -1,117 +0,0 @@
@startuml
class UsageDetailPublisherFunction [[java:com.iluwatar.producer.calldetails.functions]] {
-messageHandlerUtility: MessageHandlerUtility<UsageDetail>
-eventHandlerUtility: EventHandlerUtility<MessageHeader>
+run(): HttpResponseMessage
}
class UsageCostProcessorFunction [[java:com.iluwatar.consumer.callcostprocessor.functions]] {
-messageHandlerUtilityForUsageDetail: MessageHandlerUtility<UsageDetail>
-messageHandlerUtilityForUsageCostDetail: MessageHandlerUtility<UsageCostDetail>
+run(): HttpResponseMessage
}
class "MessageHandlerUtility<T>" as MessageHandlerUtility_T [[java:com.iluwatar.claimcheckpattern.utility]] {
+readFromPersistantStorage(messageReference: MessageReference, logger: Logger): Message
+dropToPersistantStorage(message: Message, logger: Logger): void
}
class "EventHandlerUtility<T>" as EventHandlerUtility_T [[java:com.callusage.utility.PersistentLocalStorageUtility]] {
+publishEvent(customEvent: T, logger: Logger): void
}
class "Message<T>" as Message_T [[java:com.iluwatar.claimcheckpattern.domain]] {
-messageHeader: MessageHeader
-messageData: MessageData<T>
+Message(messageHeader: MessageHeader, messageData: MessageData<T>)
+getMessageData(): MessageData<T>
+getMessageHeader(): MessageHeader
}
class MessageHeader [[java:com.iluwatar.claimcheckpattern.domain]] {
-id: String
-subject: String
-topic: String
-eventType: String
-eventTime: String
-data: Object
-dataVersion: String
+getId(): String
+setId(id: String): void
+getSubject(): String
+setSubject(subject: String): void
+getTopic(): String
+setTopic(topic: String): void
+getEventType(): String
+setEventType(eventType: String): void
+getEventTime(): String
+setEventTime(eventTime: String): void
+getData(): Object
+setData(data: Object): void
+getDataVersion(): String
+setDataVersion(dataVersion:String): void
}
class "MessageBody<T>" as MessageBody_T [[java:com.iluwatar.claimcheckpattern.domain]] {
-data: List[] T
+getData(): List[] T
+setData(data:List[] T): void
}
class MessageReference [[java:com.iluwatar.claimcheckpattern.domain]] {
-dataLocation: String
-dataFileName: String
+getDataLocation(): String
+setDataLocation(dataLocation:String): void
+getDataFileName(): String
+setDataFileName(dataFileName:String): void
}
class UsageDetail [[java:com.iluwatar.claimcheckpattern.domain]] {
-userId: String
-duration: int
-data: int
+getUserId(): String
+setUserId(userId: String): void
+getDuration(): long
+setDuration(duration: long): void
+getData(): long
+setData(data: long): void
}
class UsageCostDetail [[java:com.iluwatar.claimcheckpattern.domain]] {
-userId: String
-callCost: double
-dataCost: double
+getUserId(): String
+setUserId(userId: String): void
+getCallCost(): double
+setCallCost(callCost:double): void
+getDataCost(): double
+setDataCost(dataCost:double) : void
}
Message_T "1" *-- "1" MessageHeader : has
Message_T "1" *-- "1" MessageBody_T : has
MessageHeader "1" *-- "1" MessageReference : has as data object
MessageBody_T "1" *-- "1" UsageDetail: has
MessageBody_T "1" *-- "1" UsageCostDetail: has
EventHandlerUtility_T "1" *-- "1" MessageHeader: has
MessageHandlerUtility_T "1" *-- "1" Message_T: has
UsageDetailPublisherFunction "1" *-- "1" MessageHandlerUtility_T : has
UsageDetailPublisherFunction "1" *-- "1" EventHandlerUtility_T : has
UsageCostProcessorFunction "1" *-- "1" MessageHandlerUtility_T : has
UsageCostProcessorFunction "1" *-- "1" MessageHandlerUtility_T : has
@enduml
Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

-71
View File
@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License
Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<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">
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>claim-check-pattern</artifactId>
<packaging>pom</packaging>
<modules>
<module>call-usage-app</module>
</modules>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
-145
View File
@@ -1,145 +0,0 @@
---
title: Static Content Hosting
category: Cloud
language: en
tag:
- Cloud distributed
---
## Intent
Deploy static content to a cloud-based storage service that can deliver them directly to the client.
This can reduce the need for potentially expensive compute instances.
## Explanation
Real world example
> A global marketing web site with static content needs to be quickly deployed to start attracting
> potential customers. To keep the hosting expenses and maintenance minimum, a cloud hosted storage
> service along with content delivery network is used.
In plain words
> Static Content Hosting pattern utilizes cloud native storage service to store the content and
> global content delivery network to cache it in multiple data centers around the world.
>
> On a static website, individual webpages include static content. They might also contain
> client-side scripts such as Javascript. By contrast, a dynamic website relies on server-side
> processing, including server-side scripts such as PHP, JSP, or ASP.NET.
Wikipedia says
> A static web page (sometimes called a flat page or a stationary page) is a web page that is
> delivered to the user's web browser exactly as stored, in contrast to dynamic web pages which are
> generated by a web application.
>
> Static web pages are suitable for content that never or rarely needs to be updated, though modern
> web template systems are changing this. Maintaining large numbers of static pages as files can be
> impractical without automated tools, such as static site generators.
**Example**
![alt text](./etc/static-content-hosting.png "Static Content Hosting")
In this example we create a static web site using AWS S3 and utilize AWS Cloudfront to distribute
the content globally.
1. First you will need an AWS account. You can create a free one here: [AWS Free Tier](https://aws.amazon.com/free/free-tier/)
2. Login to the [AWS Console](https://console.aws.amazon.com/console/home?nc2=h_ct&src=header-signin)
3. Go to Identity and Access Management (IAM) service.
4. Create IAM user that has only the necessary rights for this application.
* Click `Users`
* Click `Add user`. Choose `User name` as you wish and `Access type` should be `Programmatic access`. Click `Next: Permissions`.
* Choose `Attach existing policies directly`. Select `AmazonS3FullAccess` and `CloudFrontFullAccess`. Click `Next: Tags`.
* No tags are necessarily needed, so just click `Next: Review`.
* Review the presented information and if all seems good click `Create user`.
* You are presented with `Access key ID` and `Secret access key` which you will need to complete this example, so store them safely.
* Click `Close`.
5. [Install AWS Command Line Interface (AWS CLI)](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv1.html) to gain programmic access to AWS cloud.
6. Configure AWS CLI with command `aws configure` as desribed in the [instructions](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config)
7. Create AWS S3 bucket for the web site content. Note that the S3 bucket names must be globally unique.
* The syntax is `aws s3 mb <bucket name>` as described in the [instructions](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-buckets-creating)
* For example `aws s3 mb s3://my-static-website-jh34jsjmg`
* Verify that the bucket was successfully created with command `aws s3 ls` which list the existing buckets
8. Configure the bucket as a web site with command `aws s3 website` as described in the [instructions](https://docs.aws.amazon.com/cli/latest/reference/s3/website.html).
* E.g. `aws s3 website s3://my-static-website-jh34jsjmg --index-document index.html --error-document error.html`
9. Upload content to the bucket.
* First create the content, at least `index.html` and `error.html` documents.
* Upload the content to your bucket as described [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-copy)
* E.g. `aws s3 cp index.html s3://my-static-website-jh34jsjmg` and `aws s3 cp error.html s3://my-static-website-jh34jsjmg`
10. Next we need to set the bucket policy to allow read access.
* Create `policy.json` with the following contents (note that you need to replace the bucket name with your own).
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-static-website-jh34jsjmg/*"
}
]
}
```
* Set the bucket policy according to these [instructions](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-policy.html)
* E.g. `aws s3api put-bucket-policy --bucket my-static-website-jh34jsjmg --policy file://policy.json`
11. Test the web site in your browser.
* The web site URL format is `http://<bucket-name>.s3-website-<region-name>.amazonaws.com`
* E.g. this web site was created in `eu-west-1` region with name `my-static-website-jh34jsjmg` so it can be accessed via url `http://my-static-website-jh34jsjmg.s3-website-eu-west-1.amazonaws.com`
12. Create CloudFormation distribution for the web site.
* The syntax is described in [this reference](https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-distribution.html)
* E.g. the easiest way is to call `aws cloudfront create-distribution --origin-domain-name my-static-website-jh34jsjmg.s3.amazonaws.com --default-root-object index.html`
* There's also JSON syntax e.g. `--distribution-config file://dist-config.json` to pass distribution configuration arguments in file
* The output of the call will show you the exact distribution settings including the generated CloudFront domain name you can use for testing e.g. `d2k3xwnaqa8nqx.cloudfront.net`
* CloudFormation distribution deployment takes some time, but once it's completed your web site is served from data centers all around the globe!
13. That's it! You have implemented a static web site with content distribution network serving it lightning fast all around the world.
* To update the web site you need to update the objects in S3 bucket and invalidate the objects in the CloudFront distribution
* To do it from AWS CLI see [this reference](https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-invalidation.html)
* Some further development you might want to do is serve the content over https and add a domain name for your site
## Applicability
Use the Static Content Hosting pattern when you want to:
* Minimize the hosting cost for websites and applications that contain some static resources.
* Build a globally available web site with static content
* Monitor the web site traffic, bandwidth usage, costs etc.
## Typical Use Case
* Web sites with global reach
* Content produced by static web site generators
* Web sites with no dynamic content requirements
## Real world examples
* [Java Design Patterns web site](https://java-design-patterns.com)
## Credits
* [Static Content Hosting pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/static-content-hosting)
@@ -1 +0,0 @@
<mxfile host="Electron" modified="2021-02-14T18:28:17.242Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/12.1.0 Chrome/76.0.3809.139 Electron/6.0.7 Safari/537.36" etag="QnfBZG6C0m1UGNnIYq8H" version="12.1.0" type="device" pages="1"><diagram id="yBzANdZOEQQZAcADu5ny" name="Page-1">7Vpbc9o6EP41PMZjW77AY0Kh56HttMOcIX3qCCxsNbLlyjKX/PqzMvING9rMQGE4mWQS72olrfbbXe8KBmgcbz8KnEafeUDYwDaD7QB9GNi2hRwL/inObs8ZIc0IBQ20UM2Y0Veimabm5jQgWUtQcs4kTdvMJU8SspQtHhaCb9piK87au6Y4JB3GbIlZlzungYz23KFr1vx/CA2jcmfL1CMxLoU1I4twwDcNFpoM0FhwLvdP8XZMmDJeaZf9vOmR0UoxQRL5JxOePj8vvN38XzEXc/KNzvDj65cH29PKyV15YhKAATTJhYx4yBPMJjX3aZmLNVGrWkDUIp84TzXzJ5Fyp8HEueTAimTM9CgoLHbPQJiGW5Lfm2MflPuYFbUrqS2Vz6UcPH+vlwCqnqSIcs7+eOpMR82mWRnPxZKcspWr/Q+LkMgTgsMKXQgLwmMCh4B5gjAs6bqtCNb+GVZyNYTwoFF8C6L+NRDVyJg3jox/VWS0lmvMcr1VFyrGILMpSDYRlWSW4uLgG0iubYvrpYiQZHvaft3j6gn2SCukU7M30plqUyc6G2le1Exy/uhCJhreloWc0iLaQrZvGe61beTflo089/ZMNLqhl5r5m5faGdNgWR/ddBYsC7vrgGO9Q3NCSes9bm62rrN60vzfBGfYRsewQKHflOuK+koEBQMQ8X+FrfO2DsiaMJ6CRQ7x5LlkNCHjqpVVBlvxRI4546KQQfAzVds/hQIHlNRjCU8U4ivKWJ94JgV/IQfCAc6iwj3URqoKoND4fsILwr7yjErKExhbcCl53BB4ZDRUA1I50RPW1BJ0USg33Ufprn2r8JaC1udVW+Is3R90RbdKj6eUU7XKZA2LZXoR6JtTNSHehuqKwcCbzDHyrNjrDPUL8ts1ntctgv1ht3gpeefvEjoO8zifAWOG4M8iX76Ar/+R41wa3B5ckLFX8MeGyugHX/wEbbKuU05M13bQcR8+A6qW20YVoS6sXk9N6lXV69mBRUeAHTOeB1MBoXHDuC6VkqtCyQ6cU3foIu+icLqeZ7T7DGQ5PX2GX4k1QR2hSyX3LqaWoS4jBcGSHI9XMIQ8MHpfem7aWbM6wB3iG9MgYMc6P8HzJKh85xxhZrfDzOrJnpbVE2flvPND4nQgsRUkOAhgvTwN9sCUuelukaku0Mu+3OxBpq8rvxwy3esv1AqWgIKt6SIvUtjdAuOWSaMMGf/qwHRvlJwqZEyaAJ9C2AAqdxwvnnMQL9bVYRl1YClfJ+aGLOBVr4KGJEFRNA9sjykkFtBWeOEeEwmWelRKqF8Px8pwe6FinYcEx6QaKOYYGXrQaz+0pwgSggP0TcExfoWmcpMZS6g47tZBDvKp0/OmQ30V5cX8o1y44R+NKg3SKY8xhURqasxOuUdgv6DtJsG/8DD5tW1Ue0ai/G16v7A69s3h2r0yUN1uN/e+Xxe86bogO9d9gdVyGN8x+xqRv3llYL3pY7PSQEWQHwmzM3+M1g4xt6cU9Z2eEKua+DdYDMj6KxXFWOOLKWjyHw==</diagram></mxfile>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

-30
View File
@@ -1,30 +0,0 @@
---
title: EIP Aggregator
category: Integration
language: en
tag:
- Enterprise Integration Pattern
---
## Intent
Sometimes in enterprise systems there is a need to group incoming data in order to process it as a whole. For example
you may need to gather offers and after defined number of offers has been received you would like to choose the one with
the best parameters.
Aggregator allows you to merge messages based on defined criteria and parameters. It gathers original messages,
applies aggregation strategy and upon fulfilling given criteria, releasing merged messages.
## Diagram
![alt text](./etc/aggregator.gif "Splitter")
## Applicability
Use the Aggregator pattern when
* You need to combine multiple incoming messages
* You want to process grouped data
## Credits
* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/Aggregator.html)
* [Apache Camel - Documentation](http://camel.apache.org/aggregator2.html)
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

@@ -1,14 +0,0 @@
@startuml
package com.iluwatar.eip.aggregator {
class App {
+ App()
+ main(args : String[]) {static}
}
}
package com.iluwatar.eip.aggregator.routes {
class MessageAggregationStrategy {
+ MessageAggregationStrategy()
+ aggregate(oldExchange : Exchange, newExchange : Exchange) : Exchange
}
}
@enduml
-93
View File
@@ -1,93 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License
Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<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>
<artifactId>eip-aggregator</artifactId>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring</artifactId>
<version>${camel.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.epi.aggregator.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,71 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.aggregator;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Sometimes in enterprise systems there is a need to group incoming data in order to process it as
* a whole. For example you may need to gather offers and after defined number of offers has been
* received you would like to choose the one with the best parameters.
*
* <p>Aggregator allows you to merge messages based on defined criteria and parameters. It gathers
* original messages, applies aggregation strategy and upon fulfilling given criteria, releasing
* merged messages.
*/
@SpringBootApplication
public class App {
/**
* Program entry point. It starts Spring Boot application and using Apache Camel it
* auto-configures routes.
*
* @param args command line args
*/
public static void main(String[] args) throws Exception {
// Run Spring Boot application and obtain ApplicationContext
var context = SpringApplication.run(App.class, args);
// Get CamelContext from ApplicationContext
var camelContext = (CamelContext) context.getBean("camelContext");
// Add a new routes that will handle endpoints form SplitterRoute class.
camelContext.addRoutes(new RouteBuilder() {
@Override
public void configure() {
from("{{endpoint}}").log("ENDPOINT: ${body}");
}
});
// Add producer that will send test message to an entry point in WireTapRoute
String[] stringArray = {"Test item #1", "Test item #2", "Test item #3"};
camelContext.createProducerTemplate().sendBody("{{entry}}", stringArray);
SpringApplication.exit(context);
}
}
@@ -1,60 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.aggregator.routes;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Sample aggregator route definition.
*
* <p>It consumes messages out of the <i>direct:entry</i> entry point and forwards them to
* <i>direct:endpoint</i>. Route accepts messages containing String as a body, it aggregates the
* messages based on the settings and forwards them as CSV to the output chanel.
*
* <p>Settings for the aggregation are: aggregate until 3 messages are bundled or wait 2000ms
* before sending bundled messages further.
*
* <p>In this example input/output endpoints names are stored in <i>application.properties</i>
* file.
*/
@Component
public class AggregatorRoute extends RouteBuilder {
@Autowired
private MessageAggregationStrategy aggregator;
/**
* Configures the route.
*/
@Override
public void configure() {
// Main route
from("{{entry}}").aggregate(constant(true), aggregator)
.completionSize(3).completionInterval(2000)
.to("{{endpoint}}");
}
}
@@ -1,51 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.aggregator.routes;
import org.apache.camel.AggregationStrategy;
import org.apache.camel.Exchange;
import org.springframework.stereotype.Component;
/**
* Aggregation strategy joining bodies of messages. If message is first one <i>oldMessage</i> is
* null. All changes are made on IN messages.
*/
@Component
public class MessageAggregationStrategy implements AggregationStrategy {
@Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
if (oldExchange == null) {
return newExchange;
}
var in1 = (String) oldExchange.getIn().getBody();
var in2 = (String) newExchange.getIn().getBody();
oldExchange.getIn().setBody(in1 + ";" + in2);
return oldExchange;
}
}
@@ -1,25 +0,0 @@
#
# The MIT License
# Copyright © 2014-2021 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
entry=direct:entry
endpoint=direct:endpoint
@@ -1,47 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.aggregator;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/**
* Test for App class
*/
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
* Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
* throws an exception.
*/
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
@@ -1,87 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.aggregator.routes;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Test class for <i>AggregatorRoute</i>.
* <p>
* In order for it to work we have to mock endpoints we want to read/write to. To mock those we need
* to substitute original endpoint names to mocks.
* </p>
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = AggregatorRouteTest.class)
@ActiveProfiles("test")
@EnableAutoConfiguration
@ComponentScan
class AggregatorRouteTest {
@EndpointInject(uri = "{{entry}}")
private ProducerTemplate entry;
@EndpointInject(uri = "{{endpoint}}")
private MockEndpoint endpoint;
/**
* Test if endpoint receives three separate messages.
*
* @throws Exception in case of en exception during the test
*/
@Test
@DirtiesContext
void testSplitter() throws Exception {
// Three items in one entry message
entry.sendBody("TEST1");
entry.sendBody("TEST2");
entry.sendBody("TEST3");
entry.sendBody("TEST4");
entry.sendBody("TEST5");
// Endpoint should have three different messages in the end order of the messages is not important
endpoint.expectedMessageCount(2);
endpoint.assertIsSatisfied();
var body = (String) endpoint.getReceivedExchanges().get(0).getIn().getBody();
assertEquals(3, body.split(";").length);
var body2 = (String) endpoint.getReceivedExchanges().get(1).getIn().getBody();
assertEquals(2, body2.split(";").length);
}
}
@@ -1,68 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.aggregator.routes;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.camel.CamelContext;
import org.apache.camel.impl.engine.SimpleCamelContext;
import org.apache.camel.support.DefaultExchange;
import org.junit.jupiter.api.Test;
/**
* Tests MessageAggregationStrategy
*/
class MessageAggregationStrategyTest {
private final CamelContext context = new SimpleCamelContext();
@Test
void testAggregate() {
var mas = new MessageAggregationStrategy();
var oldExchange = new DefaultExchange(context);
oldExchange.getIn().setBody("TEST1");
var newExchange = new DefaultExchange(context);
newExchange.getIn().setBody("TEST2");
var output = mas.aggregate(oldExchange, newExchange);
var outputBody = (String) output.getIn().getBody();
assertEquals("TEST1;TEST2", outputBody);
}
@Test
void testAggregateOldNull() {
var mas = new MessageAggregationStrategy();
var newExchange = new DefaultExchange(context);
newExchange.getIn().setBody("TEST2");
var output = mas.aggregate(null, newExchange);
var outputBody = (String) output.getIn().getBody();
assertEquals(newExchange, output);
assertEquals("TEST2", outputBody);
}
}
@@ -1,25 +0,0 @@
#
# The MIT License
# Copyright © 2014-2021 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
entry=direct:entry
endpoint=mock:endpoint
-1
View File
@@ -1 +0,0 @@
/target/
-24
View File
@@ -1,24 +0,0 @@
---
title: EIP Message Channel
category: Integration
language: en
tag:
- Enterprise Integration Pattern
---
## Intent
When two applications communicate using a messaging system they do it by using logical addresses
of the system, so called Message Channels.
## Class diagram
![alt text](./etc/message-channel.png "Message Channel")
## Applicability
Use the Message Channel pattern when
* two or more applications need to communicate using a messaging system
## Real world examples
* [akka-camel](http://doc.akka.io/docs/akka/snapshot/scala/camel.html)
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
@@ -1,9 +0,0 @@
@startuml
package com.iluwatar.eip.message.channel {
class App {
- LOGGER : Logger {static}
+ App()
+ main(args : String[]) {static}
}
}
@enduml
Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

@@ -1,320 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<class id="1" language="java" name="org.apache.camel.builder.RouteBuilder" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="920" y="458"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<interface id="2" language="java" name="org.apache.camel.CamelContext" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="674" y="458"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<class id="3" language="java" name="org.apache.camel.model.RouteDefinition" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="673" y="48"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="4" language="java" name="org.apache.camel.model.ProcessorDefinition" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="673" y="-96"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="5" language="java" name="org.apache.camel.model.OptionalIdentifiedDefinition" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="248" y="-96"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<interface id="6" language="java" name="org.apache.camel.NamedNode" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="249" y="0"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<interface id="7" language="java" name="org.apache.camel.model.Block" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="464" y="13"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<interface id="8" language="java" name="org.apache.camel.RoutesBuilder" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="920" y="590"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<class id="9" language="java" name="org.apache.camel.builder.BuilderSupport" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="920" y="297"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<interface id="10" language="java" name="org.apache.camel.SuspendableService" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="267" y="458"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<interface id="11" language="java" name="org.apache.camel.Service" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="267" y="297"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<interface id="12" language="java" name="org.apache.camel.RuntimeConfiguration" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="674" y="590"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<class id="13" language="java" name="org.apache.camel.impl.DefaultCamelContext" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="673" y="167"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="14" language="java" name="org.apache.camel.support.ServiceSupport" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="265" y="91"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<interface id="15" language="java" name="org.apache.camel.StatefulService" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="5" y="188"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<interface id="16" language="java" name="org.apache.camel.ShutdownableService" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="265" y="188"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<interface id="17" language="java" name="org.apache.camel.model.ModelCamelContext" project="message-channel"
file="C:/Users/ilkka/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="674" y="297"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</interface>
<generalization id="18">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="5"/>
</generalization>
<generalization id="19">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="10"/>
</generalization>
<realization id="20">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="8"/>
</realization>
<association id="21">
<end type="SOURCE" refId="9" navigable="false">
<attribute id="22" name="context">
<position height="0" width="0" x="-583" y="-276"/>
</attribute>
<multiplicity id="23" minimum="0" maximum="1">
<position height="0" width="0" x="-583" y="-276"/>
</multiplicity>
</end>
<end type="TARGET" refId="17" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="24">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="25" name="blocks">
<position height="0" width="0" x="145" y="-462"/>
</attribute>
<multiplicity id="26" minimum="0" maximum="2147483647">
<position height="0" width="0" x="145" y="-462"/>
</multiplicity>
</end>
<end type="TARGET" refId="7" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="27">
<end type="SOURCE" refId="13" navigable="false">
<attribute id="28" name="servicesToClose">
<position height="0" width="0" x="-409" y="37"/>
</attribute>
<multiplicity id="29" minimum="0" maximum="2147483647">
<position height="0" width="0" x="-409" y="37"/>
</multiplicity>
</end>
<end type="TARGET" refId="11" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="30">
<end type="SOURCE" refId="13"/>
<end type="TARGET" refId="17"/>
</realization>
<realization id="31">
<end type="SOURCE" refId="14"/>
<end type="TARGET" refId="15"/>
</realization>
<generalization id="32">
<end type="SOURCE" refId="17"/>
<end type="TARGET" refId="2"/>
</generalization>
<generalization id="33">
<end type="SOURCE" refId="15"/>
<end type="TARGET" refId="10"/>
</generalization>
<generalization id="34">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="9"/>
</generalization>
<generalization id="35">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="4"/>
</generalization>
<generalization id="36">
<end type="SOURCE" refId="16"/>
<end type="TARGET" refId="11"/>
</generalization>
<generalization id="37">
<end type="SOURCE" refId="15"/>
<end type="TARGET" refId="16"/>
</generalization>
<generalization id="38">
<end type="SOURCE" refId="10"/>
<end type="TARGET" refId="11"/>
</generalization>
<realization id="39">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="7"/>
</realization>
<association id="40">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="41" name="parent">
<position height="0" width="0" x="329" y="-461"/>
</attribute>
<multiplicity id="42" minimum="0" maximum="1">
<position height="0" width="0" x="329" y="-461"/>
</multiplicity>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="43">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="6"/>
</realization>
<generalization id="44">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="12"/>
</generalization>
<generalization id="45">
<end type="SOURCE" refId="13"/>
<end type="TARGET" refId="14"/>
</generalization>
<association id="46">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="47" name="outputs">
<position height="0" width="0" x="329" y="-461"/>
</attribute>
<multiplicity id="48" minimum="0" maximum="2147483647">
<position height="0" width="0" x="329" y="-461"/>
</multiplicity>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="49">
<end type="SOURCE" refId="13" navigable="false">
<attribute id="50" name="routeDefinitions">
<position height="0" width="0" x="291" y="-218"/>
</attribute>
<multiplicity id="51" minimum="0" maximum="2147483647">
<position height="0" width="0" x="291" y="-218"/>
</multiplicity>
</end>
<end type="TARGET" refId="3" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="52">
<end type="SOURCE" refId="13"/>
<end type="TARGET" refId="10"/>
</realization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>
-70
View File
@@ -1,70 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License
Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<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>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>eip-message-channel</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-stream</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.eip.message.channel.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,73 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.message.channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
/**
* When two applications communicate with each other using a messaging system they first need to
* establish a communication channel that will carry the data. Message Channel decouples Message
* producers and consumers.
*
* <p>The sending application doesn't necessarily know what particular application will end up
* retrieving it, but it can be assured that the application that retrieves the information is
* interested in that information. This is because the messaging system has different Message
* Channels for different types of information the applications want to communicate. When an
* application sends information, it doesn't randomly add the information to any channel available;
* it adds it to a channel whose specific purpose is to communicate that sort of information.
* Likewise, an application that wants to receive particular information doesn't pull info off some
* random channel; it selects what channel to get information from based on what type of information
* it wants.
*
* <p>In this example we use Apache Camel to establish two different Message Channels. The first
* one reads from standard input and delivers messages to Direct endpoint. The second Message
* Channel is established from the Direct component to console output. No actual messages are sent,
* only the established routes are printed to standard output.
*/
@Slf4j
public class App {
/**
* Program entry point.
*/
public static void main(String[] args) throws Exception {
var context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
@Override
public void configure() throws Exception {
from("stream:in").to("direct:greetings");
from("direct:greetings").to("stream:out");
}
});
context.start();
context.getRoutes().forEach(r -> LOGGER.info(r.toString()));
context.stop();
}
}
@@ -1,47 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.message.channel;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/**
* Application test
*/
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
* Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
* throws an exception.
*/
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
-1
View File
@@ -1 +0,0 @@
/target/
-23
View File
@@ -1,23 +0,0 @@
---
title: EIP Publish Subscribe
category: Integration
language: en
tag:
- Enterprise Integration Pattern
---
## Intent
Broadcast messages from sender to all the interested receivers.
## Class diagram
![alt text](./etc/publish-subscribe.png "Publish Subscribe Channel")
## Applicability
Use the Publish Subscribe Channel pattern when
* two or more applications need to communicate using a messaging system for broadcasts.
## Credits
* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
@@ -1,9 +0,0 @@
@startuml
package com.iluwatar.eip.publish.subscribe {
class App {
- LOGGER : Logger {static}
+ App()
+ main(args : String[]) {static}
}
}
@enduml
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

@@ -1,218 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true">
<class id="1" language="java" name="org.apache.camel.builder.RouteBuilder" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="269" y="526"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</class>
<interface id="2" language="java" name="org.apache.camel.RoutesBuilder" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="270" y="406"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</interface>
<class id="3" language="java" name="org.apache.camel.model.RouteDefinition" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="574" y="421"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</class>
<class id="4" language="java" name="org.apache.camel.model.RoutesDefinition" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="574" y="526"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</class>
<class id="5" language="java" name="org.apache.camel.model.MulticastDefinition" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="574" y="68"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</class>
<class id="6" language="java" name="org.apache.camel.model.OutputDefinition" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="576" y="164"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</class>
<class id="7" language="java" name="org.apache.camel.model.ProcessorDefinition" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="575" y="284"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</class>
<interface id="8" language="java" name="org.apache.camel.CamelContext" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_LEFT">
<position height="-1" width="-1" x="1086" y="346"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</interface>
<interface id="9" language="java" name="org.apache.camel.model.ModelCamelContext" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_LEFT">
<position height="-1" width="-1" x="1086" y="526"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</interface>
<class id="10" language="java" name="org.apache.camel.impl.DefaultCamelContext" project="message-channel"
file="D:/Users/mdaehner/.m2/repository/org/apache/camel/camel-core/2.15.3/camel-core-2.15.3.jar" binary="true"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="885" y="421"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</display>
</class>
<realization id="11">
<end type="SOURCE" refId="10"/>
<end type="TARGET" refId="9"/>
</realization>
<generalization id="12">
<end type="SOURCE" refId="6"/>
<end type="TARGET" refId="7"/>
</generalization>
<association id="13">
<end type="SOURCE" refId="6" navigable="false">
<attribute id="14" name="outputs">
<position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="15" minimum="0" maximum="2147483647">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end>
<end type="TARGET" refId="7" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="16">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="2"/>
</realization>
<generalization id="17">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="7"/>
</generalization>
<association id="18">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="19" name="camelContext">
<position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="20" minimum="0" maximum="1">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end>
<end type="TARGET" refId="9" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="21">
<end type="SOURCE" refId="7" navigable="false">
<attribute id="22" name="parent">
<position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="23" minimum="0" maximum="1">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end>
<end type="TARGET" refId="7" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="24">
<end type="SOURCE" refId="1" navigable="false">
<attribute id="25" name="routeCollection">
<position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="26" minimum="0" maximum="1">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="27">
<end type="SOURCE" refId="10" navigable="false">
<attribute id="28" name="routeDefinitions">
<position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="29" minimum="0" maximum="2147483647">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end>
<end type="TARGET" refId="3" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="30">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="31" name="outputs">
<position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="32" minimum="0" maximum="2147483647">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end>
<end type="TARGET" refId="7" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="33">
<end type="SOURCE" refId="9"/>
<end type="TARGET" refId="8"/>
</generalization>
<association id="34">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="35" name="routes">
<position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="36" minimum="0" maximum="2147483647">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end>
<end type="TARGET" refId="3" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="37">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="6"/>
</generalization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="false"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="false"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>
-70
View File
@@ -1,70 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License
Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<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>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>eip-publish-subscribe</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-stream</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.eip.publish.subscribe.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,68 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.publish.subscribe;
import lombok.extern.slf4j.Slf4j;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
/**
* There are well-established patterns for implementing broadcasting. The Observer pattern describes
* the need to decouple observers from their subject (that is, the originator of the event) so that
* the subject can easily provide event notification to all interested observers no matter how many
* observers there are (even none). The Publish-Subscribe pattern expands upon Observer by adding
* the notion of an event channel for communicating event notifications.
*
* <p>A Publish-Subscribe Channel works like this: It has one input channel that splits into
* multiple output channels, one for each subscriber. When an event is published into the channel,
* the Publish-Subscribe Channel delivers a copy of the message to each of the output channels. Each
* output end of the channel has only one subscriber, which is allowed to consume a message only
* once. In this way, each subscriber gets the message only once, and consumed copies disappear from
* their channels.
*
* <p>In this example we use Apache Camel to establish a Publish-Subscribe Channel from
* "direct-origin" to "mock:foo", "mock:bar" and "stream:out".
*/
@Slf4j
public class App {
/**
* Program entry point.
*/
public static void main(String[] args) throws Exception {
var context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
@Override
public void configure() throws Exception {
from("direct:origin").multicast().to("mock:foo", "mock:bar", "stream:out");
}
});
var template = context.createProducerTemplate();
context.start();
context.getRoutes().forEach(r -> LOGGER.info(r.toString()));
template.sendBody("direct:origin", "Hello from origin");
context.stop();
}
}
@@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright © 2014-2021 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>publish-subscribe.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>publish-subscribe-%d.log</fileNamePattern>
<maxHistory>5</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%-5p [%d{ISO8601,UTC}] %c: %m%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5p [%d{ISO8601,UTC}] %c: %m%n</pattern>
</encoder>
</appender>
<logger name="com.iluwatar" additivity="false">
<level value="DEBUG" />
<appender-ref ref="FILE" />
</logger>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -1,47 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.publish.subscribe;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/**
* Application test
*/
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
* Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
* throws an exception.
*/
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
-29
View File
@@ -1,29 +0,0 @@
---
title: EIP Splitter
category: Integration
language: en
tag:
- Enterprise Integration Pattern
---
## Intent
It is very common in integration systems that incoming messages consists of many items bundled together. For example
an invoice document contains multiple invoice lines describing transaction (quantity, name of provided
service/sold goods, price etc.). Such bundled messages may not be accepted by other systems. This is where splitter
pattern comes in handy. It will take the whole document, split it based on given criteria and send individual
items to the endpoint.
## Diagram
![alt text](./etc/sequencer.gif "Splitter")
## Applicability
Use the Splitter pattern when
* You need to split received data into smaller pieces to process them individually
* You need to control the size of data batches you are able to process
## Credits
* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/Sequencer.html)
* [Apache Camel - Documentation](http://camel.apache.org/splitter.html)
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
-8
View File
@@ -1,8 +0,0 @@
@startuml
package com.iluwatar.eip.splitter {
class App {
+ App()
+ main(args : String[]) {static}
}
}
@enduml
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

-93
View File
@@ -1,93 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License
Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<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>
<artifactId>eip-splitter</artifactId>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring</artifactId>
<version>${camel.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.eip.splitter.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,77 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.splitter;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* It is very common in integration systems that incoming messages consists of many items bundled
* together. For example an invoice document contains multiple invoice lines describing transaction
* (quantity, name of provided service/sold goods, price etc.). Such bundled messages may not be
* accepted by other systems. This is where splitter pattern comes in handy. It will take the whole
* document, split it based on given criteria and send individual items to the endpoint.
*
* <p>
* Splitter allows you to split messages based on defined criteria. It takes original message,
* process it and send multiple parts to the output channel. It is not defined if it should keep the
* order of items though.
* </p>
*/
@SpringBootApplication
public class App {
/**
* Program entry point. It starts Spring Boot application and using Apache Camel it
* auto-configures routes.
*
* @param args command line args
*/
public static void main(String[] args) throws Exception {
// Run Spring Boot application and obtain ApplicationContext
var context = SpringApplication.run(App.class, args);
// Get CamelContext from ApplicationContext
var camelContext = (CamelContext) context.getBean("camelContext");
// Add a new routes that will handle endpoints form SplitterRoute class.
camelContext.addRoutes(new RouteBuilder() {
@Override
public void configure() throws Exception {
from("{{endpoint}}").log("ENDPOINT: ${body}");
}
});
// Add producer that will send test message to an entry point in WireTapRoute
String[] stringArray = {"Test item #1", "Test item #2", "Test item #3"};
camelContext.createProducerTemplate().sendBody("{{entry}}", stringArray);
SpringApplication.exit(context);
}
}
@@ -1,53 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.splitter.routes;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
/**
* Sample splitter route definition.
*
* <p>It consumes messages out of the <i>direct:entry</i> entry point and forwards them to
* <i>direct:endpoint</i>. Route accepts messages having body of array or collection of objects.
* Splitter component split message body and forwards single objects to the endpoint.
*
* <p>In this example input/output endpoints names are stored in <i>application.properties</i>
* file.
*/
@Component
public class SplitterRoute extends RouteBuilder {
/**
* Configures the route.
*
* @throws Exception in case of exception during configuration
*/
@Override
public void configure() throws Exception {
// Main route
from("{{entry}}").split().body().to("{{endpoint}}");
}
}
@@ -1,25 +0,0 @@
#
# The MIT License
# Copyright © 2014-2021 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
entry=direct:entry
endpoint=direct:endpoint
@@ -1,47 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.splitter;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/**
* Test for App class
*/
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
* Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
* throws an exception.
*/
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
@@ -1,75 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.splitter.routes;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Test class for <i>SplitterRoute</i>.
* <p>
* In order for it to work we have to mock endpoints we want to read/write to. To mock those we need
* to substitute original endpoint names to mocks.
* </p>
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SplitterRouteTest.class)
@ActiveProfiles("test")
@EnableAutoConfiguration
@ComponentScan
class SplitterRouteTest {
@EndpointInject(uri = "{{entry}}")
private ProducerTemplate entry;
@EndpointInject(uri = "{{endpoint}}")
private MockEndpoint endpoint;
/**
* Test if endpoint receives three separate messages.
*
* @throws Exception in case of en exception during the test
*/
@Test
@DirtiesContext
void testSplitter() throws Exception {
// Three items in one entry message
entry.sendBody(new String[]{"TEST1", "TEST2", "TEST3"});
// Endpoint should have three different messages in the end order of the messages is not important
endpoint.expectedMessageCount(3);
endpoint.assertIsSatisfied();
}
}
@@ -1,25 +0,0 @@
#
# The MIT License
# Copyright © 2014-2021 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
entry=direct:entry
endpoint=mock:endpoint
-27
View File
@@ -1,27 +0,0 @@
---
title: EIP Wire Tap
category: Integration
language: en
tag:
- Enterprise Integration Pattern
---
## Intent
In most integration cases there is a need to monitor the messages flowing through the system. It is usually achieved
by intercepting the message and redirecting it to a different location like console, filesystem or the database.
It is important that such functionality should not modify the original message and influence the processing path.
## Diagram
![alt text](./etc/wiretap.gif "Wire Tap")
## Applicability
Use the Wire Tap pattern when
* You need to monitor messages flowing through the system
* You need to redirect the same, unchanged message to two different endpoints/paths
## Credits
* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/WireTap.html)
* [Apache Camel - Documentation](http://camel.apache.org/wire-tap.html)
* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
-8
View File
@@ -1,8 +0,0 @@
@startuml
package com.iluwatar.eip.wiretap {
class App {
+ App()
+ main(args : String[]) {static}
}
}
@enduml
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

-93
View File
@@ -1,93 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License
Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<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>
<artifactId>eip-wire-tap</artifactId>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring</artifactId>
<version>${camel.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.eip.wiretap.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,76 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.wiretap;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* In most integration cases there is a need to monitor the messages flowing through the system. It
* is usually achieved by intercepting the message and redirecting it to a different location like
* console, filesystem or the database. It is important that such functionality should not modify
* the original message and influence the processing path.
*
* <p>
* Wire Tap allows you to route messages to a separate location while they are being forwarded to
* the ultimate destination. It basically consumes messages of the input channel and publishes the
* unmodified message to both output channels.
* </p>
*/
@SpringBootApplication
public class App {
/**
* Program entry point. It starts Spring Boot application and using Apache Camel it
* auto-configures routes.
*
* @param args command line args
*/
public static void main(String[] args) throws Exception {
// Run Spring Boot application and obtain ApplicationContext
var context = SpringApplication.run(App.class, args);
// Get CamelContext from ApplicationContext
var camelContext = (CamelContext) context.getBean("camelContext");
// Add a new routes that will handle endpoints form WireTapRoute class.
camelContext.addRoutes(new RouteBuilder() {
@Override
public void configure() throws Exception {
from("{{endpoint}}").log("ENDPOINT: ${body}");
from("{{wireTapEndpoint}}").log("WIRETAPPED ENDPOINT: ${body}");
}
});
// Add producer that will send test message to an entry point in WireTapRoute
camelContext.createProducerTemplate().sendBody("{{entry}}", "Test message");
SpringApplication.exit(context);
}
}
@@ -1,57 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.wiretap.routes;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
/**
* Sample wire tap route definition.
*
* <p>It consumes messages out of the <i>direct:entry</i> entry point and forwards them to
* <i>direct:endpoint</i>. Wire Tap intercepts the message and sends it to <i>direct:wireTap</i>,
* which in turn forwards it to
* <i>direct:wireTapEndpoint</i>.
*
* <p>In this example input/output endpoints names are stored in <i>application.properties</i>
* file.
*/
@Component
public class WireTapRoute extends RouteBuilder {
/**
* Configures the route.
*
* @throws Exception in case of exception during configuration
*/
@Override
public void configure() throws Exception {
// Main route
from("{{entry}}").wireTap("direct:wireTap").to("{{endpoint}}");
// Wire tap route
from("direct:wireTap").log("Message: ${body}").to("{{wireTapEndpoint}}");
}
}
@@ -1,26 +0,0 @@
#
# The MIT License
# Copyright © 2014-2021 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
entry=direct:entry
endpoint=direct:endpoint
wireTapEndpoint=direct:wireTapEndpoint
@@ -1,47 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.wiretap;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/**
* Test for App class
*/
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
* Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
* throws an exception.
*/
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
@@ -1,86 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.eip.wiretap.routes;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Test class for <i>WireTapRoute</i>.
* <p>
* In order for it to work we have to mock endpoints we want to read/write to. To mock those we need
* to substitute original endpoint names to mocks.
* </p>
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = WireTapRouteTest.class)
@ActiveProfiles("test")
@EnableAutoConfiguration
@ComponentScan
class WireTapRouteTest {
@EndpointInject(uri = "{{entry}}")
private ProducerTemplate entry;
@EndpointInject(uri = "{{endpoint}}")
private MockEndpoint endpoint;
@EndpointInject(uri = "{{wireTapEndpoint}}")
private MockEndpoint wireTapEndpoint;
/**
* Test if both endpoints receive exactly one message containing the same, unchanged body.
*
* @throws Exception in case of en exception during the test
*/
@Test
@DirtiesContext
void testWireTap() throws Exception {
entry.sendBody("TEST");
endpoint.expectedMessageCount(1);
wireTapEndpoint.expectedMessageCount(1);
endpoint.assertIsSatisfied();
wireTapEndpoint.assertIsSatisfied();
var endpointIn = endpoint.getExchanges().get(0).getIn();
var wireTapEndpointIn = wireTapEndpoint.getExchanges().get(0).getIn();
assertEquals("TEST", endpointIn.getBody());
assertEquals("TEST", wireTapEndpointIn.getBody());
}
}
@@ -1,26 +0,0 @@
#
# The MIT License
# Copyright © 2014-2021 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
entry=direct:entry
endpoint=mock:endpoint
wireTapEndpoint=mock:wireTapEndpoint
-20
View File
@@ -40,14 +40,10 @@
<spring-boot.version>2.7.5</spring-boot.version>
<jacoco.version>0.8.8</jacoco.version>
<commons-dbcp.version>1.4</commons-dbcp.version>
<camel.version>3.18.3</camel.version>
<guava.version>19.0</guava.version>
<htmlunit.version>2.66.0</htmlunit.version>
<guice.version>4.0</guice.version>
<mongo-java-driver.version>3.12.8</mongo-java-driver.version>
<aws-lambda-core.version>1.1.0</aws-lambda-core.version>
<aws-java-sdk-dynamodb.version>1.12.13</aws-java-sdk-dynamodb.version>
<aws-lambda-java-events.version>2.0.1</aws-lambda-java-events.version>
<system-lambda.version>1.1.0</system-lambda.version>
<urm.version>2.0.0</urm.version>
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
@@ -127,11 +123,9 @@
<module>business-delegate</module>
<module>half-sync-half-async</module>
<module>layers</module>
<module>eip-message-channel</module>
<module>fluentinterface</module>
<module>reactor</module>
<module>caching</module>
<module>eip-publish-subscribe</module>
<module>delegation</module>
<module>event-driven-architecture</module>
<module>api-gateway</module>
@@ -162,13 +156,9 @@
<module>throttling</module>
<module>unit-of-work</module>
<module>partial-response</module>
<module>eip-wire-tap</module>
<module>eip-splitter</module>
<module>eip-aggregator</module>
<module>retry</module>
<module>dirty-flag</module>
<module>trampoline</module>
<module>serverless</module>
<module>ambassador</module>
<module>acyclic-visitor</module>
<module>collection-pipeline</module>
@@ -234,16 +224,6 @@
<artifactId>commons-dbcp</artifactId>
<version>${commons-dbcp.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-stream</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
-201
View File
@@ -1,201 +0,0 @@
---
title: Serverless
category: Architectural
language: en
tag:
- Cloud distributed
---
## Serverless
Serverless eliminates the need to plan for infrastructure and let's you focus on your
application.
Following are optimization katas you should be aware of while building a serverless
applications
* The Lean function
* Concise logic - Use functions to transform, not transport (utilize some of the
integration available from the provider to transport), and make sure you read only
what you need
* Efficient/single purpose code - avoid conditional/routing logic and break down
into individual functions, avoid "fat"/monolithic functions and control the
dependencies in the function deployment package to reduce the load time for your
function
* ephemeral environment - Utilize container start for expensive initializations
* Eventful Invocations
* Succinct payloads - Scrutinize the event as much as possible, and watch for
payload constraints (async - 128K)
* resilient routing - Understand retry policies and leverage dead letter queues
(SQS or SNS for replays) and remember retries count as invocations
* concurrent execution - lambda thinks of it's scale in terms of concurrency and
its not request based/duration based. Lambda will spin up the number of instances
based on the request.
* Coordinated calls
* Decoupled via APIs - best practice to setup your application is to have API's as
contracts that ensures separation of concerns
* scale-matched downstream - make sure when Lambda is calling downstream
components, you are matching scale configuration to it (by specifying max
concurrency based on downstream services)
* secured - Always ask a question, do I need a VPC?
* Serviceful operations
* Automated - use automated tools to manage and maintain the stack
* monitored applications - use monitoring services to get holistic view of your
serverless applications
## Intent
Whether to reduce your infrastructure costs, shrink the time you spend on ops tasks,
simplify your deployment processes, reach infinite scalability, serverless cuts time
to market in half.
## Explanation
Serverless computing is a cloud computing execution model in which the cloud provider
dynamically manages the allocation of machine resources. Pricing is based on the
actual amount of resources consumed by an application, rather than on pre-purchased
units of capacity.
## Class diagram
![alt text](./etc/serverless.urm.png "Serverless pattern class diagram")
## Serverless framework
[Serverless](https://serverless.com/) is a toolkit for deploying and operating serverless architectures.
## (Function as a Service or "FaaS")
The term Serverless is confusing since with such applications there are both server
hardware and server processes running somewhere, but the difference to normal
approaches is that the organization building and supporting a Serverless application
is not looking after the hardware or the processes - they are outsourcing this to a vendor.
Some of the Serverless Cloud Providers are
![https://serverless.com/framework/docs/providers/aws/](./etc/aws-black.png "aws")
![https://serverless.com/framework/docs/providers/azure/](./etc/azure-black.png "azure")
![https://serverless.com/framework/docs/providers/openwhisk/](./etc/openwhisk-black.png "openwhisk")
![https://serverless.com/framework/docs/providers/google/](./etc/gcf-black.png "google")
![https://serverless.com/framework/docs/providers/kubeless/](./etc/kubeless-logos-black.png "kubeless")
![https://serverless.com/framework/docs/providers/spotinst/](./etc/spotinst-logos-black-small.png "spotinst")
![https://serverless.com/framework/docs/providers/webtasks/](./etc/webtask-small-grayscale.png "webtask")
...
Anything that triggers an Lambda Function to execute is regarded by the Framework as
an Event. Most of the Serverless Cloud Providers support following Events
- Http
- PubSub Events
- scheduled
AWS supports processing event generated from AWS Services (S3/Cloudwatch/etc) and
using aws as a compute engine is our first choice.
## (Backend as a Service or "BaaS")
This example creates a backend for persons collection which uses DynamoDB NoSQL
database service also provided by Amazon.
## AWS lambda function implementation
[AWS Lambda SDK](https://aws.amazon.com/sdk-for-java/) provides pre-defined interface
`com.amazonaws.services.lambda.runtime
.RequestHandler` to implement our lambda function.
```java
public class LambdaInfoApiHandler implements RequestHandler<Map<String, Object>, ApiGatewayResponse> {
private static final Logger LOG = Logger.getLogger(LambdaInfoApiHandler.class);
private static final Integer SUCCESS_STATUS_CODE = 200;
@Override
public ApiGatewayResponse handleRequest(Map<String, Object> input, Context context) {
}
}
```
handleRequest method is where the function code is implemented. Context provides
useful information about Lambda execution environment. AWS Lambda function needs a
deployment package. This package is either a .zip or .jar file that contains all the
dependencies of the function.
`serverless.yml` contains configuration to manage deployments for your functions.
## Run example in local
# Pre-requisites
* Node.js v6.5.0 or later.
* Serverless CLI v1.9.0 or later. You can run npm install -g serverless to install it.
* An AWS account. If you don't already have one, you can sign up for a free trial that includes 1 million free Lambda requests per month.
* [Set-up](https://serverless.com/framework/docs/providers/aws/guide/credentials/) your Provider Credentials. Watch the video on setting up credentials
# build and deploy
* `cd serverless`
* `mvn clean package`
* `serverless deploy --stage=dev --verbose`
Based on the configuration in `serverless.yml` serverless framework creates following
resources
* CloudFormation stack for S3 (ServerlessDeploymentBucket)
* IAM Role (IamRoleLambdaExecution)
* CloudWatch (log groups)
* API Gateway (ApiGatewayRestApi)
* Lambda function
* DynamoDB collection
The command will print out Stack Outputs which looks something like this
```yaml
endpoints:
GET - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/info
POST - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/api/person
GET - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/api/person/{id}
```
```yaml
CurrentTimeLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:xxxxxxxxxxx:function:lambda-info-http-endpoint-dev-currentTime:4
ServiceEndpoint: https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev
ServerlessDeploymentBucketName: lambda-info-http-endpoin-serverlessdeploymentbuck-2u8uz2i7cap2
```
access the endpoint to invoke the function.
Use the following cURL commands to test the endpoints
```cURL
curl -X GET \
https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/info \
-H 'cache-control: no-cache'
```
```cURL
curl -X POST \
https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/api/person \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"firstName": "Thor",
"lastName": "Odinson",
"address": {
"addressLineOne": "1 Odin ln",
"addressLineTwo": "100",
"city": "Asgard",
"state": "country of the Gods",
"zipCode": "00001"
}
}'
```
```cURL
curl -X GET \
https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/api/person/{id} \
-H 'cache-control: no-cache'
```
## Credits
* [serverless docs](https://serverless.com/framework/docs/)
* [Serverless Architectures](https://martinfowler.com/articles/serverless.html)
* [Serverless Black Belt](https://youtu.be/oQFORsso2go)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

-138
View File
@@ -1,138 +0,0 @@
@startuml
package com.iluwatar.serverless.faas.api {
class LambdaInfoApiHandler {
- LOG : Logger {static}
- SUCCESS_STATUS_CODE : Integer {static}
+ LambdaInfoApiHandler()
+ handleRequest(input : Map<String, Object>, context : Context) : ApiGatewayResponse<T extends Serializable>
- headers() : Map<String, String>
- lambdaInfo(context : Context) : LambdaInfo
}
}
package com.iluwatar.serverless.baas.model {
class Address {
- addressLineOne : String
- addressLineTwo : String
- city : String
- serialVersionUID : long {static}
- state : String
- zipCode : String
+ Address()
+ equals(o : Object) : boolean
+ getAddressLineOne() : String
+ getAddressLineTwo() : String
+ getCity() : String
+ getState() : String
+ getZipCode() : String
+ hashCode() : int
+ setAddressLineOne(addressLineOne : String)
+ setAddressLineTwo(addressLineTwo : String)
+ setCity(city : String)
+ setState(state : String)
+ setZipCode(zipCode : String)
+ toString() : String
}
class Person {
- address : Address
- firstName : String
- id : String
- lastName : String
- serialVersionUID : long {static}
+ Person()
+ equals(o : Object) : boolean
+ getAddress() : Address
+ getFirstName() : String
+ getId() : String
+ getLastName() : String
+ hashCode() : int
+ setAddress(address : Address)
+ setFirstName(firstName : String)
+ setId(id : String)
+ setLastName(lastName : String)
+ toString() : String
}
}
package com.iluwatar.serverless.faas {
class ApiGatewayResponse<T extends Serializable> {
- body : String
- headers : Map<String, String>
- isBase64Encoded : Boolean
- serialVersionUID : long {static}
- statusCode : Integer
+ ApiGatewayResponse<T extends Serializable>(statusCode : Integer, body : String, headers : Map<String, String>, isBase64Encoded : Boolean)
+ getBody() : String
+ getHeaders() : Map<String, String>
+ getStatusCode() : Integer
+ isBase64Encoded() : Boolean
}
class ApiGatewayResponseBuilder<T extends Serializable> {
- OBJECT_MAPPER : ObjectMapper {static}
- body : T extends Serializable
- headers : Map<String, String>
- isBase64Encoded : Boolean
- statusCode : Integer
+ ApiGatewayResponseBuilder<T extends Serializable>()
+ base64Encoded(isBase64Encoded : Boolean) : ApiGatewayResponseBuilder<T extends Serializable>
+ body(body : T extends Serializable) : ApiGatewayResponseBuilder<T extends Serializable>
+ build() : ApiGatewayResponse<T extends Serializable>
+ headers(headers : Map<String, String>) : ApiGatewayResponseBuilder<T extends Serializable>
+ statusCode(statusCode : Integer) : ApiGatewayResponseBuilder<T extends Serializable>
}
class LambdaInfo {
- awsRequestId : String
- functionName : String
- functionVersion : String
- logGroupName : String
- logStreamName : String
- memoryLimitInMb : Integer
- serialVersionUID : long {static}
+ LambdaInfo()
+ equals(o : Object) : boolean
+ getAwsRequestId() : String
+ getFunctionName() : String
+ getFunctionVersion() : String
+ getLogGroupName() : String
+ getLogStreamName() : String
+ getMemoryLimitInMb() : Integer
+ hashCode() : int
+ setAwsRequestId(awsRequestId : String)
+ setFunctionName(functionName : String)
+ setFunctionVersion(functionVersion : String)
+ setLogGroupName(logGroupName : String)
+ setLogStreamName(logStreamName : String)
+ setMemoryLimitInMb(memoryLimitInMb : Integer)
+ toString() : String
}
}
package com.iluwatar.serverless.baas.api {
abstract class AbstractDynamoDbHandler<T extends Serializable> {
- dynamoDbMapper : DynamoDBMapper
- objectMapper : ObjectMapper
+ AbstractDynamoDbHandler<T extends Serializable>()
# apiGatewayProxyResponseEvent(statusCode : Integer, body : T extends Serializable) : APIGatewayProxyResponseEvent
# getDynamoDbMapper() : DynamoDBMapper
# getObjectMapper() : ObjectMapper
# headers() : Map<String, String>
- initAmazonDynamoDb()
+ setDynamoDbMapper(dynamoDbMapper : DynamoDBMapper)
}
class FindPersonApiHandler {
- LOG : Logger {static}
- SUCCESS_STATUS_CODE : Integer {static}
+ FindPersonApiHandler()
+ handleRequest(apiGatewayProxyRequestEvent : APIGatewayProxyRequestEvent, context : Context) : APIGatewayProxyResponseEvent
}
class SavePersonApiHandler {
- BAD_REQUEST_STATUS_CODE : Integer {static}
- CREATED_STATUS_CODE : Integer {static}
- LOG : Logger {static}
+ SavePersonApiHandler()
+ handleRequest(apiGatewayProxyRequestEvent : APIGatewayProxyRequestEvent, context : Context) : APIGatewayProxyResponseEvent
}
}
ApiGatewayResponseBuilder ..+ ApiGatewayResponse
Person --> "-address" Address
Access ..+ JsonProperty
FindPersonApiHandler --|> AbstractDynamoDbHandler
SavePersonApiHandler --|> AbstractDynamoDbHandler
@enduml
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

-112
View File
@@ -1,112 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License
Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<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>
<artifactId>serverless</artifactId>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>${aws-lambda-core.version}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
<version>${aws-java-sdk-dynamodb.version}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>${aws-lambda-java-events.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!--
Using the Apache Maven Shade plugin to package the jar
"This plugin provides the capability to package the artifact
in an uber-jar, including its dependencies and to shade - i.e. rename -
the packages of some of the dependencies."
Link: https://maven.apache.org/plugins/maven-shade-plugin/
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>${project.artifactId}</finalName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
-104
View File
@@ -1,104 +0,0 @@
#
# This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
#
# The MIT License
# Copyright © 2014-2022 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
service: serverless-services
frameworkVersion: ">=1.2.0 <2.0.0"
provider:
name: aws
runtime: java8
usagePlan:
quota:
limit: 500
offset: 2
period: MONTH
throttle:
burstLimit: 20
rateLimit: 10
package:
artifact: target/serverless.jar
functions:
lambdaInfoApi:
handler: com.iluwatar.serverless.faas.api.LambdaInfoApiHandler
events:
- http:
path: info
method: get
savePerson:
handler: com.iluwatar.serverless.baas.api.SavePersonApiHandler
events:
- http:
path: api/person
method: post
cors: true
integration: lambda-proxy
getPerson:
handler: com.iluwatar.serverless.baas.api.FindPersonApiHandler
events:
- http:
path: api/person/{id}
method: get
cors: true
integration: lambda-proxy
resources:
Resources:
DynamoDbTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: persons
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
DynamoDBIamPolicy:
Type: AWS::IAM::Policy
DependsOn: DynamoDbTable
Properties:
PolicyName: lambda-dynamodb
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- dynamodb:Query
- dynamodb:Scan
Resource: arn:aws:dynamodb:*:*:table/persons
Roles:
- Ref: IamRoleLambdaExecution
@@ -1,95 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.serverless.baas.api;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.Serializable;
import java.util.Map;
/**
* abstract dynamodb handler.
*
* @param <T> - serializable collection
*/
public abstract class AbstractDynamoDbHandler<T extends Serializable> {
private DynamoDBMapper dynamoDbMapper;
private final ObjectMapper objectMapper;
public AbstractDynamoDbHandler() {
this.initAmazonDynamoDb();
this.objectMapper = new ObjectMapper();
}
private void initAmazonDynamoDb() {
var amazonDynamoDb = AmazonDynamoDBClientBuilder
.standard()
.withRegion(Regions.US_EAST_1)
.build();
this.dynamoDbMapper = new DynamoDBMapper(amazonDynamoDb);
}
protected DynamoDBMapper getDynamoDbMapper() {
return this.dynamoDbMapper;
}
protected ObjectMapper getObjectMapper() {
return objectMapper;
}
public void setDynamoDbMapper(DynamoDBMapper dynamoDbMapper) {
this.dynamoDbMapper = dynamoDbMapper;
}
protected Map<String, String> headers() {
return Map.of("Content-Type", "application/json");
}
/**
* API Gateway response.
*
* @param statusCode - status code
* @param body - Object body
* @return - api gateway proxy response
*/
protected APIGatewayProxyResponseEvent apiGatewayProxyResponseEvent(Integer statusCode, T body) {
var apiGatewayProxyResponseEvent = new APIGatewayProxyResponseEvent().withHeaders(headers());
try {
apiGatewayProxyResponseEvent
.withStatusCode(statusCode)
.withBody(getObjectMapper().writeValueAsString(body));
} catch (JsonProcessingException jsonProcessingException) {
throw new RuntimeException(jsonProcessingException);
}
return apiGatewayProxyResponseEvent;
}
}
@@ -1,56 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.serverless.baas.api;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.iluwatar.serverless.baas.model.Person;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* find person from persons collection Created by dheeraj.mummar on 3/5/18.
*/
public class FindPersonApiHandler extends AbstractDynamoDbHandler<Person>
implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
private static final Logger LOG = LoggerFactory.getLogger(FindPersonApiHandler.class);
private static final Integer SUCCESS_STATUS_CODE = 200;
@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent req, Context ctx) {
req.getPathParameters().forEach(FindPersonApiHandler::logKeyValue);
var id = req.getPathParameters().get("id");
var person = this.getDynamoDbMapper().load(Person.class, id);
return apiGatewayProxyResponseEvent(SUCCESS_STATUS_CODE, person);
}
private static void logKeyValue(String key, String value) {
LOG.info(key + "=" + value);
}
}
@@ -1,58 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.serverless.baas.api;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.iluwatar.serverless.baas.model.Person;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* save person into persons collection Created by dheeraj.mummar on 3/4/18.
*/
public class SavePersonApiHandler extends AbstractDynamoDbHandler<Person>
implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
private static final Logger LOG = LoggerFactory.getLogger(SavePersonApiHandler.class);
private static final Integer CREATED_STATUS_CODE = 201;
private static final Integer BAD_REQUEST_STATUS_CODE = 400;
@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent req, Context ctx) {
try {
var objectMapper = getObjectMapper();
var person = objectMapper.readValue(req.getBody(), Person.class);
getDynamoDbMapper().save(person);
return apiGatewayProxyResponseEvent(CREATED_STATUS_CODE, person);
} catch (IOException ioException) {
LOG.error("unable to parse body", ioException);
return apiGatewayProxyResponseEvent(BAD_REQUEST_STATUS_CODE, null);
}
}
}
@@ -1,62 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.serverless.baas.model;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBDocument;
import java.io.Serializable;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* Address class Created by dheeraj.mummarareddy on 3/4/18.
*/
@Getter
@Setter
@EqualsAndHashCode
@ToString
@DynamoDBDocument
public class Address implements Serializable {
private static final long serialVersionUID = 6760844284799736970L;
@DynamoDBAttribute(attributeName = "addressLineOne")
private String addressLineOne;
@DynamoDBAttribute(attributeName = "addressLineTwo")
private String addressLineTwo;
@DynamoDBAttribute(attributeName = "city")
private String city;
@DynamoDBAttribute(attributeName = "state")
private String state;
@DynamoDBAttribute(attributeName = "zipCode")
private String zipCode;
}
@@ -1,64 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.serverless.baas.model;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* Person class Created by dheeraj.mummarareddy on 3/4/18.
*/
@Getter
@Setter
@ToString
@EqualsAndHashCode
@DynamoDBTable(tableName = "persons")
public class Person implements Serializable {
private static final long serialVersionUID = -3413087924608627075L;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@DynamoDBHashKey(attributeName = "id")
@DynamoDBAutoGeneratedKey
private String id;
@DynamoDBAttribute(attributeName = "firstName")
private String firstName;
@DynamoDBAttribute(attributeName = "lastName")
private String lastName;
@DynamoDBAttribute(attributeName = "address")
private Address address;
}
@@ -1,174 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.serverless.faas;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.Serializable;
import java.util.Map;
/**
* Api gateway response.
*/
public class ApiGatewayResponse implements Serializable {
private static final long serialVersionUID = 1181159426782844892L;
private final Integer statusCode;
private final String body;
private final Map<String, String> headers;
private final Boolean isBase64Encoded;
/**
* api gateway response.
*
* @param statusCode - http status code
* @param body - response body
* @param headers - response headers
* @param isBase64Encoded - base64Encoded flag
*/
public ApiGatewayResponse(
Integer statusCode,
String body,
Map<String, String> headers,
Boolean isBase64Encoded
) {
this.statusCode = statusCode;
this.body = body;
this.headers = headers;
this.isBase64Encoded = isBase64Encoded;
}
/**
* http status code.
*
* @return statusCode - http status code
*/
public Integer getStatusCode() {
return statusCode;
}
/**
* response body.
*
* @return string body
*/
public String getBody() {
return body;
}
/**
* response headers.
*
* @return response headers
*/
public Map<String, String> getHeaders() {
return headers;
}
/**
* base64Encoded flag, API Gateway expects the property to be called "isBase64Encoded".
*
* @return base64Encoded flag
*/
public Boolean isBase64Encoded() {
return isBase64Encoded;
}
/**
* ApiGatewayResponse Builder class.
*
* @param <T> Serializable object
*/
public static class ApiGatewayResponseBuilder<T extends Serializable> {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private Integer statusCode;
private T body;
private Map<String, String> headers;
private Boolean isBase64Encoded;
/**
* http status code.
*
* @param statusCode - http status code
* @return ApiGatewayResponseBuilder
*/
public ApiGatewayResponseBuilder statusCode(Integer statusCode) {
this.statusCode = statusCode;
return this;
}
/**
* Serializable body.
*
* @param body - Serializable object
* @return ApiGatewayResponseBuilder
*/
public ApiGatewayResponseBuilder body(T body) {
this.body = body;
return this;
}
/**
* response headers.
*
* @param headers - response headers
* @return ApiGatewayResponseBuilder
*/
public ApiGatewayResponseBuilder headers(Map<String, String> headers) {
this.headers = headers;
return this;
}
/**
* base64Encoded flag.
*
* @param isBase64Encoded - base64Encoded flag
* @return ApiGatewayResponseBuilder
*/
public ApiGatewayResponseBuilder base64Encoded(Boolean isBase64Encoded) {
this.isBase64Encoded = isBase64Encoded;
return this;
}
/**
* build ApiGatewayResponse.
*
* @return ApiGatewayResponse
*/
public ApiGatewayResponse build() {
String strBody = null;
if (this.body != null) {
try {
strBody = OBJECT_MAPPER.writeValueAsString(body);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
return new ApiGatewayResponse(this.statusCode, strBody, this.headers, this.isBase64Encoded);
}
}
}
@@ -1,51 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.serverless.faas;
import java.io.Serializable;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* Lambda context information.
*/
@ToString
@EqualsAndHashCode
@Setter
@Getter
public class LambdaInfo implements Serializable {
private static final long serialVersionUID = 3936130599040848923L;
private String awsRequestId;
private String logGroupName;
private String logStreamName;
private String functionName;
private String functionVersion;
private Integer memoryLimitInMb;
}

Some files were not shown because too many files have changed in this diff Show More