From 4ec80159499917973f8f56ae06878f3a62b0d962 Mon Sep 17 00:00:00 2001 From: Roshan Thomas Date: Wed, 6 Apr 2016 18:22:46 -0400 Subject: [PATCH 01/76] Spring HATEOAS support --- spring-security-rest/pom.xml | 8 +++ .../baeldung/persistence/model/Customer.java | 56 +++++++++++++++ .../org/baeldung/persistence/model/Order.java | 50 ++++++++++++++ .../web/controller/CustomerController.java | 53 ++++++++++++++ .../baeldung/web/service/CustomerService.java | 18 +++++ .../web/service/CustomerServiceImpl.java | 69 +++++++++++++++++++ 6 files changed, 254 insertions(+) create mode 100644 spring-security-rest/src/main/java/org/baeldung/persistence/model/Customer.java create mode 100644 spring-security-rest/src/main/java/org/baeldung/persistence/model/Order.java create mode 100644 spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java create mode 100644 spring-security-rest/src/main/java/org/baeldung/web/service/CustomerService.java create mode 100644 spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java diff --git a/spring-security-rest/pom.xml b/spring-security-rest/pom.xml index 48791a83c1..5928a41530 100644 --- a/spring-security-rest/pom.xml +++ b/spring-security-rest/pom.xml @@ -76,6 +76,14 @@ spring-webmvc ${org.springframework.version} + + + + + org.springframework.hateoas + spring-hateoas + 0.19.0.RELEASE + diff --git a/spring-security-rest/src/main/java/org/baeldung/persistence/model/Customer.java b/spring-security-rest/src/main/java/org/baeldung/persistence/model/Customer.java new file mode 100644 index 0000000000..16b482ecf6 --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/persistence/model/Customer.java @@ -0,0 +1,56 @@ +package org.baeldung.persistence.model; + +import java.util.Map; + +import org.springframework.hateoas.ResourceSupport; + +public class Customer extends ResourceSupport { + private String customerId; + private String customerName; + private String companyName; + private Map orders; + + public Customer() { + super(); + } + + public Customer(final String customerId, final String customerName, final String companyName) { + super(); + this.customerId = customerId; + this.customerName = customerName; + this.companyName = companyName; + } + + public String getCustomerId() { + return customerId; + } + + public void setCustomerId(final String customerId) { + this.customerId = customerId; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(final String customerName) { + this.customerName = customerName; + } + + public String getCompanyName() { + return companyName; + } + + public void setCompanyName(final String companyName) { + this.companyName = companyName; + } + + public Map getOrders() { + return orders; + } + + public void setOrders(final Map orders) { + this.orders = orders; + } + +} diff --git a/spring-security-rest/src/main/java/org/baeldung/persistence/model/Order.java b/spring-security-rest/src/main/java/org/baeldung/persistence/model/Order.java new file mode 100644 index 0000000000..ca551423e8 --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/persistence/model/Order.java @@ -0,0 +1,50 @@ +package org.baeldung.persistence.model; + +import org.springframework.hateoas.ResourceSupport; + +public class Order extends ResourceSupport { + private String orderId; + private double price; + private int quantity; + + public Order() { + super(); + } + + public Order(final String orderId, final double price, final int quantity) { + super(); + this.orderId = orderId; + this.price = price; + this.quantity = quantity; + } + + public String getOrderId() { + return orderId; + } + + public void setOrderId(final String orderId) { + this.orderId = orderId; + } + + public double getPrice() { + return price; + } + + public void setPrice(final double price) { + this.price = price; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(final int quantity) { + this.quantity = quantity; + } + + @Override + public String toString() { + return "Order [orderId=" + orderId + ", price=" + price + ", quantity=" + quantity + "]"; + } + +} \ No newline at end of file diff --git a/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java b/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java new file mode 100644 index 0000000000..ffc3921623 --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java @@ -0,0 +1,53 @@ +package org.baeldung.web.controller; + +import java.util.List; + +import org.baeldung.persistence.model.Customer; +import org.baeldung.persistence.model.Order; +import org.baeldung.web.service.CustomerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.mvc.ControllerLinkBuilder; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CustomerController { + @Autowired + private CustomerService customerService; + + @RequestMapping(value = "/customer/{customerId}", method = RequestMethod.GET) + public Customer getCustomerById(@PathVariable final String customerId) { + return customerService.getCustomerDetail(customerId); + } + + @RequestMapping(value = "/customer/{customerId}/{orderId}", method = RequestMethod.GET) + public Order getOrderByIdForCustomer(@PathVariable final String customerId, @PathVariable final String orderId) { + return customerService.getOrderByIdForCustomer(customerId, orderId); + } + + @RequestMapping(value = "/customer/{customerId}/orders", method = RequestMethod.GET) + public List getOrdersForCustomer(@PathVariable final String customerId) { + return customerService.getAllOrdersForCustomer(customerId); + } + + @RequestMapping(value = "/customers", method = RequestMethod.GET) + public List getAllCustomers() { + final List allCustomers = customerService.allCustomers(); + for (final Customer customer : allCustomers) { + final Link selfLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getCustomerById(customer.getCustomerId())).withSelfRel(); + customer.add(selfLink); + final Link ordersLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getOrdersForCustomer(customer.getCustomerId())).withRel("allOrders"); + customer.add(ordersLink); + + for (final Order order : customer.getOrders().values()) { + final Link orderLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getOrderByIdForCustomer(customer.getCustomerId(), order.getOrderId())).withRel("order"); + order.add(orderLink); + } + } + return allCustomers; + } + +} diff --git a/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerService.java b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerService.java new file mode 100644 index 0000000000..abad3ac0c6 --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerService.java @@ -0,0 +1,18 @@ +package org.baeldung.web.service; + +import java.util.List; + +import org.baeldung.persistence.model.Customer; +import org.baeldung.persistence.model.Order; + +public interface CustomerService { + + List allCustomers(); + + Customer getCustomerDetail(final String id); + + List getAllOrdersForCustomer(String customerId); + + Order getOrderByIdForCustomer(String customerId, String orderId); + +} diff --git a/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java new file mode 100644 index 0000000000..621abcdf1f --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java @@ -0,0 +1,69 @@ +package org.baeldung.web.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.baeldung.persistence.model.Customer; +import org.baeldung.persistence.model.Order; +import org.springframework.stereotype.Service; + +@Service +public class CustomerServiceImpl implements CustomerService { + + private HashMap customerMap; + private HashMap customerOneOrderMap; + private HashMap customerTwoOrderMap; + + public CustomerServiceImpl() { + + customerMap = new HashMap<>(); + customerOneOrderMap = new HashMap<>(); + customerTwoOrderMap = new HashMap<>(); + + final Customer customerOne = new Customer("10A", "Jane", "ABC Company"); + final Customer customerTwo = new Customer("20B", "Bob", "XYZ Company"); + + customerOneOrderMap.put("001A", new Order("001A", 150.00, 25)); + customerOneOrderMap.put("002A", new Order("002A", 250.00, 15)); + + customerTwoOrderMap.put("002B", new Order("002B", 550.00, 325)); + customerTwoOrderMap.put("002B", new Order("002B", 450.00, 525)); + + customerOne.setOrders(customerOneOrderMap); + customerTwo.setOrders(customerTwoOrderMap); + customerMap.put("10A", customerOne); + customerMap.put("20B", customerTwo); + + } + + @Override + public List allCustomers() { + return new ArrayList<>(customerMap.values()); + } + + @Override + public Customer getCustomerDetail(final String customerId) { + return customerMap.get(customerId); + } + + @Override + public List getAllOrdersForCustomer(final String customerId) { + return new ArrayList<>(customerMap.get(customerId).getOrders().values()); + } + + @Override + public Order getOrderByIdForCustomer(final String customerId, final String orderId) { + + final List orders = (List) customerMap.get(customerId).getOrders().values(); + Order selectedOrder = null; + for (final Order order : orders) { + if (order.getId().equals(orderId)) { + selectedOrder = order; + } + } + return selectedOrder; + + } + +} From cdeb7147b4ff6104c8e0ed650b2ce61a688f4c70 Mon Sep 17 00:00:00 2001 From: Roshan Thomas Date: Wed, 6 Apr 2016 23:45:35 -0400 Subject: [PATCH 02/76] Spring HATEOAS support - multiple links --- .../main/java/org/baeldung/spring/SecurityJavaConfig.java | 2 ++ .../org/baeldung/web/controller/CustomerController.java | 7 ++++--- .../java/org/baeldung/web/service/CustomerServiceImpl.java | 5 +++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/spring-security-rest/src/main/java/org/baeldung/spring/SecurityJavaConfig.java b/spring-security-rest/src/main/java/org/baeldung/spring/SecurityJavaConfig.java index 9abe604def..bb51f5fd17 100644 --- a/spring-security-rest/src/main/java/org/baeldung/spring/SecurityJavaConfig.java +++ b/spring-security-rest/src/main/java/org/baeldung/spring/SecurityJavaConfig.java @@ -43,6 +43,8 @@ public class SecurityJavaConfig extends WebSecurityConfigurerAdapter { .and() .authorizeRequests() .antMatchers("/api/csrfAttacker*").permitAll() + .antMatchers("/api/customers**").permitAll() + .antMatchers("/api/customer/**").permitAll() .antMatchers("/api/**").authenticated() .and() .formLogin() diff --git a/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java b/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java index ffc3921623..ac63a81d97 100644 --- a/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java +++ b/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java @@ -39,9 +39,10 @@ public class CustomerController { for (final Customer customer : allCustomers) { final Link selfLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getCustomerById(customer.getCustomerId())).withSelfRel(); customer.add(selfLink); - final Link ordersLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getOrdersForCustomer(customer.getCustomerId())).withRel("allOrders"); - customer.add(ordersLink); - + if (customer.getOrders().values().size() > 0) { + final Link ordersLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getOrdersForCustomer(customer.getCustomerId())).withRel("allOrders"); + customer.add(ordersLink); + } for (final Order order : customer.getOrders().values()) { final Link orderLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getOrderByIdForCustomer(customer.getCustomerId(), order.getOrderId())).withRel("order"); order.add(orderLink); diff --git a/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java index 621abcdf1f..d41b532f78 100644 --- a/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java +++ b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java @@ -14,15 +14,18 @@ public class CustomerServiceImpl implements CustomerService { private HashMap customerMap; private HashMap customerOneOrderMap; private HashMap customerTwoOrderMap; + private HashMap customerThreeOrderMap; public CustomerServiceImpl() { customerMap = new HashMap<>(); customerOneOrderMap = new HashMap<>(); customerTwoOrderMap = new HashMap<>(); + customerThreeOrderMap = new HashMap<>(); final Customer customerOne = new Customer("10A", "Jane", "ABC Company"); final Customer customerTwo = new Customer("20B", "Bob", "XYZ Company"); + final Customer customerThree = new Customer("30C", "Tim", "CKV Company"); customerOneOrderMap.put("001A", new Order("001A", 150.00, 25)); customerOneOrderMap.put("002A", new Order("002A", 250.00, 15)); @@ -32,8 +35,10 @@ public class CustomerServiceImpl implements CustomerService { customerOne.setOrders(customerOneOrderMap); customerTwo.setOrders(customerTwoOrderMap); + customerThree.setOrders(customerThreeOrderMap); customerMap.put("10A", customerOne); customerMap.put("20B", customerTwo); + customerMap.put("30C", customerThree); } From c6575d0bc50ef2e91cbda29d5a590fa3a1722138 Mon Sep 17 00:00:00 2001 From: RoshanThomas Date: Wed, 6 Apr 2016 23:50:08 -0400 Subject: [PATCH 03/76] formating Spring HATEOAS dependency --- spring-security-rest/pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-security-rest/pom.xml b/spring-security-rest/pom.xml index 5928a41530..5973f8fa5e 100644 --- a/spring-security-rest/pom.xml +++ b/spring-security-rest/pom.xml @@ -79,11 +79,11 @@ - - org.springframework.hateoas - spring-hateoas - 0.19.0.RELEASE - + + org.springframework.hateoas + spring-hateoas + 0.19.0.RELEASE + @@ -337,4 +337,4 @@ - \ No newline at end of file + From b05648e02f43516dd4242bad44676fb4dd825266 Mon Sep 17 00:00:00 2001 From: oborkovskyi Date: Thu, 7 Apr 2016 17:15:34 +0300 Subject: [PATCH 04/76] Added spel project --- spring-spel/pom.xml | 56 +++++ .../com/baeldung/spring/spel/SpelProgram.java | 17 ++ .../com/baeldung/spring/spel/entity/Car.java | 91 +++++++ .../baeldung/spring/spel/entity/CarPark.java | 48 ++++ .../baeldung/spring/spel/entity/Engine.java | 47 ++++ .../spring/spel/examples/SpelArithmetic.java | 131 ++++++++++ .../spring/spel/examples/SpelCollections.java | 59 +++++ .../spring/spel/examples/SpelConditional.java | 49 ++++ .../spring/spel/examples/SpelLogical.java | 82 +++++++ .../spring/spel/examples/SpelOperators.java | 204 ++++++++++++++++ .../spring/spel/examples/SpelParser.java | 35 +++ .../spring/spel/examples/SpelRegex.java | 74 ++++++ .../spring/spel/examples/SpelRelational.java | 229 ++++++++++++++++++ .../src/main/resources/applicationContext.xml | 8 + 14 files changed, 1130 insertions(+) create mode 100644 spring-spel/pom.xml create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/entity/Car.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/entity/CarPark.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/entity/Engine.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelArithmetic.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelCollections.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelConditional.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelLogical.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelOperators.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelParser.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelRegex.java create mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelRelational.java create mode 100644 spring-spel/src/main/resources/applicationContext.xml diff --git a/spring-spel/pom.xml b/spring-spel/pom.xml new file mode 100644 index 0000000000..d7a9f9f7f3 --- /dev/null +++ b/spring-spel/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + com.baeldung + guava + 1.0-SNAPSHOT + + + + com.google.guava + guava + 19.0 + + + junit + junit + 4.12 + + + org.hamcrest + hamcrest-all + 1.3 + + + org.springframework + spring-core + 4.0.6.RELEASE + + + org.springframework + spring-context + 4.0.6.RELEASE + + + + + + maven-compiler-plugin + 3.3 + + true + true + 1.8 + 1.8 + UTF-8 + true + true + + + + + + \ No newline at end of file diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java b/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java new file mode 100644 index 0000000000..1b1e1e6ae4 --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java @@ -0,0 +1,17 @@ +package com.baeldung.spring.spel; + +import com.baeldung.spring.spel.examples.*; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class SpelProgram { + public static void main(String[] args) { + ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); + SpelConditional spelCollections = (SpelConditional) context.getBean("spelConditional"); + + // Here you can choose which bean do you want to load insted of spelConditional: spelCollections, spelLogical, etc. + + System.out.println(spelCollections); + } + +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/entity/Car.java b/spring-spel/src/main/java/com/baeldung/spring/spel/entity/Car.java new file mode 100644 index 0000000000..6ad665bcba --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/entity/Car.java @@ -0,0 +1,91 @@ +package com.baeldung.spring.spel.entity; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("someCar") +public class Car { + @Value("Some make") + private String make; + @Value("Some model") + private String model; + @Value("#{engine}") + private Engine engine; + @Value("#{engine.horsePower}") + private int horsePower; + @Value("2007") + private int yearOfProduction; + @Value("#{engine.capacity >= 3000}") + private boolean isBigEngine; + @Value("#{someCar.yearOfProduction > 2016}") + private boolean isNewCar; + + public String getMake() { + return make; + } + + public void setMake(String make) { + this.make = make; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public Engine getEngine() { + return engine; + } + + public void setEngine(Engine engine) { + this.engine = engine; + } + + public int getHorsePower() { + return horsePower; + } + + public void setHorsePower(int horsePower) { + this.horsePower = horsePower; + } + + public int getYearOfProduction() { + return yearOfProduction; + } + + public void setYearOfProduction(int yearOfProduction) { + this.yearOfProduction = yearOfProduction; + } + + public boolean isBigEngine() { + return isBigEngine; + } + + public void setBigEngine(boolean bigEngine) { + isBigEngine = bigEngine; + } + + public boolean isNewCar() { + return isNewCar; + } + + public void setNewCar(boolean newCar) { + isNewCar = newCar; + } + + @Override + public String toString() { + return "Car{" + + "make='" + make + '\'' + + ", model='" + model + '\'' + + ", engine=" + engine + + ", horsePower=" + horsePower + + ", yearOfProduction=" + yearOfProduction + + ", isBigEngine=" + isBigEngine + + ", isNewCar=" + isNewCar + + '}'; + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/entity/CarPark.java b/spring-spel/src/main/java/com/baeldung/spring/spel/entity/CarPark.java new file mode 100644 index 0000000000..6475405383 --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/entity/CarPark.java @@ -0,0 +1,48 @@ +package com.baeldung.spring.spel.entity; + +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component("carPark") +public class CarPark { + private List cars = new ArrayList<>(); + private Map carsByDriver = new HashMap<>(); + + public CarPark() { + Car model1 = new Car(); + model1.setMake("Good company"); + model1.setModel("Model1"); + model1.setYearOfProduction(1998); + + Car model2 = new Car(); + model2.setMake("Good company"); + model2.setModel("Model2"); + model2.setYearOfProduction(2005); + + cars.add(model1); + cars.add(model2); + + carsByDriver.put("Driver1", model1); + carsByDriver.put("Driver2", model2); + } + + public List getCars() { + return cars; + } + + public void setCars(List cars) { + this.cars = cars; + } + + public Map getCarsByDriver() { + return carsByDriver; + } + + public void setCarsByDriver(Map carsByDriver) { + this.carsByDriver = carsByDriver; + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/entity/Engine.java b/spring-spel/src/main/java/com/baeldung/spring/spel/entity/Engine.java new file mode 100644 index 0000000000..9b9d125170 --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/entity/Engine.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.spel.entity; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("engine") +public class Engine { + @Value("3200") + private int capacity; + @Value("250") + private int horsePower; + @Value("6") + private int numberOfCylinders; + + public int getCapacity() { + return capacity; + } + + public void setCapacity(int capacity) { + this.capacity = capacity; + } + + public int getHorsePower() { + return horsePower; + } + + public void setHorsePower(int horsePower) { + this.horsePower = horsePower; + } + + public int getNumberOfCylinders() { + return numberOfCylinders; + } + + public void setNumberOfCylinders(int numberOfCylinders) { + this.numberOfCylinders = numberOfCylinders; + } + + @Override + public String toString() { + return "Engine{" + + "capacity=" + capacity + + ", horsePower=" + horsePower + + ", numberOfCylinders=" + numberOfCylinders + + '}'; + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelArithmetic.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelArithmetic.java new file mode 100644 index 0000000000..ba21c87931 --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelArithmetic.java @@ -0,0 +1,131 @@ +package com.baeldung.spring.spel.examples; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("spelArithmetic") +public class SpelArithmetic { + @Value("#{19 + 1}") + private double add; + + @Value("#{'Some string ' + 'plus other string'}") + private String addString; + + @Value("#{20 - 1}") + private double subtract; + + @Value("#{10 * 2}") + private double multiply; + + @Value("#{36 / 2}") + private double divide; + @Value("#{36 div 2}") + private double divideAlphabetic; + + @Value("#{37 % 10}") + private double modulo; + @Value("#{37 mod 10}") + private double moduloAlphabetic; + + @Value("#{2 ^ 9}") + private double powerOf; + + @Value("#{(2 + 2) * 2 + 9}") + private double brackets; + + public double getAdd() { + return add; + } + + public void setAdd(double add) { + this.add = add; + } + + public String getAddString() { + return addString; + } + + public void setAddString(String addString) { + this.addString = addString; + } + + public double getSubtract() { + return subtract; + } + + public void setSubtract(double subtract) { + this.subtract = subtract; + } + + public double getMultiply() { + return multiply; + } + + public void setMultiply(double multiply) { + this.multiply = multiply; + } + + public double getDivide() { + return divide; + } + + public void setDivide(double divide) { + this.divide = divide; + } + + public double getDivideAlphabetic() { + return divideAlphabetic; + } + + public void setDivideAlphabetic(double divideAlphabetic) { + this.divideAlphabetic = divideAlphabetic; + } + + public double getModulo() { + return modulo; + } + + public void setModulo(double modulo) { + this.modulo = modulo; + } + + public double getModuloAlphabetic() { + return moduloAlphabetic; + } + + public void setModuloAlphabetic(double moduloAlphabetic) { + this.moduloAlphabetic = moduloAlphabetic; + } + + public double getPowerOf() { + return powerOf; + } + + public void setPowerOf(double powerOf) { + this.powerOf = powerOf; + } + + public double getBrackets() { + return brackets; + } + + public void setBrackets(double brackets) { + this.brackets = brackets; + } + + @Override + public String toString() { + return "SpelArithmetic{" + + "add=" + add + + ", addString='" + addString + '\'' + + ", subtract=" + subtract + + ", multiply=" + multiply + + ", divide=" + divide + + ", divideAlphabetic=" + divideAlphabetic + + ", modulo=" + modulo + + ", moduloAlphabetic=" + moduloAlphabetic + + ", powerOf=" + powerOf + + ", brackets=" + brackets + + '}'; + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelCollections.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelCollections.java new file mode 100644 index 0000000000..6c606bbd75 --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelCollections.java @@ -0,0 +1,59 @@ +package com.baeldung.spring.spel.examples; + +import com.baeldung.spring.spel.entity.Car; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("spelCollections") +public class SpelCollections { + @Value("#{carPark.carsByDriver['Driver1']}") + private Car driver1Car; + @Value("#{carPark.carsByDriver['Driver2']}") + private Car driver2Car; + @Value("#{carPark.cars[0]}") + private Car firstCarInPark; + @Value("#{carPark.cars.size()}") + private Integer numberOfCarsInPark; + + public Car getDriver1Car() { + return driver1Car; + } + + public void setDriver1Car(Car driver1Car) { + this.driver1Car = driver1Car; + } + + public Car getDriver2Car() { + return driver2Car; + } + + public void setDriver2Car(Car driver2Car) { + this.driver2Car = driver2Car; + } + + public Car getFirstCarInPark() { + return firstCarInPark; + } + + public void setFirstCarInPark(Car firstCarInPark) { + this.firstCarInPark = firstCarInPark; + } + + public Integer getNumberOfCarsInPark() { + return numberOfCarsInPark; + } + + public void setNumberOfCarsInPark(Integer numberOfCarsInPark) { + this.numberOfCarsInPark = numberOfCarsInPark; + } + + @Override + public String toString() { + return "[" + + "driver1Car=" + driver1Car + + ", driver2Car=" + driver2Car + + ", firstCarInPark=" + firstCarInPark + + ", numberOfCarsInPark=" + numberOfCarsInPark + + ']'; + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelConditional.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelConditional.java new file mode 100644 index 0000000000..6e255c0425 --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelConditional.java @@ -0,0 +1,49 @@ +package com.baeldung.spring.spel.examples; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("spelConditional") +public class SpelConditional { + @Value("#{false ? 'There was true value' : 'Something went wrong. There was false value'}") + private String ternary; + + @Value("#{someCar.model != null ? someCar.model : 'Unknown model'}") + private String ternary2; + + @Value("#{someCar.model?:'Unknown model'}") + private String elvis; + + public String getTernary() { + return ternary; + } + + public void setTernary(String ternary) { + this.ternary = ternary; + } + + public String getTernary2() { + return ternary2; + } + + public void setTernary2(String ternary2) { + this.ternary2 = ternary2; + } + + public String getElvis() { + return elvis; + } + + public void setElvis(String elvis) { + this.elvis = elvis; + } + + @Override + public String toString() { + return "SpelConditional{" + + "ternary='" + ternary + '\'' + + ", ternary2='" + ternary2 + '\'' + + ", elvis='" + elvis + '\'' + + '}'; + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelLogical.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelLogical.java new file mode 100644 index 0000000000..09b4e35c9d --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelLogical.java @@ -0,0 +1,82 @@ +package com.baeldung.spring.spel.examples; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("spelLogical") +public class SpelLogical { + @Value("#{250 > 200 && 200 < 4000}") + private boolean and; + @Value("#{250 > 200 and 200 < 4000}") + private boolean andAlphabetic; + + @Value("#{400 > 300 || 150 < 100}") + private boolean or; + @Value("#{400 > 300 or 150 < 100}") + private boolean orAlphabetic; + + @Value("#{!true}") + private boolean not; + @Value("#{not true}") + private boolean notAlphabetic; + + public boolean isAnd() { + return and; + } + + public void setAnd(boolean and) { + this.and = and; + } + + public boolean isAndAlphabetic() { + return andAlphabetic; + } + + public void setAndAlphabetic(boolean andAlphabetic) { + this.andAlphabetic = andAlphabetic; + } + + public boolean isOr() { + return or; + } + + public void setOr(boolean or) { + this.or = or; + } + + public boolean isOrAlphabetic() { + return orAlphabetic; + } + + public void setOrAlphabetic(boolean orAlphabetic) { + this.orAlphabetic = orAlphabetic; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean not) { + this.not = not; + } + + public boolean isNotAlphabetic() { + return notAlphabetic; + } + + public void setNotAlphabetic(boolean notAlphabetic) { + this.notAlphabetic = notAlphabetic; + } + + @Override + public String toString() { + return "SpelLogical{" + + "and=" + and + + ", andAlphabetic=" + andAlphabetic + + ", or=" + or + + ", orAlphabetic=" + orAlphabetic + + ", not=" + not + + ", notAlphabetic=" + notAlphabetic + + '}'; + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelOperators.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelOperators.java new file mode 100644 index 0000000000..884ef73f6c --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelOperators.java @@ -0,0 +1,204 @@ +package com.baeldung.spring.spel.examples; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("spelOperators") +public class SpelOperators { + + @Value("#{1 == 1}") + private boolean equal; + + @Value("#{1 != 1}") + private boolean notEqual; + + @Value("#{1 < 1}") + private boolean lessThan; + + @Value("#{1 <= 1}") + private boolean lessThanOrEqual; + + @Value("#{1 > 1}") + private boolean greaterThan; + + @Value("#{someCar.engine.numberOfCylinders >= 6}") + private boolean greaterThanOrEqual; + + @Value("#{someCar.horsePower == 250 and someCar.engine.capacity < 4000}") + private boolean and; + + @Value("#{someCar.horsePower > 300 or someCar.engine.capacity > 3000}") + private boolean or; + + @Value("#{!(someCar.engine.numberOfCylinders == 6)}") + private boolean not; + + @Value("#{1 + 1}") + private double add; + + @Value("#{someCar.model + ' manufactored by ' + someCar.make}") + private String addString; + + @Value("#{1 - 1}") + private double subtraction; + + @Value("#{1 * 1}") + private double multiply; + + @Value("#{10 / 2}") + private double divide; + + @Value("#{37 % 10}") + private double modulo; + + @Value("#{2 ^ 2}") + private double powerOf; + + public boolean isEqual() { + return equal; + } + + public void setEqual(boolean equal) { + this.equal = equal; + } + + public boolean isNotEqual() { + return notEqual; + } + + public void setNotEqual(boolean notEqual) { + this.notEqual = notEqual; + } + + public boolean isLessThan() { + return lessThan; + } + + public void setLessThan(boolean lessThan) { + this.lessThan = lessThan; + } + + public boolean isLessThanOrEqual() { + return lessThanOrEqual; + } + + public void setLessThanOrEqual(boolean lessThanOrEqual) { + this.lessThanOrEqual = lessThanOrEqual; + } + + public boolean isGreaterThan() { + return greaterThan; + } + + public void setGreaterThan(boolean greaterThan) { + this.greaterThan = greaterThan; + } + + public boolean isGreaterThanOrEqual() { + return greaterThanOrEqual; + } + + public void setGreaterThanOrEqual(boolean greaterThanOrEqual) { + this.greaterThanOrEqual = greaterThanOrEqual; + } + + public boolean isAnd() { + return and; + } + + public void setAnd(boolean and) { + this.and = and; + } + + public boolean isOr() { + return or; + } + + public void setOr(boolean or) { + this.or = or; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean not) { + this.not = not; + } + + public double getAdd() { + return add; + } + + public void setAdd(double add) { + this.add = add; + } + + public String getAddString() { + return addString; + } + + public void setAddString(String addString) { + this.addString = addString; + } + + public double getSubtraction() { + return subtraction; + } + + public void setSubtraction(double subtraction) { + this.subtraction = subtraction; + } + + public double getMultiply() { + return multiply; + } + + public void setMultiply(double multiply) { + this.multiply = multiply; + } + + public double getDivide() { + return divide; + } + + public void setDivide(double divide) { + this.divide = divide; + } + + public double getModulo() { + return modulo; + } + + public void setModulo(double modulo) { + this.modulo = modulo; + } + + public double getPowerOf() { + return powerOf; + } + + public void setPowerOf(double powerOf) { + this.powerOf = powerOf; + } + + @Override + public String toString() { + return "[equal=" + equal + ", " + + "notEqual=" + notEqual + ", " + + "lessThan=" + lessThan + ", " + + "lessThanOrEqual=" + lessThanOrEqual + ", " + + "greaterThan=" + greaterThan + ", " + + "greaterThanOrEqual=" + greaterThanOrEqual + ", " + + "and=" + and + ", " + + "or=" + or + ", " + + "not=" + not + ", " + + "add=" + add + ", " + + "addString=" + addString + ", " + + "subtraction=" + subtraction + ", " + + "multiply=" + multiply + ", " + + "divide=" + divide + ", " + + "modulo=" + modulo + ", " + + "powerOf=" + powerOf + "]"; + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelParser.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelParser.java new file mode 100644 index 0000000000..ed34afddbd --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelParser.java @@ -0,0 +1,35 @@ +package com.baeldung.spring.spel.examples; + +import com.baeldung.spring.spel.entity.Car; +import com.baeldung.spring.spel.entity.CarPark; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.SpelParserConfiguration; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +import java.util.ArrayList; +import java.util.List; + +public class SpelParser { + public static void main(String[] args) { + Car car = new Car(); + car.setMake("Good manufacturer"); + car.setModel("Model 3"); + car.setYearOfProduction(2014); + + CarPark carPark = new CarPark(); + + SpelParserConfiguration config = new SpelParserConfiguration(true, true); + + StandardEvaluationContext context = new StandardEvaluationContext(carPark); + + ExpressionParser expressionParser = new SpelExpressionParser(config); + expressionParser.parseExpression("cars[0]").setValue(context, car); + + Car result = carPark.getCars().get(0); + + System.out.println(result); + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelRegex.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelRegex.java new file mode 100644 index 0000000000..a08142ca1c --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelRegex.java @@ -0,0 +1,74 @@ +package com.baeldung.spring.spel.examples; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("spelRegex") +public class SpelRegex { + + @Value("#{100 matches '\\d+' }") + private boolean validNumericStringResult; + + @Value("#{'100fghdjf' matches '\\d+' }") + private boolean invalidNumericStringResult; + + @Value("#{'valid alphabetic string' matches '[a-zA-Z\\s]+' }") + private boolean validAlphabeticStringResult; + + @Value("#{'invalid alphabetic string #$1' matches '[a-zA-Z\\s]+' }") + private boolean invalidAlphabeticStringResult; + + @Value("#{engine.horsePower matches '\\d+'}") + private boolean validFormatOfHorsePower; + + public boolean isValidNumericStringResult() { + return validNumericStringResult; + } + + public void setValidNumericStringResult(boolean validNumericStringResult) { + this.validNumericStringResult = validNumericStringResult; + } + + public boolean isInvalidNumericStringResult() { + return invalidNumericStringResult; + } + + public void setInvalidNumericStringResult(boolean invalidNumericStringResult) { + this.invalidNumericStringResult = invalidNumericStringResult; + } + + public boolean isValidAlphabeticStringResult() { + return validAlphabeticStringResult; + } + + public void setValidAlphabeticStringResult(boolean validAlphabeticStringResult) { + this.validAlphabeticStringResult = validAlphabeticStringResult; + } + + public boolean isInvalidAlphabeticStringResult() { + return invalidAlphabeticStringResult; + } + + public void setInvalidAlphabeticStringResult(boolean invalidAlphabeticStringResult) { + this.invalidAlphabeticStringResult = invalidAlphabeticStringResult; + } + + public boolean isValidFormatOfHorsePower() { + return validFormatOfHorsePower; + } + + public void setValidFormatOfHorsePower(boolean validFormatOfHorsePower) { + this.validFormatOfHorsePower = validFormatOfHorsePower; + } + + @Override + public String toString() { + return "SpelRegex{" + + "validNumericStringResult=" + validNumericStringResult + + ", invalidNumericStringResult=" + invalidNumericStringResult + + ", validAlphabeticStringResult=" + validAlphabeticStringResult + + ", invalidAlphabeticStringResult=" + invalidAlphabeticStringResult + + ", validFormatOfHorsePower=" + validFormatOfHorsePower + + '}'; + } +} diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelRelational.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelRelational.java new file mode 100644 index 0000000000..b811595429 --- /dev/null +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelRelational.java @@ -0,0 +1,229 @@ +package com.baeldung.spring.spel.examples; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("spelRelational") +public class SpelRelational { + @Value("#{1 == 1}") + private boolean equal; + + @Value("#{1 eq 1}") + private boolean equalAlphabetic; + + @Value("#{1 != 1}") + private boolean notEqual; + + @Value("#{1 ne 1}") + private boolean notEqualAlphabetic; + + @Value("#{1 < 1}") + private boolean lessThan; + + @Value("#{1 lt 1}") + private boolean lessThanAlphabetic; + + @Value("#{1 <= 1}") + private boolean lessThanOrEqual; + + @Value("#{1 le 1}") + private boolean lessThanOrEqualAlphabetic; + + @Value("#{1 > 1}") + private boolean greaterThan; + + @Value("#{1 gt 1}") + private boolean greaterThanAlphabetic; + + @Value("#{9 >= 6}") + private boolean greaterThanOrEqual; + + @Value("#{9 ge 6}") + private boolean greaterThanOrEqualAlphabetic; + + @Value("#{250 > 200 && 200 < 4000}") + private boolean and; + + @Value("#{250 > 200 and 200 < 4000}") + private boolean andAlphabetic; + + @Value("#{400 > 300 || 150 < 100}") + private boolean or; + + @Value("#{400 > 300 or 150 < 100}") + private boolean orAlphabetic; + + @Value("#{!true}") + private boolean not; + + @Value("#{not true}") + private boolean notAlphabetic; + + public boolean isEqual() { + return equal; + } + + public void setEqual(boolean equal) { + this.equal = equal; + } + + public boolean isEqualAlphabetic() { + return equalAlphabetic; + } + + public void setEqualAlphabetic(boolean equalAlphabetic) { + this.equalAlphabetic = equalAlphabetic; + } + + public boolean isNotEqual() { + return notEqual; + } + + public void setNotEqual(boolean notEqual) { + this.notEqual = notEqual; + } + + public boolean isNotEqualAlphabetic() { + return notEqualAlphabetic; + } + + public void setNotEqualAlphabetic(boolean notEqualAlphabetic) { + this.notEqualAlphabetic = notEqualAlphabetic; + } + + public boolean isLessThan() { + return lessThan; + } + + public void setLessThan(boolean lessThan) { + this.lessThan = lessThan; + } + + public boolean isLessThanAlphabetic() { + return lessThanAlphabetic; + } + + public void setLessThanAlphabetic(boolean lessThanAlphabetic) { + this.lessThanAlphabetic = lessThanAlphabetic; + } + + public boolean isLessThanOrEqual() { + return lessThanOrEqual; + } + + public void setLessThanOrEqual(boolean lessThanOrEqual) { + this.lessThanOrEqual = lessThanOrEqual; + } + + public boolean isLessThanOrEqualAlphabetic() { + return lessThanOrEqualAlphabetic; + } + + public void setLessThanOrEqualAlphabetic(boolean lessThanOrEqualAlphabetic) { + this.lessThanOrEqualAlphabetic = lessThanOrEqualAlphabetic; + } + + public boolean isGreaterThan() { + return greaterThan; + } + + public void setGreaterThan(boolean greaterThan) { + this.greaterThan = greaterThan; + } + + public boolean isGreaterThanAlphabetic() { + return greaterThanAlphabetic; + } + + public void setGreaterThanAlphabetic(boolean greaterThanAlphabetic) { + this.greaterThanAlphabetic = greaterThanAlphabetic; + } + + public boolean isGreaterThanOrEqual() { + return greaterThanOrEqual; + } + + public void setGreaterThanOrEqual(boolean greaterThanOrEqual) { + this.greaterThanOrEqual = greaterThanOrEqual; + } + + public boolean isGreaterThanOrEqualAlphabetic() { + return greaterThanOrEqualAlphabetic; + } + + public void setGreaterThanOrEqualAlphabetic(boolean greaterThanOrEqualAlphabetic) { + this.greaterThanOrEqualAlphabetic = greaterThanOrEqualAlphabetic; + } + + public boolean isAnd() { + return and; + } + + public void setAnd(boolean and) { + this.and = and; + } + + public boolean isAndAlphabetic() { + return andAlphabetic; + } + + public void setAndAlphabetic(boolean andAlphabetic) { + this.andAlphabetic = andAlphabetic; + } + + public boolean isOr() { + return or; + } + + public void setOr(boolean or) { + this.or = or; + } + + public boolean isOrAlphabetic() { + return orAlphabetic; + } + + public void setOrAlphabetic(boolean orAlphabetic) { + this.orAlphabetic = orAlphabetic; + } + + public boolean isNot() { + return not; + } + + public void setNot(boolean not) { + this.not = not; + } + + public boolean isNotAlphabetic() { + return notAlphabetic; + } + + public void setNotAlphabetic(boolean notAlphabetic) { + this.notAlphabetic = notAlphabetic; + } + + @Override + public String toString() { + return "SpelRelational{" + + "equal=" + equal + + ", equalAlphabetic=" + equalAlphabetic + + ", notEqual=" + notEqual + + ", notEqualAlphabetic=" + notEqualAlphabetic + + ", lessThan=" + lessThan + + ", lessThanAlphabetic=" + lessThanAlphabetic + + ", lessThanOrEqual=" + lessThanOrEqual + + ", lessThanOrEqualAlphabetic=" + lessThanOrEqualAlphabetic + + ", greaterThan=" + greaterThan + + ", greaterThanAlphabetic=" + greaterThanAlphabetic + + ", greaterThanOrEqual=" + greaterThanOrEqual + + ", greaterThanOrEqualAlphabetic=" + greaterThanOrEqualAlphabetic + + ", and=" + and + + ", andAlphabetic=" + andAlphabetic + + ", or=" + or + + ", orAlphabetic=" + orAlphabetic + + ", not=" + not + + ", notAlphabetic=" + notAlphabetic + + '}'; + } +} diff --git a/spring-spel/src/main/resources/applicationContext.xml b/spring-spel/src/main/resources/applicationContext.xml new file mode 100644 index 0000000000..815ac35837 --- /dev/null +++ b/spring-spel/src/main/resources/applicationContext.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file From fdcea198633029ca8a618fcaa410068a4b0d2fb8 Mon Sep 17 00:00:00 2001 From: Alex Ve Date: Thu, 7 Apr 2016 20:22:59 +0300 Subject: [PATCH 05/76] The example code for the article "A Guide to the Fork/Join Framework" --- .../forkjoin/CustomRecursiveAction.java | 46 ++++++++ .../forkjoin/CustomRecursiveTask.java | 51 +++++++++ .../com/baeldung/forkjoin/util/PoolUtil.java | 10 ++ .../com/baeldung/java8/Java8ForkJoinTest.java | 101 ++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 core-java-8/src/main/java/com/baeldung/forkjoin/CustomRecursiveAction.java create mode 100644 core-java-8/src/main/java/com/baeldung/forkjoin/CustomRecursiveTask.java create mode 100644 core-java-8/src/main/java/com/baeldung/forkjoin/util/PoolUtil.java create mode 100644 core-java-8/src/test/java/com/baeldung/java8/Java8ForkJoinTest.java diff --git a/core-java-8/src/main/java/com/baeldung/forkjoin/CustomRecursiveAction.java b/core-java-8/src/main/java/com/baeldung/forkjoin/CustomRecursiveAction.java new file mode 100644 index 0000000000..84476f8c66 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/forkjoin/CustomRecursiveAction.java @@ -0,0 +1,46 @@ +package com.baeldung.forkjoin; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveAction; + +public class CustomRecursiveAction extends RecursiveAction { + + private String workLoad = ""; + + private static final int THRESHOLD = 4; + + public CustomRecursiveAction(String workLoad) { + this.workLoad = workLoad; + } + + @Override + protected void compute() { + + if(workLoad.length() > THRESHOLD) { + ForkJoinTask.invokeAll(createSubtasks()); + } else { + processing(workLoad); + } + } + + private Collection createSubtasks() { + + List subtasks = + new ArrayList<>(); + + String partOne = workLoad.substring(0, workLoad.length()/2); + String partTwo = workLoad.substring(workLoad.length()/2, workLoad.length()); + + subtasks.add(new CustomRecursiveAction(partOne)); + subtasks.add(new CustomRecursiveAction(partTwo)); + + return subtasks; + } + + private void processing(String work) { + work.toUpperCase(); + } +} diff --git a/core-java-8/src/main/java/com/baeldung/forkjoin/CustomRecursiveTask.java b/core-java-8/src/main/java/com/baeldung/forkjoin/CustomRecursiveTask.java new file mode 100644 index 0000000000..c78dd51d27 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/forkjoin/CustomRecursiveTask.java @@ -0,0 +1,51 @@ +package com.baeldung.forkjoin; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveTask; + +public class CustomRecursiveTask extends RecursiveTask { + + private int[] arr; + + private static final int THRESHOLD = 20; + + public CustomRecursiveTask(int[] arr) { + this.arr = arr; + } + + @Override + protected Integer compute() { + + if (arr.length > THRESHOLD) { + + return ForkJoinTask.invokeAll(createSubtasks()) + .stream() + .mapToInt(ForkJoinTask::join) + .sum(); + + } else { + return processing(arr); + } + } + + private Collection createSubtasks() { + List dividedTasks = new ArrayList<>(); + dividedTasks.add(new CustomRecursiveTask( + Arrays.copyOfRange(arr, 0, arr.length / 2))); + dividedTasks.add(new CustomRecursiveTask( + Arrays.copyOfRange(arr, arr.length / 2, arr.length))); + return dividedTasks; + } + + private Integer processing(int[] arr) { + return Arrays.stream(arr) + .filter(a -> a > 10 && a < 27) + .map(a -> a * 10) + .sum(); + } +} diff --git a/core-java-8/src/main/java/com/baeldung/forkjoin/util/PoolUtil.java b/core-java-8/src/main/java/com/baeldung/forkjoin/util/PoolUtil.java new file mode 100644 index 0000000000..521616600f --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/forkjoin/util/PoolUtil.java @@ -0,0 +1,10 @@ +package com.baeldung.forkjoin.util; + + +import java.util.concurrent.ForkJoinPool; + +public class PoolUtil { + + public static ForkJoinPool forkJoinPool = new ForkJoinPool(2); + +} diff --git a/core-java-8/src/test/java/com/baeldung/java8/Java8ForkJoinTest.java b/core-java-8/src/test/java/com/baeldung/java8/Java8ForkJoinTest.java new file mode 100644 index 0000000000..d37c1c89d7 --- /dev/null +++ b/core-java-8/src/test/java/com/baeldung/java8/Java8ForkJoinTest.java @@ -0,0 +1,101 @@ +package com.baeldung.java8; + + + +import com.baeldung.forkjoin.CustomRecursiveAction; +import com.baeldung.forkjoin.CustomRecursiveTask; +import com.baeldung.forkjoin.util.PoolUtil; +import org.junit.Before; +import org.junit.Test; + +import java.util.Random; +import java.util.concurrent.ForkJoinPool; + +import static org.junit.Assert.*; + +public class Java8ForkJoinTest { + + private int[] arr; + private CustomRecursiveTask customRecursiveTask; + + @Before + public void init() { + Random random = new Random(); + arr = new int[50]; + for (int i = 0; i < arr.length; i++) { + arr[i] = random.nextInt(35); + } + customRecursiveTask = new CustomRecursiveTask(arr); + } + + + @Test + public void callPoolUtil_whenExistsAndExpectedType_thenCorrect() { + + ForkJoinPool forkJoinPool = PoolUtil.forkJoinPool; + ForkJoinPool forkJoinPoolTwo = PoolUtil.forkJoinPool; + + assertNotNull(forkJoinPool); + assertEquals(2, forkJoinPool.getParallelism()); + assertEquals(forkJoinPool, forkJoinPoolTwo); + + } + + @Test + public void callCommonPool_whenExistsAndExpectedType_thenCorrect() { + + ForkJoinPool commonPool = ForkJoinPool.commonPool(); + ForkJoinPool commonPoolTwo = ForkJoinPool.commonPool(); + + assertNotNull(commonPool); + assertEquals(commonPool, commonPoolTwo); + + } + + @Test + public void executeRecursiveAction_whenExecuted_thenCorrect() { + + CustomRecursiveAction myRecursiveAction = new CustomRecursiveAction("ddddffffgggghhhh"); + ForkJoinPool.commonPool().invoke(myRecursiveAction); + + assertTrue(myRecursiveAction.isDone()); + + } + + @Test + public void executeRecursiveTask_whenExecuted_thenCorrect() { + + ForkJoinPool forkJoinPool = ForkJoinPool.commonPool(); + + forkJoinPool.execute(customRecursiveTask); + int result = customRecursiveTask.join(); + assertTrue(customRecursiveTask.isDone()); + + forkJoinPool.submit(customRecursiveTask); + int resultTwo = customRecursiveTask.join(); + assertTrue(customRecursiveTask.isDone()); + + } + + @Test + public void executeRecursiveTaskWithFJ_whenExecuted_thenCorrect() { + + CustomRecursiveTask customRecursiveTaskFirst = new CustomRecursiveTask(arr); + CustomRecursiveTask customRecursiveTaskSecond = new CustomRecursiveTask(arr); + CustomRecursiveTask customRecursiveTaskLast = new CustomRecursiveTask(arr); + + customRecursiveTaskFirst.fork(); + customRecursiveTaskSecond.fork(); + customRecursiveTaskLast.fork(); + int result = 0; + result += customRecursiveTaskLast.join(); + result += customRecursiveTaskSecond.join(); + result += customRecursiveTaskFirst.join(); + + assertTrue(customRecursiveTaskFirst.isDone()); + assertTrue(customRecursiveTaskSecond.isDone()); + assertTrue(customRecursiveTaskLast.isDone()); + assertTrue(result != 0); + + } +} From ab3f68f1c29fed04560ba8a2eece66f403a44406 Mon Sep 17 00:00:00 2001 From: ankur-singhal Date: Fri, 8 Apr 2016 21:03:51 +0530 Subject: [PATCH 06/76] Test Cases - XStream Object to-from json Test Cases - XStream Object to-from json --- xstream/pom.xml | 6 +++ .../initializer/SimpleXstreamInitializer.java | 26 +++++----- .../baeldung/test/XStreamJettisonTest.java | 48 +++++++++++++++++++ .../test/XStreamJsonHierarchicalTest.java | 45 +++++++++++++++++ 4 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 xstream/src/test/java/com/baeldung/test/XStreamJettisonTest.java create mode 100644 xstream/src/test/java/com/baeldung/test/XStreamJsonHierarchicalTest.java diff --git a/xstream/pom.xml b/xstream/pom.xml index 8a5aec41e9..22c2ce2941 100644 --- a/xstream/pom.xml +++ b/xstream/pom.xml @@ -14,6 +14,12 @@ 1.4.5 + + org.codehaus.jettison + jettison + 1.3.7 + + junit junit diff --git a/xstream/src/main/java/com/baeldung/initializer/SimpleXstreamInitializer.java b/xstream/src/main/java/com/baeldung/initializer/SimpleXstreamInitializer.java index 5dec19d181..744b60f78e 100644 --- a/xstream/src/main/java/com/baeldung/initializer/SimpleXstreamInitializer.java +++ b/xstream/src/main/java/com/baeldung/initializer/SimpleXstreamInitializer.java @@ -1,19 +1,23 @@ package com.baeldung.initializer; import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; +import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver; public class SimpleXstreamInitializer { - private XStream xtreamInstance; + public XStream getXstreamInstance() { + XStream xtreamInstance = new XStream(); + return xtreamInstance; + } - public XStream getXstreamInstance() { - if (xtreamInstance == null) { - synchronized (SimpleXstreamInitializer.class) { - if (xtreamInstance == null) { - xtreamInstance = new XStream(); - } - } - } - return xtreamInstance; - } + public XStream getXstreamJettisonMappedInstance() { + XStream xstreamInstance = new XStream(new JettisonMappedXmlDriver()); + return xstreamInstance; + } + + public XStream getXstreamJsonHierarchicalInstance() { + XStream xstreamInstance = new XStream(new JsonHierarchicalStreamDriver()); + return xstreamInstance; + } } \ No newline at end of file diff --git a/xstream/src/test/java/com/baeldung/test/XStreamJettisonTest.java b/xstream/src/test/java/com/baeldung/test/XStreamJettisonTest.java new file mode 100644 index 0000000000..6111dfbb24 --- /dev/null +++ b/xstream/src/test/java/com/baeldung/test/XStreamJettisonTest.java @@ -0,0 +1,48 @@ +package com.baeldung.test; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.baeldung.initializer.SimpleXstreamInitializer; +import com.baeldung.pojo.ContactDetails; +import com.baeldung.pojo.Customer; +import com.baeldung.utility.SimpleDataGeneration; +import com.thoughtworks.xstream.XStream; + +public class XStreamJettisonTest { + + private Customer customer = null; + + private String dataJson = null; + + private XStream xstream = null; + + @Before + public void dataSetup() { + SimpleXstreamInitializer simpleXstreamInitializer = new SimpleXstreamInitializer(); + xstream = simpleXstreamInitializer.getXstreamJettisonMappedInstance(); + xstream.processAnnotations(Customer.class); + } + + @Test + public void convertObjectToJson() { + customer = SimpleDataGeneration.generateData(); + xstream.alias("customer" , Customer.class); + xstream.alias("contactDetails" , ContactDetails.class); + xstream.aliasField("fn" , Customer.class , "firstName"); + dataJson = xstream.toXML(customer); + System.out.println(dataJson); + Assert.assertNotNull(dataJson); + } + + @Test + public void convertJsonToObject() { + customer = SimpleDataGeneration.generateData(); + dataJson = xstream.toXML(customer); + customer = (Customer) xstream.fromXML(dataJson); + System.out.println(customer); + Assert.assertNotNull(customer); + } + +} diff --git a/xstream/src/test/java/com/baeldung/test/XStreamJsonHierarchicalTest.java b/xstream/src/test/java/com/baeldung/test/XStreamJsonHierarchicalTest.java new file mode 100644 index 0000000000..f92faa7196 --- /dev/null +++ b/xstream/src/test/java/com/baeldung/test/XStreamJsonHierarchicalTest.java @@ -0,0 +1,45 @@ +package com.baeldung.test; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.baeldung.initializer.SimpleXstreamInitializer; +import com.baeldung.pojo.ContactDetails; +import com.baeldung.pojo.Customer; +import com.baeldung.utility.SimpleDataGeneration; +import com.thoughtworks.xstream.XStream; + +public class XStreamJsonHierarchicalTest { + + private Customer customer = null; + private String dataJson = null; + private XStream xstream = null; + + @Before + public void dataSetup() { + SimpleXstreamInitializer simpleXstreamInitializer = new SimpleXstreamInitializer(); + xstream = simpleXstreamInitializer.getXstreamJsonHierarchicalInstance(); + xstream.processAnnotations(Customer.class); + } + + @Test + public void convertObjectToJson() { + customer = SimpleDataGeneration.generateData(); + xstream.alias("customer", Customer.class); + xstream.alias("contactDetails", ContactDetails.class); + xstream.aliasField("fn", Customer.class, "firstName"); + dataJson = xstream.toXML(customer); + System.out.println(dataJson); + Assert.assertNotNull(dataJson); + } + + @Test + public void convertJsonToObject() { + customer = SimpleDataGeneration.generateData(); + dataJson = xstream.toXML(customer); + // customer = (Customer) xstream.fromXML(dataJson); + // Assert.assertNotNull(customer); + } + +} From a6768e6a9dc6c31fcd4121824c1b9847c924a106 Mon Sep 17 00:00:00 2001 From: DOHA Date: Sat, 9 Apr 2016 18:32:49 +0200 Subject: [PATCH 07/76] minor fix --- .../persistence/IntegrationTestSuite.java | 18 +++++++++++------- ... FooSortingPersistenceIntegrationTest.java} | 2 +- ...ooPaginationPersistenceIntegrationTest.java | 9 ++++++--- .../FooServicePersistenceIntegrationTest.java | 4 ++-- ...a => FooServiceSortingIntegrationTest.java} | 7 +++++-- ...eSortingWitNullsManualIntegrationTest.java} | 4 +++- ....java => JpaMultipleDBIntegrationTest.java} | 4 +--- .../service/PersistenceTestSuite.java | 7 +++---- 8 files changed, 32 insertions(+), 23 deletions(-) rename spring-hibernate4/src/test/java/com/baeldung/persistence/hibernate/{FooSortingPersistenceServiceTest.java => FooSortingPersistenceIntegrationTest.java} (99%) rename spring-jpa/src/test/java/org/baeldung/persistence/service/{FooServiceSortingTests.java => FooServiceSortingIntegrationTest.java} (95%) rename spring-jpa/src/test/java/org/baeldung/persistence/service/{FooServiceSortingWitNullsManualTest.java => FooServiceSortingWitNullsManualIntegrationTest.java} (93%) rename spring-jpa/src/test/java/org/baeldung/persistence/service/{JpaMultipleDBTest.java => JpaMultipleDBIntegrationTest.java} (94%) diff --git a/spring-hibernate4/src/test/java/com/baeldung/persistence/IntegrationTestSuite.java b/spring-hibernate4/src/test/java/com/baeldung/persistence/IntegrationTestSuite.java index c107a221d6..f5c45a5d6f 100644 --- a/spring-hibernate4/src/test/java/com/baeldung/persistence/IntegrationTestSuite.java +++ b/spring-hibernate4/src/test/java/com/baeldung/persistence/IntegrationTestSuite.java @@ -1,20 +1,24 @@ package com.baeldung.persistence; -import com.baeldung.persistence.hibernate.FooPaginationPersistenceIntegrationTest; -import com.baeldung.persistence.hibernate.FooSortingPersistenceServiceTest; -import com.baeldung.persistence.service.FooServicePersistenceIntegrationTest; -import com.baeldung.persistence.service.FooServiceBasicPersistenceIntegrationTest; -import com.baeldung.persistence.service.ParentServicePersistenceIntegrationTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; +import com.baeldung.persistence.audit.AuditTestSuite; +import com.baeldung.persistence.hibernate.FooPaginationPersistenceIntegrationTest; +import com.baeldung.persistence.hibernate.FooSortingPersistenceIntegrationTest; +import com.baeldung.persistence.service.FooServiceBasicPersistenceIntegrationTest; +import com.baeldung.persistence.service.FooServicePersistenceIntegrationTest; +import com.baeldung.persistence.service.ParentServicePersistenceIntegrationTest; + @RunWith(Suite.class) @Suite.SuiteClasses({ // @formatter:off - FooServiceBasicPersistenceIntegrationTest.class + AuditTestSuite.class + ,FooServiceBasicPersistenceIntegrationTest.class ,FooPaginationPersistenceIntegrationTest.class ,FooServicePersistenceIntegrationTest.class ,ParentServicePersistenceIntegrationTest.class - ,FooSortingPersistenceServiceTest.class + ,FooSortingPersistenceIntegrationTest.class + }) // @formatter:on public class IntegrationTestSuite { // diff --git a/spring-hibernate4/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceServiceTest.java b/spring-hibernate4/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceIntegrationTest.java similarity index 99% rename from spring-hibernate4/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceServiceTest.java rename to spring-hibernate4/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceIntegrationTest.java index c890e08d15..0f76526960 100644 --- a/spring-hibernate4/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceServiceTest.java +++ b/spring-hibernate4/src/test/java/com/baeldung/persistence/hibernate/FooSortingPersistenceIntegrationTest.java @@ -26,7 +26,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { PersistenceConfig.class }, loader = AnnotationConfigContextLoader.class) @SuppressWarnings("unchecked") -public class FooSortingPersistenceServiceTest { +public class FooSortingPersistenceIntegrationTest { @Autowired private SessionFactory sessionFactory; diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/service/FooPaginationPersistenceIntegrationTest.java b/spring-jpa/src/test/java/org/baeldung/persistence/service/FooPaginationPersistenceIntegrationTest.java index 73c36190f9..091bec0ba0 100644 --- a/spring-jpa/src/test/java/org/baeldung/persistence/service/FooPaginationPersistenceIntegrationTest.java +++ b/spring-jpa/src/test/java/org/baeldung/persistence/service/FooPaginationPersistenceIntegrationTest.java @@ -88,11 +88,12 @@ public class FooPaginationPersistenceIntegrationTest { public final void givenEntitiesExist_whenRetrievingPage_thenCorrect() { final int pageSize = 10; - final Query queryIds = entityManager.createQuery("Select f.id from Foo f order by f.lastName"); + final Query queryIds = entityManager.createQuery("Select f.id from Foo f order by f.name"); final List fooIds = queryIds.getResultList(); - final Query query = entityManager.createQuery("Select f from Foo e whet f.id in :ids"); + final Query query = entityManager.createQuery("Select f from Foo as f where f.id in :ids"); query.setParameter("ids", fooIds.subList(0, pageSize)); + final List fooList = query.getResultList(); // Then @@ -129,13 +130,15 @@ public class FooPaginationPersistenceIntegrationTest { final Root from = criteriaQuery.from(Foo.class); final CriteriaQuery select = criteriaQuery.select(from); - final TypedQuery typedQuery = entityManager.createQuery(select); + TypedQuery typedQuery; while (pageNumber < count.intValue()) { + typedQuery = entityManager.createQuery(select); typedQuery.setFirstResult(pageNumber - 1); typedQuery.setMaxResults(pageSize); System.out.println("Current page: " + typedQuery.getResultList()); pageNumber += pageSize; } + } // UTIL diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServicePersistenceIntegrationTest.java b/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServicePersistenceIntegrationTest.java index 96b3235f64..4c57865f74 100644 --- a/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServicePersistenceIntegrationTest.java +++ b/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServicePersistenceIntegrationTest.java @@ -36,7 +36,7 @@ public class FooServicePersistenceIntegrationTest { @Test(expected = DataIntegrityViolationException.class) public final void whenInvalidEntityIsCreated_thenDataException() { - service.create(new Foo()); + service.create(new Foo(randomAlphabetic(2048))); } @Test(expected = DataIntegrityViolationException.class) @@ -53,7 +53,7 @@ public class FooServicePersistenceIntegrationTest { @Test(expected = DataAccessException.class) public final void temp_whenInvalidEntityIsCreated_thenDataException() { - service.create(new Foo()); + service.create(new Foo(randomAlphabetic(2048))); } @Test diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingTests.java b/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingIntegrationTest.java similarity index 95% rename from spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingTests.java rename to spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingIntegrationTest.java index 319d36151e..3c9f509da5 100644 --- a/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingTests.java +++ b/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingIntegrationTest.java @@ -22,7 +22,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { PersistenceJPAConfig.class }, loader = AnnotationConfigContextLoader.class) @SuppressWarnings("unchecked") -public class FooServiceSortingTests { +public class FooServiceSortingIntegrationTest { @PersistenceContext private EntityManager entityManager; @@ -65,7 +65,10 @@ public class FooServiceSortingTests { final Query barJoinQuery = entityManager.createQuery(jql); final List fooList = barJoinQuery.getResultList(); for (final Foo foo : fooList) { - System.out.println("Name:" + foo.getName() + "-------BarId:" + foo.getBar().getId()); + System.out.println("Name:" + foo.getName()); + if (foo.getBar() != null) { + System.out.print("-------BarId:" + foo.getBar().getId()); + } } } diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingWitNullsManualTest.java b/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingWitNullsManualIntegrationTest.java similarity index 93% rename from spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingWitNullsManualTest.java rename to spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingWitNullsManualIntegrationTest.java index 986e4e4a7d..040eee1c73 100644 --- a/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingWitNullsManualTest.java +++ b/spring-jpa/src/test/java/org/baeldung/persistence/service/FooServiceSortingWitNullsManualIntegrationTest.java @@ -20,7 +20,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { PersistenceJPAConfig.class }, loader = AnnotationConfigContextLoader.class) -public class FooServiceSortingWitNullsManualTest { +public class FooServiceSortingWitNullsManualIntegrationTest { @PersistenceContext private EntityManager entityManager; @@ -30,6 +30,7 @@ public class FooServiceSortingWitNullsManualTest { // tests + @SuppressWarnings("unchecked") @Test public final void whenSortingByStringNullLast_thenLastNull() { service.create(new Foo()); @@ -44,6 +45,7 @@ public class FooServiceSortingWitNullsManualTest { } } + @SuppressWarnings("unchecked") @Test public final void whenSortingByStringNullFirst_thenFirstNull() { service.create(new Foo()); diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/service/JpaMultipleDBTest.java b/spring-jpa/src/test/java/org/baeldung/persistence/service/JpaMultipleDBIntegrationTest.java similarity index 94% rename from spring-jpa/src/test/java/org/baeldung/persistence/service/JpaMultipleDBTest.java rename to spring-jpa/src/test/java/org/baeldung/persistence/service/JpaMultipleDBIntegrationTest.java index 427e182d9e..e036a4f3c1 100644 --- a/spring-jpa/src/test/java/org/baeldung/persistence/service/JpaMultipleDBTest.java +++ b/spring-jpa/src/test/java/org/baeldung/persistence/service/JpaMultipleDBIntegrationTest.java @@ -15,13 +15,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { UserConfig.class, ProductConfig.class }) -@TransactionConfiguration -public class JpaMultipleDBTest { +public class JpaMultipleDBIntegrationTest { @Autowired private UserRepository userRepository; diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/service/PersistenceTestSuite.java b/spring-jpa/src/test/java/org/baeldung/persistence/service/PersistenceTestSuite.java index 919171de9f..aa2dfb5293 100644 --- a/spring-jpa/src/test/java/org/baeldung/persistence/service/PersistenceTestSuite.java +++ b/spring-jpa/src/test/java/org/baeldung/persistence/service/PersistenceTestSuite.java @@ -7,10 +7,9 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ // @formatter:off FooPaginationPersistenceIntegrationTest.class ,FooServicePersistenceIntegrationTest.class - ,FooServiceSortingTests.class - ,JpaMultipleDBTest.class - // manual only - // ,FooServiceSortingWitNullsManualTest.class + ,FooServiceSortingIntegrationTest.class + ,JpaMultipleDBIntegrationTest.class + ,FooServiceSortingWitNullsManualIntegrationTest.class }) // @formatter:on public class PersistenceTestSuite { // From 17a72d00a3d79e9fa9072b85980e05a1d3a2cec9 Mon Sep 17 00:00:00 2001 From: Roshan Thomas Date: Sat, 9 Apr 2016 15:54:40 -0400 Subject: [PATCH 08/76] Spring HATEOAS - separating order service --- .../baeldung/persistence/model/Customer.java | 4 ++ .../web/controller/CustomerController.java | 17 ++--- .../baeldung/web/service/CustomerService.java | 5 -- .../web/service/CustomerServiceImpl.java | 35 ---------- .../baeldung/web/service/OrderService.java | 13 ++++ .../web/service/OrderServiceImpl.java | 64 +++++++++++++++++++ 6 files changed, 90 insertions(+), 48 deletions(-) create mode 100644 spring-security-rest/src/main/java/org/baeldung/web/service/OrderService.java create mode 100644 spring-security-rest/src/main/java/org/baeldung/web/service/OrderServiceImpl.java diff --git a/spring-security-rest/src/main/java/org/baeldung/persistence/model/Customer.java b/spring-security-rest/src/main/java/org/baeldung/persistence/model/Customer.java index 16b482ecf6..b302ec057a 100644 --- a/spring-security-rest/src/main/java/org/baeldung/persistence/model/Customer.java +++ b/spring-security-rest/src/main/java/org/baeldung/persistence/model/Customer.java @@ -4,6 +4,10 @@ import java.util.Map; import org.springframework.hateoas.ResourceSupport; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +@JsonInclude(Include.NON_NULL) public class Customer extends ResourceSupport { private String customerId; private String customerName; diff --git a/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java b/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java index ac63a81d97..e103edcc19 100644 --- a/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java +++ b/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java @@ -5,6 +5,7 @@ import java.util.List; import org.baeldung.persistence.model.Customer; import org.baeldung.persistence.model.Order; import org.baeldung.web.service.CustomerService; +import org.baeldung.web.service.OrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.Link; import org.springframework.hateoas.mvc.ControllerLinkBuilder; @@ -18,19 +19,22 @@ public class CustomerController { @Autowired private CustomerService customerService; + @Autowired + private OrderService orderService; + @RequestMapping(value = "/customer/{customerId}", method = RequestMethod.GET) public Customer getCustomerById(@PathVariable final String customerId) { return customerService.getCustomerDetail(customerId); } @RequestMapping(value = "/customer/{customerId}/{orderId}", method = RequestMethod.GET) - public Order getOrderByIdForCustomer(@PathVariable final String customerId, @PathVariable final String orderId) { - return customerService.getOrderByIdForCustomer(customerId, orderId); + public Order getOrderById(@PathVariable final String customerId, @PathVariable final String orderId) { + return orderService.getOrderByIdForCustomer(customerId, orderId); } @RequestMapping(value = "/customer/{customerId}/orders", method = RequestMethod.GET) public List getOrdersForCustomer(@PathVariable final String customerId) { - return customerService.getAllOrdersForCustomer(customerId); + return orderService.getAllOrdersForCustomer(customerId); } @RequestMapping(value = "/customers", method = RequestMethod.GET) @@ -39,14 +43,11 @@ public class CustomerController { for (final Customer customer : allCustomers) { final Link selfLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getCustomerById(customer.getCustomerId())).withSelfRel(); customer.add(selfLink); - if (customer.getOrders().values().size() > 0) { + if (orderService.getAllOrdersForCustomer(customer.getCustomerId()).size() > 0) { final Link ordersLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getOrdersForCustomer(customer.getCustomerId())).withRel("allOrders"); customer.add(ordersLink); } - for (final Order order : customer.getOrders().values()) { - final Link orderLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getOrderByIdForCustomer(customer.getCustomerId(), order.getOrderId())).withRel("order"); - order.add(orderLink); - } + } return allCustomers; } diff --git a/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerService.java b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerService.java index abad3ac0c6..da016af2d5 100644 --- a/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerService.java +++ b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerService.java @@ -3,7 +3,6 @@ package org.baeldung.web.service; import java.util.List; import org.baeldung.persistence.model.Customer; -import org.baeldung.persistence.model.Order; public interface CustomerService { @@ -11,8 +10,4 @@ public interface CustomerService { Customer getCustomerDetail(final String id); - List getAllOrdersForCustomer(String customerId); - - Order getOrderByIdForCustomer(String customerId, String orderId); - } diff --git a/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java index d41b532f78..e179de2554 100644 --- a/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java +++ b/spring-security-rest/src/main/java/org/baeldung/web/service/CustomerServiceImpl.java @@ -5,37 +5,21 @@ import java.util.HashMap; import java.util.List; import org.baeldung.persistence.model.Customer; -import org.baeldung.persistence.model.Order; import org.springframework.stereotype.Service; @Service public class CustomerServiceImpl implements CustomerService { private HashMap customerMap; - private HashMap customerOneOrderMap; - private HashMap customerTwoOrderMap; - private HashMap customerThreeOrderMap; public CustomerServiceImpl() { customerMap = new HashMap<>(); - customerOneOrderMap = new HashMap<>(); - customerTwoOrderMap = new HashMap<>(); - customerThreeOrderMap = new HashMap<>(); final Customer customerOne = new Customer("10A", "Jane", "ABC Company"); final Customer customerTwo = new Customer("20B", "Bob", "XYZ Company"); final Customer customerThree = new Customer("30C", "Tim", "CKV Company"); - customerOneOrderMap.put("001A", new Order("001A", 150.00, 25)); - customerOneOrderMap.put("002A", new Order("002A", 250.00, 15)); - - customerTwoOrderMap.put("002B", new Order("002B", 550.00, 325)); - customerTwoOrderMap.put("002B", new Order("002B", 450.00, 525)); - - customerOne.setOrders(customerOneOrderMap); - customerTwo.setOrders(customerTwoOrderMap); - customerThree.setOrders(customerThreeOrderMap); customerMap.put("10A", customerOne); customerMap.put("20B", customerTwo); customerMap.put("30C", customerThree); @@ -52,23 +36,4 @@ public class CustomerServiceImpl implements CustomerService { return customerMap.get(customerId); } - @Override - public List getAllOrdersForCustomer(final String customerId) { - return new ArrayList<>(customerMap.get(customerId).getOrders().values()); - } - - @Override - public Order getOrderByIdForCustomer(final String customerId, final String orderId) { - - final List orders = (List) customerMap.get(customerId).getOrders().values(); - Order selectedOrder = null; - for (final Order order : orders) { - if (order.getId().equals(orderId)) { - selectedOrder = order; - } - } - return selectedOrder; - - } - } diff --git a/spring-security-rest/src/main/java/org/baeldung/web/service/OrderService.java b/spring-security-rest/src/main/java/org/baeldung/web/service/OrderService.java new file mode 100644 index 0000000000..9a23488c50 --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/web/service/OrderService.java @@ -0,0 +1,13 @@ +package org.baeldung.web.service; + +import java.util.List; + +import org.baeldung.persistence.model.Order; + +public interface OrderService { + + List getAllOrdersForCustomer(String customerId); + + Order getOrderByIdForCustomer(String customerId, String orderId); + +} diff --git a/spring-security-rest/src/main/java/org/baeldung/web/service/OrderServiceImpl.java b/spring-security-rest/src/main/java/org/baeldung/web/service/OrderServiceImpl.java new file mode 100644 index 0000000000..1b8e7b0dde --- /dev/null +++ b/spring-security-rest/src/main/java/org/baeldung/web/service/OrderServiceImpl.java @@ -0,0 +1,64 @@ +package org.baeldung.web.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.baeldung.persistence.model.Customer; +import org.baeldung.persistence.model.Order; +import org.springframework.stereotype.Service; + +@Service +public class OrderServiceImpl implements OrderService { + + private HashMap customerMap; + private HashMap customerOneOrderMap; + private HashMap customerTwoOrderMap; + private HashMap customerThreeOrderMap; + + public OrderServiceImpl() { + + customerMap = new HashMap<>(); + customerOneOrderMap = new HashMap<>(); + customerTwoOrderMap = new HashMap<>(); + customerThreeOrderMap = new HashMap<>(); + + customerOneOrderMap.put("001A", new Order("001A", 150.00, 25)); + customerOneOrderMap.put("002A", new Order("002A", 250.00, 15)); + + customerTwoOrderMap.put("002B", new Order("002B", 550.00, 325)); + customerTwoOrderMap.put("002B", new Order("002B", 450.00, 525)); + + final Customer customerOne = new Customer("10A", "Jane", "ABC Company"); + final Customer customerTwo = new Customer("20B", "Bob", "XYZ Company"); + final Customer customerThree = new Customer("30C", "Tim", "CKV Company"); + + customerOne.setOrders(customerOneOrderMap); + customerTwo.setOrders(customerTwoOrderMap); + customerThree.setOrders(customerThreeOrderMap); + customerMap.put("10A", customerOne); + customerMap.put("20B", customerTwo); + customerMap.put("30C", customerThree); + + } + + @Override + public List getAllOrdersForCustomer(final String customerId) { + return new ArrayList<>(customerMap.get(customerId).getOrders().values()); + } + + @Override + public Order getOrderByIdForCustomer(final String customerId, final String orderId) { + + final List orders = (List) customerMap.get(customerId).getOrders().values(); + Order selectedOrder = null; + for (final Order order : orders) { + if (order.getId().equals(orderId)) { + selectedOrder = order; + } + } + return selectedOrder; + + } + +} From 44c327d1a36f3a83c9bf20bf9c8e72363e005fb6 Mon Sep 17 00:00:00 2001 From: David Morley Date: Mon, 11 Apr 2016 05:26:22 -0500 Subject: [PATCH 09/76] Add expected exception handling to test --- .../test/XStreamJsonHierarchicalTest.java | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/xstream/src/test/java/com/baeldung/test/XStreamJsonHierarchicalTest.java b/xstream/src/test/java/com/baeldung/test/XStreamJsonHierarchicalTest.java index f92faa7196..1e71cb7066 100644 --- a/xstream/src/test/java/com/baeldung/test/XStreamJsonHierarchicalTest.java +++ b/xstream/src/test/java/com/baeldung/test/XStreamJsonHierarchicalTest.java @@ -1,45 +1,44 @@ package com.baeldung.test; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - import com.baeldung.initializer.SimpleXstreamInitializer; import com.baeldung.pojo.ContactDetails; import com.baeldung.pojo.Customer; import com.baeldung.utility.SimpleDataGeneration; import com.thoughtworks.xstream.XStream; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; public class XStreamJsonHierarchicalTest { - private Customer customer = null; - private String dataJson = null; - private XStream xstream = null; + private Customer customer = null; + private String dataJson = null; + private XStream xstream = null; - @Before - public void dataSetup() { - SimpleXstreamInitializer simpleXstreamInitializer = new SimpleXstreamInitializer(); - xstream = simpleXstreamInitializer.getXstreamJsonHierarchicalInstance(); - xstream.processAnnotations(Customer.class); - } + @Before + public void dataSetup() { + SimpleXstreamInitializer simpleXstreamInitializer = new SimpleXstreamInitializer(); + xstream = simpleXstreamInitializer.getXstreamJsonHierarchicalInstance(); + xstream.processAnnotations(Customer.class); + } - @Test - public void convertObjectToJson() { - customer = SimpleDataGeneration.generateData(); - xstream.alias("customer", Customer.class); - xstream.alias("contactDetails", ContactDetails.class); - xstream.aliasField("fn", Customer.class, "firstName"); - dataJson = xstream.toXML(customer); - System.out.println(dataJson); - Assert.assertNotNull(dataJson); - } + @Test + public void convertObjectToJson() { + customer = SimpleDataGeneration.generateData(); + xstream.alias("customer", Customer.class); + xstream.alias("contactDetails", ContactDetails.class); + xstream.aliasField("fn", Customer.class, "firstName"); + dataJson = xstream.toXML(customer); + System.out.println(dataJson); + Assert.assertNotNull(dataJson); + } - @Test - public void convertJsonToObject() { - customer = SimpleDataGeneration.generateData(); - dataJson = xstream.toXML(customer); - // customer = (Customer) xstream.fromXML(dataJson); - // Assert.assertNotNull(customer); - } + @Test(expected = UnsupportedOperationException.class) + public void convertJsonToObject() { + customer = SimpleDataGeneration.generateData(); + dataJson = xstream.toXML(customer); + customer = (Customer) xstream.fromXML(dataJson); + Assert.assertNotNull(customer); + } } From ce2891ed68adad0c96665f43509183394eecbcba Mon Sep 17 00:00:00 2001 From: David Morley Date: Mon, 11 Apr 2016 05:26:50 -0500 Subject: [PATCH 10/76] Clean up formatting --- xstream/pom.xml | 88 +++++++++---------- .../initializer/SimpleXstreamInitializer.java | 24 ++--- xstream/src/main/resources/log4j.properties | 2 - .../baeldung/test/XStreamJettisonTest.java | 61 +++++++------ 4 files changed, 86 insertions(+), 89 deletions(-) diff --git a/xstream/pom.xml b/xstream/pom.xml index 22c2ce2941..f505019d71 100644 --- a/xstream/pom.xml +++ b/xstream/pom.xml @@ -1,50 +1,50 @@ - 4.0.0 - org.baeldung - xstream-introduction - 0.0.1-SNAPSHOT - xstream-introduction - An Introduction To XStream + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + org.baeldung + xstream-introduction + 0.0.1-SNAPSHOT + xstream-introduction + An Introduction To XStream - - - com.thoughtworks.xstream - xstream - 1.4.5 - + + + com.thoughtworks.xstream + xstream + 1.4.5 + - - org.codehaus.jettison - jettison - 1.3.7 - - - - junit - junit - 4.12 - - - - log4j - log4j - 1.2.17 - - + + org.codehaus.jettison + jettison + 1.3.7 + - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - 1.8 - 1.8 - - - - + + junit + junit + 4.12 + + + + log4j + log4j + 1.2.17 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + \ No newline at end of file diff --git a/xstream/src/main/java/com/baeldung/initializer/SimpleXstreamInitializer.java b/xstream/src/main/java/com/baeldung/initializer/SimpleXstreamInitializer.java index 744b60f78e..a15bea5481 100644 --- a/xstream/src/main/java/com/baeldung/initializer/SimpleXstreamInitializer.java +++ b/xstream/src/main/java/com/baeldung/initializer/SimpleXstreamInitializer.java @@ -6,18 +6,18 @@ import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver; public class SimpleXstreamInitializer { - public XStream getXstreamInstance() { - XStream xtreamInstance = new XStream(); - return xtreamInstance; - } + public XStream getXstreamInstance() { + XStream xtreamInstance = new XStream(); + return xtreamInstance; + } - public XStream getXstreamJettisonMappedInstance() { - XStream xstreamInstance = new XStream(new JettisonMappedXmlDriver()); - return xstreamInstance; - } + public XStream getXstreamJettisonMappedInstance() { + XStream xstreamInstance = new XStream(new JettisonMappedXmlDriver()); + return xstreamInstance; + } - public XStream getXstreamJsonHierarchicalInstance() { - XStream xstreamInstance = new XStream(new JsonHierarchicalStreamDriver()); - return xstreamInstance; - } + public XStream getXstreamJsonHierarchicalInstance() { + XStream xstreamInstance = new XStream(new JsonHierarchicalStreamDriver()); + return xstreamInstance; + } } \ No newline at end of file diff --git a/xstream/src/main/resources/log4j.properties b/xstream/src/main/resources/log4j.properties index 9cdafc6bdb..03d8c51aa0 100644 --- a/xstream/src/main/resources/log4j.properties +++ b/xstream/src/main/resources/log4j.properties @@ -1,12 +1,10 @@ # Root logger option log4j.rootLogger=DEBUG, file - # Redirect log messages to console # log4j.appender.stdout=org.apache.log4j.ConsoleAppender # log4j.appender.stdout.Target=System.out # log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n - # Redirect log messages to a log file, support file rolling. log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=D:\\Test\\xstream-application.log diff --git a/xstream/src/test/java/com/baeldung/test/XStreamJettisonTest.java b/xstream/src/test/java/com/baeldung/test/XStreamJettisonTest.java index 6111dfbb24..f37605cc98 100644 --- a/xstream/src/test/java/com/baeldung/test/XStreamJettisonTest.java +++ b/xstream/src/test/java/com/baeldung/test/XStreamJettisonTest.java @@ -1,48 +1,47 @@ package com.baeldung.test; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - import com.baeldung.initializer.SimpleXstreamInitializer; import com.baeldung.pojo.ContactDetails; import com.baeldung.pojo.Customer; import com.baeldung.utility.SimpleDataGeneration; import com.thoughtworks.xstream.XStream; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; public class XStreamJettisonTest { - private Customer customer = null; + private Customer customer = null; - private String dataJson = null; + private String dataJson = null; - private XStream xstream = null; + private XStream xstream = null; - @Before - public void dataSetup() { - SimpleXstreamInitializer simpleXstreamInitializer = new SimpleXstreamInitializer(); - xstream = simpleXstreamInitializer.getXstreamJettisonMappedInstance(); - xstream.processAnnotations(Customer.class); - } + @Before + public void dataSetup() { + SimpleXstreamInitializer simpleXstreamInitializer = new SimpleXstreamInitializer(); + xstream = simpleXstreamInitializer.getXstreamJettisonMappedInstance(); + xstream.processAnnotations(Customer.class); + } - @Test - public void convertObjectToJson() { - customer = SimpleDataGeneration.generateData(); - xstream.alias("customer" , Customer.class); - xstream.alias("contactDetails" , ContactDetails.class); - xstream.aliasField("fn" , Customer.class , "firstName"); - dataJson = xstream.toXML(customer); - System.out.println(dataJson); - Assert.assertNotNull(dataJson); - } + @Test + public void convertObjectToJson() { + customer = SimpleDataGeneration.generateData(); + xstream.alias("customer", Customer.class); + xstream.alias("contactDetails", ContactDetails.class); + xstream.aliasField("fn", Customer.class, "firstName"); + dataJson = xstream.toXML(customer); + System.out.println(dataJson); + Assert.assertNotNull(dataJson); + } - @Test - public void convertJsonToObject() { - customer = SimpleDataGeneration.generateData(); - dataJson = xstream.toXML(customer); - customer = (Customer) xstream.fromXML(dataJson); - System.out.println(customer); - Assert.assertNotNull(customer); - } + @Test + public void convertJsonToObject() { + customer = SimpleDataGeneration.generateData(); + dataJson = xstream.toXML(customer); + customer = (Customer) xstream.fromXML(dataJson); + System.out.println(customer); + Assert.assertNotNull(customer); + } } From edabeee9d82c1e34459be95bc951e3b403ef7407 Mon Sep 17 00:00:00 2001 From: oborkovskyi Date: Mon, 11 Apr 2016 18:59:05 +0300 Subject: [PATCH 11/76] Added tests --- spring-spel/pom.xml | 12 +- .../spring/spel/examples/SpelOperators.java | 204 ------------------ .../test/java/com/baeldung/spel/SpelTest.java | 99 +++++++++ .../src/test/resources/applicationContext.xml | 8 + 4 files changed, 111 insertions(+), 212 deletions(-) delete mode 100644 spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelOperators.java create mode 100644 spring-spel/src/test/java/com/baeldung/spel/SpelTest.java create mode 100644 spring-spel/src/test/resources/applicationContext.xml diff --git a/spring-spel/pom.xml b/spring-spel/pom.xml index d7a9f9f7f3..12b7164e27 100644 --- a/spring-spel/pom.xml +++ b/spring-spel/pom.xml @@ -5,15 +5,10 @@ 4.0.0 com.baeldung - guava + spel 1.0-SNAPSHOT - - com.google.guava - guava - 19.0 - junit junit @@ -26,13 +21,14 @@ org.springframework - spring-core + spring-context 4.0.6.RELEASE org.springframework - spring-context + spring-test 4.0.6.RELEASE + test diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelOperators.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelOperators.java deleted file mode 100644 index 884ef73f6c..0000000000 --- a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelOperators.java +++ /dev/null @@ -1,204 +0,0 @@ -package com.baeldung.spring.spel.examples; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -@Component("spelOperators") -public class SpelOperators { - - @Value("#{1 == 1}") - private boolean equal; - - @Value("#{1 != 1}") - private boolean notEqual; - - @Value("#{1 < 1}") - private boolean lessThan; - - @Value("#{1 <= 1}") - private boolean lessThanOrEqual; - - @Value("#{1 > 1}") - private boolean greaterThan; - - @Value("#{someCar.engine.numberOfCylinders >= 6}") - private boolean greaterThanOrEqual; - - @Value("#{someCar.horsePower == 250 and someCar.engine.capacity < 4000}") - private boolean and; - - @Value("#{someCar.horsePower > 300 or someCar.engine.capacity > 3000}") - private boolean or; - - @Value("#{!(someCar.engine.numberOfCylinders == 6)}") - private boolean not; - - @Value("#{1 + 1}") - private double add; - - @Value("#{someCar.model + ' manufactored by ' + someCar.make}") - private String addString; - - @Value("#{1 - 1}") - private double subtraction; - - @Value("#{1 * 1}") - private double multiply; - - @Value("#{10 / 2}") - private double divide; - - @Value("#{37 % 10}") - private double modulo; - - @Value("#{2 ^ 2}") - private double powerOf; - - public boolean isEqual() { - return equal; - } - - public void setEqual(boolean equal) { - this.equal = equal; - } - - public boolean isNotEqual() { - return notEqual; - } - - public void setNotEqual(boolean notEqual) { - this.notEqual = notEqual; - } - - public boolean isLessThan() { - return lessThan; - } - - public void setLessThan(boolean lessThan) { - this.lessThan = lessThan; - } - - public boolean isLessThanOrEqual() { - return lessThanOrEqual; - } - - public void setLessThanOrEqual(boolean lessThanOrEqual) { - this.lessThanOrEqual = lessThanOrEqual; - } - - public boolean isGreaterThan() { - return greaterThan; - } - - public void setGreaterThan(boolean greaterThan) { - this.greaterThan = greaterThan; - } - - public boolean isGreaterThanOrEqual() { - return greaterThanOrEqual; - } - - public void setGreaterThanOrEqual(boolean greaterThanOrEqual) { - this.greaterThanOrEqual = greaterThanOrEqual; - } - - public boolean isAnd() { - return and; - } - - public void setAnd(boolean and) { - this.and = and; - } - - public boolean isOr() { - return or; - } - - public void setOr(boolean or) { - this.or = or; - } - - public boolean isNot() { - return not; - } - - public void setNot(boolean not) { - this.not = not; - } - - public double getAdd() { - return add; - } - - public void setAdd(double add) { - this.add = add; - } - - public String getAddString() { - return addString; - } - - public void setAddString(String addString) { - this.addString = addString; - } - - public double getSubtraction() { - return subtraction; - } - - public void setSubtraction(double subtraction) { - this.subtraction = subtraction; - } - - public double getMultiply() { - return multiply; - } - - public void setMultiply(double multiply) { - this.multiply = multiply; - } - - public double getDivide() { - return divide; - } - - public void setDivide(double divide) { - this.divide = divide; - } - - public double getModulo() { - return modulo; - } - - public void setModulo(double modulo) { - this.modulo = modulo; - } - - public double getPowerOf() { - return powerOf; - } - - public void setPowerOf(double powerOf) { - this.powerOf = powerOf; - } - - @Override - public String toString() { - return "[equal=" + equal + ", " + - "notEqual=" + notEqual + ", " + - "lessThan=" + lessThan + ", " + - "lessThanOrEqual=" + lessThanOrEqual + ", " + - "greaterThan=" + greaterThan + ", " + - "greaterThanOrEqual=" + greaterThanOrEqual + ", " + - "and=" + and + ", " + - "or=" + or + ", " + - "not=" + not + ", " + - "add=" + add + ", " + - "addString=" + addString + ", " + - "subtraction=" + subtraction + ", " + - "multiply=" + multiply + ", " + - "divide=" + divide + ", " + - "modulo=" + modulo + ", " + - "powerOf=" + powerOf + "]"; - } -} diff --git a/spring-spel/src/test/java/com/baeldung/spel/SpelTest.java b/spring-spel/src/test/java/com/baeldung/spel/SpelTest.java new file mode 100644 index 0000000000..3ca0a04f5b --- /dev/null +++ b/spring-spel/src/test/java/com/baeldung/spel/SpelTest.java @@ -0,0 +1,99 @@ +package com.baeldung.spel; + +import com.baeldung.spring.spel.examples.*; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.hamcrest.Matchers.equalTo; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = {"/applicationContext.xml"}) +public class SpelTest { + + @Autowired + private SpelArithmetic spelArithmetic = new SpelArithmetic(); + @Autowired + private SpelCollections spelCollections = new SpelCollections(); + @Autowired + private SpelConditional spelConditional = new SpelConditional(); + @Autowired + private SpelLogical spelLogical = new SpelLogical(); + @Autowired + private SpelRegex spelRegex = new SpelRegex(); + @Autowired + private SpelRelational spelRelational = new SpelRelational(); + + @Test + public void testArithmetic() throws Exception { + Assert.assertThat(spelArithmetic.getAdd(), equalTo(20.0)); + Assert.assertThat(spelArithmetic.getAddString(), equalTo("Some string plus other string")); + Assert.assertThat(spelArithmetic.getSubtract(), equalTo(19.0)); + Assert.assertThat(spelArithmetic.getMultiply(), equalTo(20.0)); + Assert.assertThat(spelArithmetic.getDivide(), equalTo(18.0)); + Assert.assertThat(spelArithmetic.getDivideAlphabetic(), equalTo(18.0)); + Assert.assertThat(spelArithmetic.getModulo(), equalTo(7.0)); + Assert.assertThat(spelArithmetic.getModuloAlphabetic(), equalTo(7.0)); + Assert.assertThat(spelArithmetic.getPowerOf(), equalTo(512.0)); + Assert.assertThat(spelArithmetic.getBrackets(), equalTo(17.0)); + } + + @Test + public void testCollections() throws Exception { + Assert.assertThat(spelCollections.getDriver1Car().getModel(), equalTo("Model1")); + Assert.assertThat(spelCollections.getDriver2Car().getModel(), equalTo("Model2")); + Assert.assertThat(spelCollections.getFirstCarInPark().getModel(), equalTo("Model1")); + Assert.assertThat(spelCollections.getNumberOfCarsInPark(), equalTo(2)); + } + + @Test + public void testConditional() throws Exception { + Assert.assertThat(spelConditional.getTernary(), equalTo("Something went wrong. There was false value")); + Assert.assertThat(spelConditional.getTernary2(), equalTo("Some model")); + Assert.assertThat(spelConditional.getElvis(), equalTo("Some model")); + } + + @Test + public void testLogical() throws Exception { + Assert.assertThat(spelLogical.isAnd(), equalTo(true)); + Assert.assertThat(spelLogical.isAndAlphabetic(), equalTo(true)); + Assert.assertThat(spelLogical.isOr(), equalTo(true)); + Assert.assertThat(spelLogical.isOrAlphabetic(), equalTo(true)); + Assert.assertThat(spelLogical.isNot(), equalTo(false)); + Assert.assertThat(spelLogical.isNotAlphabetic(), equalTo(false)); + } + + @Test + public void testRegex() throws Exception { + Assert.assertThat(spelRegex.isValidNumericStringResult(), equalTo(true)); + Assert.assertThat(spelRegex.isInvalidNumericStringResult(), equalTo(false)); + Assert.assertThat(spelRegex.isValidAlphabeticStringResult(), equalTo(true)); + Assert.assertThat(spelRegex.isInvalidAlphabeticStringResult(), equalTo(false)); + Assert.assertThat(spelRegex.isValidFormatOfHorsePower(), equalTo(true)); + } + + @Test + public void testRelational() throws Exception { + Assert.assertThat(spelRelational.isEqual(), equalTo(true)); + Assert.assertThat(spelRelational.isEqualAlphabetic(), equalTo(true)); + Assert.assertThat(spelRelational.isNotEqual(), equalTo(false)); + Assert.assertThat(spelRelational.isNotEqualAlphabetic(), equalTo(false)); + Assert.assertThat(spelRelational.isLessThan(), equalTo(false)); + Assert.assertThat(spelRelational.isLessThanAlphabetic(), equalTo(false)); + Assert.assertThat(spelRelational.isLessThanOrEqual(), equalTo(true)); + Assert.assertThat(spelRelational.isLessThanOrEqualAlphabetic(), equalTo(true)); + Assert.assertThat(spelRelational.isGreaterThan(), equalTo(false)); + Assert.assertThat(spelRelational.isGreaterThanAlphabetic(), equalTo(false)); + Assert.assertThat(spelRelational.isGreaterThanOrEqual(), equalTo(true)); + Assert.assertThat(spelRelational.isGreaterThanOrEqualAlphabetic(), equalTo(true)); + Assert.assertThat(spelRelational.isAnd(), equalTo(true)); + Assert.assertThat(spelRelational.isAndAlphabetic(), equalTo(true)); + Assert.assertThat(spelRelational.isOr(), equalTo(true)); + Assert.assertThat(spelRelational.isOrAlphabetic(), equalTo(true)); + Assert.assertThat(spelRelational.isNot(), equalTo(false)); + Assert.assertThat(spelRelational.isNotAlphabetic(), equalTo(false)); + } +} diff --git a/spring-spel/src/test/resources/applicationContext.xml b/spring-spel/src/test/resources/applicationContext.xml new file mode 100644 index 0000000000..815ac35837 --- /dev/null +++ b/spring-spel/src/test/resources/applicationContext.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file From 471444063aa6260a304cfc055f852440352c54c7 Mon Sep 17 00:00:00 2001 From: DOHA Date: Tue, 12 Apr 2016 11:57:26 +0200 Subject: [PATCH 12/76] cleanup spring security test --- .../client/RestClientLiveManualTest.java | 3 +++ .../java/org/baeldung/test/LiveTestSuite.java | 16 ++++++++++++++ spring-security-rest-digest-auth/.classpath | 1 + .../java/org/baeldung/test/LiveTestSuite.java | 18 +++++++++++++++ .../java/org/baeldung/spring/Application.java | 6 +++++ .../spring/SecurityWithoutCsrfConfig.java | 2 ++ .../src/test/java/org/baeldung/TestSuite.java | 18 +++++++++++++++ .../baeldung/client/RestTemplateLiveTest.java | 16 +++++++++++--- .../baeldung/common/web/AbstractLiveTest.java | 15 +++++++------ .../persistence/PersistenceTestSuite.java | 22 +++++++++++++++++++ .../query/JPACriteriaQueryTest.java | 4 ++-- .../persistence/query/JPAQuerydslTest.java | 4 ++-- .../query/JPASpecificationLiveTest.java | 2 +- .../query/JPASpecificationTest.java | 4 ++-- .../baeldung/persistence/query/RsqlTest.java | 4 ++-- .../baeldung/security/SecurityTestSuite.java | 17 ++++++++++++++ .../csrf/CsrfAbstractIntegrationTest.java | 2 +- .../csrf/CsrfDisabledIntegrationTest.java | 2 +- .../csrf/CsrfEnabledIntegrationTest.java | 3 +-- .../csrf}/SecurityWithCsrfConfig.java | 12 +++++----- .../java/org/baeldung/web/LiveTestSuite.java | 19 ++++++++++++++++ .../java/org/baeldung/web/MyUserLiveTest.java | 11 +++++----- .../java/org/baeldung/web/FooLiveTest.java | 17 +++++++++++--- 23 files changed, 182 insertions(+), 36 deletions(-) create mode 100644 spring-security-rest-basic-auth/src/test/java/org/baeldung/test/LiveTestSuite.java create mode 100644 spring-security-rest-digest-auth/src/test/java/org/baeldung/test/LiveTestSuite.java create mode 100644 spring-security-rest-full/src/test/java/org/baeldung/TestSuite.java create mode 100644 spring-security-rest-full/src/test/java/org/baeldung/persistence/PersistenceTestSuite.java create mode 100644 spring-security-rest-full/src/test/java/org/baeldung/security/SecurityTestSuite.java rename spring-security-rest-full/src/test/java/org/baeldung/{ => security}/csrf/CsrfAbstractIntegrationTest.java (97%) rename spring-security-rest-full/src/test/java/org/baeldung/{ => security}/csrf/CsrfDisabledIntegrationTest.java (96%) rename spring-security-rest-full/src/test/java/org/baeldung/{ => security}/csrf/CsrfEnabledIntegrationTest.java (93%) rename spring-security-rest-full/src/{main/java/org/baeldung/spring => test/java/org/baeldung/security/csrf}/SecurityWithCsrfConfig.java (90%) create mode 100644 spring-security-rest-full/src/test/java/org/baeldung/web/LiveTestSuite.java diff --git a/spring-security-rest-basic-auth/src/test/java/org/baeldung/client/RestClientLiveManualTest.java b/spring-security-rest-basic-auth/src/test/java/org/baeldung/client/RestClientLiveManualTest.java index b47f893b17..44c5c0cbb1 100644 --- a/spring-security-rest-basic-auth/src/test/java/org/baeldung/client/RestClientLiveManualTest.java +++ b/spring-security-rest-basic-auth/src/test/java/org/baeldung/client/RestClientLiveManualTest.java @@ -17,6 +17,7 @@ import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.junit.Ignore; import org.junit.Test; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; @@ -33,6 +34,8 @@ public class RestClientLiveManualTest { // tests + // old httpClient will throw UnsupportedOperationException + @Ignore @Test public final void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException() throws GeneralSecurityException { final HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); diff --git a/spring-security-rest-basic-auth/src/test/java/org/baeldung/test/LiveTestSuite.java b/spring-security-rest-basic-auth/src/test/java/org/baeldung/test/LiveTestSuite.java new file mode 100644 index 0000000000..8c9b48d056 --- /dev/null +++ b/spring-security-rest-basic-auth/src/test/java/org/baeldung/test/LiveTestSuite.java @@ -0,0 +1,16 @@ +package org.baeldung.test; + +import org.baeldung.client.ClientLiveTest; +import org.baeldung.client.RestClientLiveManualTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ +// @formatter:off + RestClientLiveManualTest.class + ,ClientLiveTest.class +}) // +public class LiveTestSuite { + +} diff --git a/spring-security-rest-digest-auth/.classpath b/spring-security-rest-digest-auth/.classpath index fa5dbd4c0e..5778c9435e 100644 --- a/spring-security-rest-digest-auth/.classpath +++ b/spring-security-rest-digest-auth/.classpath @@ -25,6 +25,7 @@ + diff --git a/spring-security-rest-digest-auth/src/test/java/org/baeldung/test/LiveTestSuite.java b/spring-security-rest-digest-auth/src/test/java/org/baeldung/test/LiveTestSuite.java new file mode 100644 index 0000000000..9e141a87cd --- /dev/null +++ b/spring-security-rest-digest-auth/src/test/java/org/baeldung/test/LiveTestSuite.java @@ -0,0 +1,18 @@ +package org.baeldung.test; + +import org.baeldung.client.ClientNoSpringLiveTest; +import org.baeldung.client.ClientWithSpringLiveTest; +import org.baeldung.client.RawClientLiveTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ +// @formatter:off + RawClientLiveTest.class + ,ClientWithSpringLiveTest.class + ,ClientNoSpringLiveTest.class +}) // +public class LiveTestSuite { + +} diff --git a/spring-security-rest-full/src/main/java/org/baeldung/spring/Application.java b/spring-security-rest-full/src/main/java/org/baeldung/spring/Application.java index 58a6ad02d8..c44e37fee8 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/spring/Application.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/spring/Application.java @@ -2,8 +2,10 @@ package org.baeldung.spring; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.filter.ShallowEtagHeaderFilter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** @@ -20,4 +22,8 @@ public class Application extends WebMvcConfigurerAdapter { SpringApplication.run(Application.class, args); } + @Bean + public ShallowEtagHeaderFilter shallowEtagHeaderFilter() { + return new ShallowEtagHeaderFilter(); + } } \ No newline at end of file diff --git a/spring-security-rest-full/src/main/java/org/baeldung/spring/SecurityWithoutCsrfConfig.java b/spring-security-rest-full/src/main/java/org/baeldung/spring/SecurityWithoutCsrfConfig.java index 6e3974f86d..fcb28f6ae2 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/spring/SecurityWithoutCsrfConfig.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/spring/SecurityWithoutCsrfConfig.java @@ -51,6 +51,8 @@ public class SecurityWithoutCsrfConfig extends WebSecurityConfigurerAdapter { .and() // .exceptionHandling().accessDeniedPage("/my-error-page") .exceptionHandling().accessDeniedHandler(accessDeniedHandler) + .and() + .headers().cacheControl().disable() ; // @formatter:on } diff --git a/spring-security-rest-full/src/test/java/org/baeldung/TestSuite.java b/spring-security-rest-full/src/test/java/org/baeldung/TestSuite.java new file mode 100644 index 0000000000..52e3607b12 --- /dev/null +++ b/spring-security-rest-full/src/test/java/org/baeldung/TestSuite.java @@ -0,0 +1,18 @@ +package org.baeldung; + +import org.baeldung.persistence.PersistenceTestSuite; +import org.baeldung.security.SecurityTestSuite; +import org.baeldung.web.LiveTestSuite; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ +// @formatter:off + PersistenceTestSuite.class + ,SecurityTestSuite.class + ,LiveTestSuite.class +}) // +public class TestSuite { + +} diff --git a/spring-security-rest-full/src/test/java/org/baeldung/client/RestTemplateLiveTest.java b/spring-security-rest-full/src/test/java/org/baeldung/client/RestTemplateLiveTest.java index b6753bdad2..fb40bd9d62 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/client/RestTemplateLiveTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/client/RestTemplateLiveTest.java @@ -6,6 +6,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -20,8 +21,10 @@ import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.baeldung.persistence.model.Foo; +import org.baeldung.spring.ConfigTest; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -30,6 +33,10 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RequestCallback; import org.springframework.web.client.RestTemplate; @@ -38,10 +45,13 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Charsets; +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { ConfigTest.class }, loader = AnnotationConfigContextLoader.class) +@ActiveProfiles("test") public class RestTemplateLiveTest { private RestTemplate restTemplate; - private static final String fooResourceUrl = "http://localhost:" + APPLICATION_PORT + "/spring-security-rest-full/foos"; + private static final String fooResourceUrl = "http://localhost:" + APPLICATION_PORT + "/foos"; @Before public void beforeTest() { @@ -66,7 +76,7 @@ public class RestTemplateLiveTest { final JsonNode root = mapper.readTree(response.getBody()); final JsonNode name = root.path("name"); - assertThat(name.asText(), is("bar")); + assertNotNull(name); final JsonNode owner = root.path("id"); assertThat(owner.asText(), is("1")); @@ -75,7 +85,7 @@ public class RestTemplateLiveTest { @Test public void givenResourceUrl_whenSendGetForObject_thenReturnsRepoObject() { final Foo foo = restTemplate.getForObject(fooResourceUrl + "/1", Foo.class); - assertThat(foo.getName(), is("bar")); + assertNotNull(foo.getName()); assertThat(foo.getId(), is(1L)); } diff --git a/spring-security-rest-full/src/test/java/org/baeldung/common/web/AbstractLiveTest.java b/spring-security-rest-full/src/test/java/org/baeldung/common/web/AbstractLiveTest.java index 32a736b546..95fce10e45 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/common/web/AbstractLiveTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/common/web/AbstractLiveTest.java @@ -1,16 +1,17 @@ package org.baeldung.common.web; +import static org.baeldung.Consts.APPLICATION_PORT; + +import java.io.Serializable; + +import org.baeldung.test.IMarshaller; +import org.springframework.beans.factory.annotation.Autowired; + import com.google.common.base.Preconditions; import com.google.common.net.HttpHeaders; import com.jayway.restassured.RestAssured; import com.jayway.restassured.response.Response; import com.jayway.restassured.specification.RequestSpecification; -import org.baeldung.test.IMarshaller; -import org.springframework.beans.factory.annotation.Autowired; - -import java.io.Serializable; - -import static org.baeldung.Consts.APPLICATION_PORT; public abstract class AbstractLiveTest { @@ -56,7 +57,7 @@ public abstract class AbstractLiveTest { // protected String getURL() { - return "http://localhost:" + APPLICATION_PORT + "/spring-security-rest-full/foos"; + return "http://localhost:" + APPLICATION_PORT + "/foos"; } protected final RequestSpecification givenAuth() { diff --git a/spring-security-rest-full/src/test/java/org/baeldung/persistence/PersistenceTestSuite.java b/spring-security-rest-full/src/test/java/org/baeldung/persistence/PersistenceTestSuite.java new file mode 100644 index 0000000000..0ce8c0300b --- /dev/null +++ b/spring-security-rest-full/src/test/java/org/baeldung/persistence/PersistenceTestSuite.java @@ -0,0 +1,22 @@ +package org.baeldung.persistence; + +import org.baeldung.persistence.query.JPACriteriaQueryTest; +import org.baeldung.persistence.query.JPAQuerydslTest; +import org.baeldung.persistence.query.JPASpecificationTest; +import org.baeldung.persistence.query.RsqlTest; +import org.baeldung.persistence.service.FooServicePersistenceIntegrationTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + // @formatter:off + RsqlTest.class + ,JPASpecificationTest.class + ,FooServicePersistenceIntegrationTest.class + ,JPAQuerydslTest.class + ,JPACriteriaQueryTest.class +}) // +public class PersistenceTestSuite { + +} diff --git a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPACriteriaQueryTest.java b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPACriteriaQueryTest.java index b805263cf7..f9f9435d75 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPACriteriaQueryTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPACriteriaQueryTest.java @@ -15,15 +15,15 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { PersistenceConfig.class }) @Transactional -@TransactionConfiguration +@Rollback public class JPACriteriaQueryTest { @Autowired diff --git a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPAQuerydslTest.java b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPAQuerydslTest.java index 5afd69b8be..b7b38a4fcb 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPAQuerydslTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPAQuerydslTest.java @@ -14,15 +14,15 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { PersistenceConfig.class }) @Transactional -@TransactionConfiguration +@Rollback public class JPAQuerydslTest { @Autowired diff --git a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationLiveTest.java b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationLiveTest.java index cd51250cb6..544161dfd5 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationLiveTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationLiveTest.java @@ -32,7 +32,7 @@ public class JPASpecificationLiveTest { private User userTom; - private final String URL_PREFIX = "http://localhost:8080/spring-security-rest-full/users/spec?search="; + private final String URL_PREFIX = "http://localhost:8080/users/spec?search="; @Before public void init() { diff --git a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationTest.java b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationTest.java index 0b8daa5a12..97b2274cf9 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationTest.java @@ -17,15 +17,15 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.Specifications; +import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { PersistenceConfig.class }) @Transactional -@TransactionConfiguration +@Rollback public class JPASpecificationTest { @Autowired diff --git a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/RsqlTest.java b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/RsqlTest.java index 0b02f533e8..e0deb8d4ec 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/RsqlTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/RsqlTest.java @@ -15,9 +15,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.Specification; +import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; import cz.jirutka.rsql.parser.RSQLParser; @@ -26,7 +26,7 @@ import cz.jirutka.rsql.parser.ast.Node; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { PersistenceConfig.class }) @Transactional -@TransactionConfiguration +@Rollback public class RsqlTest { @Autowired diff --git a/spring-security-rest-full/src/test/java/org/baeldung/security/SecurityTestSuite.java b/spring-security-rest-full/src/test/java/org/baeldung/security/SecurityTestSuite.java new file mode 100644 index 0000000000..5b19d9fbcc --- /dev/null +++ b/spring-security-rest-full/src/test/java/org/baeldung/security/SecurityTestSuite.java @@ -0,0 +1,17 @@ +package org.baeldung.security; + + +import org.baeldung.security.csrf.CsrfDisabledIntegrationTest; +import org.baeldung.security.csrf.CsrfEnabledIntegrationTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + // @formatter:off + CsrfEnabledIntegrationTest.class + ,CsrfDisabledIntegrationTest.class +}) // +public class SecurityTestSuite { + +} diff --git a/spring-security-rest-full/src/test/java/org/baeldung/csrf/CsrfAbstractIntegrationTest.java b/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfAbstractIntegrationTest.java similarity index 97% rename from spring-security-rest-full/src/test/java/org/baeldung/csrf/CsrfAbstractIntegrationTest.java rename to spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfAbstractIntegrationTest.java index a94dd554f1..3af91b82a2 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/csrf/CsrfAbstractIntegrationTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfAbstractIntegrationTest.java @@ -1,4 +1,4 @@ -package org.baeldung.csrf; +package org.baeldung.security.csrf; import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; diff --git a/spring-security-rest-full/src/test/java/org/baeldung/csrf/CsrfDisabledIntegrationTest.java b/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfDisabledIntegrationTest.java similarity index 96% rename from spring-security-rest-full/src/test/java/org/baeldung/csrf/CsrfDisabledIntegrationTest.java rename to spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfDisabledIntegrationTest.java index d223e89fe0..50b8ae3b44 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/csrf/CsrfDisabledIntegrationTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfDisabledIntegrationTest.java @@ -1,4 +1,4 @@ -package org.baeldung.csrf; +package org.baeldung.security.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; diff --git a/spring-security-rest-full/src/test/java/org/baeldung/csrf/CsrfEnabledIntegrationTest.java b/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfEnabledIntegrationTest.java similarity index 93% rename from spring-security-rest-full/src/test/java/org/baeldung/csrf/CsrfEnabledIntegrationTest.java rename to spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfEnabledIntegrationTest.java index fe6580bd05..c7caf61525 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/csrf/CsrfEnabledIntegrationTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/CsrfEnabledIntegrationTest.java @@ -1,11 +1,10 @@ -package org.baeldung.csrf; +package org.baeldung.security.csrf; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.baeldung.spring.PersistenceConfig; -import org.baeldung.spring.SecurityWithCsrfConfig; import org.baeldung.spring.WebConfig; import org.junit.Test; import org.springframework.http.MediaType; diff --git a/spring-security-rest-full/src/main/java/org/baeldung/spring/SecurityWithCsrfConfig.java b/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/SecurityWithCsrfConfig.java similarity index 90% rename from spring-security-rest-full/src/main/java/org/baeldung/spring/SecurityWithCsrfConfig.java rename to spring-security-rest-full/src/test/java/org/baeldung/security/csrf/SecurityWithCsrfConfig.java index c2a21c3f9e..99b94cd7b5 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/spring/SecurityWithCsrfConfig.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/security/csrf/SecurityWithCsrfConfig.java @@ -1,4 +1,4 @@ -package org.baeldung.spring; +package org.baeldung.security.csrf; import org.baeldung.web.error.CustomAccessDeniedHandler; import org.springframework.beans.factory.annotation.Autowired; @@ -11,10 +11,10 @@ import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -// @Configuration -// @EnableAutoConfiguration -// @EnableWebSecurity -// @EnableGlobalMethodSecurity(prePostEnabled = true) +@Configuration +@EnableAutoConfiguration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityWithCsrfConfig extends WebSecurityConfigurerAdapter { @Autowired @@ -47,6 +47,8 @@ public class SecurityWithCsrfConfig extends WebSecurityConfigurerAdapter { .httpBasic() .and() .exceptionHandling().accessDeniedHandler(accessDeniedHandler) + .and() + .headers().cacheControl().disable() ; // @formatter:on } diff --git a/spring-security-rest-full/src/test/java/org/baeldung/web/LiveTestSuite.java b/spring-security-rest-full/src/test/java/org/baeldung/web/LiveTestSuite.java new file mode 100644 index 0000000000..4c26350151 --- /dev/null +++ b/spring-security-rest-full/src/test/java/org/baeldung/web/LiveTestSuite.java @@ -0,0 +1,19 @@ +package org.baeldung.web; + +import org.baeldung.client.RestTemplateLiveTest; +import org.baeldung.persistence.query.JPASpecificationLiveTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ +// @formatter:off + JPASpecificationLiveTest.class + ,FooDiscoverabilityLiveTest.class + ,FooLiveTest.class + ,MyUserLiveTest.class + ,RestTemplateLiveTest.class +}) // +public class LiveTestSuite { + +} diff --git a/spring-security-rest-full/src/test/java/org/baeldung/web/MyUserLiveTest.java b/spring-security-rest-full/src/test/java/org/baeldung/web/MyUserLiveTest.java index ea5f609677..835b32c95c 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/web/MyUserLiveTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/web/MyUserLiveTest.java @@ -3,14 +3,15 @@ package org.baeldung.web; import static org.junit.Assert.assertEquals; import org.baeldung.persistence.model.MyUser; -import org.baeldung.spring.Application; +import org.baeldung.spring.ConfigTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -19,8 +20,8 @@ import com.jayway.restassured.response.Response; import com.jayway.restassured.specification.RequestSpecification; @RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(classes = Application.class) -@WebAppConfiguration +@ContextConfiguration(classes = { ConfigTest.class }, loader = AnnotationConfigContextLoader.class) +@ActiveProfiles("test") public class MyUserLiveTest { private ObjectMapper mapper = new ObjectMapper(); diff --git a/spring-security-rest/src/test/java/org/baeldung/web/FooLiveTest.java b/spring-security-rest/src/test/java/org/baeldung/web/FooLiveTest.java index 6e03300483..dc3a576b7b 100644 --- a/spring-security-rest/src/test/java/org/baeldung/web/FooLiveTest.java +++ b/spring-security-rest/src/test/java/org/baeldung/web/FooLiveTest.java @@ -11,7 +11,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.jayway.restassured.RestAssured; -import com.jayway.restassured.authentication.FormAuthConfig; import com.jayway.restassured.response.Response; import com.jayway.restassured.specification.RequestSpecification; @@ -19,10 +18,22 @@ import com.jayway.restassured.specification.RequestSpecification; @ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class) public class FooLiveTest { private static final String URL_PREFIX = "http://localhost:8080/spring-security-rest"; - private FormAuthConfig formConfig = new FormAuthConfig(URL_PREFIX + "/login", "username", "password"); + // private FormAuthConfig formConfig = new FormAuthConfig(URL_PREFIX + "/login", "temporary", "temporary"); + private String cookie; private RequestSpecification givenAuth() { - return RestAssured.given().auth().form("user", "userPass", formConfig); + // return RestAssured.given().auth().form("user", "userPass", formConfig); + if (cookie == null) + cookie = RestAssured.given().contentType("application/x-www-form-urlencoded").formParam("password", "userPass").formParam("username", "user").post(URL_PREFIX + "/login").getCookie("JSESSIONID"); + return RestAssured.given().cookie("JSESSIONID", cookie); + } + + @Test + public void whenTry_thenOK() { + final Response response = givenAuth().get(URL_PREFIX + "/api/foos"); + assertEquals(200, response.statusCode()); + System.out.println(response.asString()); + } @Test From f2dd909cec1039adb25672d501a1aa01c5d2aab1 Mon Sep 17 00:00:00 2001 From: David Morley Date: Tue, 12 Apr 2016 06:01:29 -0500 Subject: [PATCH 13/76] Clean up SpEL examples --- .../src/main/java/com/baeldung/spring/spel/SpelProgram.java | 2 +- .../java/com/baeldung/spring/spel/examples/SpelParser.java | 5 ----- spring-spel/src/main/resources/applicationContext.xml | 2 +- spring-spel/src/test/resources/applicationContext.xml | 2 +- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java b/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java index 1b1e1e6ae4..f399ce4a9c 100644 --- a/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java @@ -1,6 +1,6 @@ package com.baeldung.spring.spel; -import com.baeldung.spring.spel.examples.*; +import com.baeldung.spring.spel.examples.SpelConditional; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelParser.java b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelParser.java index ed34afddbd..f7063d4c46 100644 --- a/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelParser.java +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/examples/SpelParser.java @@ -2,16 +2,11 @@ package com.baeldung.spring.spel.examples; import com.baeldung.spring.spel.entity.Car; import com.baeldung.spring.spel.entity.CarPark; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; -import java.util.ArrayList; -import java.util.List; - public class SpelParser { public static void main(String[] args) { Car car = new Car(); diff --git a/spring-spel/src/main/resources/applicationContext.xml b/spring-spel/src/main/resources/applicationContext.xml index 815ac35837..998addae67 100644 --- a/spring-spel/src/main/resources/applicationContext.xml +++ b/spring-spel/src/main/resources/applicationContext.xml @@ -3,6 +3,6 @@ xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> - + \ No newline at end of file diff --git a/spring-spel/src/test/resources/applicationContext.xml b/spring-spel/src/test/resources/applicationContext.xml index 815ac35837..998addae67 100644 --- a/spring-spel/src/test/resources/applicationContext.xml +++ b/spring-spel/src/test/resources/applicationContext.xml @@ -3,6 +3,6 @@ xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> - + \ No newline at end of file From 9ca4d9f1b2e18612b6059291f8b1c226266388db Mon Sep 17 00:00:00 2001 From: SHYAM RAMATH Date: Tue, 12 Apr 2016 23:20:05 -0500 Subject: [PATCH 14/76] added my github name --- spring-rest-docs/.classpath | 31 +++ .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 49502 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + spring-rest-docs/.project | 35 +++ spring-rest-docs/mvnw | 233 ++++++++++++++++++ spring-rest-docs/mvnw.cmd | 145 +++++++++++ spring-rest-docs/pom.xml | 59 +++++ .../java/com/example/IndexController.java | 23 ++ .../java/com/example/MyRestController.java | 16 ++ .../example/SpringRestDocsApplication.java | 12 + .../src/main/resources/application.properties | 0 .../java/com/example/ApiDocumentation.java | 63 +++++ 12 files changed, 618 insertions(+) create mode 100644 spring-rest-docs/.classpath create mode 100644 spring-rest-docs/.mvn/wrapper/maven-wrapper.jar create mode 100644 spring-rest-docs/.mvn/wrapper/maven-wrapper.properties create mode 100644 spring-rest-docs/.project create mode 100755 spring-rest-docs/mvnw create mode 100644 spring-rest-docs/mvnw.cmd create mode 100644 spring-rest-docs/pom.xml create mode 100644 spring-rest-docs/src/main/java/com/example/IndexController.java create mode 100644 spring-rest-docs/src/main/java/com/example/MyRestController.java create mode 100644 spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java create mode 100644 spring-rest-docs/src/main/resources/application.properties create mode 100644 spring-rest-docs/src/test/java/com/example/ApiDocumentation.java diff --git a/spring-rest-docs/.classpath b/spring-rest-docs/.classpath new file mode 100644 index 0000000000..6d7587a819 --- /dev/null +++ b/spring-rest-docs/.classpath @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-rest-docs/.mvn/wrapper/maven-wrapper.jar b/spring-rest-docs/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5fd4d5023f1463b5ba3970e33c460c1eb26d748d GIT binary patch literal 49502 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%0A$pwhjXzyA0ZzErH{1@M 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 OJOEd-=rxTO#Qy`$*1Hh^ literal 0 HcmV?d00001 diff --git a/spring-rest-docs/.mvn/wrapper/maven-wrapper.properties b/spring-rest-docs/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..eb91947648 --- /dev/null +++ b/spring-rest-docs/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip \ No newline at end of file diff --git a/spring-rest-docs/.project b/spring-rest-docs/.project new file mode 100644 index 0000000000..bc93d30054 --- /dev/null +++ b/spring-rest-docs/.project @@ -0,0 +1,35 @@ + + + spring-rest-docs + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/spring-rest-docs/mvnw b/spring-rest-docs/mvnw new file mode 100755 index 0000000000..a1ba1bf554 --- /dev/null +++ b/spring-rest-docs/mvnw @@ -0,0 +1,233 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} "$@" diff --git a/spring-rest-docs/mvnw.cmd b/spring-rest-docs/mvnw.cmd new file mode 100644 index 0000000000..2b934e89dd --- /dev/null +++ b/spring-rest-docs/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% \ No newline at end of file diff --git a/spring-rest-docs/pom.xml b/spring-rest-docs/pom.xml new file mode 100644 index 0000000000..8514fd224f --- /dev/null +++ b/spring-rest-docs/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + com.example + demo + 0.0.1-SNAPSHOT + jar + + spring-rest-docs + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 1.3.3.RELEASE + + + + + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-hateoas + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.restdocs + spring-restdocs-mockmvc + 1.0.1.RELEASE + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/spring-rest-docs/src/main/java/com/example/IndexController.java b/spring-rest-docs/src/main/java/com/example/IndexController.java new file mode 100644 index 0000000000..063fad6037 --- /dev/null +++ b/spring-rest-docs/src/main/java/com/example/IndexController.java @@ -0,0 +1,23 @@ +package com.example; + + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; + +import org.springframework.hateoas.ResourceSupport; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api") +public class IndexController { + + @RequestMapping(method=RequestMethod.GET) + public ResourceSupport index() { + ResourceSupport index = new ResourceSupport(); + index.add(linkTo(MyRestController.class).withRel("notes")); + index.add(linkTo(MyRestController.class).withRel("tags")); + return index; + } + +} \ No newline at end of file diff --git a/spring-rest-docs/src/main/java/com/example/MyRestController.java b/spring-rest-docs/src/main/java/com/example/MyRestController.java new file mode 100644 index 0000000000..4ce40a1423 --- /dev/null +++ b/spring-rest-docs/src/main/java/com/example/MyRestController.java @@ -0,0 +1,16 @@ +package com.example; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/rest/api") +public class MyRestController { + + @RequestMapping(method=RequestMethod.GET) + public String index() { + return "Hello"; + } + +} diff --git a/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java b/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java new file mode 100644 index 0000000000..dd20ef324e --- /dev/null +++ b/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java @@ -0,0 +1,12 @@ +package com.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringRestDocsApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringRestDocsApplication.class, args); + } +} diff --git a/spring-rest-docs/src/main/resources/application.properties b/spring-rest-docs/src/main/resources/application.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java b/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java new file mode 100644 index 0000000000..530d307266 --- /dev/null +++ b/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java @@ -0,0 +1,63 @@ +package com.example; + +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.restdocs.RestDocumentation; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = SpringRestDocsApplication.class) +@WebAppConfiguration +public class ApiDocumentation { + + @Rule + public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets"); + + @Autowired + private WebApplicationContext context; + + private RestDocumentationResultHandler document; + + private MockMvc mockMvc; + + @Before + public void setUp() { + this.document = document("{method-name}",preprocessRequest(prettyPrint()),preprocessResponse(prettyPrint())); + + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).apply(documentationConfiguration(this.restDocumentation)).alwaysDo(this.document).build(); + } + + @Test + public void indexExample() throws Exception { + this.document.snippets(links(linkWithRel("notes").description("The <>"),linkWithRel("tags").description("The <>")), + responseFields(fieldWithPath("_links").description("<> to other resources"))); + + this.mockMvc.perform(get("/api")).andExpect(status().isOk()); + } + + @Test + public void contextLoads() { + } +} \ No newline at end of file From 4ab9fe66020b7955f031e27c4ac3c0c76dc45028 Mon Sep 17 00:00:00 2001 From: David Morley Date: Thu, 14 Apr 2016 06:08:48 -0500 Subject: [PATCH 15/76] Clean up SpEL examples --- .../com/baeldung/spring/spel/SpelProgram.java | 2 +- .../test/java/com/baeldung/spel/SpelTest.java | 100 ++++++++++-------- 2 files changed, 54 insertions(+), 48 deletions(-) diff --git a/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java b/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java index f399ce4a9c..3ad9e7d05d 100644 --- a/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java +++ b/spring-spel/src/main/java/com/baeldung/spring/spel/SpelProgram.java @@ -9,7 +9,7 @@ public class SpelProgram { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); SpelConditional spelCollections = (SpelConditional) context.getBean("spelConditional"); - // Here you can choose which bean do you want to load insted of spelConditional: spelCollections, spelLogical, etc. + // Here you can choose which bean do you want to load instead of spelConditional: spelCollections, spelLogical, etc. System.out.println(spelCollections); } diff --git a/spring-spel/src/test/java/com/baeldung/spel/SpelTest.java b/spring-spel/src/test/java/com/baeldung/spel/SpelTest.java index 3ca0a04f5b..5cf18bc1d2 100644 --- a/spring-spel/src/test/java/com/baeldung/spel/SpelTest.java +++ b/spring-spel/src/test/java/com/baeldung/spel/SpelTest.java @@ -9,91 +9,97 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = {"/applicationContext.xml"}) +@ContextConfiguration(locations = {"classpath:applicationContext.xml"}) public class SpelTest { @Autowired private SpelArithmetic spelArithmetic = new SpelArithmetic(); + @Autowired private SpelCollections spelCollections = new SpelCollections(); + @Autowired private SpelConditional spelConditional = new SpelConditional(); + @Autowired private SpelLogical spelLogical = new SpelLogical(); + @Autowired private SpelRegex spelRegex = new SpelRegex(); + @Autowired private SpelRelational spelRelational = new SpelRelational(); @Test public void testArithmetic() throws Exception { - Assert.assertThat(spelArithmetic.getAdd(), equalTo(20.0)); - Assert.assertThat(spelArithmetic.getAddString(), equalTo("Some string plus other string")); - Assert.assertThat(spelArithmetic.getSubtract(), equalTo(19.0)); - Assert.assertThat(spelArithmetic.getMultiply(), equalTo(20.0)); - Assert.assertThat(spelArithmetic.getDivide(), equalTo(18.0)); - Assert.assertThat(spelArithmetic.getDivideAlphabetic(), equalTo(18.0)); - Assert.assertThat(spelArithmetic.getModulo(), equalTo(7.0)); - Assert.assertThat(spelArithmetic.getModuloAlphabetic(), equalTo(7.0)); - Assert.assertThat(spelArithmetic.getPowerOf(), equalTo(512.0)); - Assert.assertThat(spelArithmetic.getBrackets(), equalTo(17.0)); + assertThat(spelArithmetic.getAdd(), equalTo(20.0)); + assertThat(spelArithmetic.getAddString(), equalTo("Some string plus other string")); + assertThat(spelArithmetic.getSubtract(), equalTo(19.0)); + assertThat(spelArithmetic.getMultiply(), equalTo(20.0)); + assertThat(spelArithmetic.getDivide(), equalTo(18.0)); + assertThat(spelArithmetic.getDivideAlphabetic(), equalTo(18.0)); + assertThat(spelArithmetic.getModulo(), equalTo(7.0)); + assertThat(spelArithmetic.getModuloAlphabetic(), equalTo(7.0)); + assertThat(spelArithmetic.getPowerOf(), equalTo(512.0)); + assertThat(spelArithmetic.getBrackets(), equalTo(17.0)); } @Test public void testCollections() throws Exception { - Assert.assertThat(spelCollections.getDriver1Car().getModel(), equalTo("Model1")); - Assert.assertThat(spelCollections.getDriver2Car().getModel(), equalTo("Model2")); - Assert.assertThat(spelCollections.getFirstCarInPark().getModel(), equalTo("Model1")); - Assert.assertThat(spelCollections.getNumberOfCarsInPark(), equalTo(2)); + assertThat(spelCollections.getDriver1Car().getModel(), equalTo("Model1")); + assertThat(spelCollections.getDriver2Car().getModel(), equalTo("Model2")); + assertThat(spelCollections.getFirstCarInPark().getModel(), equalTo("Model1")); + assertThat(spelCollections.getNumberOfCarsInPark(), equalTo(2)); } @Test public void testConditional() throws Exception { - Assert.assertThat(spelConditional.getTernary(), equalTo("Something went wrong. There was false value")); - Assert.assertThat(spelConditional.getTernary2(), equalTo("Some model")); - Assert.assertThat(spelConditional.getElvis(), equalTo("Some model")); + assertThat(spelConditional.getTernary(), equalTo("Something went wrong. There was false value")); + assertThat(spelConditional.getTernary2(), equalTo("Some model")); + assertThat(spelConditional.getElvis(), equalTo("Some model")); } @Test public void testLogical() throws Exception { - Assert.assertThat(spelLogical.isAnd(), equalTo(true)); - Assert.assertThat(spelLogical.isAndAlphabetic(), equalTo(true)); - Assert.assertThat(spelLogical.isOr(), equalTo(true)); - Assert.assertThat(spelLogical.isOrAlphabetic(), equalTo(true)); - Assert.assertThat(spelLogical.isNot(), equalTo(false)); - Assert.assertThat(spelLogical.isNotAlphabetic(), equalTo(false)); + assertThat(spelLogical.isAnd(), equalTo(true)); + assertThat(spelLogical.isAndAlphabetic(), equalTo(true)); + assertThat(spelLogical.isOr(), equalTo(true)); + assertThat(spelLogical.isOrAlphabetic(), equalTo(true)); + assertThat(spelLogical.isNot(), equalTo(false)); + assertThat(spelLogical.isNotAlphabetic(), equalTo(false)); } @Test public void testRegex() throws Exception { - Assert.assertThat(spelRegex.isValidNumericStringResult(), equalTo(true)); - Assert.assertThat(spelRegex.isInvalidNumericStringResult(), equalTo(false)); - Assert.assertThat(spelRegex.isValidAlphabeticStringResult(), equalTo(true)); - Assert.assertThat(spelRegex.isInvalidAlphabeticStringResult(), equalTo(false)); - Assert.assertThat(spelRegex.isValidFormatOfHorsePower(), equalTo(true)); + assertThat(spelRegex.isValidNumericStringResult(), equalTo(true)); + assertThat(spelRegex.isInvalidNumericStringResult(), equalTo(false)); + assertThat(spelRegex.isValidAlphabeticStringResult(), equalTo(true)); + assertThat(spelRegex.isInvalidAlphabeticStringResult(), equalTo(false)); + assertThat(spelRegex.isValidFormatOfHorsePower(), equalTo(true)); } @Test public void testRelational() throws Exception { - Assert.assertThat(spelRelational.isEqual(), equalTo(true)); - Assert.assertThat(spelRelational.isEqualAlphabetic(), equalTo(true)); - Assert.assertThat(spelRelational.isNotEqual(), equalTo(false)); - Assert.assertThat(spelRelational.isNotEqualAlphabetic(), equalTo(false)); - Assert.assertThat(spelRelational.isLessThan(), equalTo(false)); - Assert.assertThat(spelRelational.isLessThanAlphabetic(), equalTo(false)); - Assert.assertThat(spelRelational.isLessThanOrEqual(), equalTo(true)); - Assert.assertThat(spelRelational.isLessThanOrEqualAlphabetic(), equalTo(true)); - Assert.assertThat(spelRelational.isGreaterThan(), equalTo(false)); - Assert.assertThat(spelRelational.isGreaterThanAlphabetic(), equalTo(false)); - Assert.assertThat(spelRelational.isGreaterThanOrEqual(), equalTo(true)); - Assert.assertThat(spelRelational.isGreaterThanOrEqualAlphabetic(), equalTo(true)); - Assert.assertThat(spelRelational.isAnd(), equalTo(true)); - Assert.assertThat(spelRelational.isAndAlphabetic(), equalTo(true)); - Assert.assertThat(spelRelational.isOr(), equalTo(true)); - Assert.assertThat(spelRelational.isOrAlphabetic(), equalTo(true)); - Assert.assertThat(spelRelational.isNot(), equalTo(false)); - Assert.assertThat(spelRelational.isNotAlphabetic(), equalTo(false)); + assertThat(spelRelational.isEqual(), equalTo(true)); + assertThat(spelRelational.isEqualAlphabetic(), equalTo(true)); + assertThat(spelRelational.isNotEqual(), equalTo(false)); + assertThat(spelRelational.isNotEqualAlphabetic(), equalTo(false)); + assertThat(spelRelational.isLessThan(), equalTo(false)); + assertThat(spelRelational.isLessThanAlphabetic(), equalTo(false)); + assertThat(spelRelational.isLessThanOrEqual(), equalTo(true)); + assertThat(spelRelational.isLessThanOrEqualAlphabetic(), equalTo(true)); + assertThat(spelRelational.isGreaterThan(), equalTo(false)); + assertThat(spelRelational.isGreaterThanAlphabetic(), equalTo(false)); + assertThat(spelRelational.isGreaterThanOrEqual(), equalTo(true)); + assertThat(spelRelational.isGreaterThanOrEqualAlphabetic(), equalTo(true)); + assertThat(spelRelational.isAnd(), equalTo(true)); + assertThat(spelRelational.isAndAlphabetic(), equalTo(true)); + assertThat(spelRelational.isOr(), equalTo(true)); + assertThat(spelRelational.isOrAlphabetic(), equalTo(true)); + assertThat(spelRelational.isNot(), equalTo(false)); + assertThat(spelRelational.isNotAlphabetic(), equalTo(false)); } } From c2d84ae1d2125168b7f34473d97409d3cdb92ce6 Mon Sep 17 00:00:00 2001 From: Roshan Thomas Date: Fri, 15 Apr 2016 00:13:17 -0400 Subject: [PATCH 16/76] allOrders URL implementation --- .../org/baeldung/web/controller/CustomerController.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java b/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java index e103edcc19..3a14094440 100644 --- a/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java +++ b/spring-security-rest/src/main/java/org/baeldung/web/controller/CustomerController.java @@ -34,7 +34,12 @@ public class CustomerController { @RequestMapping(value = "/customer/{customerId}/orders", method = RequestMethod.GET) public List getOrdersForCustomer(@PathVariable final String customerId) { - return orderService.getAllOrdersForCustomer(customerId); + final List orders = orderService.getAllOrdersForCustomer(customerId); + for (final Order order : orders) { + final Link selfLink = ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(CustomerController.class).getOrderById(customerId, order.getOrderId())).withSelfRel(); + order.add(selfLink); + } + return orders; } @RequestMapping(value = "/customers", method = RequestMethod.GET) From 46da6af6ba415720cd133ee0d5959abd1a0d52d7 Mon Sep 17 00:00:00 2001 From: David Morley Date: Fri, 15 Apr 2016 05:53:48 -0500 Subject: [PATCH 17/76] Clean up Spring REST Docs examples --- spring-rest-docs/.classpath | 31 --- .../.mvn/wrapper/maven-wrapper.jar | Bin 49502 -> 0 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 - spring-rest-docs/.project | 35 --- spring-rest-docs/mvnw | 233 ------------------ spring-rest-docs/mvnw.cmd | 145 ----------- spring-rest-docs/pom.xml | 100 ++++---- .../java/com/example/IndexController.java | 18 +- .../java/com/example/MyRestController.java | 10 +- .../example/SpringRestDocsApplication.java | 6 +- .../java/com/example/ApiDocumentation.java | 79 +++--- 11 files changed, 108 insertions(+), 550 deletions(-) delete mode 100644 spring-rest-docs/.classpath delete mode 100644 spring-rest-docs/.mvn/wrapper/maven-wrapper.jar delete mode 100644 spring-rest-docs/.mvn/wrapper/maven-wrapper.properties delete mode 100644 spring-rest-docs/.project delete mode 100755 spring-rest-docs/mvnw delete mode 100644 spring-rest-docs/mvnw.cmd diff --git a/spring-rest-docs/.classpath b/spring-rest-docs/.classpath deleted file mode 100644 index 6d7587a819..0000000000 --- a/spring-rest-docs/.classpath +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-rest-docs/.mvn/wrapper/maven-wrapper.jar b/spring-rest-docs/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 5fd4d5023f1463b5ba3970e33c460c1eb26d748d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49502 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%0A$pwhjXzyA0ZzErH{1@M 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 OJOEd-=rxTO#Qy`$*1Hh^ diff --git a/spring-rest-docs/.mvn/wrapper/maven-wrapper.properties b/spring-rest-docs/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index eb91947648..0000000000 --- a/spring-rest-docs/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip \ No newline at end of file diff --git a/spring-rest-docs/.project b/spring-rest-docs/.project deleted file mode 100644 index bc93d30054..0000000000 --- a/spring-rest-docs/.project +++ /dev/null @@ -1,35 +0,0 @@ - - - spring-rest-docs - - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.springframework.ide.eclipse.core.springbuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.springframework.ide.eclipse.core.springnature - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - org.eclipse.wst.common.project.facet.core.nature - - diff --git a/spring-rest-docs/mvnw b/spring-rest-docs/mvnw deleted file mode 100755 index a1ba1bf554..0000000000 --- a/spring-rest-docs/mvnw +++ /dev/null @@ -1,233 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # - # Look for the Apple JDKs first to preserve the existing behaviour, and then look - # for the new JDKs provided by Oracle. - # - if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then - # - # Apple JDKs - # - export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home - fi - - if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then - # - # Apple JDKs - # - export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home - fi - - if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then - # - # Oracle JDKs - # - export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home - fi - - if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then - # - # Apple JDKs - # - export JAVA_HOME=`/usr/libexec/java_home` - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Migwn, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` -fi - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - local basedir=$(pwd) - local wdir=$(pwd) - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - wdir=$(cd "$wdir/.."; pwd) - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} "$@" diff --git a/spring-rest-docs/mvnw.cmd b/spring-rest-docs/mvnw.cmd deleted file mode 100644 index 2b934e89dd..0000000000 --- a/spring-rest-docs/mvnw.cmd +++ /dev/null @@ -1,145 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -set MAVEN_CMD_LINE_ARGS=%* - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% \ No newline at end of file diff --git a/spring-rest-docs/pom.xml b/spring-rest-docs/pom.xml index 8514fd224f..decdd3a5df 100644 --- a/spring-rest-docs/pom.xml +++ b/spring-rest-docs/pom.xml @@ -1,59 +1,59 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - com.example - demo - 0.0.1-SNAPSHOT - jar + com.example + demo + 0.0.1-SNAPSHOT + jar - spring-rest-docs - Demo project for Spring Boot + spring-rest-docs + Demo project for Spring Boot - - org.springframework.boot - spring-boot-starter-parent - 1.3.3.RELEASE - - + + org.springframework.boot + spring-boot-starter-parent + 1.3.3.RELEASE + + - - UTF-8 - 1.8 - + + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-hateoas + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.restdocs + spring-restdocs-mockmvc + 1.0.1.RELEASE + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + - - - org.springframework.boot - spring-boot-starter-hateoas - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.restdocs - spring-restdocs-mockmvc - 1.0.1.RELEASE - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/spring-rest-docs/src/main/java/com/example/IndexController.java b/spring-rest-docs/src/main/java/com/example/IndexController.java index 063fad6037..92d987f05d 100644 --- a/spring-rest-docs/src/main/java/com/example/IndexController.java +++ b/spring-rest-docs/src/main/java/com/example/IndexController.java @@ -1,23 +1,23 @@ package com.example; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; - import org.springframework.hateoas.ResourceSupport; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; + @RestController @RequestMapping("/api") public class IndexController { - @RequestMapping(method=RequestMethod.GET) - public ResourceSupport index() { - ResourceSupport index = new ResourceSupport(); - index.add(linkTo(MyRestController.class).withRel("notes")); - index.add(linkTo(MyRestController.class).withRel("tags")); - return index; - } + @RequestMapping(method = RequestMethod.GET) + public ResourceSupport index() { + ResourceSupport index = new ResourceSupport(); + index.add(linkTo(MyRestController.class).withRel("notes")); + index.add(linkTo(MyRestController.class).withRel("tags")); + return index; + } } \ No newline at end of file diff --git a/spring-rest-docs/src/main/java/com/example/MyRestController.java b/spring-rest-docs/src/main/java/com/example/MyRestController.java index 4ce40a1423..896b82abfb 100644 --- a/spring-rest-docs/src/main/java/com/example/MyRestController.java +++ b/spring-rest-docs/src/main/java/com/example/MyRestController.java @@ -7,10 +7,10 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/rest/api") public class MyRestController { - - @RequestMapping(method=RequestMethod.GET) - public String index() { - return "Hello"; - } + + @RequestMapping(method = RequestMethod.GET) + public String index() { + return "Hello"; + } } diff --git a/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java b/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java index dd20ef324e..da09f9accc 100644 --- a/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java +++ b/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java @@ -6,7 +6,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringRestDocsApplication { - public static void main(String[] args) { - SpringApplication.run(SpringRestDocsApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(SpringRestDocsApplication.class, args); + } } diff --git a/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java b/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java index 530d307266..5490e90ff5 100644 --- a/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java +++ b/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java @@ -1,12 +1,5 @@ package com.example; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -21,43 +14,53 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = SpringRestDocsApplication.class) @WebAppConfiguration public class ApiDocumentation { - @Rule - public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets"); - - @Autowired - private WebApplicationContext context; - - private RestDocumentationResultHandler document; - - private MockMvc mockMvc; - - @Before - public void setUp() { - this.document = document("{method-name}",preprocessRequest(prettyPrint()),preprocessResponse(prettyPrint())); - - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).apply(documentationConfiguration(this.restDocumentation)).alwaysDo(this.document).build(); - } - - @Test - public void indexExample() throws Exception { - this.document.snippets(links(linkWithRel("notes").description("The <>"),linkWithRel("tags").description("The <>")), - responseFields(fieldWithPath("_links").description("<> to other resources"))); - - this.mockMvc.perform(get("/api")).andExpect(status().isOk()); - } - - @Test - public void contextLoads() { - } + @Rule + public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets"); + + @Autowired + private WebApplicationContext context; + + private RestDocumentationResultHandler document; + + private MockMvc mockMvc; + + @Before + public void setUp() { + this.document = document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())); + + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(documentationConfiguration(this.restDocumentation)) + .alwaysDo(this.document).build(); + } + + @Test + public void indexExample() throws Exception { + this.document.snippets( + links( + linkWithRel("notes").description("The <>"), + linkWithRel("tags").description("The <>") + ), + responseFields(fieldWithPath("_links").description("<> to other resources"))); + + this.mockMvc.perform(get("/api")).andExpect(status().isOk()); + } + + @Test + public void contextLoads() { + } } \ No newline at end of file From c574b94ea34092088373bd96d1ce42a8582b751a Mon Sep 17 00:00:00 2001 From: amedviediev Date: Fri, 15 Apr 2016 23:47:32 +0300 Subject: [PATCH 18/76] - Added code for JUnit 5 article --- junit5/pom.xml | 83 +++++++++++++++++++ .../java/com/baeldung/AssumptionTest.java | 31 +++++++ .../test/java/com/baeldung/ExceptionTest.java | 15 ++++ .../src/test/java/com/baeldung/FirstTest.java | 27 ++++++ .../test/java/com/baeldung/NestedTest.java | 77 +++++++++++++++++ 5 files changed, 233 insertions(+) create mode 100644 junit5/pom.xml create mode 100644 junit5/src/test/java/com/baeldung/AssumptionTest.java create mode 100644 junit5/src/test/java/com/baeldung/ExceptionTest.java create mode 100644 junit5/src/test/java/com/baeldung/FirstTest.java create mode 100644 junit5/src/test/java/com/baeldung/NestedTest.java diff --git a/junit5/pom.xml b/junit5/pom.xml new file mode 100644 index 0000000000..5a2ea61668 --- /dev/null +++ b/junit5/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + com.baeldung + junit5 + 1.0-SNAPSHOT + + junit5 + Intro to JUnit 5 + + + UTF-8 + 1.8 + + 5.0.0-SNAPSHOT + + + + + snapshots-repo + https://oss.sonatype.org/content/repositories/snapshots + + false + + + + always + true + + + + + + + snapshots-repo + https://oss.sonatype.org/content/repositories/snapshots + + false + + + + always + true + + + + + + + + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 2.19 + + + org.junit + surefire-junit5 + ${junit.gen5.version} + + + + + + + + + org.junit + junit5-api + ${junit.gen5.version} + test + + + \ No newline at end of file diff --git a/junit5/src/test/java/com/baeldung/AssumptionTest.java b/junit5/src/test/java/com/baeldung/AssumptionTest.java new file mode 100644 index 0000000000..6e6e56fe0a --- /dev/null +++ b/junit5/src/test/java/com/baeldung/AssumptionTest.java @@ -0,0 +1,31 @@ +package com.baeldung; + +import org.junit.gen5.api.Test; + +import static org.junit.gen5.api.Assumptions.assumeFalse; +import static org.junit.gen5.api.Assumptions.assumeTrue; +import static org.junit.gen5.api.Assumptions.assumingThat; + +public class AssumptionTest { + + @Test + void trueAssumption() { + assumeTrue(5 > 1); + System.out.println("Was true"); + } + + @Test + void falseAssumption() { + assumeFalse(5 < 1); + System.out.println("Was false"); + } + + @Test + void assumptionThat() { + String someString = "Just a string"; + assumingThat( + someString.equals("Just a string"), + () -> System.out.println("Assumption was correct") + ); + } +} diff --git a/junit5/src/test/java/com/baeldung/ExceptionTest.java b/junit5/src/test/java/com/baeldung/ExceptionTest.java new file mode 100644 index 0000000000..a9a5b8301d --- /dev/null +++ b/junit5/src/test/java/com/baeldung/ExceptionTest.java @@ -0,0 +1,15 @@ +package com.baeldung; + +import org.junit.gen5.api.Test; + +import static org.junit.gen5.api.Assertions.assertEquals; +import static org.junit.gen5.api.Assertions.expectThrows; + +public class ExceptionTest { + + @Test + void shouldThrowException() { + Throwable exception = expectThrows(NullPointerException.class, null); + assertEquals(exception.getClass(), NullPointerException.class); + } +} diff --git a/junit5/src/test/java/com/baeldung/FirstTest.java b/junit5/src/test/java/com/baeldung/FirstTest.java new file mode 100644 index 0000000000..c0dc6f345c --- /dev/null +++ b/junit5/src/test/java/com/baeldung/FirstTest.java @@ -0,0 +1,27 @@ +package com.baeldung; + +import org.junit.gen5.api.Test; + +import static org.junit.gen5.api.Assertions.assertAll; +import static org.junit.gen5.api.Assertions.assertEquals; +import static org.junit.gen5.api.Assertions.assertTrue; + +class FirstTest { + + @Test + void lambdaExpressions() { + String string = ""; + assertTrue(() -> string.isEmpty(), "String should be empty"); + } + + @Test + void groupAssertions() { + int[] numbers = {0,1,2,3,4}; + assertAll("numbers", () -> { + assertEquals(numbers[0], 1); + assertEquals(numbers[3], 3); + assertEquals(numbers[4], 1); + }); + } + +} diff --git a/junit5/src/test/java/com/baeldung/NestedTest.java b/junit5/src/test/java/com/baeldung/NestedTest.java new file mode 100644 index 0000000000..3fbe4f8644 --- /dev/null +++ b/junit5/src/test/java/com/baeldung/NestedTest.java @@ -0,0 +1,77 @@ +package com.baeldung; + +import org.junit.gen5.api.*; + +import java.util.EmptyStackException; +import java.util.Stack; + +public class NestedTest { + Stack stack; + boolean isRun = false; + + @Test + @DisplayName("is instantiated with new Stack()") + void isInstantiatedWithNew() { + new Stack(); + } + + @Nested + @DisplayName("when new") + class WhenNew { + + @BeforeEach + void init() { + stack = new Stack(); + } + + @Test + @DisplayName("is empty") + void isEmpty() { + Assertions.assertTrue(stack.isEmpty()); + } + + @Test + @DisplayName("throws EmptyStackException when popped") + void throwsExceptionWhenPopped() { + Assertions.expectThrows(EmptyStackException.class, () -> stack.pop()); + } + + @Test + @DisplayName("throws EmptyStackException when peeked") + void throwsExceptionWhenPeeked() { + Assertions.expectThrows(EmptyStackException.class, () -> stack.peek()); + } + + @Nested + @DisplayName("after pushing an element") + class AfterPushing { + + String anElement = "an element"; + + @BeforeEach + void init() { + stack.push(anElement); + } + + @Test + @DisplayName("it is no longer empty") + void isEmpty() { + Assertions.assertFalse(stack.isEmpty()); + } + + @Test + @DisplayName("returns the element when popped and is empty") + void returnElementWhenPopped() { + Assertions.assertEquals(anElement, stack.pop()); + Assertions.assertTrue(stack.isEmpty()); + } + + @Test + @DisplayName("returns the element when peeked but remains not empty") + void returnElementWhenPeeked() { + Assertions.assertEquals(anElement, stack.peek()); + Assertions.assertFalse(stack.isEmpty()); + } + } + } +} From 70e9d2fa18d6c72185f5a204ebfd81dfdbc22f4f Mon Sep 17 00:00:00 2001 From: Ivan Paolillo Date: Mon, 18 Apr 2016 16:42:18 +0200 Subject: [PATCH 19/76] Add websocket example --- spring-mvc-java/pom.xml | 22 +++++++++++++++++++ .../web/config/MainWebAppInitializer.java | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/spring-mvc-java/pom.xml b/spring-mvc-java/pom.xml index 4358e7939f..19610f5ab0 100644 --- a/spring-mvc-java/pom.xml +++ b/spring-mvc-java/pom.xml @@ -17,6 +17,28 @@ spring-webmvc ${org.springframework.version} + + org.springframework + spring-websocket + ${org.springframework.version} + + + org.springframework + spring-messaging + ${org.springframework.version} + + + + com.fasterxml.jackson.core + jackson-core + 2.7.3 + + + com.fasterxml.jackson.core + jackson-databind + 2.7.3 + + javax.servlet diff --git a/spring-mvc-java/src/main/java/org/baeldung/spring/web/config/MainWebAppInitializer.java b/spring-mvc-java/src/main/java/org/baeldung/spring/web/config/MainWebAppInitializer.java index ad37bbec5e..60043ef342 100644 --- a/spring-mvc-java/src/main/java/org/baeldung/spring/web/config/MainWebAppInitializer.java +++ b/spring-mvc-java/src/main/java/org/baeldung/spring/web/config/MainWebAppInitializer.java @@ -14,7 +14,7 @@ import org.springframework.web.servlet.DispatcherServlet; public class MainWebAppInitializer implements WebApplicationInitializer { - private static final String TMP_FOLDER = "C:/Users/ivan/Desktop/tmp"; + private static final String TMP_FOLDER = "/tmp"; private static final int MAX_UPLOAD_SIZE = 5 * 1024 * 1024; // 5 MB /** From 4d1dd06a26f0aeb703b99dba63ec1ef28e4c58bf Mon Sep 17 00:00:00 2001 From: Ivan Paolillo Date: Mon, 18 Apr 2016 16:44:01 +0200 Subject: [PATCH 20/76] Add websocket example --- .../main/java/org/baeldung/model/Message.java | 10 + .../org/baeldung/model/OutputMessage.java | 20 + .../spring/web/config/WebSocketConfig.java | 24 + .../web/controller/ChatController.java | 30 + .../src/main/webapp/WEB-INF/view/chat.jsp | 84 + .../main/webapp/resources/js/sockjs-0.3.4.js | 2378 +++++++++++++++++ .../src/main/webapp/resources/js/stomp.js | 475 ++++ 7 files changed, 3021 insertions(+) create mode 100644 spring-mvc-java/src/main/java/org/baeldung/model/Message.java create mode 100644 spring-mvc-java/src/main/java/org/baeldung/model/OutputMessage.java create mode 100644 spring-mvc-java/src/main/java/org/baeldung/spring/web/config/WebSocketConfig.java create mode 100644 spring-mvc-java/src/main/java/org/baeldung/web/controller/ChatController.java create mode 100644 spring-mvc-java/src/main/webapp/WEB-INF/view/chat.jsp create mode 100644 spring-mvc-java/src/main/webapp/resources/js/sockjs-0.3.4.js create mode 100644 spring-mvc-java/src/main/webapp/resources/js/stomp.js diff --git a/spring-mvc-java/src/main/java/org/baeldung/model/Message.java b/spring-mvc-java/src/main/java/org/baeldung/model/Message.java new file mode 100644 index 0000000000..9a1350996f --- /dev/null +++ b/spring-mvc-java/src/main/java/org/baeldung/model/Message.java @@ -0,0 +1,10 @@ +package org.baeldung.model; + +public class Message { + + private String text; + + public String getText() { + return text; + } +} diff --git a/spring-mvc-java/src/main/java/org/baeldung/model/OutputMessage.java b/spring-mvc-java/src/main/java/org/baeldung/model/OutputMessage.java new file mode 100644 index 0000000000..4bee5e9e4c --- /dev/null +++ b/spring-mvc-java/src/main/java/org/baeldung/model/OutputMessage.java @@ -0,0 +1,20 @@ +package org.baeldung.model; + +public class OutputMessage { + + private String text; + private String time; + + public OutputMessage(final String text, final String time) { + this.text = text; + this.time = time; + } + + public String getText() { + return text; + } + + public String getTime() { + return time; + } +} diff --git a/spring-mvc-java/src/main/java/org/baeldung/spring/web/config/WebSocketConfig.java b/spring-mvc-java/src/main/java/org/baeldung/spring/web/config/WebSocketConfig.java new file mode 100644 index 0000000000..03f4cb54e5 --- /dev/null +++ b/spring-mvc-java/src/main/java/org/baeldung/spring/web/config/WebSocketConfig.java @@ -0,0 +1,24 @@ +package org.baeldung.spring.web.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; + +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { + + @Override + public void configureMessageBroker(final MessageBrokerRegistry config) { + config.enableSimpleBroker("/topic"); + config.setApplicationDestinationPrefixes("/app"); + } + + @Override + public void registerStompEndpoints(final StompEndpointRegistry registry) { + registry.addEndpoint("/chat").withSockJS(); + } + +} \ No newline at end of file diff --git a/spring-mvc-java/src/main/java/org/baeldung/web/controller/ChatController.java b/spring-mvc-java/src/main/java/org/baeldung/web/controller/ChatController.java new file mode 100644 index 0000000000..0570636752 --- /dev/null +++ b/spring-mvc-java/src/main/java/org/baeldung/web/controller/ChatController.java @@ -0,0 +1,30 @@ +package org.baeldung.web.controller; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.baeldung.model.Message; +import org.baeldung.model.OutputMessage; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class ChatController { + + @RequestMapping(value = "/showChat", method = RequestMethod.GET) + public String displayChat() { + return "chat"; + } + + @MessageMapping("/chat") + @SendTo("/topic/") + public OutputMessage send(final Message message) throws Exception { + + final String time = new SimpleDateFormat("HH:mm").format(new Date()); + return new OutputMessage(message.getText(), time); + } + +} diff --git a/spring-mvc-java/src/main/webapp/WEB-INF/view/chat.jsp b/spring-mvc-java/src/main/webapp/WEB-INF/view/chat.jsp new file mode 100644 index 0000000000..1d0d13e211 --- /dev/null +++ b/spring-mvc-java/src/main/webapp/WEB-INF/view/chat.jsp @@ -0,0 +1,84 @@ +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + Chat WebSocket + + + + + + + + + + + + + +
+
+ + +
+
+
+ + +

+
+
+ + + \ No newline at end of file diff --git a/spring-mvc-java/src/main/webapp/resources/js/sockjs-0.3.4.js b/spring-mvc-java/src/main/webapp/resources/js/sockjs-0.3.4.js new file mode 100644 index 0000000000..9f5b8f3deb --- /dev/null +++ b/spring-mvc-java/src/main/webapp/resources/js/sockjs-0.3.4.js @@ -0,0 +1,2378 @@ +/* SockJS client, version 0.3.4, http://sockjs.org, MIT License + +Copyright (c) 2011-2012 VMware, Inc. + +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. +*/ + +// JSON2 by Douglas Crockford (minified). +var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c 1) { + this._listeners[eventType] = arr.slice(0, idx).concat( arr.slice(idx+1) ); + } else { + delete this._listeners[eventType]; + } + return; + } + return; +}; + +REventTarget.prototype.dispatchEvent = function (event) { + var t = event.type; + var args = Array.prototype.slice.call(arguments, 0); + if (this['on'+t]) { + this['on'+t].apply(this, args); + } + if (this._listeners && t in this._listeners) { + for(var i=0; i < this._listeners[t].length; i++) { + this._listeners[t][i].apply(this, args); + } + } +}; +// [*] End of lib/reventtarget.js + + +// [*] Including lib/simpleevent.js +/* + * ***** BEGIN LICENSE BLOCK ***** + * Copyright (c) 2011-2012 VMware, Inc. + * + * For the license see COPYING. + * ***** END LICENSE BLOCK ***** + */ + +var SimpleEvent = function(type, obj) { + this.type = type; + if (typeof obj !== 'undefined') { + for(var k in obj) { + if (!obj.hasOwnProperty(k)) continue; + this[k] = obj[k]; + } + } +}; + +SimpleEvent.prototype.toString = function() { + var r = []; + for(var k in this) { + if (!this.hasOwnProperty(k)) continue; + var v = this[k]; + if (typeof v === 'function') v = '[function]'; + r.push(k + '=' + v); + } + return 'SimpleEvent(' + r.join(', ') + ')'; +}; +// [*] End of lib/simpleevent.js + + +// [*] Including lib/eventemitter.js +/* + * ***** BEGIN LICENSE BLOCK ***** + * Copyright (c) 2011-2012 VMware, Inc. + * + * For the license see COPYING. + * ***** END LICENSE BLOCK ***** + */ + +var EventEmitter = function(events) { + var that = this; + that._events = events || []; + that._listeners = {}; +}; +EventEmitter.prototype.emit = function(type) { + var that = this; + that._verifyType(type); + if (that._nuked) return; + + var args = Array.prototype.slice.call(arguments, 1); + if (that['on'+type]) { + that['on'+type].apply(that, args); + } + if (type in that._listeners) { + for(var i = 0; i < that._listeners[type].length; i++) { + that._listeners[type][i].apply(that, args); + } + } +}; + +EventEmitter.prototype.on = function(type, callback) { + var that = this; + that._verifyType(type); + if (that._nuked) return; + + if (!(type in that._listeners)) { + that._listeners[type] = []; + } + that._listeners[type].push(callback); +}; + +EventEmitter.prototype._verifyType = function(type) { + var that = this; + if (utils.arrIndexOf(that._events, type) === -1) { + utils.log('Event ' + JSON.stringify(type) + + ' not listed ' + JSON.stringify(that._events) + + ' in ' + that); + } +}; + +EventEmitter.prototype.nuke = function() { + var that = this; + that._nuked = true; + for(var i=0; i= 3000 && code <= 4999); +}; + +// See: http://www.erg.abdn.ac.uk/~gerrit/dccp/notes/ccid2/rto_estimator/ +// and RFC 2988. +utils.countRTO = function (rtt) { + var rto; + if (rtt > 100) { + rto = 3 * rtt; // rto > 300msec + } else { + rto = rtt + 200; // 200msec < rto <= 300msec + } + return rto; +} + +utils.log = function() { + if (_window.console && console.log && console.log.apply) { + console.log.apply(console, arguments); + } +}; + +utils.bind = function(fun, that) { + if (fun.bind) { + return fun.bind(that); + } else { + return function() { + return fun.apply(that, arguments); + }; + } +}; + +utils.flatUrl = function(url) { + return url.indexOf('?') === -1 && url.indexOf('#') === -1; +}; + +utils.amendUrl = function(url) { + var dl = _document.location; + if (!url) { + throw new Error('Wrong url for SockJS'); + } + if (!utils.flatUrl(url)) { + throw new Error('Only basic urls are supported in SockJS'); + } + + // '//abc' --> 'http://abc' + if (url.indexOf('//') === 0) { + url = dl.protocol + url; + } + // '/abc' --> 'http://localhost:80/abc' + if (url.indexOf('/') === 0) { + url = dl.protocol + '//' + dl.host + url; + } + // strip trailing slashes + url = url.replace(/[/]+$/,''); + return url; +}; + +// IE doesn't support [].indexOf. +utils.arrIndexOf = function(arr, obj){ + for(var i=0; i < arr.length; i++){ + if(arr[i] === obj){ + return i; + } + } + return -1; +}; + +utils.arrSkip = function(arr, obj) { + var idx = utils.arrIndexOf(arr, obj); + if (idx === -1) { + return arr.slice(); + } else { + var dst = arr.slice(0, idx); + return dst.concat(arr.slice(idx+1)); + } +}; + +// Via: https://gist.github.com/1133122/2121c601c5549155483f50be3da5305e83b8c5df +utils.isArray = Array.isArray || function(value) { + return {}.toString.call(value).indexOf('Array') >= 0 +}; + +utils.delay = function(t, fun) { + if(typeof t === 'function') { + fun = t; + t = 0; + } + return setTimeout(fun, t); +}; + + +// Chars worth escaping, as defined by Douglas Crockford: +// https://github.com/douglascrockford/JSON-js/blob/47a9882cddeb1e8529e07af9736218075372b8ac/json2.js#L196 +var json_escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + json_lookup = { +"\u0000":"\\u0000","\u0001":"\\u0001","\u0002":"\\u0002","\u0003":"\\u0003", +"\u0004":"\\u0004","\u0005":"\\u0005","\u0006":"\\u0006","\u0007":"\\u0007", +"\b":"\\b","\t":"\\t","\n":"\\n","\u000b":"\\u000b","\f":"\\f","\r":"\\r", +"\u000e":"\\u000e","\u000f":"\\u000f","\u0010":"\\u0010","\u0011":"\\u0011", +"\u0012":"\\u0012","\u0013":"\\u0013","\u0014":"\\u0014","\u0015":"\\u0015", +"\u0016":"\\u0016","\u0017":"\\u0017","\u0018":"\\u0018","\u0019":"\\u0019", +"\u001a":"\\u001a","\u001b":"\\u001b","\u001c":"\\u001c","\u001d":"\\u001d", +"\u001e":"\\u001e","\u001f":"\\u001f","\"":"\\\"","\\":"\\\\", +"\u007f":"\\u007f","\u0080":"\\u0080","\u0081":"\\u0081","\u0082":"\\u0082", +"\u0083":"\\u0083","\u0084":"\\u0084","\u0085":"\\u0085","\u0086":"\\u0086", +"\u0087":"\\u0087","\u0088":"\\u0088","\u0089":"\\u0089","\u008a":"\\u008a", +"\u008b":"\\u008b","\u008c":"\\u008c","\u008d":"\\u008d","\u008e":"\\u008e", +"\u008f":"\\u008f","\u0090":"\\u0090","\u0091":"\\u0091","\u0092":"\\u0092", +"\u0093":"\\u0093","\u0094":"\\u0094","\u0095":"\\u0095","\u0096":"\\u0096", +"\u0097":"\\u0097","\u0098":"\\u0098","\u0099":"\\u0099","\u009a":"\\u009a", +"\u009b":"\\u009b","\u009c":"\\u009c","\u009d":"\\u009d","\u009e":"\\u009e", +"\u009f":"\\u009f","\u00ad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601", +"\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f", +"\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d", +"\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029", +"\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d", +"\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061", +"\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065", +"\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069", +"\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d", +"\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0", +"\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4", +"\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8", +"\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc", +"\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"}; + +// Some extra characters that Chrome gets wrong, and substitutes with +// something else on the wire. +var extra_escapable = /[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g, + extra_lookup; + +// JSON Quote string. Use native implementation when possible. +var JSONQuote = (JSON && JSON.stringify) || function(string) { + json_escapable.lastIndex = 0; + if (json_escapable.test(string)) { + string = string.replace(json_escapable, function(a) { + return json_lookup[a]; + }); + } + return '"' + string + '"'; +}; + +// This may be quite slow, so let's delay until user actually uses bad +// characters. +var unroll_lookup = function(escapable) { + var i; + var unrolled = {} + var c = [] + for(i=0; i<65536; i++) { + c.push( String.fromCharCode(i) ); + } + escapable.lastIndex = 0; + c.join('').replace(escapable, function (a) { + unrolled[ a ] = '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + return ''; + }); + escapable.lastIndex = 0; + return unrolled; +}; + +// Quote string, also taking care of unicode characters that browsers +// often break. Especially, take care of unicode surrogates: +// http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates +utils.quote = function(string) { + var quoted = JSONQuote(string); + + // In most cases this should be very fast and good enough. + extra_escapable.lastIndex = 0; + if(!extra_escapable.test(quoted)) { + return quoted; + } + + if(!extra_lookup) extra_lookup = unroll_lookup(extra_escapable); + + return quoted.replace(extra_escapable, function(a) { + return extra_lookup[a]; + }); +} + +var _all_protocols = ['websocket', + 'xdr-streaming', + 'xhr-streaming', + 'iframe-eventsource', + 'iframe-htmlfile', + 'xdr-polling', + 'xhr-polling', + 'iframe-xhr-polling', + 'jsonp-polling']; + +utils.probeProtocols = function() { + var probed = {}; + for(var i=0; i<_all_protocols.length; i++) { + var protocol = _all_protocols[i]; + // User can have a typo in protocol name. + probed[protocol] = SockJS[protocol] && + SockJS[protocol].enabled(); + } + return probed; +}; + +utils.detectProtocols = function(probed, protocols_whitelist, info) { + var pe = {}, + protocols = []; + if (!protocols_whitelist) protocols_whitelist = _all_protocols; + for(var i=0; i 0) { + maybe_push(protos); + } + } + } + + // 1. Websocket + if (info.websocket !== false) { + maybe_push(['websocket']); + } + + // 2. Streaming + if (pe['xhr-streaming'] && !info.null_origin) { + protocols.push('xhr-streaming'); + } else { + if (pe['xdr-streaming'] && !info.cookie_needed && !info.null_origin) { + protocols.push('xdr-streaming'); + } else { + maybe_push(['iframe-eventsource', + 'iframe-htmlfile']); + } + } + + // 3. Polling + if (pe['xhr-polling'] && !info.null_origin) { + protocols.push('xhr-polling'); + } else { + if (pe['xdr-polling'] && !info.cookie_needed && !info.null_origin) { + protocols.push('xdr-polling'); + } else { + maybe_push(['iframe-xhr-polling', + 'jsonp-polling']); + } + } + return protocols; +} +// [*] End of lib/utils.js + + +// [*] Including lib/dom.js +/* + * ***** BEGIN LICENSE BLOCK ***** + * Copyright (c) 2011-2012 VMware, Inc. + * + * For the license see COPYING. + * ***** END LICENSE BLOCK ***** + */ + +// May be used by htmlfile jsonp and transports. +var MPrefix = '_sockjs_global'; +utils.createHook = function() { + var window_id = 'a' + utils.random_string(8); + if (!(MPrefix in _window)) { + var map = {}; + _window[MPrefix] = function(window_id) { + if (!(window_id in map)) { + map[window_id] = { + id: window_id, + del: function() {delete map[window_id];} + }; + } + return map[window_id]; + } + } + return _window[MPrefix](window_id); +}; + + + +utils.attachMessage = function(listener) { + utils.attachEvent('message', listener); +}; +utils.attachEvent = function(event, listener) { + if (typeof _window.addEventListener !== 'undefined') { + _window.addEventListener(event, listener, false); + } else { + // IE quirks. + // According to: http://stevesouders.com/misc/test-postmessage.php + // the message gets delivered only to 'document', not 'window'. + _document.attachEvent("on" + event, listener); + // I get 'window' for ie8. + _window.attachEvent("on" + event, listener); + } +}; + +utils.detachMessage = function(listener) { + utils.detachEvent('message', listener); +}; +utils.detachEvent = function(event, listener) { + if (typeof _window.addEventListener !== 'undefined') { + _window.removeEventListener(event, listener, false); + } else { + _document.detachEvent("on" + event, listener); + _window.detachEvent("on" + event, listener); + } +}; + + +var on_unload = {}; +// Things registered after beforeunload are to be called immediately. +var after_unload = false; + +var trigger_unload_callbacks = function() { + for(var ref in on_unload) { + on_unload[ref](); + delete on_unload[ref]; + }; +}; + +var unload_triggered = function() { + if(after_unload) return; + after_unload = true; + trigger_unload_callbacks(); +}; + +// 'unload' alone is not reliable in opera within an iframe, but we +// can't use `beforeunload` as IE fires it on javascript: links. +utils.attachEvent('unload', unload_triggered); + +utils.unload_add = function(listener) { + var ref = utils.random_string(8); + on_unload[ref] = listener; + if (after_unload) { + utils.delay(trigger_unload_callbacks); + } + return ref; +}; +utils.unload_del = function(ref) { + if (ref in on_unload) + delete on_unload[ref]; +}; + + +utils.createIframe = function (iframe_url, error_callback) { + var iframe = _document.createElement('iframe'); + var tref, unload_ref; + var unattach = function() { + clearTimeout(tref); + // Explorer had problems with that. + try {iframe.onload = null;} catch (x) {} + iframe.onerror = null; + }; + var cleanup = function() { + if (iframe) { + unattach(); + // This timeout makes chrome fire onbeforeunload event + // within iframe. Without the timeout it goes straight to + // onunload. + setTimeout(function() { + if(iframe) { + iframe.parentNode.removeChild(iframe); + } + iframe = null; + }, 0); + utils.unload_del(unload_ref); + } + }; + var onerror = function(r) { + if (iframe) { + cleanup(); + error_callback(r); + } + }; + var post = function(msg, origin) { + try { + // When the iframe is not loaded, IE raises an exception + // on 'contentWindow'. + if (iframe && iframe.contentWindow) { + iframe.contentWindow.postMessage(msg, origin); + } + } catch (x) {}; + }; + + iframe.src = iframe_url; + iframe.style.display = 'none'; + iframe.style.position = 'absolute'; + iframe.onerror = function(){onerror('onerror');}; + iframe.onload = function() { + // `onload` is triggered before scripts on the iframe are + // executed. Give it few seconds to actually load stuff. + clearTimeout(tref); + tref = setTimeout(function(){onerror('onload timeout');}, 2000); + }; + _document.body.appendChild(iframe); + tref = setTimeout(function(){onerror('timeout');}, 15000); + unload_ref = utils.unload_add(cleanup); + return { + post: post, + cleanup: cleanup, + loaded: unattach + }; +}; + +utils.createHtmlfile = function (iframe_url, error_callback) { + var doc = new ActiveXObject('htmlfile'); + var tref, unload_ref; + var iframe; + var unattach = function() { + clearTimeout(tref); + }; + var cleanup = function() { + if (doc) { + unattach(); + utils.unload_del(unload_ref); + iframe.parentNode.removeChild(iframe); + iframe = doc = null; + CollectGarbage(); + } + }; + var onerror = function(r) { + if (doc) { + cleanup(); + error_callback(r); + } + }; + var post = function(msg, origin) { + try { + // When the iframe is not loaded, IE raises an exception + // on 'contentWindow'. + if (iframe && iframe.contentWindow) { + iframe.contentWindow.postMessage(msg, origin); + } + } catch (x) {}; + }; + + doc.open(); + doc.write('' + + 'document.domain="' + document.domain + '";' + + ''); + doc.close(); + doc.parentWindow[WPrefix] = _window[WPrefix]; + var c = doc.createElement('div'); + doc.body.appendChild(c); + iframe = doc.createElement('iframe'); + c.appendChild(iframe); + iframe.src = iframe_url; + tref = setTimeout(function(){onerror('timeout');}, 15000); + unload_ref = utils.unload_add(cleanup); + return { + post: post, + cleanup: cleanup, + loaded: unattach + }; +}; +// [*] End of lib/dom.js + + +// [*] Including lib/dom2.js +/* + * ***** BEGIN LICENSE BLOCK ***** + * Copyright (c) 2011-2012 VMware, Inc. + * + * For the license see COPYING. + * ***** END LICENSE BLOCK ***** + */ + +var AbstractXHRObject = function(){}; +AbstractXHRObject.prototype = new EventEmitter(['chunk', 'finish']); + +AbstractXHRObject.prototype._start = function(method, url, payload, opts) { + var that = this; + + try { + that.xhr = new XMLHttpRequest(); + } catch(x) {}; + + if (!that.xhr) { + try { + that.xhr = new _window.ActiveXObject('Microsoft.XMLHTTP'); + } catch(x) {}; + } + if (_window.ActiveXObject || _window.XDomainRequest) { + // IE8 caches even POSTs + url += ((url.indexOf('?') === -1) ? '?' : '&') + 't='+(+new Date); + } + + // Explorer tends to keep connection open, even after the + // tab gets closed: http://bugs.jquery.com/ticket/5280 + that.unload_ref = utils.unload_add(function(){that._cleanup(true);}); + try { + that.xhr.open(method, url, true); + } catch(e) { + // IE raises an exception on wrong port. + that.emit('finish', 0, ''); + that._cleanup(); + return; + }; + + if (!opts || !opts.no_credentials) { + // Mozilla docs says https://developer.mozilla.org/en/XMLHttpRequest : + // "This never affects same-site requests." + that.xhr.withCredentials = 'true'; + } + if (opts && opts.headers) { + for(var key in opts.headers) { + that.xhr.setRequestHeader(key, opts.headers[key]); + } + } + + that.xhr.onreadystatechange = function() { + if (that.xhr) { + var x = that.xhr; + switch (x.readyState) { + case 3: + // IE doesn't like peeking into responseText or status + // on Microsoft.XMLHTTP and readystate=3 + try { + var status = x.status; + var text = x.responseText; + } catch (x) {}; + // IE returns 1223 for 204: http://bugs.jquery.com/ticket/1450 + if (status === 1223) status = 204; + + // IE does return readystate == 3 for 404 answers. + if (text && text.length > 0) { + that.emit('chunk', status, text); + } + break; + case 4: + var status = x.status; + // IE returns 1223 for 204: http://bugs.jquery.com/ticket/1450 + if (status === 1223) status = 204; + + that.emit('finish', status, x.responseText); + that._cleanup(false); + break; + } + } + }; + that.xhr.send(payload); +}; + +AbstractXHRObject.prototype._cleanup = function(abort) { + var that = this; + if (!that.xhr) return; + utils.unload_del(that.unload_ref); + + // IE needs this field to be a function + that.xhr.onreadystatechange = function(){}; + + if (abort) { + try { + that.xhr.abort(); + } catch(x) {}; + } + that.unload_ref = that.xhr = null; +}; + +AbstractXHRObject.prototype.close = function() { + var that = this; + that.nuke(); + that._cleanup(true); +}; + +var XHRCorsObject = utils.XHRCorsObject = function() { + var that = this, args = arguments; + utils.delay(function(){that._start.apply(that, args);}); +}; +XHRCorsObject.prototype = new AbstractXHRObject(); + +var XHRLocalObject = utils.XHRLocalObject = function(method, url, payload) { + var that = this; + utils.delay(function(){ + that._start(method, url, payload, { + no_credentials: true + }); + }); +}; +XHRLocalObject.prototype = new AbstractXHRObject(); + + + +// References: +// http://ajaxian.com/archives/100-line-ajax-wrapper +// http://msdn.microsoft.com/en-us/library/cc288060(v=VS.85).aspx +var XDRObject = utils.XDRObject = function(method, url, payload) { + var that = this; + utils.delay(function(){that._start(method, url, payload);}); +}; +XDRObject.prototype = new EventEmitter(['chunk', 'finish']); +XDRObject.prototype._start = function(method, url, payload) { + var that = this; + var xdr = new XDomainRequest(); + // IE caches even POSTs + url += ((url.indexOf('?') === -1) ? '?' : '&') + 't='+(+new Date); + + var onerror = xdr.ontimeout = xdr.onerror = function() { + that.emit('finish', 0, ''); + that._cleanup(false); + }; + xdr.onprogress = function() { + that.emit('chunk', 200, xdr.responseText); + }; + xdr.onload = function() { + that.emit('finish', 200, xdr.responseText); + that._cleanup(false); + }; + that.xdr = xdr; + that.unload_ref = utils.unload_add(function(){that._cleanup(true);}); + try { + // Fails with AccessDenied if port number is bogus + that.xdr.open(method, url); + that.xdr.send(payload); + } catch(x) { + onerror(); + } +}; + +XDRObject.prototype._cleanup = function(abort) { + var that = this; + if (!that.xdr) return; + utils.unload_del(that.unload_ref); + + that.xdr.ontimeout = that.xdr.onerror = that.xdr.onprogress = + that.xdr.onload = null; + if (abort) { + try { + that.xdr.abort(); + } catch(x) {}; + } + that.unload_ref = that.xdr = null; +}; + +XDRObject.prototype.close = function() { + var that = this; + that.nuke(); + that._cleanup(true); +}; + +// 1. Is natively via XHR +// 2. Is natively via XDR +// 3. Nope, but postMessage is there so it should work via the Iframe. +// 4. Nope, sorry. +utils.isXHRCorsCapable = function() { + if (_window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()) { + return 1; + } + // XDomainRequest doesn't work if page is served from file:// + if (_window.XDomainRequest && _document.domain) { + return 2; + } + if (IframeTransport.enabled()) { + return 3; + } + return 4; +}; +// [*] End of lib/dom2.js + + +// [*] Including lib/sockjs.js +/* + * ***** BEGIN LICENSE BLOCK ***** + * Copyright (c) 2011-2012 VMware, Inc. + * + * For the license see COPYING. + * ***** END LICENSE BLOCK ***** + */ + +var SockJS = function(url, dep_protocols_whitelist, options) { + if (this === _window) { + // makes `new` optional + return new SockJS(url, dep_protocols_whitelist, options); + } + + var that = this, protocols_whitelist; + that._options = {devel: false, debug: false, protocols_whitelist: [], + info: undefined, rtt: undefined}; + if (options) { + utils.objectExtend(that._options, options); + } + that._base_url = utils.amendUrl(url); + that._server = that._options.server || utils.random_number_string(1000); + if (that._options.protocols_whitelist && + that._options.protocols_whitelist.length) { + protocols_whitelist = that._options.protocols_whitelist; + } else { + // Deprecated API + if (typeof dep_protocols_whitelist === 'string' && + dep_protocols_whitelist.length > 0) { + protocols_whitelist = [dep_protocols_whitelist]; + } else if (utils.isArray(dep_protocols_whitelist)) { + protocols_whitelist = dep_protocols_whitelist + } else { + protocols_whitelist = null; + } + if (protocols_whitelist) { + that._debug('Deprecated API: Use "protocols_whitelist" option ' + + 'instead of supplying protocol list as a second ' + + 'parameter to SockJS constructor.'); + } + } + that._protocols = []; + that.protocol = null; + that.readyState = SockJS.CONNECTING; + that._ir = createInfoReceiver(that._base_url); + that._ir.onfinish = function(info, rtt) { + that._ir = null; + if (info) { + if (that._options.info) { + // Override if user supplies the option + info = utils.objectExtend(info, that._options.info); + } + if (that._options.rtt) { + rtt = that._options.rtt; + } + that._applyInfo(info, rtt, protocols_whitelist); + that._didClose(); + } else { + that._didClose(1002, 'Can\'t connect to server', true); + } + }; +}; +// Inheritance +SockJS.prototype = new REventTarget(); + +SockJS.version = "0.3.4"; + +SockJS.CONNECTING = 0; +SockJS.OPEN = 1; +SockJS.CLOSING = 2; +SockJS.CLOSED = 3; + +SockJS.prototype._debug = function() { + if (this._options.debug) + utils.log.apply(utils, arguments); +}; + +SockJS.prototype._dispatchOpen = function() { + var that = this; + if (that.readyState === SockJS.CONNECTING) { + if (that._transport_tref) { + clearTimeout(that._transport_tref); + that._transport_tref = null; + } + that.readyState = SockJS.OPEN; + that.dispatchEvent(new SimpleEvent("open")); + } else { + // The server might have been restarted, and lost track of our + // connection. + that._didClose(1006, "Server lost session"); + } +}; + +SockJS.prototype._dispatchMessage = function(data) { + var that = this; + if (that.readyState !== SockJS.OPEN) + return; + that.dispatchEvent(new SimpleEvent("message", {data: data})); +}; + +SockJS.prototype._dispatchHeartbeat = function(data) { + var that = this; + if (that.readyState !== SockJS.OPEN) + return; + that.dispatchEvent(new SimpleEvent('heartbeat', {})); +}; + +SockJS.prototype._didClose = function(code, reason, force) { + var that = this; + if (that.readyState !== SockJS.CONNECTING && + that.readyState !== SockJS.OPEN && + that.readyState !== SockJS.CLOSING) + throw new Error('INVALID_STATE_ERR'); + if (that._ir) { + that._ir.nuke(); + that._ir = null; + } + + if (that._transport) { + that._transport.doCleanup(); + that._transport = null; + } + + var close_event = new SimpleEvent("close", { + code: code, + reason: reason, + wasClean: utils.userSetCode(code)}); + + if (!utils.userSetCode(code) && + that.readyState === SockJS.CONNECTING && !force) { + if (that._try_next_protocol(close_event)) { + return; + } + close_event = new SimpleEvent("close", {code: 2000, + reason: "All transports failed", + wasClean: false, + last_event: close_event}); + } + that.readyState = SockJS.CLOSED; + + utils.delay(function() { + that.dispatchEvent(close_event); + }); +}; + +SockJS.prototype._didMessage = function(data) { + var that = this; + var type = data.slice(0, 1); + switch(type) { + case 'o': + that._dispatchOpen(); + break; + case 'a': + var payload = JSON.parse(data.slice(1) || '[]'); + for(var i=0; i < payload.length; i++){ + that._dispatchMessage(payload[i]); + } + break; + case 'm': + var payload = JSON.parse(data.slice(1) || 'null'); + that._dispatchMessage(payload); + break; + case 'c': + var payload = JSON.parse(data.slice(1) || '[]'); + that._didClose(payload[0], payload[1]); + break; + case 'h': + that._dispatchHeartbeat(); + break; + } +}; + +SockJS.prototype._try_next_protocol = function(close_event) { + var that = this; + if (that.protocol) { + that._debug('Closed transport:', that.protocol, ''+close_event); + that.protocol = null; + } + if (that._transport_tref) { + clearTimeout(that._transport_tref); + that._transport_tref = null; + } + + while(1) { + var protocol = that.protocol = that._protocols.shift(); + if (!protocol) { + return false; + } + // Some protocols require access to `body`, what if were in + // the `head`? + if (SockJS[protocol] && + SockJS[protocol].need_body === true && + (!_document.body || + (typeof _document.readyState !== 'undefined' + && _document.readyState !== 'complete'))) { + that._protocols.unshift(protocol); + that.protocol = 'waiting-for-load'; + utils.attachEvent('load', function(){ + that._try_next_protocol(); + }); + return true; + } + + if (!SockJS[protocol] || + !SockJS[protocol].enabled(that._options)) { + that._debug('Skipping transport:', protocol); + } else { + var roundTrips = SockJS[protocol].roundTrips || 1; + var to = ((that._options.rto || 0) * roundTrips) || 5000; + that._transport_tref = utils.delay(to, function() { + if (that.readyState === SockJS.CONNECTING) { + // I can't understand how it is possible to run + // this timer, when the state is CLOSED, but + // apparently in IE everythin is possible. + that._didClose(2007, "Transport timeouted"); + } + }); + + var connid = utils.random_string(8); + var trans_url = that._base_url + '/' + that._server + '/' + connid; + that._debug('Opening transport:', protocol, ' url:'+trans_url, + ' RTO:'+that._options.rto); + that._transport = new SockJS[protocol](that, trans_url, + that._base_url); + return true; + } + } +}; + +SockJS.prototype.close = function(code, reason) { + var that = this; + if (code && !utils.userSetCode(code)) + throw new Error("INVALID_ACCESS_ERR"); + if(that.readyState !== SockJS.CONNECTING && + that.readyState !== SockJS.OPEN) { + return false; + } + that.readyState = SockJS.CLOSING; + that._didClose(code || 1000, reason || "Normal closure"); + return true; +}; + +SockJS.prototype.send = function(data) { + var that = this; + if (that.readyState === SockJS.CONNECTING) + throw new Error('INVALID_STATE_ERR'); + if (that.readyState === SockJS.OPEN) { + that._transport.doSend(utils.quote('' + data)); + } + return true; +}; + +SockJS.prototype._applyInfo = function(info, rtt, protocols_whitelist) { + var that = this; + that._options.info = info; + that._options.rtt = rtt; + that._options.rto = utils.countRTO(rtt); + that._options.info.null_origin = !_document.domain; + var probed = utils.probeProtocols(); + that._protocols = utils.detectProtocols(probed, protocols_whitelist, info); +}; +// [*] End of lib/sockjs.js + + +// [*] Including lib/trans-websocket.js +/* + * ***** BEGIN LICENSE BLOCK ***** + * Copyright (c) 2011-2012 VMware, Inc. + * + * For the license see COPYING. + * ***** END LICENSE BLOCK ***** + */ + +var WebSocketTransport = SockJS.websocket = function(ri, trans_url) { + var that = this; + var url = trans_url + '/websocket'; + if (url.slice(0, 5) === 'https') { + url = 'wss' + url.slice(5); + } else { + url = 'ws' + url.slice(4); + } + that.ri = ri; + that.url = url; + var Constructor = _window.WebSocket || _window.MozWebSocket; + + that.ws = new Constructor(that.url); + that.ws.onmessage = function(e) { + that.ri._didMessage(e.data); + }; + // Firefox has an interesting bug. If a websocket connection is + // created after onunload, it stays alive even when user + // navigates away from the page. In such situation let's lie - + // let's not open the ws connection at all. See: + // https://github.com/sockjs/sockjs-client/issues/28 + // https://bugzilla.mozilla.org/show_bug.cgi?id=696085 + that.unload_ref = utils.unload_add(function(){that.ws.close()}); + that.ws.onclose = function() { + that.ri._didMessage(utils.closeFrame(1006, "WebSocket connection broken")); + }; +}; + +WebSocketTransport.prototype.doSend = function(data) { + this.ws.send('[' + data + ']'); +}; + +WebSocketTransport.prototype.doCleanup = function() { + var that = this; + var ws = that.ws; + if (ws) { + ws.onmessage = ws.onclose = null; + ws.close(); + utils.unload_del(that.unload_ref); + that.unload_ref = that.ri = that.ws = null; + } +}; + +WebSocketTransport.enabled = function() { + return !!(_window.WebSocket || _window.MozWebSocket); +}; + +// In theory, ws should require 1 round trip. But in chrome, this is +// not very stable over SSL. Most likely a ws connection requires a +// separate SSL connection, in which case 2 round trips are an +// absolute minumum. +WebSocketTransport.roundTrips = 2; +// [*] End of lib/trans-websocket.js + + +// [*] Including lib/trans-sender.js +/* + * ***** BEGIN LICENSE BLOCK ***** + * Copyright (c) 2011-2012 VMware, Inc. + * + * For the license see COPYING. + * ***** END LICENSE BLOCK ***** + */ + +var BufferedSender = function() {}; +BufferedSender.prototype.send_constructor = function(sender) { + var that = this; + that.send_buffer = []; + that.sender = sender; +}; +BufferedSender.prototype.doSend = function(message) { + var that = this; + that.send_buffer.push(message); + if (!that.send_stop) { + that.send_schedule(); + } +}; + +// For polling transports in a situation when in the message callback, +// new message is being send. If the sending connection was started +// before receiving one, it is possible to saturate the network and +// timeout due to the lack of receiving socket. To avoid that we delay +// sending messages by some small time, in order to let receiving +// connection be started beforehand. This is only a halfmeasure and +// does not fix the big problem, but it does make the tests go more +// stable on slow networks. +BufferedSender.prototype.send_schedule_wait = function() { + var that = this; + var tref; + that.send_stop = function() { + that.send_stop = null; + clearTimeout(tref); + }; + tref = utils.delay(25, function() { + that.send_stop = null; + that.send_schedule(); + }); +}; + +BufferedSender.prototype.send_schedule = function() { + var that = this; + if (that.send_buffer.length > 0) { + var payload = '[' + that.send_buffer.join(',') + ']'; + that.send_stop = that.sender(that.trans_url, payload, function(success, abort_reason) { + that.send_stop = null; + if (success === false) { + that.ri._didClose(1006, 'Sending error ' + abort_reason); + } else { + that.send_schedule_wait(); + } + }); + that.send_buffer = []; + } +}; + +BufferedSender.prototype.send_destructor = function() { + var that = this; + if (that._send_stop) { + that._send_stop(); + } + that._send_stop = null; +}; + +var jsonPGenericSender = function(url, payload, callback) { + var that = this; + + if (!('_send_form' in that)) { + var form = that._send_form = _document.createElement('form'); + var area = that._send_area = _document.createElement('textarea'); + area.name = 'd'; + form.style.display = 'none'; + form.style.position = 'absolute'; + form.method = 'POST'; + form.enctype = 'application/x-www-form-urlencoded'; + form.acceptCharset = "UTF-8"; + form.appendChild(area); + _document.body.appendChild(form); + } + var form = that._send_form; + var area = that._send_area; + var id = 'a' + utils.random_string(8); + form.target = id; + form.action = url + '/jsonp_send?i=' + id; + + var iframe; + try { + // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) + iframe = _document.createElement('