diff --git a/.bash_aliases b/.bash_aliases index 30e12da..173dc4e 100644 --- a/.bash_aliases +++ b/.bash_aliases @@ -12,6 +12,7 @@ alias ex2s="cd ${GITPOD_REPO_ROOT}/exercises/querying-workflows/solution" alias ex2w="mvn exec:java -Dexec.mainClass='queryingworkflows.QueryingWorkflowsWorker'" alias ex2st="mvn exec:java -Dexec.mainClass='queryingworkflows.Starter'" alias ex2sg="mvn exec:java -Dexec.mainClass='queryingworkflows.SignalClient'" +alias ex2q="mvn exec:java -Dexec.mainClass='queryingworkflows.QueryClient'" alias ex3="cd ${GITPOD_REPO_ROOT}/exercises/async-activity-completion/practice" diff --git a/exercises/sending-signals-client/README.md b/exercises/sending-signals-client/README.md new file mode 100644 index 0000000..9b0dc87 --- /dev/null +++ b/exercises/sending-signals-client/README.md @@ -0,0 +1,66 @@ +## Exercise #2: Sending a Signal from the Client + +This exercise shows how to send a Signal from just the Client and not from another Workflow. + +During this exercise, you will: + +- Retrieve a stub on a running Workflow to Signal +- Send a Signal from the Client + +In this part of the exercise, we have removed the `FulfillOrderWorkflow` Workflow from the previous exercise, since this exercise is just for sending a Signal from a Client, and not from another Workflow. + +We will skip the defining and handling of `FulfillOrderSignal` that were handled in `src/main/java/sendingsignalsclient/orderpizza`. This exercise will solely focus on the Client file. + +### GitPod Environment Shortcuts + +If you are executing the exercises in the provided GitPod environment, you +can take advantage of certain aliases to aid in navigation and execution of +the code. + +| Command | Action | +| :------ | :--------------------------------------------------------------------------------------------------------------------------------- | +| `ex2` | Change to Exercise 1 Practice Directory | +| `ex2s` | Change to Exercise 1 Solution Directory | +| `ex2w` | Execute the Exercise 1 Worker. Must be within the appropriate directory for this to succeed. (either `practice` or `solution`) | +| `ex2st` | Execute the Exercise 1 Starter. Must be within the appropriate directory for this to succeed. (either `practice` or `solution`) | +| `ex2sg` | Send the Exercise 1 Signal. Must be within the appropriate directory for this to succeed. (either `practice` or `solution`) | + + +## Part A: Create a Stub on the Pizza Workflow + +In this part of the exercise, you will create a stub on the Workflow that you wish to Signal, which is `PizzaWorkflow`. + +1. Recall that the Workflow ID for this Workflow is `pizza-workflow-order-XD001`. +1. Edit the `SignalClient.java` in the `src/main/java/sendingsignalsclient` subdirectory and modify the `newWorkflowStub()` method to get a stub of a running workflow. + 1. Change the Workflow ID from `CHANGE_ME` to the Workflow ID of the running Workflow. +1. Save the file. + +## Part B: Sending a Signal to the Pizza Workflow + +Now that you have a stub on the Workflow you wish to Signal (`PizzaWorkflow`), we will now send `fulfillOrderSignal`. + +1. Continue editing the `SignalClient.java` in the `src/main/java/sendingsignalsclient` subdirectory. +1. Using the stub defined above, send the signal by calling the `fulfillOrderSignal()` method, passing `true` as a parameter. +1. Save the file. + + +## Part C: Run the Workflow + +To run the Workflow: + +1. In all terminals, change directories to the Exercise 2 directory. + 1. If you're in the GitPod environment, you can run `ex2` +1. In one terminal, start the Worker by running `mvn exec:java -Dexec.mainClass="sendingsignalsclient.SendSignalClientWorker"`. + 1. If you're in the GitPod environment, you can instead run `ex2w` to start the worker +1. In another terminal, start the Workflow by running `mvn exec:java -Dexec.mainClass="sendingsignalsclient.Starter"`. This will start the Workflow, which you will see waiting for a Signal. + 1. If you're in the GitPod environment, you can instead run `ex2st` to start the Workflow +1. In another terminal, send the signal by running `mvn exec:java -Dexec.mainClass="sendingsignalsclient.SignalClient"` + 1. If you're in the GitPod environment, you can instead run `ex2sg` to send the Signal +1. You should see the following output in the terminal where you ran the Starter: + +```bash +09:41:34.326 INFO - Created WorkflowServiceStubs for channel: ManagedChannelOrphanWrapper{delegate=ManagedChannelImpl{logId=1, target=127.0.0.1:7233}} +Workflow result: OrderConfirmation{orderNumber='XD001', status='SUCCESS', confirmationNumber='P24601', billingTimestamp=1712500897, amount=3500} +``` + +### This is the end of the exercise. diff --git a/exercises/sending-signals-client/practice/pom.xml b/exercises/sending-signals-client/practice/pom.xml new file mode 100644 index 0000000..329ac36 --- /dev/null +++ b/exercises/sending-signals-client/practice/pom.xml @@ -0,0 +1,109 @@ + + + + 4.0.0 + + io.temporal.learn + sending-signals-client + 1.0.0-SNAPSHOT + + sending signals client + https://learn.temporal.io/ + + + UTF-8 + 1.8 + 1.8 + + + + + + io.temporal + temporal-sdk + 1.20.1 + + + + ch.qos.logback + logback-classic + 1.4.8 + + + + org.apache.commons + commons-lang3 + 3.11 + + + + io.temporal + temporal-testing + 1.20.1 + test + + + + + org.junit.jupiter + junit-jupiter + 5.5.2 + test + + + + org.mockito + mockito-junit-jupiter + 5.3.1 + test + + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/Constants.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/Constants.java new file mode 100644 index 0000000..9ef9bd1 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/Constants.java @@ -0,0 +1,7 @@ +package sendingsignalsclient; + +public class Constants { + + public static final String TASK_QUEUE_NAME = "pizza-tasks"; + +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/SendSignalClientWorker.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/SendSignalClientWorker.java new file mode 100644 index 0000000..ff66ba4 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/SendSignalClientWorker.java @@ -0,0 +1,26 @@ +package sendingsignalsclient; + +import io.temporal.client.WorkflowClient; +import io.temporal.serviceclient.WorkflowServiceStubs; +import io.temporal.worker.Worker; +import io.temporal.worker.WorkerFactory; + +import sendingsignalsclient.orderpizza.PizzaActivitiesImpl; +import sendingsignalsclient.orderpizza.PizzaWorkflowImpl; + +public class SendSignalClientWorker { + public static void main(String[] args) { + + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + WorkflowClient client = WorkflowClient.newInstance(service); + WorkerFactory factory = WorkerFactory.newInstance(client); + + Worker worker = factory.newWorker(Constants.TASK_QUEUE_NAME); + + worker.registerWorkflowImplementationTypes(PizzaWorkflowImpl.class); + + worker.registerActivitiesImplementations(new PizzaActivitiesImpl()); + + factory.start(); + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/SignalClient.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/SignalClient.java new file mode 100644 index 0000000..bc677a2 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/SignalClient.java @@ -0,0 +1,22 @@ +package sendingsignalsclient; + +import io.temporal.client.WorkflowClient; +import io.temporal.serviceclient.WorkflowServiceStubs; +import sendingsignalsclient.orderpizza.PizzaWorkflow; + + +public class SignalClient { + public static void main(String[] args) throws Exception { + + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + + WorkflowClient client = WorkflowClient.newInstance(service); + + // TODO: PART A: Set Workflow ID + PizzaWorkflow workflow = client.newWorkflowStub(PizzaWorkflow.class, "CHANGE_ME"); + + // TODO: PART A: Call fulfillOrderSignal, passing `true` + + System.exit(0); + } +} \ No newline at end of file diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/Starter.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/Starter.java new file mode 100644 index 0000000..6b9836a --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/Starter.java @@ -0,0 +1,58 @@ +package sendingsignalsclient; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowOptions; +import java.util.concurrent.CompletableFuture; +import io.temporal.serviceclient.WorkflowServiceStubs; + +import sendingsignalsclient.model.PizzaOrder; +import sendingsignalsclient.model.Pizza; +import sendingsignalsclient.model.Customer; +import sendingsignalsclient.model.OrderConfirmation; +import sendingsignalsclient.model.Address; +import sendingsignalsclient.orderpizza.PizzaWorkflow; + +import java.util.Arrays; +import java.util.List; + +public class Starter { + public static void main(String[] args) throws Exception { + + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + + WorkflowClient client = WorkflowClient.newInstance(service); + + PizzaOrder order = createPizzaOrder(); + + // Setup the Pizza Order Workflow Client + String pizzaWorkflowID = String.format("pizza-workflow-order-%s", order.getOrderNumber()); + + WorkflowOptions pizzaWorkflowOptions = WorkflowOptions.newBuilder() + .setWorkflowId(pizzaWorkflowID) + .setTaskQueue(Constants.TASK_QUEUE_NAME) + .build(); + + PizzaWorkflow pizzaWorkflow = client.newWorkflowStub(PizzaWorkflow.class, pizzaWorkflowOptions); + + OrderConfirmation orderConfirmation = pizzaWorkflow.orderPizza(order); + //CompletableFuture orderConfirmation = WorkflowClient.execute(pizzaWorkflow::orderPizza, order); + + + System.out.printf("Workflow result: %s\n", orderConfirmation); + System.exit(0); + } + + private static PizzaOrder createPizzaOrder() { + Customer customer = new Customer(8675309, "Lisa Anderson", "lisa@example.com", "555-555-0000"); + Address address = new Address("742 Evergreen Terrace", "Apartment 221B", "Albuquerque", "NM", "87101"); + Pizza pizza1 = new Pizza("Large, with mushrooms and onions", 1500); + Pizza pizza2 = new Pizza("Small, with pepperoni", 1200); + Pizza pizza3 = new Pizza("Medium, with extra cheese", 1300); + + List orderList = Arrays.asList(pizza1, pizza2, pizza3); + + PizzaOrder order = new PizzaOrder("XD001", customer, orderList, true, address); + + return order; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/exceptions/InvalidChargeAmountException.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/exceptions/InvalidChargeAmountException.java new file mode 100644 index 0000000..09e8ec0 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/exceptions/InvalidChargeAmountException.java @@ -0,0 +1,20 @@ +package sendingsignalsclient.exceptions; + +public class InvalidChargeAmountException extends Exception { + + public InvalidChargeAmountException() { + super(); + } + + public InvalidChargeAmountException(String message) { + super(message); + } + + public InvalidChargeAmountException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidChargeAmountException(Throwable cause) { + super(cause); + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/exceptions/OutOfServiceAreaException.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/exceptions/OutOfServiceAreaException.java new file mode 100644 index 0000000..9bb4dfa --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/exceptions/OutOfServiceAreaException.java @@ -0,0 +1,20 @@ +package sendingsignalsclient.exceptions; + +public class OutOfServiceAreaException extends Exception { + + public OutOfServiceAreaException() { + super(); + } + + public OutOfServiceAreaException(String message) { + super(message); + } + + public OutOfServiceAreaException(String message, Throwable cause) { + super(message, cause); + } + + public OutOfServiceAreaException(Throwable cause) { + super(cause); + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Address.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Address.java new file mode 100644 index 0000000..c028ab3 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Address.java @@ -0,0 +1,65 @@ +package sendingsignalsclient.model; + +public class Address { + + private String line1; + private String line2; + private String city; + private String state; + private String postalCode; + + public Address() { + } + + public Address(String line1, String line2, String city, String state, String postalCode) { + this.line1 = line1; + this.line2 = line2; + this.city = city; + this.state = state; + this.postalCode = postalCode; + } + + public String getLine1() { + return line1; + } + + public void setLine1(String line1) { + this.line1 = line1; + } + + public String getLine2() { + return line2; + } + + public void setLine2(String line2) { + this.line2 = line2; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getPostalCode() { + return postalCode; + } + + public void setPostalCode(String postalCode) { + this.postalCode = postalCode; + } + + public String toString() { + return this.line1 + " " + this.line2 + " " + this.city + " " + this.state + " " + this.postalCode; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Bill.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Bill.java new file mode 100644 index 0000000..31ff32e --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Bill.java @@ -0,0 +1,55 @@ +package sendingsignalsclient.model; + +public class Bill { + + private int customerID; + private String orderNumber; + private String description; + private int amount; + + public Bill() { + } + + public Bill(int customerID, String orderNumber, String description, int amount) { + this.customerID = customerID; + this.orderNumber = orderNumber; + this.description = description; + this.amount = amount; + } + + public int getCustomerID() { + return customerID; + } + + public void setCustomerID(int customerID) { + this.customerID = customerID; + } + + public String getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber(String orderNumber) { + this.orderNumber = orderNumber; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + public String toString() { + return this.customerID + ", " + this.orderNumber + ", " + this.amount; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Customer.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Customer.java new file mode 100644 index 0000000..df6428b --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Customer.java @@ -0,0 +1,51 @@ +package sendingsignalsclient.model; + +public class Customer { + + private int customerID; + private String name; + private String email; + private String phone; + + public Customer() { + } + + public Customer(int customerID, String name, String email, String phone) { + this.customerID = customerID; + this.name = name; + this.email = email; + this.phone = phone; + } + + public int getCustomerID() { + return customerID; + } + + public void setCustomerID(int customerID) { + this.customerID = customerID; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Distance.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Distance.java new file mode 100644 index 0000000..1f0d772 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Distance.java @@ -0,0 +1,21 @@ +package sendingsignalsclient.model; + +public class Distance { + + private int kilometers; + + public Distance() { + } + + public Distance(int kilometers) { + this.kilometers = kilometers; + } + + public int getKilometers() { + return kilometers; + } + + public void setKilometers(int kilometers) { + this.kilometers = kilometers; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/OrderConfirmation.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/OrderConfirmation.java new file mode 100644 index 0000000..724b6b0 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/OrderConfirmation.java @@ -0,0 +1,70 @@ +package sendingsignalsclient.model; + +public class OrderConfirmation { + + private String orderNumber; + private String status; + private String confirmationNumber; + private long billingTimestamp; + private int amount; + + public OrderConfirmation() { + } + + public OrderConfirmation(String orderNumber, String status, String confirmationNumber, + long billingTimestamp, int amount) { + + this.orderNumber = orderNumber; + this.status = status; + this.confirmationNumber = confirmationNumber; + this.billingTimestamp = billingTimestamp; + this.amount = amount; + } + + public String getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber(String orderNumber) { + this.orderNumber = orderNumber; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getConfirmationNumber() { + return confirmationNumber; + } + + public void setConfirmationNumber(String confirmationNumber) { + this.confirmationNumber = confirmationNumber; + } + + public long getBillingTimestamp() { + return billingTimestamp; + } + + public void setBillingTimestamp(long billingTimestamp) { + this.billingTimestamp = billingTimestamp; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + @Override + public String toString() { + return "OrderConfirmation{" + "orderNumber='" + orderNumber + '\'' + ", status='" + status + + '\'' + ", confirmationNumber='" + confirmationNumber + '\'' + ", billingTimestamp=" + + billingTimestamp + ", amount=" + amount + '}'; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Pizza.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Pizza.java new file mode 100644 index 0000000..a4f88f7 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/Pizza.java @@ -0,0 +1,30 @@ +package sendingsignalsclient.model; + +public class Pizza { + private String description; + private int price; + + public Pizza() { + } + + public Pizza(String description, int price) { + this.description = description; + this.price = price; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getPrice() { + return price; + } + + public void setPrice(int price) { + this.price = price; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/PizzaOrder.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/PizzaOrder.java new file mode 100644 index 0000000..ec17722 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/model/PizzaOrder.java @@ -0,0 +1,65 @@ +package sendingsignalsclient.model; + +import java.util.List; + +public class PizzaOrder { + + private String orderNumber; + private Customer customer; + private List items; + private boolean isDelivery; + private Address address; + + public PizzaOrder() { + } + + public PizzaOrder(String orderNumber, Customer customer, List items, boolean isDelivery, + Address address) { + + this.orderNumber = orderNumber; + this.customer = customer; + this.items = items; + this.isDelivery = isDelivery; + this.address = address; + } + + public String getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber(String orderNumber) { + this.orderNumber = orderNumber; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public boolean isDelivery() { + return isDelivery; + } + + public void setDelivery(boolean delivery) { + isDelivery = delivery; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaActivities.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaActivities.java new file mode 100644 index 0000000..5574763 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaActivities.java @@ -0,0 +1,16 @@ +package sendingsignalsclient.orderpizza; + +import io.temporal.activity.ActivityInterface; +import sendingsignalsclient.model.Distance; +import sendingsignalsclient.exceptions.InvalidChargeAmountException; +import sendingsignalsclient.model.Address; +import sendingsignalsclient.model.OrderConfirmation; +import sendingsignalsclient.model.Bill; + +@ActivityInterface +public interface PizzaActivities { + + Distance getDistance(Address address); + + OrderConfirmation sendBill(Bill bill) throws InvalidChargeAmountException; +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaActivitiesImpl.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaActivitiesImpl.java new file mode 100644 index 0000000..54968b9 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaActivitiesImpl.java @@ -0,0 +1,73 @@ +package sendingsignalsclient.orderpizza; + +import sendingsignalsclient.model.OrderConfirmation; +import sendingsignalsclient.model.Address; +import sendingsignalsclient.model.Distance; +import sendingsignalsclient.model.Bill; + +import sendingsignalsclient.exceptions.InvalidChargeAmountException; + +import java.time.Instant; + +import io.temporal.activity.Activity; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PizzaActivitiesImpl implements PizzaActivities { + + private static final Logger logger = LoggerFactory.getLogger(PizzaActivitiesImpl.class); + + @Override + public Distance getDistance(Address address) { + + logger.info("getDistance invoked; determining distance to customer address"); + + // this is a simulation, which calculates a fake (but consistent) + // distance for a customer address based on its length. The value + // will therefore be different when called with different addresses, + // but will be the same across all invocations with the same address. + + int kilometers = address.getLine1().length() + address.getLine2().length() - 10; + if (kilometers < 1) { + kilometers = 5; + } + + Distance distance = new Distance(kilometers); + + logger.info("getDistance complete: {}", distance.getKilometers()); + return distance; + } + + @Override + public OrderConfirmation sendBill(Bill bill) { + int amount = bill.getAmount(); + + logger.info("sendBill invoked: customer: {} amount: {}", bill.getCustomerID(), amount); + + int chargeAmount = amount; + + // This month's special offer: Get $5 off all orders over $30 + if (amount > 3000) { + logger.info("Applying discount"); + + chargeAmount -= 500; // reduce amount charged by 500 cents + } + + // reject invalid amounts before calling the payment processor + if (chargeAmount < 0) { + logger.error("invalid charge amount: {%d} (must be above zero)", chargeAmount); + String errorMessage = "invalid charge amount: " + chargeAmount; + throw Activity.wrap(new InvalidChargeAmountException(errorMessage)); + } + + // pretend we called a payment processing service here + OrderConfirmation confirmation = new OrderConfirmation(bill.getOrderNumber(), "SUCCESS", + "P24601", Instant.now().getEpochSecond(), chargeAmount); + + logger.debug("Sendbill complete: Confirmation Number: {}", + confirmation.getConfirmationNumber()); + + return confirmation; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflow.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflow.java new file mode 100644 index 0000000..b881ed2 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflow.java @@ -0,0 +1,18 @@ +package sendingsignalsclient.orderpizza; + +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; +import io.temporal.workflow.SignalMethod; +import sendingsignalsclient.model.PizzaOrder; +import sendingsignalsclient.model.OrderConfirmation; + +@WorkflowInterface +public interface PizzaWorkflow { + + @WorkflowMethod + OrderConfirmation orderPizza(PizzaOrder order); + + @SignalMethod + void fulfillOrderSignal(boolean bool); + +} diff --git a/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflowImpl.java b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflowImpl.java new file mode 100644 index 0000000..d318a33 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflowImpl.java @@ -0,0 +1,91 @@ +package sendingsignalsclient.orderpizza; + +import io.temporal.activity.ActivityOptions; +import io.temporal.workflow.Workflow; +import io.temporal.failure.ApplicationFailure; + +import sendingsignalsclient.model.Address; +import sendingsignalsclient.model.Bill; +import sendingsignalsclient.model.Customer; +import sendingsignalsclient.model.Distance; +import sendingsignalsclient.model.OrderConfirmation; +import sendingsignalsclient.model.Pizza; +import sendingsignalsclient.model.PizzaOrder; +import sendingsignalsclient.exceptions.InvalidChargeAmountException; +import sendingsignalsclient.exceptions.OutOfServiceAreaException; + +import java.time.Duration; +import java.util.List; + +import org.slf4j.Logger; + +public class PizzaWorkflowImpl implements PizzaWorkflow { + + public static final Logger logger = Workflow.getLogger(PizzaWorkflowImpl.class); + + private boolean fulfilled; + + ActivityOptions options = ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build(); + + private final PizzaActivities activities = Workflow.newActivityStub(PizzaActivities.class, options); + + @Override + public OrderConfirmation orderPizza(PizzaOrder order) { + + String orderNumber = order.getOrderNumber(); + Customer customer = order.getCustomer(); + List items = order.getItems(); + boolean isDelivery = order.isDelivery(); + Address address = order.getAddress(); + + logger.info("orderPizza Workflow Invoked"); + + int totalPrice = 0; + for (Pizza pizza : items) { + totalPrice += pizza.getPrice(); + } + + Distance distance; + try { + distance = activities.getDistance(address); + } catch (NullPointerException e) { + logger.error("Unable to get distance"); + throw new NullPointerException("Unable to get distance"); + } + + if (isDelivery && (distance.getKilometers() > 25)) { + logger.error("Customer lives outside the service area"); + throw ApplicationFailure.newFailure("Customer lives outside the service area", + OutOfServiceAreaException.class.getName()); + } + + logger.info("distance is {}", distance.getKilometers()); + + Workflow.await(() -> this.fulfilled); + + OrderConfirmation confirmation; + + if (this.fulfilled) { + Bill bill = new Bill(customer.getCustomerID(), orderNumber, "Pizza", totalPrice); + + try { + confirmation = activities.sendBill(bill); + logger.info("Bill sent to customer {}", customer.getCustomerID()); + } catch (InvalidChargeAmountException e) { + logger.error("Unable to bill customer"); + throw Workflow.wrap(new InvalidChargeAmountException("Unable to bill customer")); + } + } else { + confirmation = null; + logger.info("Order was not fulfilled. Not billing the customer."); + } + + return confirmation; + + } + + @Override + public void fulfillOrderSignal(boolean bool) { + this.fulfilled = bool; + } +} diff --git a/exercises/sending-signals-client/practice/src/main/resources/logback.xml b/exercises/sending-signals-client/practice/src/main/resources/logback.xml new file mode 100644 index 0000000..8e488b0 --- /dev/null +++ b/exercises/sending-signals-client/practice/src/main/resources/logback.xml @@ -0,0 +1,34 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %-5level - %msg %n + + + + + + + \ No newline at end of file diff --git a/exercises/sending-signals-client/solution/pom.xml b/exercises/sending-signals-client/solution/pom.xml new file mode 100644 index 0000000..eb7ff16 --- /dev/null +++ b/exercises/sending-signals-client/solution/pom.xml @@ -0,0 +1,109 @@ + + + + 4.0.0 + + io.temporal.learn + sending-signals-client-solution + 1.0.0-SNAPSHOT + + sending signals client (solution) + https://learn.temporal.io/ + + + UTF-8 + 1.8 + 1.8 + + + + + + io.temporal + temporal-sdk + 1.20.1 + + + + ch.qos.logback + logback-classic + 1.4.8 + + + + org.apache.commons + commons-lang3 + 3.11 + + + + io.temporal + temporal-testing + 1.20.1 + test + + + + + org.junit.jupiter + junit-jupiter + 5.5.2 + test + + + + org.mockito + mockito-junit-jupiter + 5.3.1 + test + + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/Constants.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/Constants.java new file mode 100644 index 0000000..9ef9bd1 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/Constants.java @@ -0,0 +1,7 @@ +package sendingsignalsclient; + +public class Constants { + + public static final String TASK_QUEUE_NAME = "pizza-tasks"; + +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/SendSignalClientWorker.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/SendSignalClientWorker.java new file mode 100644 index 0000000..ff66ba4 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/SendSignalClientWorker.java @@ -0,0 +1,26 @@ +package sendingsignalsclient; + +import io.temporal.client.WorkflowClient; +import io.temporal.serviceclient.WorkflowServiceStubs; +import io.temporal.worker.Worker; +import io.temporal.worker.WorkerFactory; + +import sendingsignalsclient.orderpizza.PizzaActivitiesImpl; +import sendingsignalsclient.orderpizza.PizzaWorkflowImpl; + +public class SendSignalClientWorker { + public static void main(String[] args) { + + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + WorkflowClient client = WorkflowClient.newInstance(service); + WorkerFactory factory = WorkerFactory.newInstance(client); + + Worker worker = factory.newWorker(Constants.TASK_QUEUE_NAME); + + worker.registerWorkflowImplementationTypes(PizzaWorkflowImpl.class); + + worker.registerActivitiesImplementations(new PizzaActivitiesImpl()); + + factory.start(); + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/SignalClient.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/SignalClient.java new file mode 100644 index 0000000..e9ad234 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/SignalClient.java @@ -0,0 +1,21 @@ +package sendingsignalsclient; + +import io.temporal.client.WorkflowClient; +import io.temporal.serviceclient.WorkflowServiceStubs; +import sendingsignalsclient.orderpizza.PizzaWorkflow; + + +public class SignalClient { + public static void main(String[] args) throws Exception { + + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + + WorkflowClient client = WorkflowClient.newInstance(service); + + PizzaWorkflow workflow = client.newWorkflowStub(PizzaWorkflow.class, "pizza-workflow-order-XD001"); + + workflow.fulfillOrderSignal(true); + + System.exit(0); + } +} \ No newline at end of file diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/Starter.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/Starter.java new file mode 100644 index 0000000..6b9836a --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/Starter.java @@ -0,0 +1,58 @@ +package sendingsignalsclient; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowOptions; +import java.util.concurrent.CompletableFuture; +import io.temporal.serviceclient.WorkflowServiceStubs; + +import sendingsignalsclient.model.PizzaOrder; +import sendingsignalsclient.model.Pizza; +import sendingsignalsclient.model.Customer; +import sendingsignalsclient.model.OrderConfirmation; +import sendingsignalsclient.model.Address; +import sendingsignalsclient.orderpizza.PizzaWorkflow; + +import java.util.Arrays; +import java.util.List; + +public class Starter { + public static void main(String[] args) throws Exception { + + WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); + + WorkflowClient client = WorkflowClient.newInstance(service); + + PizzaOrder order = createPizzaOrder(); + + // Setup the Pizza Order Workflow Client + String pizzaWorkflowID = String.format("pizza-workflow-order-%s", order.getOrderNumber()); + + WorkflowOptions pizzaWorkflowOptions = WorkflowOptions.newBuilder() + .setWorkflowId(pizzaWorkflowID) + .setTaskQueue(Constants.TASK_QUEUE_NAME) + .build(); + + PizzaWorkflow pizzaWorkflow = client.newWorkflowStub(PizzaWorkflow.class, pizzaWorkflowOptions); + + OrderConfirmation orderConfirmation = pizzaWorkflow.orderPizza(order); + //CompletableFuture orderConfirmation = WorkflowClient.execute(pizzaWorkflow::orderPizza, order); + + + System.out.printf("Workflow result: %s\n", orderConfirmation); + System.exit(0); + } + + private static PizzaOrder createPizzaOrder() { + Customer customer = new Customer(8675309, "Lisa Anderson", "lisa@example.com", "555-555-0000"); + Address address = new Address("742 Evergreen Terrace", "Apartment 221B", "Albuquerque", "NM", "87101"); + Pizza pizza1 = new Pizza("Large, with mushrooms and onions", 1500); + Pizza pizza2 = new Pizza("Small, with pepperoni", 1200); + Pizza pizza3 = new Pizza("Medium, with extra cheese", 1300); + + List orderList = Arrays.asList(pizza1, pizza2, pizza3); + + PizzaOrder order = new PizzaOrder("XD001", customer, orderList, true, address); + + return order; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/exceptions/InvalidChargeAmountException.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/exceptions/InvalidChargeAmountException.java new file mode 100644 index 0000000..09e8ec0 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/exceptions/InvalidChargeAmountException.java @@ -0,0 +1,20 @@ +package sendingsignalsclient.exceptions; + +public class InvalidChargeAmountException extends Exception { + + public InvalidChargeAmountException() { + super(); + } + + public InvalidChargeAmountException(String message) { + super(message); + } + + public InvalidChargeAmountException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidChargeAmountException(Throwable cause) { + super(cause); + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/exceptions/OutOfServiceAreaException.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/exceptions/OutOfServiceAreaException.java new file mode 100644 index 0000000..9bb4dfa --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/exceptions/OutOfServiceAreaException.java @@ -0,0 +1,20 @@ +package sendingsignalsclient.exceptions; + +public class OutOfServiceAreaException extends Exception { + + public OutOfServiceAreaException() { + super(); + } + + public OutOfServiceAreaException(String message) { + super(message); + } + + public OutOfServiceAreaException(String message, Throwable cause) { + super(message, cause); + } + + public OutOfServiceAreaException(Throwable cause) { + super(cause); + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Address.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Address.java new file mode 100644 index 0000000..c028ab3 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Address.java @@ -0,0 +1,65 @@ +package sendingsignalsclient.model; + +public class Address { + + private String line1; + private String line2; + private String city; + private String state; + private String postalCode; + + public Address() { + } + + public Address(String line1, String line2, String city, String state, String postalCode) { + this.line1 = line1; + this.line2 = line2; + this.city = city; + this.state = state; + this.postalCode = postalCode; + } + + public String getLine1() { + return line1; + } + + public void setLine1(String line1) { + this.line1 = line1; + } + + public String getLine2() { + return line2; + } + + public void setLine2(String line2) { + this.line2 = line2; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getPostalCode() { + return postalCode; + } + + public void setPostalCode(String postalCode) { + this.postalCode = postalCode; + } + + public String toString() { + return this.line1 + " " + this.line2 + " " + this.city + " " + this.state + " " + this.postalCode; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Bill.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Bill.java new file mode 100644 index 0000000..31ff32e --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Bill.java @@ -0,0 +1,55 @@ +package sendingsignalsclient.model; + +public class Bill { + + private int customerID; + private String orderNumber; + private String description; + private int amount; + + public Bill() { + } + + public Bill(int customerID, String orderNumber, String description, int amount) { + this.customerID = customerID; + this.orderNumber = orderNumber; + this.description = description; + this.amount = amount; + } + + public int getCustomerID() { + return customerID; + } + + public void setCustomerID(int customerID) { + this.customerID = customerID; + } + + public String getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber(String orderNumber) { + this.orderNumber = orderNumber; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + public String toString() { + return this.customerID + ", " + this.orderNumber + ", " + this.amount; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Customer.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Customer.java new file mode 100644 index 0000000..df6428b --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Customer.java @@ -0,0 +1,51 @@ +package sendingsignalsclient.model; + +public class Customer { + + private int customerID; + private String name; + private String email; + private String phone; + + public Customer() { + } + + public Customer(int customerID, String name, String email, String phone) { + this.customerID = customerID; + this.name = name; + this.email = email; + this.phone = phone; + } + + public int getCustomerID() { + return customerID; + } + + public void setCustomerID(int customerID) { + this.customerID = customerID; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Distance.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Distance.java new file mode 100644 index 0000000..1f0d772 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Distance.java @@ -0,0 +1,21 @@ +package sendingsignalsclient.model; + +public class Distance { + + private int kilometers; + + public Distance() { + } + + public Distance(int kilometers) { + this.kilometers = kilometers; + } + + public int getKilometers() { + return kilometers; + } + + public void setKilometers(int kilometers) { + this.kilometers = kilometers; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/OrderConfirmation.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/OrderConfirmation.java new file mode 100644 index 0000000..724b6b0 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/OrderConfirmation.java @@ -0,0 +1,70 @@ +package sendingsignalsclient.model; + +public class OrderConfirmation { + + private String orderNumber; + private String status; + private String confirmationNumber; + private long billingTimestamp; + private int amount; + + public OrderConfirmation() { + } + + public OrderConfirmation(String orderNumber, String status, String confirmationNumber, + long billingTimestamp, int amount) { + + this.orderNumber = orderNumber; + this.status = status; + this.confirmationNumber = confirmationNumber; + this.billingTimestamp = billingTimestamp; + this.amount = amount; + } + + public String getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber(String orderNumber) { + this.orderNumber = orderNumber; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getConfirmationNumber() { + return confirmationNumber; + } + + public void setConfirmationNumber(String confirmationNumber) { + this.confirmationNumber = confirmationNumber; + } + + public long getBillingTimestamp() { + return billingTimestamp; + } + + public void setBillingTimestamp(long billingTimestamp) { + this.billingTimestamp = billingTimestamp; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + @Override + public String toString() { + return "OrderConfirmation{" + "orderNumber='" + orderNumber + '\'' + ", status='" + status + + '\'' + ", confirmationNumber='" + confirmationNumber + '\'' + ", billingTimestamp=" + + billingTimestamp + ", amount=" + amount + '}'; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Pizza.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Pizza.java new file mode 100644 index 0000000..a4f88f7 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/Pizza.java @@ -0,0 +1,30 @@ +package sendingsignalsclient.model; + +public class Pizza { + private String description; + private int price; + + public Pizza() { + } + + public Pizza(String description, int price) { + this.description = description; + this.price = price; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getPrice() { + return price; + } + + public void setPrice(int price) { + this.price = price; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/PizzaOrder.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/PizzaOrder.java new file mode 100644 index 0000000..ec17722 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/model/PizzaOrder.java @@ -0,0 +1,65 @@ +package sendingsignalsclient.model; + +import java.util.List; + +public class PizzaOrder { + + private String orderNumber; + private Customer customer; + private List items; + private boolean isDelivery; + private Address address; + + public PizzaOrder() { + } + + public PizzaOrder(String orderNumber, Customer customer, List items, boolean isDelivery, + Address address) { + + this.orderNumber = orderNumber; + this.customer = customer; + this.items = items; + this.isDelivery = isDelivery; + this.address = address; + } + + public String getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber(String orderNumber) { + this.orderNumber = orderNumber; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public boolean isDelivery() { + return isDelivery; + } + + public void setDelivery(boolean delivery) { + isDelivery = delivery; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaActivities.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaActivities.java new file mode 100644 index 0000000..5574763 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaActivities.java @@ -0,0 +1,16 @@ +package sendingsignalsclient.orderpizza; + +import io.temporal.activity.ActivityInterface; +import sendingsignalsclient.model.Distance; +import sendingsignalsclient.exceptions.InvalidChargeAmountException; +import sendingsignalsclient.model.Address; +import sendingsignalsclient.model.OrderConfirmation; +import sendingsignalsclient.model.Bill; + +@ActivityInterface +public interface PizzaActivities { + + Distance getDistance(Address address); + + OrderConfirmation sendBill(Bill bill) throws InvalidChargeAmountException; +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaActivitiesImpl.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaActivitiesImpl.java new file mode 100644 index 0000000..54968b9 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaActivitiesImpl.java @@ -0,0 +1,73 @@ +package sendingsignalsclient.orderpizza; + +import sendingsignalsclient.model.OrderConfirmation; +import sendingsignalsclient.model.Address; +import sendingsignalsclient.model.Distance; +import sendingsignalsclient.model.Bill; + +import sendingsignalsclient.exceptions.InvalidChargeAmountException; + +import java.time.Instant; + +import io.temporal.activity.Activity; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PizzaActivitiesImpl implements PizzaActivities { + + private static final Logger logger = LoggerFactory.getLogger(PizzaActivitiesImpl.class); + + @Override + public Distance getDistance(Address address) { + + logger.info("getDistance invoked; determining distance to customer address"); + + // this is a simulation, which calculates a fake (but consistent) + // distance for a customer address based on its length. The value + // will therefore be different when called with different addresses, + // but will be the same across all invocations with the same address. + + int kilometers = address.getLine1().length() + address.getLine2().length() - 10; + if (kilometers < 1) { + kilometers = 5; + } + + Distance distance = new Distance(kilometers); + + logger.info("getDistance complete: {}", distance.getKilometers()); + return distance; + } + + @Override + public OrderConfirmation sendBill(Bill bill) { + int amount = bill.getAmount(); + + logger.info("sendBill invoked: customer: {} amount: {}", bill.getCustomerID(), amount); + + int chargeAmount = amount; + + // This month's special offer: Get $5 off all orders over $30 + if (amount > 3000) { + logger.info("Applying discount"); + + chargeAmount -= 500; // reduce amount charged by 500 cents + } + + // reject invalid amounts before calling the payment processor + if (chargeAmount < 0) { + logger.error("invalid charge amount: {%d} (must be above zero)", chargeAmount); + String errorMessage = "invalid charge amount: " + chargeAmount; + throw Activity.wrap(new InvalidChargeAmountException(errorMessage)); + } + + // pretend we called a payment processing service here + OrderConfirmation confirmation = new OrderConfirmation(bill.getOrderNumber(), "SUCCESS", + "P24601", Instant.now().getEpochSecond(), chargeAmount); + + logger.debug("Sendbill complete: Confirmation Number: {}", + confirmation.getConfirmationNumber()); + + return confirmation; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflow.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflow.java new file mode 100644 index 0000000..b881ed2 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflow.java @@ -0,0 +1,18 @@ +package sendingsignalsclient.orderpizza; + +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; +import io.temporal.workflow.SignalMethod; +import sendingsignalsclient.model.PizzaOrder; +import sendingsignalsclient.model.OrderConfirmation; + +@WorkflowInterface +public interface PizzaWorkflow { + + @WorkflowMethod + OrderConfirmation orderPizza(PizzaOrder order); + + @SignalMethod + void fulfillOrderSignal(boolean bool); + +} diff --git a/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflowImpl.java b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflowImpl.java new file mode 100644 index 0000000..d318a33 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/java/sendingsignalsclient/orderpizza/PizzaWorkflowImpl.java @@ -0,0 +1,91 @@ +package sendingsignalsclient.orderpizza; + +import io.temporal.activity.ActivityOptions; +import io.temporal.workflow.Workflow; +import io.temporal.failure.ApplicationFailure; + +import sendingsignalsclient.model.Address; +import sendingsignalsclient.model.Bill; +import sendingsignalsclient.model.Customer; +import sendingsignalsclient.model.Distance; +import sendingsignalsclient.model.OrderConfirmation; +import sendingsignalsclient.model.Pizza; +import sendingsignalsclient.model.PizzaOrder; +import sendingsignalsclient.exceptions.InvalidChargeAmountException; +import sendingsignalsclient.exceptions.OutOfServiceAreaException; + +import java.time.Duration; +import java.util.List; + +import org.slf4j.Logger; + +public class PizzaWorkflowImpl implements PizzaWorkflow { + + public static final Logger logger = Workflow.getLogger(PizzaWorkflowImpl.class); + + private boolean fulfilled; + + ActivityOptions options = ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build(); + + private final PizzaActivities activities = Workflow.newActivityStub(PizzaActivities.class, options); + + @Override + public OrderConfirmation orderPizza(PizzaOrder order) { + + String orderNumber = order.getOrderNumber(); + Customer customer = order.getCustomer(); + List items = order.getItems(); + boolean isDelivery = order.isDelivery(); + Address address = order.getAddress(); + + logger.info("orderPizza Workflow Invoked"); + + int totalPrice = 0; + for (Pizza pizza : items) { + totalPrice += pizza.getPrice(); + } + + Distance distance; + try { + distance = activities.getDistance(address); + } catch (NullPointerException e) { + logger.error("Unable to get distance"); + throw new NullPointerException("Unable to get distance"); + } + + if (isDelivery && (distance.getKilometers() > 25)) { + logger.error("Customer lives outside the service area"); + throw ApplicationFailure.newFailure("Customer lives outside the service area", + OutOfServiceAreaException.class.getName()); + } + + logger.info("distance is {}", distance.getKilometers()); + + Workflow.await(() -> this.fulfilled); + + OrderConfirmation confirmation; + + if (this.fulfilled) { + Bill bill = new Bill(customer.getCustomerID(), orderNumber, "Pizza", totalPrice); + + try { + confirmation = activities.sendBill(bill); + logger.info("Bill sent to customer {}", customer.getCustomerID()); + } catch (InvalidChargeAmountException e) { + logger.error("Unable to bill customer"); + throw Workflow.wrap(new InvalidChargeAmountException("Unable to bill customer")); + } + } else { + confirmation = null; + logger.info("Order was not fulfilled. Not billing the customer."); + } + + return confirmation; + + } + + @Override + public void fulfillOrderSignal(boolean bool) { + this.fulfilled = bool; + } +} diff --git a/exercises/sending-signals-client/solution/src/main/resources/logback.xml b/exercises/sending-signals-client/solution/src/main/resources/logback.xml new file mode 100644 index 0000000..8e488b0 --- /dev/null +++ b/exercises/sending-signals-client/solution/src/main/resources/logback.xml @@ -0,0 +1,34 @@ + + + + + + + + + %d{HH:mm:ss.SSS} %-5level - %msg %n + + + + + + + \ No newline at end of file