diff --git a/libraries/pom.xml b/libraries/pom.xml
index 3e802e76c8..a330494bb3 100644
--- a/libraries/pom.xml
+++ b/libraries/pom.xml
@@ -710,6 +710,11 @@
test
test
+
+ org.milyn
+ milyn-smooks-all
+ ${smooks.version}
+
@@ -789,6 +794,7 @@
1.23.0
v4-rev493-1.21.0
1.0.0
+ 1.7.0
3.0.14
\ No newline at end of file
diff --git a/libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java b/libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java
new file mode 100644
index 0000000000..d11f5a29b2
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/smooks/converter/OrderConverter.java
@@ -0,0 +1,45 @@
+package com.baeldung.smooks.converter;
+
+import com.baeldung.smooks.model.Order;
+import org.milyn.Smooks;
+import org.milyn.payload.JavaResult;
+import org.milyn.payload.StringResult;
+import org.xml.sax.SAXException;
+
+import javax.xml.transform.stream.StreamSource;
+import java.io.IOException;
+
+public class OrderConverter {
+
+ public Order convertOrderXMLToOrderObject(String path) throws IOException, SAXException {
+ Smooks smooks = new Smooks(OrderConverter.class.getResourceAsStream("/smooks/smooks-mapping.xml"));
+ try {
+ JavaResult javaResult = new JavaResult();
+ smooks.filterSource(new StreamSource(OrderConverter.class.getResourceAsStream(path)), javaResult);
+ return (Order) javaResult.getBean("order");
+ } finally {
+ smooks.close();
+ }
+ }
+
+
+ public String convertOrderXMLtoEDIFACT(String path) throws IOException, SAXException {
+ return convertDocumentWithTempalte(path, "/smooks/smooks-transform-edi.xml");
+ }
+
+ public String convertOrderXMLtoEmailMessage(String path) throws IOException, SAXException {
+ return convertDocumentWithTempalte(path, "/smooks/smooks-transform-email.xml");
+ }
+
+ private String convertDocumentWithTempalte(String path, String config) throws IOException, SAXException {
+ Smooks smooks = new Smooks(config);
+
+ try {
+ StringResult stringResult = new StringResult();
+ smooks.filterSource(new StreamSource(OrderConverter.class.getResourceAsStream(path)), stringResult);
+ return stringResult.toString();
+ } finally {
+ smooks.close();
+ }
+ }
+}
diff --git a/libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java b/libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java
new file mode 100644
index 0000000000..3975921da0
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/smooks/converter/OrderValidator.java
@@ -0,0 +1,27 @@
+package com.baeldung.smooks.converter;
+
+import org.milyn.Smooks;
+import org.milyn.payload.JavaResult;
+import org.milyn.payload.StringResult;
+import org.milyn.validation.ValidationResult;
+import org.xml.sax.SAXException;
+
+import javax.xml.transform.stream.StreamSource;
+import java.io.IOException;
+
+public class OrderValidator {
+
+ public ValidationResult validate(String path) throws IOException, SAXException {
+ Smooks smooks = new Smooks(OrderValidator.class.getResourceAsStream("/smooks/smooks-validation.xml"));
+
+ try {
+ StringResult xmlResult = new StringResult();
+ JavaResult javaResult = new JavaResult();
+ ValidationResult validationResult = new ValidationResult();
+ smooks.filterSource(new StreamSource(OrderValidator.class.getResourceAsStream(path)), xmlResult, javaResult, validationResult);
+ return validationResult;
+ } finally {
+ smooks.close();
+ }
+ }
+}
diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Item.java b/libraries/src/main/java/com/baeldung/smooks/model/Item.java
new file mode 100644
index 0000000000..a7f7783b3f
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/smooks/model/Item.java
@@ -0,0 +1,71 @@
+package com.baeldung.smooks.model;
+
+public class Item {
+
+ public Item() {
+ }
+
+ public Item(String code, Double price, Integer quantity) {
+ this.code = code;
+ this.price = price;
+ this.quantity = quantity;
+ }
+
+ private String code;
+ private Double price;
+ private Integer quantity;
+
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public Double getPrice() {
+ return price;
+ }
+
+ public void setPrice(Double price) {
+ this.price = price;
+ }
+
+ public Integer getQuantity() {
+ return quantity;
+ }
+
+ public void setQuantity(Integer quantity) {
+ this.quantity = quantity;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Item item = (Item) o;
+
+ if (code != null ? !code.equals(item.code) : item.code != null) return false;
+ if (price != null ? !price.equals(item.price) : item.price != null) return false;
+ return quantity != null ? quantity.equals(item.quantity) : item.quantity == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = code != null ? code.hashCode() : 0;
+ result = 31 * result + (price != null ? price.hashCode() : 0);
+ result = 31 * result + (quantity != null ? quantity.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Item{" +
+ "code='" + code + '\'' +
+ ", price=" + price +
+ ", quantity=" + quantity +
+ '}';
+ }
+}
diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Order.java b/libraries/src/main/java/com/baeldung/smooks/model/Order.java
new file mode 100644
index 0000000000..047e1fe8a3
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/smooks/model/Order.java
@@ -0,0 +1,52 @@
+package com.baeldung.smooks.model;
+
+import java.util.Date;
+import java.util.List;
+
+public class Order {
+ private Date creationDate;
+ private Long number;
+ private Status status;
+ private Supplier supplier;
+ private List- items;
+
+ public Date getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(Date creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public Long getNumber() {
+ return number;
+ }
+
+ public void setNumber(Long number) {
+ this.number = number;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+ public Supplier getSupplier() {
+ return supplier;
+ }
+
+ public void setSupplier(Supplier supplier) {
+ this.supplier = supplier;
+ }
+
+ public List
- getItems() {
+ return items;
+ }
+
+ public void setItems(List
- items) {
+ this.items = items;
+ }
+}
diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Status.java b/libraries/src/main/java/com/baeldung/smooks/model/Status.java
new file mode 100644
index 0000000000..53c50bdf46
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/smooks/model/Status.java
@@ -0,0 +1,5 @@
+package com.baeldung.smooks.model;
+
+public enum Status {
+ NEW, IN_PROGRESS, FINISHED
+}
diff --git a/libraries/src/main/java/com/baeldung/smooks/model/Supplier.java b/libraries/src/main/java/com/baeldung/smooks/model/Supplier.java
new file mode 100644
index 0000000000..31a9e1f43f
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/smooks/model/Supplier.java
@@ -0,0 +1,49 @@
+package com.baeldung.smooks.model;
+
+public class Supplier {
+
+ private String name;
+ private String phoneNumber;
+
+ public Supplier() {
+ }
+
+ public Supplier(String name, String phoneNumber) {
+ this.name = name;
+ this.phoneNumber = phoneNumber;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPhoneNumber() {
+ return phoneNumber;
+ }
+
+ public void setPhoneNumber(String phoneNumber) {
+ this.phoneNumber = phoneNumber;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Supplier supplier = (Supplier) o;
+
+ if (name != null ? !name.equals(supplier.name) : supplier.name != null) return false;
+ return phoneNumber != null ? phoneNumber.equals(supplier.phoneNumber) : supplier.phoneNumber == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (phoneNumber != null ? phoneNumber.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/libraries/src/main/resources/smooks/email.ftl b/libraries/src/main/resources/smooks/email.ftl
new file mode 100644
index 0000000000..8413046508
--- /dev/null
+++ b/libraries/src/main/resources/smooks/email.ftl
@@ -0,0 +1,8 @@
+<#setting locale="en_US">
+Hi,
+Order number #${order.number} created on ${order.creationDate?string["yyyy-MM-dd"]} is currently in ${order.status} status.
+Consider contact supplier "${supplier.name}" with phone number: "${supplier.phoneNumber}".
+Order items:
+<#list items as item>
+${item.quantity} X ${item.code} (total price ${item.price * item.quantity})
+#list>
\ No newline at end of file
diff --git a/libraries/src/main/resources/smooks/item-rules.csv b/libraries/src/main/resources/smooks/item-rules.csv
new file mode 100644
index 0000000000..c93c453f25
--- /dev/null
+++ b/libraries/src/main/resources/smooks/item-rules.csv
@@ -0,0 +1 @@
+"max_total","item.quantity * item.price < 300.00"
diff --git a/libraries/src/main/resources/smooks/order.ftl b/libraries/src/main/resources/smooks/order.ftl
new file mode 100644
index 0000000000..9d40eb55c7
--- /dev/null
+++ b/libraries/src/main/resources/smooks/order.ftl
@@ -0,0 +1,7 @@
+<#setting locale="en_US">
+UNA:+.? '
+UNH+${order.number}+${order.status}+${order.creationDate?string["yyyy-MM-dd"]}'
+CTA+${supplier.name}+${supplier.phoneNumber}'
+<#list items as item>
+LIN+${item.quantity}+${item.code}+${item.price}'
+#list>
\ No newline at end of file
diff --git a/libraries/src/main/resources/smooks/order.json b/libraries/src/main/resources/smooks/order.json
new file mode 100644
index 0000000000..bf6bc5fe93
--- /dev/null
+++ b/libraries/src/main/resources/smooks/order.json
@@ -0,0 +1,21 @@
+{
+ "creationDate":"2018-01-14",
+ "orderNumber":771,
+ "orderStatus":"IN_PROGRESS",
+ "supplier":{
+ "name":"CompanyX",
+ "phone":"1234567"
+ },
+ "orderItems":[
+ {
+ "quantity":1,
+ "code":"PX1234",
+ "price":9.99
+ },
+ {
+ "quantity":2,
+ "code":"RX1990",
+ "price":120.32
+ }
+ ]
+}
\ No newline at end of file
diff --git a/libraries/src/main/resources/smooks/order.xml b/libraries/src/main/resources/smooks/order.xml
new file mode 100644
index 0000000000..343c5cab38
--- /dev/null
+++ b/libraries/src/main/resources/smooks/order.xml
@@ -0,0 +1,20 @@
+
+ 771
+ IN_PROGRESS
+
+ CompanyX
+ 1234567
+
+
+
-
+ 1
+
PX1234
+ 9.99
+
+ -
+ 2
+
RX990
+ 120.32
+
+
+
\ No newline at end of file
diff --git a/libraries/src/main/resources/smooks/smooks-mapping.xml b/libraries/src/main/resources/smooks/smooks-mapping.xml
new file mode 100644
index 0000000000..7996834e38
--- /dev/null
+++ b/libraries/src/main/resources/smooks/smooks-mapping.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+ yyyy-MM-dd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/src/main/resources/smooks/smooks-transform-edi.xml b/libraries/src/main/resources/smooks/smooks-transform-edi.xml
new file mode 100644
index 0000000000..1dae4055a8
--- /dev/null
+++ b/libraries/src/main/resources/smooks/smooks-transform-edi.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+ /smooks/order.ftl
+
+
+
\ No newline at end of file
diff --git a/libraries/src/main/resources/smooks/smooks-transform-email.xml b/libraries/src/main/resources/smooks/smooks-transform-email.xml
new file mode 100644
index 0000000000..101aa67f0d
--- /dev/null
+++ b/libraries/src/main/resources/smooks/smooks-transform-email.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+ /smooks/email.ftl
+
+
+
\ No newline at end of file
diff --git a/libraries/src/main/resources/smooks/smooks-validation.xml b/libraries/src/main/resources/smooks/smooks-validation.xml
new file mode 100644
index 0000000000..b66722ffc5
--- /dev/null
+++ b/libraries/src/main/resources/smooks/smooks-validation.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libraries/src/main/resources/smooks/supplier.properties b/libraries/src/main/resources/smooks/supplier.properties
new file mode 100644
index 0000000000..cc17e45eb4
--- /dev/null
+++ b/libraries/src/main/resources/smooks/supplier.properties
@@ -0,0 +1,2 @@
+supplierName=[A-Za-z0-9]*
+supplierPhone=^[0-9\\-\\+]{9,15}$
diff --git a/libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java b/libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java
new file mode 100644
index 0000000000..4d2cb71329
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/smooks/converter/SmooksIntegrationTest.java
@@ -0,0 +1,70 @@
+package com.baeldung.smooks.converter;
+
+import com.baeldung.smooks.model.Item;
+import com.baeldung.smooks.model.Order;
+import com.baeldung.smooks.model.Status;
+import com.baeldung.smooks.model.Supplier;
+import org.junit.Test;
+import org.milyn.validation.ValidationResult;
+import java.text.SimpleDateFormat;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+
+public class SmooksIntegrationTest {
+
+ private static final String EDIFACT_MESSAGE =
+ "UNA:+.? '\r\n" +
+ "UNH+771+IN_PROGRESS+2018-01-14'\r\n" +
+ "CTA+CompanyX+1234567'\r\n" +
+ "LIN+1+PX1234+9.99'\r\n" +
+ "LIN+2+RX990+120.32'\r\n";
+ private static final String EMAIL_MESSAGE =
+ "Hi,\r\n" +
+ "Order number #771 created on 2018-01-14 is currently in IN_PROGRESS status.\r\n" +
+ "Consider contact supplier \"CompanyX\" with phone number: \"1234567\".\r\n" +
+ "Order items:\r\n" +
+ "1 X PX1234 (total price 9.99)\r\n" +
+ "2 X RX990 (total price 240.64)\r\n";
+
+ @Test
+ public void givenOrderXML_whenConvert_thenPOJOsConstructedCorrectly() throws Exception {
+
+ OrderConverter xmlToJavaOrderConverter = new OrderConverter();
+ Order order = xmlToJavaOrderConverter.convertOrderXMLToOrderObject("/smooks/order.xml");
+
+ assertThat(order.getNumber(),is(771L));
+ assertThat(order.getStatus(),is(Status.IN_PROGRESS));
+ assertThat(order.getCreationDate(),is(new SimpleDateFormat("yyyy-MM-dd").parse("2018-01-14")));
+ assertThat(order.getSupplier(),is(new Supplier("CompanyX","1234567")));
+ assertThat(order.getItems(),containsInAnyOrder(
+ new Item("PX1234",9.99,1),
+ new Item("RX990",120.32,2))
+ );
+
+ }
+
+ @Test
+ public void givenIncorrectOrderXML_whenValidate_thenExpectValidationErrors() throws Exception {
+ OrderValidator orderValidator = new OrderValidator();
+ ValidationResult validationResult = orderValidator.validate("/smooks/order.xml");
+
+ assertThat(validationResult.getErrors(), hasSize(1));
+ // 1234567 didn't match ^[0-9\\-\\+]{9,15}$
+ assertThat(validationResult.getErrors().get(0).getFailRuleResult().getRuleName(),is("supplierPhone"));
+ }
+
+ @Test
+ public void givenOrderXML_whenApplyEDITemplate_thenConvertedToEDIFACT() throws Exception {
+ OrderConverter orderConverter = new OrderConverter();
+ String edifact = orderConverter.convertOrderXMLtoEDIFACT("/smooks/order.xml");
+ assertThat(edifact,is(EDIFACT_MESSAGE));
+ }
+
+ @Test
+ public void givenOrderXML_whenApplyEmailTemplate_thenConvertedToEmailMessage() throws Exception {
+ OrderConverter orderConverter = new OrderConverter();
+ String emailMessage = orderConverter.convertOrderXMLtoEmailMessage("/smooks/order.xml");
+ assertThat(emailMessage,is(EMAIL_MESSAGE));
+ }
+}
\ No newline at end of file