From e7e6326a0034fd40a5bc43ac5d35a00d5371cab7 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 17 Mar 2017 13:57:34 -0400 Subject: [PATCH 001/102] Spring MVC Custom Validator --- spring-mvc-java/pom.xml | 23 ++++++++++- .../ContactNumberConstraint.java | 22 ++++++++++ .../ContactNumberValidator.java | 19 +++++++++ .../com/baeldung/model/ValidatedPhone.java | 18 ++++++++ .../controller/ValidatedPhoneController.java | 35 ++++++++++++++++ .../src/main/webapp/WEB-INF/mvc-servlet.xml | 16 ++++++-- .../main/webapp/WEB-INF/view/phoneHome.jsp | 38 +++++++++++++++++ .../controller/CustomMVCValidatorTest.java | 41 +++++++++++++++++++ 8 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java create mode 100644 spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java create mode 100644 spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java create mode 100644 spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java create mode 100644 spring-mvc-java/src/main/webapp/WEB-INF/view/phoneHome.jsp create mode 100644 spring-mvc-java/src/test/java/com/baeldung/web/controller/CustomMVCValidatorTest.java diff --git a/spring-mvc-java/pom.xml b/spring-mvc-java/pom.xml index ef18cef3e0..0f6dbfbd98 100644 --- a/spring-mvc-java/pom.xml +++ b/spring-mvc-java/pom.xml @@ -166,7 +166,28 @@ poi-ooxml ${poi.version} - + + + + javax.validation + validation-api + 1.1.0.Final + + + org.hibernate + hibernate-validator + 5.1.2.Final + + + javax.el + javax.el-api + 2.2.4 + + + org.glassfish.web + javax.el + 2.2.4 + diff --git a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java new file mode 100644 index 0000000000..2fba2720c3 --- /dev/null +++ b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java @@ -0,0 +1,22 @@ +package com.baeldung.customvalidator; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Documented +@Constraint(validatedBy = ContactNumberValidator.class) +@Target( { ElementType.METHOD, ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ContactNumberConstraint { + + String message() default "Invalid phone number"; + Class[] groups() default {}; + Class[] payload() default {}; + +} diff --git a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java new file mode 100644 index 0000000000..a7eb7a9df4 --- /dev/null +++ b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java @@ -0,0 +1,19 @@ +package com.baeldung.customvalidator; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class ContactNumberValidator implements ConstraintValidator { + + @Override + public void initialize(ContactNumberConstraint contactNumber) {} + + @Override + public boolean isValid(String contactField, ConstraintValidatorContext cxt) { + if(contactField == null) { + return false; + } + return contactField.matches("[0-9]+") && (contactField.length() > 8) && (contactField.length() < 14); + } + +} diff --git a/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java b/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java new file mode 100644 index 0000000000..f860394707 --- /dev/null +++ b/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java @@ -0,0 +1,18 @@ +package com.baeldung.model; + +import com.baeldung.customvalidator.ContactNumberConstraint; + +public class ValidatedPhone { + + @ContactNumberConstraint + private String phone; + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + +} diff --git a/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java b/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java new file mode 100644 index 0000000000..70b151e066 --- /dev/null +++ b/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java @@ -0,0 +1,35 @@ +package com.baeldung.web.controller; + +import javax.validation.Valid; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import com.baeldung.model.ValidatedPhone; + +@Controller +@EnableWebMvc +public class ValidatedPhoneController { + + @RequestMapping(value="/validatePhone", method=RequestMethod.GET) + public String loadFormPage(Model m) { + m.addAttribute("validatedPhone", new ValidatedPhone()); + return "phoneHome"; + } + + @RequestMapping(value="/addValidatePhone", method=RequestMethod.POST) + public String submitForm(@Valid ValidatedPhone validatedPhone, BindingResult result, Model m) { + if(result.hasErrors()) { + return "phoneHome"; + } + + m.addAttribute("message", "Successfully saved phone: " + validatedPhone.toString()); + return "phoneHome"; + } + + +} diff --git a/spring-mvc-java/src/main/webapp/WEB-INF/mvc-servlet.xml b/spring-mvc-java/src/main/webapp/WEB-INF/mvc-servlet.xml index 4ba9642448..b0a4d4892a 100644 --- a/spring-mvc-java/src/main/webapp/WEB-INF/mvc-servlet.xml +++ b/spring-mvc-java/src/main/webapp/WEB-INF/mvc-servlet.xml @@ -1,6 +1,14 @@ - - + + + \ No newline at end of file diff --git a/spring-mvc-java/src/main/webapp/WEB-INF/view/phoneHome.jsp b/spring-mvc-java/src/main/webapp/WEB-INF/view/phoneHome.jsp new file mode 100644 index 0000000000..b873e9bc5f --- /dev/null +++ b/spring-mvc-java/src/main/webapp/WEB-INF/view/phoneHome.jsp @@ -0,0 +1,38 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> + + + + + Sample Form + + + + +
+ +

Phone Number

+
${message}
+ + + + + + +
+ + +
+
+ + diff --git a/spring-mvc-java/src/test/java/com/baeldung/web/controller/CustomMVCValidatorTest.java b/spring-mvc-java/src/test/java/com/baeldung/web/controller/CustomMVCValidatorTest.java new file mode 100644 index 0000000000..069cc8e141 --- /dev/null +++ b/spring-mvc-java/src/test/java/com/baeldung/web/controller/CustomMVCValidatorTest.java @@ -0,0 +1,41 @@ +package com.baeldung.web.controller; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +public class CustomMVCValidatorTest { + + private MockMvc mockMvc; + + @Before + public void setup(){ + this.mockMvc = MockMvcBuilders.standaloneSetup(new ValidatedPhoneController()).build(); + } + + @Test + public void givenPhonePageUri_whenMockMvc_thenReturnsPhonePage() throws Exception{ + this.mockMvc.perform(get("/validatePhone")).andExpect(view().name("phoneHome")); + } + + @Test + public void givenPhoneURIWithPostAndFormData_whenMockMVC_thenVerifyErrorResponse() throws Exception { + this.mockMvc.perform(MockMvcRequestBuilders.post("/addValidatePhone"). + accept(MediaType.TEXT_HTML). + param("phoneInput", "123")). + andExpect(model().attributeHasFieldErrorCode("validatedPhone", "phone","ContactNumberConstraint")). + andExpect(view().name("phoneHome")). + andExpect(status().isOk()). + andDo(print()); + } + +} From 42f1ef0bf38782db75a00cfb5a7af678b490407a Mon Sep 17 00:00:00 2001 From: slavisa-baeldung Date: Sat, 18 Mar 2017 09:20:33 +0000 Subject: [PATCH 002/102] BAEL-112 - custom validator - format fixes --- .../ContactNumberConstraint.java | 6 +++-- .../ContactNumberValidator.java | 7 +++--- .../com/baeldung/model/ValidatedPhone.java | 6 ++++- .../controller/ValidatedPhoneController.java | 24 +++++++++---------- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java index 2fba2720c3..dbd38c1122 100644 --- a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java +++ b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberConstraint.java @@ -11,12 +11,14 @@ import javax.validation.Payload; @Documented @Constraint(validatedBy = ContactNumberValidator.class) -@Target( { ElementType.METHOD, ElementType.FIELD }) +@Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ContactNumberConstraint { String message() default "Invalid phone number"; + Class[] groups() default {}; + Class[] payload() default {}; - + } diff --git a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java index a7eb7a9df4..713b7429cf 100644 --- a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java +++ b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java @@ -6,14 +6,15 @@ import javax.validation.ConstraintValidatorContext; public class ContactNumberValidator implements ConstraintValidator { @Override - public void initialize(ContactNumberConstraint contactNumber) {} + public void initialize(ContactNumberConstraint contactNumber) { + } @Override public boolean isValid(String contactField, ConstraintValidatorContext cxt) { - if(contactField == null) { + if (contactField == null) { return false; } - return contactField.matches("[0-9]+") && (contactField.length() > 8) && (contactField.length() < 14); + return contactField.matches("[0-9]+") && (contactField.length() > 8) && (contactField.length() < 14); } } diff --git a/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java b/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java index f860394707..be702a8517 100644 --- a/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java +++ b/spring-mvc-java/src/main/java/com/baeldung/model/ValidatedPhone.java @@ -3,7 +3,7 @@ package com.baeldung.model; import com.baeldung.customvalidator.ContactNumberConstraint; public class ValidatedPhone { - + @ContactNumberConstraint private String phone; @@ -15,4 +15,8 @@ public class ValidatedPhone { this.phone = phone; } + @Override + public String toString() { + return phone; + } } diff --git a/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java b/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java index 70b151e066..54b0e19e60 100644 --- a/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java +++ b/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java @@ -15,21 +15,21 @@ import com.baeldung.model.ValidatedPhone; @EnableWebMvc public class ValidatedPhoneController { - @RequestMapping(value="/validatePhone", method=RequestMethod.GET) + @RequestMapping(value = "/validatePhone", method = RequestMethod.GET) public String loadFormPage(Model m) { - m.addAttribute("validatedPhone", new ValidatedPhone()); - return "phoneHome"; + m.addAttribute("validatedPhone", new ValidatedPhone()); + return "phoneHome"; } - - @RequestMapping(value="/addValidatePhone", method=RequestMethod.POST) + + @RequestMapping(value = "/addValidatePhone", method = RequestMethod.POST) public String submitForm(@Valid ValidatedPhone validatedPhone, BindingResult result, Model m) { - if(result.hasErrors()) { - return "phoneHome"; - } - - m.addAttribute("message", "Successfully saved phone: " + validatedPhone.toString()); + if (result.hasErrors()) { return "phoneHome"; + } + + m.addAttribute("message", "Successfully saved phone: " + validatedPhone.toString()); + return "phoneHome"; } - - + + } From 32c21f27221951c78f89782c6579b0814ee3599a Mon Sep 17 00:00:00 2001 From: KevinGilmore Date: Sun, 19 Mar 2017 13:55:38 -0500 Subject: [PATCH 003/102] BAEL-731: Updated README (#1452) * Add files via upload * Update pom.xml * Update RunGuice.java * Update Communication.java * Update CommunicationMode.java * Update DefaultCommunicator.java * Update EmailCommunicationMode.java * Update IMCommunicationMode.java * Update SMSCommunicationMode.java * Update MessageLogger.java * Update MessageSentLoggable.java * Update AOPModule.java * Update BasicModule.java * Update CommunicationModel.java * Update Communicator.java * Update BasicModule.java * Update RunGuice.java * Update MessageLogger.java * Update Communicator.java * Update pom.xml * BAEL-278: Updated README.md * BAEL-554: Add and update README.md files * Update pom.xml * Update pom.xml * Update pom.xml * BAEL-345: fixed assertion * BAEL-109: Updated README.md * BAEL-345: Added README.md * Reinstating reactor-core module in root-level pom * BAEL-393: Adding guide-intro module to root pom * BAEL-9: Updated README.md * BAEL-157: README.md updated * Changed project name * Update RunGuice.java Removed references to message logging and output * Update Communication.java Removed message logging-related code * BAEL-566: Updated README.md * New project name * BAEL-393: removing guice-intro directory * BAEL-393: renamed module guice-intro to guice in root pom.xml * BAEL-393 and BAEL-541 README.md files * BAEL-731: Updated README.md --- spring-boot/README.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-boot/README.MD b/spring-boot/README.MD index d70e83525b..8aa5957bad 100644 --- a/spring-boot/README.MD +++ b/spring-boot/README.MD @@ -14,4 +14,5 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [How to Register a Servlet in a Java Web Application](http://www.baeldung.com/register-servlet) - [Guide to Spring WebUtils and ServletRequestUtils](http://www.baeldung.com/spring-webutils-servletrequestutils) - [Using Custom Banners in Spring Boot](http://www.baeldung.com/spring-boot-custom-banners) +- [Guide to Internationalization in Spring Boot](http://www.baeldung.com/spring-boot-internationalization) From debde5ad67c020cdcf000a5ebb8b1ca126749e6c Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sun, 19 Mar 2017 20:02:04 +0100 Subject: [PATCH 004/102] Update README.md (#1439) --- spring-security-mvc-login/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-security-mvc-login/README.md b/spring-security-mvc-login/README.md index 35305112b4..f7eb314869 100644 --- a/spring-security-mvc-login/README.md +++ b/spring-security-mvc-login/README.md @@ -11,6 +11,7 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - [Spring Security Expressions – hasRole Example](http://www.baeldung.com/spring-security-expressions-basic) - [Spring HTTP/HTTPS Channel Security](http://www.baeldung.com/spring-channel-security-https) - [Spring Security - Customize the 403 Forbidden/Access Denied Page](http://www.baeldung.com/spring-security-custom-access-denied-page) +- [Spring Security – Redirect to the Previous URL After Login](http://www.baeldung.com/spring-security-redirect-login) ### Build the Project ``` From 7d670d2be6b1369561b02358cac3f64b5a181595 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sun, 19 Mar 2017 20:02:15 +0100 Subject: [PATCH 005/102] Update README.md (#1442) --- spring-ldap/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-ldap/README.md b/spring-ldap/README.md index 8dffadb685..f77572d982 100644 --- a/spring-ldap/README.md +++ b/spring-ldap/README.md @@ -1,8 +1,9 @@ +## Spring LDAP Example Project + ### Relevant articles - [Spring LDAP Overview](http://www.baeldung.com/spring-ldap) +- [Spring LDAP Example Project](http://www.baeldung.com/spring-ldap-overview/) -## Spring LDAP Example Project -- (http://www.baeldung.com/spring-ldap-overview/) From e693c47019d6d39c15e4596a8816984abcfe08d4 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sun, 19 Mar 2017 20:02:29 +0100 Subject: [PATCH 006/102] Delete README.md (#1440) --- spring-5/src/test/java/com/baeldung/README.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 spring-5/src/test/java/com/baeldung/README.md diff --git a/spring-5/src/test/java/com/baeldung/README.md b/spring-5/src/test/java/com/baeldung/README.md deleted file mode 100644 index f7b358ec3e..0000000000 --- a/spring-5/src/test/java/com/baeldung/README.md +++ /dev/null @@ -1,3 +0,0 @@ -### Relevant articles - -- [Concurrent Test Execution in Spring 5](http://www.baeldung.com/spring-5-concurrent-tests) From 365d9b70230c644c5012ed367a39ad06ad3262b0 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sun, 19 Mar 2017 20:02:50 +0100 Subject: [PATCH 007/102] Update README.md (#1441) --- spring-5/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spring-5/README.md b/spring-5/README.md index 0914388b49..47a5314009 100644 --- a/spring-5/README.md +++ b/spring-5/README.md @@ -1,6 +1,8 @@ ## Spring REST Example Project -###The Course +### The Course The "REST With Spring" Classes: http://bit.ly/restwithspring -### Relevant Articles: +### Relevant Articles + +- [Concurrent Test Execution in Spring 5](http://www.baeldung.com/spring-5-concurrent-tests) From e71367b3f3f972558097d56020ec46af26ea78e4 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sun, 19 Mar 2017 20:03:23 +0100 Subject: [PATCH 008/102] Assembly plugin fix (#1449) * Assembly plugin fix * Fix java opts * Fix java opts 2 * Fix java opts 3 * Add sudo: required * Remove sudo: required * Enable incremental builder * Disable incremental builder * Update * Update --- .travis.yml | 13 +++++-------- apache-poi/temp.xlsx | Bin 3510 -> 3492 bytes core-java/pom.xml | 1 + disruptor/pom.xml | 1 + jpa-storedprocedure/pom.xml | 1 + pom.xml | 14 +++++++++++++- xml/pom.xml | 1 + 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6063fbf3e5..120d365569 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ language: java -install: travis_wait 40 mvn -q clean install -Dgib.enabled=true +install: travis_wait 60 mvn -q clean install + +before_script: + - echo "MAVEN_OPTS='-Xmx2048M -Xss128M -XX:MaxPermSize=2048M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:-UseGCOverheadLimit'" > ~/.mavenrc jdk: - oraclejdk8 @@ -14,10 +17,4 @@ cache: directories: - .autoconf - $HOME/.m2 - - sudo: required - - env: - global: - JAVA_OPTS="-Xmx2048M -Xss128M -XX:MaxPermSize=2048M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC" - MAVEN_OPTS="-Xmx2048M -Xss128M -XX:MaxPermSize=2048M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC" + diff --git a/apache-poi/temp.xlsx b/apache-poi/temp.xlsx index 50307a28c2ae5c64afdcaf6baae59a0648c28c6b..cbea3a410d193a848a5321af8e311bcf61f7160f 100644 GIT binary patch literal 3492 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 + ${project.basedir} org.baeldung.executable.ExecutableMavenJar diff --git a/disruptor/pom.xml b/disruptor/pom.xml index 7f2c78c9b0..2523cc2125 100644 --- a/disruptor/pom.xml +++ b/disruptor/pom.xml @@ -145,6 +145,7 @@ single + ${project.basedir} org.baeldung.executable.ExecutableMavenJar diff --git a/jpa-storedprocedure/pom.xml b/jpa-storedprocedure/pom.xml index 797303dc29..1672afd217 100644 --- a/jpa-storedprocedure/pom.xml +++ b/jpa-storedprocedure/pom.xml @@ -30,6 +30,7 @@ maven-assembly-plugin + ${project.basedir} jar-with-dependencies diff --git a/pom.xml b/pom.xml index 4e55d4686d..d68a7d0749 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ UTF-8 refs/heads/master - false + @@ -211,6 +211,18 @@ + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + maven + + + + + maven-assembly-plugin + ${project.basedir} jar-with-dependencies From 6bd430c4644e93e7cc400e22b8cfeae712cead12 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sun, 19 Mar 2017 21:37:44 +0100 Subject: [PATCH 009/102] Update .travis.yml --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 120d365569..b5bd4684a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ language: java install: travis_wait 60 mvn -q clean install +sudo: required + before_script: - echo "MAVEN_OPTS='-Xmx2048M -Xss128M -XX:MaxPermSize=2048M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:-UseGCOverheadLimit'" > ~/.mavenrc From 6c3f4d868837fed7e5f880593fdebd8152821487 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sun, 19 Mar 2017 21:37:59 +0100 Subject: [PATCH 010/102] Update .travis.yml --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b5bd4684a3..120d365569 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,6 @@ language: java install: travis_wait 60 mvn -q clean install -sudo: required - before_script: - echo "MAVEN_OPTS='-Xmx2048M -Xss128M -XX:MaxPermSize=2048M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:-UseGCOverheadLimit'" > ~/.mavenrc From 0b78cc9e4c0b5c32f6d7469f45cd96bd91275fdd Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Sun, 19 Mar 2017 21:29:10 +0000 Subject: [PATCH 011/102] Custom aop (#1451) --- spring-custom-aop/pom.xml | 53 +++++++++++++++++++ .../main/java/org/baeldung/Application.java | 13 +++++ .../main/java/org/baeldung/ExampleAspect.java | 25 +++++++++ .../java/org/baeldung/LogExecutionTime.java | 11 ++++ .../src/main/java/org/baeldung/Service.java | 12 +++++ .../org/baeldung/CustomAnnotationTest.java | 21 ++++++++ 6 files changed, 135 insertions(+) create mode 100644 spring-custom-aop/pom.xml create mode 100644 spring-custom-aop/src/main/java/org/baeldung/Application.java create mode 100644 spring-custom-aop/src/main/java/org/baeldung/ExampleAspect.java create mode 100644 spring-custom-aop/src/main/java/org/baeldung/LogExecutionTime.java create mode 100644 spring-custom-aop/src/main/java/org/baeldung/Service.java create mode 100644 spring-custom-aop/src/test/java/org/baeldung/CustomAnnotationTest.java diff --git a/spring-custom-aop/pom.xml b/spring-custom-aop/pom.xml new file mode 100644 index 0000000000..1c3b5bdccd --- /dev/null +++ b/spring-custom-aop/pom.xml @@ -0,0 +1,53 @@ + + 4.0.0 + com.baeldung + spring-custom-aop + 0.0.1-SNAPSHOT + war + spring-custom-aop + + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + diff --git a/spring-custom-aop/src/main/java/org/baeldung/Application.java b/spring-custom-aop/src/main/java/org/baeldung/Application.java new file mode 100644 index 0000000000..e5c764ef7e --- /dev/null +++ b/spring-custom-aop/src/main/java/org/baeldung/Application.java @@ -0,0 +1,13 @@ +package org.baeldung; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-custom-aop/src/main/java/org/baeldung/ExampleAspect.java b/spring-custom-aop/src/main/java/org/baeldung/ExampleAspect.java new file mode 100644 index 0000000000..7c3b5fb599 --- /dev/null +++ b/spring-custom-aop/src/main/java/org/baeldung/ExampleAspect.java @@ -0,0 +1,25 @@ +package org.baeldung; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class ExampleAspect { + + @Around("@annotation(LogExecutionTime)") + public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { + final long start = System.currentTimeMillis(); + + final Object proceed = joinPoint.proceed(); + + final long executionTime = System.currentTimeMillis() - start; + + System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms"); + + return proceed; + } + +} diff --git a/spring-custom-aop/src/main/java/org/baeldung/LogExecutionTime.java b/spring-custom-aop/src/main/java/org/baeldung/LogExecutionTime.java new file mode 100644 index 0000000000..c10f97e78f --- /dev/null +++ b/spring-custom-aop/src/main/java/org/baeldung/LogExecutionTime.java @@ -0,0 +1,11 @@ +package org.baeldung; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface LogExecutionTime { +} diff --git a/spring-custom-aop/src/main/java/org/baeldung/Service.java b/spring-custom-aop/src/main/java/org/baeldung/Service.java new file mode 100644 index 0000000000..e4bee38438 --- /dev/null +++ b/spring-custom-aop/src/main/java/org/baeldung/Service.java @@ -0,0 +1,12 @@ +package org.baeldung; + +import org.springframework.stereotype.Component; + +@Component +public class Service { + + @LogExecutionTime + public void serve() throws InterruptedException { + Thread.sleep(2000); + } +} diff --git a/spring-custom-aop/src/test/java/org/baeldung/CustomAnnotationTest.java b/spring-custom-aop/src/test/java/org/baeldung/CustomAnnotationTest.java new file mode 100644 index 0000000000..d4712cc063 --- /dev/null +++ b/spring-custom-aop/src/test/java/org/baeldung/CustomAnnotationTest.java @@ -0,0 +1,21 @@ +package org.baeldung; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest +public class CustomAnnotationTest { + + @Autowired + private Service service; + + @Test + public void shouldApplyCustomAnnotation() throws InterruptedException { + service.serve(); + } + +} From 3accbf88156f1aa4850dd29c7dae8a9b42e0f255 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sun, 19 Mar 2017 22:31:11 +0100 Subject: [PATCH 012/102] UserController refactor (#1450) * Refactor UserController * Refactor UserController --- .../controller/UserController.java | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/controller/UserController.java b/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/controller/UserController.java index 880b224dd9..f5553cf2b7 100644 --- a/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/controller/UserController.java +++ b/spring-mvc-forms/src/main/java/com/baeldung/springmvcforms/controller/UserController.java @@ -1,48 +1,44 @@ package com.baeldung.springmvcforms.controller; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.validation.Valid; +import com.baeldung.springmvcforms.domain.User; +import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; -import com.baeldung.springmvcforms.domain.User; +import javax.validation.Valid; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; @Controller public class UserController { - List users = new ArrayList() { - { - add(new User("ana@yahoo.com", "pass", "Ana", 20)); - add(new User("bob@yahoo.com", "pass", "Bob", 30)); - add(new User("john@yahoo.com", "pass", "John", 40)); - add(new User("mary@yahoo.com", "pass", "Mary", 30)); - } - }; + private List users = Arrays.asList( + new User("ana@yahoo.com", "pass", "Ana", 20), + new User("bob@yahoo.com", "pass", "Bob", 30), + new User("john@yahoo.com", "pass", "John", 40), + new User("mary@yahoo.com", "pass", "Mary", 30)); @GetMapping("/userPage") public String getUserProfilePage() { return "user"; } - @PostMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping("/user") @ResponseBody public ResponseEntity saveUser(@Valid User user, BindingResult result, Model model) { if (result.hasErrors()) { - List errors = new ArrayList<>(); - result.getAllErrors().forEach(item->{ - errors.add(item.getDefaultMessage()); - }); + final List errors = result.getAllErrors().stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.toList()); + return new ResponseEntity<>(errors, HttpStatus.OK); } else { if (users.stream().anyMatch(it -> user.getEmail().equals(it.getEmail()))) { @@ -54,7 +50,7 @@ public class UserController { } } - @GetMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping("/users") @ResponseBody public List getUsers() { return users; From ed70f6b3380c3a6e597209e3787e14a0c098f8f0 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Mon, 20 Mar 2017 09:15:49 +0100 Subject: [PATCH 013/102] Finite Automata refactor (#1445) --- .../src/main/java/com/baeldung/automata/RtState.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/algorithms/src/main/java/com/baeldung/automata/RtState.java b/algorithms/src/main/java/com/baeldung/automata/RtState.java index ba785eeff0..b4a5df7961 100644 --- a/algorithms/src/main/java/com/baeldung/automata/RtState.java +++ b/algorithms/src/main/java/com/baeldung/automata/RtState.java @@ -21,12 +21,12 @@ public final class RtState implements State { } public State transit(final CharSequence c) { - for(final Transition t : this.transitions) { - if(t.isPossible(c)) { - return t.state(); - } - } - throw new IllegalArgumentException("Input not accepted: " + c); + return transitions + .stream() + .filter(t -> t.isPossible(c)) + .map(Transition::state) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("Input not accepted: " + c)); } public boolean isFinal() { From 64c2ef150e7cbbc1e9b9001734698790576e1706 Mon Sep 17 00:00:00 2001 From: Devendra Tiwari Date: Mon, 20 Mar 2017 13:46:34 +0530 Subject: [PATCH 014/102] Guava 21 (#1411) Code for article Guava 21 | common.collect package --- guava21/README.md | 7 + guava21/pom.xml | 41 +++++ .../guava/tutorial/ComparatorsExamples.java | 27 +++ .../guava/tutorial/ConcatStreams.java | 11 ++ .../tutorial/InternerBuilderExample.java | 19 +++ .../guava/tutorial/MoreCollectorsExample.java | 17 ++ .../guava/tutorial/StreamsUtility.java | 42 +++++ .../src/test/java/ComparatorsUnitTests.java | 76 +++++++++ guava21/src/test/java/GauavaStreamsTests.java | 155 ++++++++++++++++++ .../src/test/java/InternBuilderUnitTests.java | 19 +++ .../test/java/MoreCollectorsUnitTests.java | 36 ++++ guava21/src/test/java/StreamUtility.java | 66 ++++++++ 12 files changed, 516 insertions(+) create mode 100644 guava21/README.md create mode 100644 guava21/pom.xml create mode 100644 guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java create mode 100644 guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java create mode 100644 guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java create mode 100644 guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java create mode 100644 guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java create mode 100644 guava21/src/test/java/ComparatorsUnitTests.java create mode 100644 guava21/src/test/java/GauavaStreamsTests.java create mode 100644 guava21/src/test/java/InternBuilderUnitTests.java create mode 100644 guava21/src/test/java/MoreCollectorsUnitTests.java create mode 100644 guava21/src/test/java/StreamUtility.java diff --git a/guava21/README.md b/guava21/README.md new file mode 100644 index 0000000000..8121cf2ea6 --- /dev/null +++ b/guava21/README.md @@ -0,0 +1,7 @@ +========= + +## Guava 21 + +**Important**: Guava 21.0 requires Java 8. If you need Java 6, 7 or Android compatibility, use Guava 20.0 for now. Guava 22.0 and on will introduce a Java 6/Android compatible backport of Guava that includes all of the latest changes that don't require Java 8. + +Article 1 : Introduction to Guava21 common.collect package. \ No newline at end of file diff --git a/guava21/pom.xml b/guava21/pom.xml new file mode 100644 index 0000000000..f393c65aec --- /dev/null +++ b/guava21/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + com.baeldung.guava + tutorial + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + + com.google.guava + guava + 21.0 + + + + + junit + junit + 4.11 + + + + + + \ No newline at end of file diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java b/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java new file mode 100644 index 0000000000..6eb5c7f5ba --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java @@ -0,0 +1,27 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.Comparators; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +public class ComparatorsExamples { + + public static void main(String[] args){ + + List integers = Arrays.asList(1,2,3,4,4,6,7,8,9,10); + //This will return true + boolean isInAscendingOrder = Comparators.isInOrder(integers, new AscedingOrderComparator()); + + System.out.println(isInAscendingOrder); + + } + + private static class AscedingOrderComparator implements Comparator { + @Override + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + } +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java b/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java new file mode 100644 index 0000000000..ab95b85751 --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java @@ -0,0 +1,11 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.Streams; + +import java.util.stream.Stream; + +public class ConcatStreams { + public static Stream concatStreams(Stream stream1, Stream stream2, Stream stream3){ + return Streams.concat(stream1,stream2,stream3); + } +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java b/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java new file mode 100644 index 0000000000..6b935ba2a8 --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java @@ -0,0 +1,19 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; + +import static com.google.common.collect.Interners.newBuilder; + +public class InternerBuilderExample { + + public static void main(String[] args){ + Interner interners = Interners.newBuilder() + .concurrencyLevel(2) + .strong() + .build(); + + + } + +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java b/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java new file mode 100644 index 0000000000..6cf4b6b0ac --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java @@ -0,0 +1,17 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.MoreCollectors; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class MoreCollectorsExample { + + public static void main(String[] args) { + List numbers = Arrays.asList(1); + Optional number = numbers.stream() + .map(e -> e * 2) + .collect(MoreCollectors.toOptional()); + } +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java b/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java new file mode 100644 index 0000000000..4ec3b44ef4 --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java @@ -0,0 +1,42 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.collect.Streams; + +import java.util.*; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +public class StreamsUtility { + + public static void main(String[] args){ + + List numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,11,12,13,14,15,16,17,18,19,20); + //Using Collection + Stream streamFromCollection = Streams.stream(numbers); + //Using Iterator + Stream streamFromIterator = Streams.stream(numbers.iterator()); + //Using Iterable + Stream streamFromIterable = Streams.stream((Iterable) numbers); + //Using Optional + Stream streamFromOptional = Streams.stream(Optional.of(1)); + //Using OptionalLong to LongStream + LongStream streamFromOptionalLong = Streams.stream(OptionalLong.of(1)); + //Using OptionalInt to IntStream + IntStream streamFromOptionalInt = Streams.stream(OptionalInt.of(1)); + //Using OptionalDouble to DoubleStream + DoubleStream streamFromOptionalDouble = Streams.stream(OptionalDouble.of(1.0)); + + Stream concatenatedStreams = Streams.concat(streamFromCollection,streamFromIterable,streamFromIterator); + + List integers = Arrays.asList(1,2,3,4,5,6,7,8,9,10); + //This will return 10 + Optional lastItem = Streams.findLast(integers.stream()); + + Streams.zip( + Stream.of("candy", "chocolate", "bar"), + Stream.of("$1", "$2","$3"), + (arg1, arg2) -> arg1 + ":" + arg2); + } +} diff --git a/guava21/src/test/java/ComparatorsUnitTests.java b/guava21/src/test/java/ComparatorsUnitTests.java new file mode 100644 index 0000000000..f196c41a1b --- /dev/null +++ b/guava21/src/test/java/ComparatorsUnitTests.java @@ -0,0 +1,76 @@ +import com.google.common.collect.Comparators; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; +import java.util.function.Function; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; + +public class ComparatorsUnitTests { + + @Test + public void isInOrderTest(){ + + List numbers = Arrays.asList(1,2,3,4,4,6,7,8,9,10); + + boolean isInAscendingOrder = Comparators.isInOrder(numbers, new AscendingOrderComparator()); + + Assert.assertTrue(isInAscendingOrder); + } + + @Test + public void isInStrictOrderTest(){ + + List numbers = Arrays.asList(1,2,3,4,3,6,7,8,9,10); + + boolean isInAscendingOrder = Comparators.isInOrder(numbers, new AscendingOrderComparator()); + + Assert.assertFalse(isInAscendingOrder); + } + + + private class AscendingOrderComparator implements Comparator{ + + @Override + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + + @Override + public Comparator reversed() { + return null; + } + + @Override + public Comparator thenComparing(Comparator other) { + return null; + } + + @Override + public Comparator thenComparing(Function keyExtractor, Comparator keyComparator) { + return null; + } + + @Override + public > Comparator thenComparing(Function keyExtractor) { + return null; + } + + @Override + public Comparator thenComparingInt(ToIntFunction keyExtractor) { + return null; + } + + @Override + public Comparator thenComparingLong(ToLongFunction keyExtractor) { + return null; + } + + @Override + public Comparator thenComparingDouble(ToDoubleFunction keyExtractor) { + return null; + } + } +} diff --git a/guava21/src/test/java/GauavaStreamsTests.java b/guava21/src/test/java/GauavaStreamsTests.java new file mode 100644 index 0000000000..09e3e29b47 --- /dev/null +++ b/guava21/src/test/java/GauavaStreamsTests.java @@ -0,0 +1,155 @@ +import com.google.common.collect.Streams; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.*; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +public class GauavaStreamsTests { + + List numbers; + + @Before + public void setUp(){ + numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,11,12,13,14,15,16,17,18,19,20); + } + + + @Test + public void createStreamsWithCollection(){ + //Deprecated API to create stream from collection + Stream streamFromCollection = Streams.stream(numbers); + + //Assert.assertNotNull(streamFromCollection); + StreamUtility.assertStreamEquals(streamFromCollection, numbers.stream()); + } + + @Test + public void createStreamsWithIterable(){ + Iterable numbersIterable = (Iterable) numbers; + + Stream streamFromIterable = Streams.stream(numbersIterable); + + Assert.assertNotNull(streamFromIterable); + StreamUtility.assertStreamEquals(streamFromIterable, numbers.stream()); + } + + @Test + public void createStreamsWithIterator(){ + Iterator numbersIterator = numbers.iterator(); + + Stream streamFromIterator = Streams.stream(numbersIterator); + + Assert.assertNotNull(streamFromIterator); + StreamUtility.assertStreamEquals(streamFromIterator, numbers.stream()); + } + + @Test + public void createStreamsWithOptional(){ + + Stream streamFromOptional = Streams.stream(Optional.of(1)); + + Assert.assertNotNull(streamFromOptional); + Assert.assertEquals(streamFromOptional.count(), 1); + } + + @Test + public void createStreamsWithOptionalLong(){ + + LongStream streamFromOptionalLong = Streams.stream(OptionalLong.of(1)); + + Assert.assertNotNull(streamFromOptionalLong); + Assert.assertEquals(streamFromOptionalLong.count(), 1); + } + + @Test + public void createStreamsWithOptionalInt(){ + + IntStream streamFromOptionalInt = Streams.stream(OptionalInt.of(1)); + + //Assert.assertNotNull(streamFromOptionalInt); + Assert.assertEquals(streamFromOptionalInt.count(), 1); + } + + @Test + public void createStreamsWithOptionalDouble(){ + + DoubleStream streamFromOptionalDouble = Streams.stream(OptionalDouble.of(1.0)); + + //Assert.assertNotNull(streamFromOptionalDouble); + Assert.assertEquals(streamFromOptionalDouble.count(), 1); + + } + + @Test + public void concatStreamsOfSameType(){ + Stream oddNumbers = Arrays.asList(1,3,5,7,9,11,13,15,17,19).stream(); + Stream evenNumbers = Arrays.asList(2,4,6,8,10,12,14,16,18,20).stream(); + + Stream combinedStreams = Streams.concat(oddNumbers,evenNumbers); + + //Assert.assertNotNull(combinedStreams); + StreamUtility.assertStreamEquals(combinedStreams, Stream.concat(oddNumbers, evenNumbers)); + } + + @Test + public void concatStreamsOfTypeLongStream(){ + LongStream firstTwenty = LongStream.range(1,20); + LongStream nextTwenty = LongStream.range(21,40); + + LongStream combinedStreams = Streams.concat(firstTwenty,nextTwenty); + + Assert.assertNotNull(combinedStreams); + StreamUtility.assertStreamEquals(combinedStreams, LongStream.concat(firstTwenty, nextTwenty)); + } + + @Test + public void concatStreamsOfTypeIntStream(){ + IntStream firstTwenty = IntStream.range(1,20); + IntStream nextTwenty = IntStream.range(21,40); + + IntStream combinedStreams = Streams.concat(firstTwenty,nextTwenty); + + Assert.assertNotNull(combinedStreams); + StreamUtility.assertStreamEquals(combinedStreams, IntStream.concat(firstTwenty, nextTwenty)); + } + + + @Test + public void findLastOfStream(){ + Optional lastElement = Streams.findLast(numbers.stream()); + + Assert.assertNotNull(lastElement.get()); + Assert.assertEquals(lastElement.get(), numbers.get(20)); + } + + @Test + public void mapWithIndexTest(){ + Stream stringSream = Stream.of("a","b","c"); + + Stream mappedStream = Streams.mapWithIndex(stringSream,(str,index) -> str +":"+ index); + + //Assert.assertNotNull(mappedStream); + Assert.assertEquals(mappedStream.findFirst().get(), "a:0"); + + } + + @Test + public void streamsZipTest(){ + Stream stringSream = Stream.of("a","b","c"); + Stream intStream = Stream.of(1,2,3); + Stream mappedStream = Streams.zip(stringSream,intStream, (str,index) -> str +":"+ index); + + //Assert.assertNotNull(mappedStream); + Assert.assertEquals(mappedStream.findFirst().get(), "a:1"); + + } + + + + +} diff --git a/guava21/src/test/java/InternBuilderUnitTests.java b/guava21/src/test/java/InternBuilderUnitTests.java new file mode 100644 index 0000000000..513b44f249 --- /dev/null +++ b/guava21/src/test/java/InternBuilderUnitTests.java @@ -0,0 +1,19 @@ +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; +import org.junit.Assert; +import org.junit.Test; + +public class InternBuilderUnitTests { + + @Test + public void interBuilderTest(){ + + Interner interners = Interners.newBuilder() + .concurrencyLevel(2) + .strong() + .build(); + + Assert.assertNotNull(interners); + } + +} diff --git a/guava21/src/test/java/MoreCollectorsUnitTests.java b/guava21/src/test/java/MoreCollectorsUnitTests.java new file mode 100644 index 0000000000..956331e25f --- /dev/null +++ b/guava21/src/test/java/MoreCollectorsUnitTests.java @@ -0,0 +1,36 @@ +import com.google.common.collect.MoreCollectors; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class MoreCollectorsUnitTests { + + @Test + public void toOptionalTest(){ + + List numbers = Arrays.asList(1); + + Optional number = numbers.stream() + .map( e -> e*2) + .collect(MoreCollectors.toOptional()); + + Assert.assertEquals(number.get(),new Integer(2)); + } + + + @Test + public void onlyElementTest(){ + List numbers = Arrays.asList(1); + + Integer number = numbers.stream() + .map( e -> e*2) + .collect(MoreCollectors.onlyElement()); + + Assert.assertEquals(number,new Integer(2)); + } + + +} diff --git a/guava21/src/test/java/StreamUtility.java b/guava21/src/test/java/StreamUtility.java new file mode 100644 index 0000000000..91a03be48d --- /dev/null +++ b/guava21/src/test/java/StreamUtility.java @@ -0,0 +1,66 @@ +import org.junit.Assert; + +import java.util.Iterator; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +public class StreamUtility { + + public static boolean assertStreamEquals(Stream stream1, Stream stream2){ + + Iterator iterator1 = stream1.iterator(); + Iterator iterator2 = stream2.iterator(); + + while (iterator1.hasNext()){ + Assert.assertEquals(iterator1.next(), iterator2.next()); + } + + Assert.assertFalse(iterator2.hasNext()); + + return true; + } + + public static boolean assertStreamEquals(LongStream stream1, LongStream stream2) { + + Iterator iterator1 = stream1.iterator(); + Iterator iterator2 = stream2.iterator(); + + while (iterator1.hasNext()){ + Assert.assertEquals(iterator1.next(), iterator2.next()); + } + + Assert.assertFalse(iterator2.hasNext()); + + return true; + } + + public static boolean assertStreamEquals(DoubleStream stream1, DoubleStream stream2) { + + Iterator iterator1 = stream1.iterator(); + Iterator iterator2 = stream2.iterator(); + + while (iterator1.hasNext()){ + Assert.assertEquals(iterator1.next(), iterator2.next()); + } + + Assert.assertFalse(iterator2.hasNext()); + + return true; + } + + public static boolean assertStreamEquals(IntStream stream1, IntStream stream2) { + + Iterator iterator1 = stream1.iterator(); + Iterator iterator2 = stream2.iterator(); + + while (iterator1.hasNext()){ + Assert.assertEquals(iterator1.next(), iterator2.next()); + } + + Assert.assertFalse(iterator2.hasNext()); + + return true; + } +} From 52c1e6a3fbebf2d400d1a5182a26e7c5a99804ff Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Mon, 20 Mar 2017 09:18:03 +0100 Subject: [PATCH 015/102] Update README.md --- guava21/README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/guava21/README.md b/guava21/README.md index 8121cf2ea6..ff12555376 100644 --- a/guava21/README.md +++ b/guava21/README.md @@ -1,7 +1 @@ -========= - -## Guava 21 - -**Important**: Guava 21.0 requires Java 8. If you need Java 6, 7 or Android compatibility, use Guava 20.0 for now. Guava 22.0 and on will introduce a Java 6/Android compatible backport of Guava that includes all of the latest changes that don't require Java 8. - -Article 1 : Introduction to Guava21 common.collect package. \ No newline at end of file +## Relevant articles: From 29645fc0d12fdade2cdc03104aa8a04873ca8505 Mon Sep 17 00:00:00 2001 From: Tomasz Lelek Date: Mon, 20 Mar 2017 12:18:41 +0100 Subject: [PATCH 016/102] BAEL-724 (#1422) Property testing with Javaslang * BAEL-724 add javaslang test and property testing example * BAEL-724 make test more readable * BAEL-724 change missspelled word to the Remainder --- javaslang/pom.xml | 9 +++ .../baeldung/javaslang/PropertyBasedTest.java | 69 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java diff --git a/javaslang/pom.xml b/javaslang/pom.xml index e111132aec..7bb23c0daf 100644 --- a/javaslang/pom.xml +++ b/javaslang/pom.xml @@ -22,8 +22,17 @@ javaslang 2.1.0-alpha + + io.javaslang + javaslang-test + ${javaslang.test.version} + + + 2.0.5 + + diff --git a/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java b/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java new file mode 100644 index 0000000000..3acac34550 --- /dev/null +++ b/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java @@ -0,0 +1,69 @@ +package com.baeldung.javaslang; + + +import javaslang.CheckedFunction1; +import javaslang.collection.Stream; +import javaslang.test.Arbitrary; +import javaslang.test.CheckResult; +import javaslang.test.Property; +import org.junit.Test; + +public class PropertyBasedTest { + + public Stream stringsSupplier() { + return Stream.from(0).map(i -> { + boolean divByTwo = i % 2 == 0; + boolean divByFive = i % 5 == 0; + + if(divByFive && divByTwo){ + return "DividedByTwoAndFiveWithoutRemainder"; + }else if(divByFive){ + return "DividedByFiveWithoutRemainder"; + }else if(divByTwo){ + return "DividedByTwoWithoutRemainder"; + } + return ""; + }); + } + + @Test + public void givenArbitrarySeq_whenCheckThatEverySecondElementIsEqualToString_thenTestPass() { + //given + Arbitrary multiplesOf2 = Arbitrary.integer() + .filter(i -> i > 0) + .filter(i -> i % 2 == 0 && i % 5 != 0); + + //when + CheckedFunction1 mustEquals = + i -> stringsSupplier().get(i).equals("DividedByTwoWithoutRemainder"); + + + //then + CheckResult result = Property + .def("Every second element must equal to DividedByTwoWithoutRemainder") + .forAll(multiplesOf2) + .suchThat(mustEquals) + .check(10_000, 100); + + result.assertIsSatisfied(); + } + + @Test + public void givenArbitrarySeq_whenCheckThatEveryFifthElementIsEqualToString_thenTestPass() { + //given + Arbitrary multiplesOf5 = Arbitrary.integer() + .filter(i -> i > 0) + .filter(i -> i % 5 == 0 && i % 2 == 0); + + //when + CheckedFunction1 mustEquals = i -> + stringsSupplier().get(i).endsWith("DividedByTwoAndFiveWithoutRemainder"); + + //then + Property.def("Every fifth element must equal to DividedByTwoAndFiveWithoutRemainder") + .forAll(multiplesOf5) + .suchThat(mustEquals) + .check(10_000, 1_000) + .assertIsSatisfied(); + } +} From 5294e7d42587df12774dc50a7304c33d0beea9c8 Mon Sep 17 00:00:00 2001 From: slavisa-baeldung Date: Mon, 20 Mar 2017 15:50:43 +0000 Subject: [PATCH 017/102] BAEL-702 - Intro to Vert.x formatting changes --- .../main/java/com/baeldung/HelloVerticle.java | 10 +++---- .../com/baeldung/SimpleServerVerticle.java | 24 ++++++++--------- .../baledung/rest/RestServiceVerticle.java | 26 +++++++++---------- .../com/baeldung/RestServiceVerticleTest.java | 12 ++++----- .../baeldung/SimpleServerVerticleTest.java | 16 ++++++------ 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/vertx/src/main/java/com/baeldung/HelloVerticle.java b/vertx/src/main/java/com/baeldung/HelloVerticle.java index 98d1b336a3..59baceb0d8 100644 --- a/vertx/src/main/java/com/baeldung/HelloVerticle.java +++ b/vertx/src/main/java/com/baeldung/HelloVerticle.java @@ -10,6 +10,11 @@ import io.vertx.core.Vertx; public class HelloVerticle extends AbstractVerticle { private static final Logger LOGGER = LoggerFactory.getLogger(HelloVerticle.class); + public static void main(String[] args) { + Vertx vertx = Vertx.vertx(); + vertx.deployVerticle(new HelloVerticle()); + } + @Override public void start(Future future) { LOGGER.info("Welcome to Vertx"); @@ -19,10 +24,5 @@ public class HelloVerticle extends AbstractVerticle { public void stop() { LOGGER.info("Shutting down application"); } - - public static void main(String[] args) { - Vertx vertx = Vertx.vertx(); - vertx.deployVerticle(new HelloVerticle()); - } } diff --git a/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java b/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java index 2cee37903b..6b56896860 100644 --- a/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java +++ b/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java @@ -4,21 +4,21 @@ import io.vertx.core.AbstractVerticle; import io.vertx.core.Future; public class SimpleServerVerticle extends AbstractVerticle { - + @Override public void start(Future future) { vertx.createHttpServer() - .requestHandler(request -> { - request.response() - .end("Welcome to Vert.x Intro"); - }) - .listen(config().getInteger("http.port", 8080), result -> { - if (result.succeeded()) { - future.complete(); - } else { - future.fail(result.cause()); - } - }); + .requestHandler(request -> { + request.response() + .end("Welcome to Vert.x Intro"); + }) + .listen(config().getInteger("http.port", 8080), result -> { + if (result.succeeded()) { + future.complete(); + } else { + future.fail(result.cause()); + } + }); } } diff --git a/vertx/src/main/java/com/baledung/rest/RestServiceVerticle.java b/vertx/src/main/java/com/baledung/rest/RestServiceVerticle.java index 181b3007d5..802f74942e 100644 --- a/vertx/src/main/java/com/baledung/rest/RestServiceVerticle.java +++ b/vertx/src/main/java/com/baledung/rest/RestServiceVerticle.java @@ -14,28 +14,28 @@ public class RestServiceVerticle extends AbstractVerticle { Router router = Router.router(vertx); router.get("/api/baeldung/articles/article/:id") - .handler(this::getArticles); + .handler(this::getArticles); vertx.createHttpServer() - .requestHandler(router::accept) - .listen(config().getInteger("http.port", 8080), result -> { - if (result.succeeded()) { - future.complete(); - } else { - future.fail(result.cause()); - } - }); + .requestHandler(router::accept) + .listen(config().getInteger("http.port", 8080), result -> { + if (result.succeeded()) { + future.complete(); + } else { + future.fail(result.cause()); + } + }); } private void getArticles(RoutingContext routingContext) { String articleId = routingContext.request() - .getParam("id"); + .getParam("id"); Article article = new Article(articleId, "This is an intro to vertx", "baeldung", "01-02-2017", 1578); routingContext.response() - .putHeader("content-type", "application/json") - .setStatusCode(200) - .end(Json.encodePrettily(article)); + .putHeader("content-type", "application/json") + .setStatusCode(200) + .end(Json.encodePrettily(article)); } } diff --git a/vertx/src/test/java/com/baeldung/RestServiceVerticleTest.java b/vertx/src/test/java/com/baeldung/RestServiceVerticleTest.java index 8eebe1396d..b5be0734f4 100644 --- a/vertx/src/test/java/com/baeldung/RestServiceVerticleTest.java +++ b/vertx/src/test/java/com/baeldung/RestServiceVerticleTest.java @@ -34,13 +34,13 @@ public class RestServiceVerticleTest { final Async async = testContext.async(); vertx.createHttpClient() - .getNow(8080, "localhost", "/api/baeldung/articles/article/12345", response -> { - response.handler(responseBody -> { - testContext.assertTrue(responseBody.toString() - .contains("\"id\" : \"12345\"")); - async.complete(); + .getNow(8080, "localhost", "/api/baeldung/articles/article/12345", response -> { + response.handler(responseBody -> { + testContext.assertTrue(responseBody.toString() + .contains("\"id\" : \"12345\"")); + async.complete(); + }); }); - }); } } diff --git a/vertx/src/test/java/com/baeldung/SimpleServerVerticleTest.java b/vertx/src/test/java/com/baeldung/SimpleServerVerticleTest.java index 177f8d8435..189d2f6604 100644 --- a/vertx/src/test/java/com/baeldung/SimpleServerVerticleTest.java +++ b/vertx/src/test/java/com/baeldung/SimpleServerVerticleTest.java @@ -20,8 +20,8 @@ public class SimpleServerVerticleTest { public void setup(TestContext testContext) { vertx = Vertx.vertx(); - vertx.deployVerticle(SimpleServerVerticle.class.getName(), - testContext.asyncAssertSuccess()); + vertx.deployVerticle(SimpleServerVerticle.class.getName(), + testContext.asyncAssertSuccess()); } @After @@ -34,13 +34,13 @@ public class SimpleServerVerticleTest { final Async async = testContext.async(); vertx.createHttpClient() - .getNow(8080, "localhost", "/", response -> { - response.handler(responseBody -> { - testContext.assertTrue(responseBody.toString() - .contains("Welcome")); - async.complete(); + .getNow(8080, "localhost", "/", response -> { + response.handler(responseBody -> { + testContext.assertTrue(responseBody.toString() + .contains("Welcome")); + async.complete(); + }); }); - }); } } From b95a014c8ac1c1566d2725c2055a142bfc701b61 Mon Sep 17 00:00:00 2001 From: Tomasz Lelek Date: Mon, 20 Mar 2017 17:54:02 +0100 Subject: [PATCH 018/102] BAEL-738 (#1456) PUT and PATCH * BAEL-724 code for put/patch article * BAEL-724 fix typo --- .../repository/HeavyResourceRepository.java | 14 +++++ .../controller/HeavyResourceController.java | 31 ++++++++++ .../org/baeldung/web/dto/HeavyResource.java | 62 +++++++++++++++++++ .../HeavyResourceAddressPartialUpdate.java | 31 ++++++++++ .../HeavyResourceControllerTest.java | 57 +++++++++++++++++ 5 files changed, 195 insertions(+) create mode 100644 spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java create mode 100644 spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java create mode 100644 spring-rest/src/main/java/org/baeldung/web/dto/HeavyResource.java create mode 100644 spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressPartialUpdate.java create mode 100644 spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java diff --git a/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java b/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java new file mode 100644 index 0000000000..cff78442d0 --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java @@ -0,0 +1,14 @@ +package org.baeldung.repository; + +import org.baeldung.web.dto.HeavyResource; +import org.baeldung.web.dto.HeavyResourceAddressPartialUpdate; + +public class HeavyResourceRepository { + + public void save(HeavyResource heavyResource) { + } + + public void save(HeavyResourceAddressPartialUpdate partialUpdate) { + + } +} diff --git a/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java b/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java new file mode 100644 index 0000000000..a2d5cfbd7b --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java @@ -0,0 +1,31 @@ +package org.baeldung.web.controller; + + +import org.baeldung.repository.HeavyResourceRepository; +import org.baeldung.web.dto.HeavyResource; +import org.baeldung.web.dto.HeavyResourceAddressPartialUpdate; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HeavyResourceController { + + private HeavyResourceRepository heavyResourceRepository = new HeavyResourceRepository(); + + @RequestMapping(value = "/heavy", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity saveResource(@RequestBody HeavyResource heavyResource) { + heavyResourceRepository.save(heavyResource); + return ResponseEntity.ok("resource saved"); + } + + @RequestMapping(value = "/heavy", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity partialUpdateName(@RequestBody HeavyResourceAddressPartialUpdate partialUpdate) { + heavyResourceRepository.save(partialUpdate); + return ResponseEntity.ok("resource address updated"); + } + +} diff --git a/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResource.java b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResource.java new file mode 100644 index 0000000000..934e76606f --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResource.java @@ -0,0 +1,62 @@ +package org.baeldung.web.dto; + + +public class HeavyResource { + private Integer id; + private String name; + private String surname; + private Integer age; + private String address; + + + public HeavyResource() { + } + + public HeavyResource(Integer id, String name, String surname, Integer age, String address) { + this.id = id; + this.name = name; + this.surname = surname; + this.age = age; + this.address = address; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressPartialUpdate.java b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressPartialUpdate.java new file mode 100644 index 0000000000..f90f02ea08 --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressPartialUpdate.java @@ -0,0 +1,31 @@ +package org.baeldung.web.dto; + + +public class HeavyResourceAddressPartialUpdate { + private Integer id; + private String address; + + public HeavyResourceAddressPartialUpdate() { + } + + public HeavyResourceAddressPartialUpdate(Integer id, String address) { + this.id = id; + this.address = address; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java b/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java new file mode 100644 index 0000000000..e68506701d --- /dev/null +++ b/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java @@ -0,0 +1,57 @@ +package org.baeldung.web.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.baeldung.config.WebConfig; +import org.baeldung.web.dto.HeavyResource; +import org.baeldung.web.dto.HeavyResourceAddressPartialUpdate; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +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.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = WebConfig.class) +@WebAppConfiguration +public class HeavyResourceControllerTest { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext webApplicationContext; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Before + public void setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + } + + @Test + public void givenHeavyResource_whenSendPutRequest_thenCreateResource() throws Exception { + mockMvc.perform(put("/heavy") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(new HeavyResource(1, "Tom", "Jackson", 12, "heaven street"))) + ).andExpect(status().isOk()); + } + + @Test + public void givenNewAddressOfResource_whenExecutePutRequest_thenUpdateResourcePartially() throws Exception { + mockMvc.perform(patch("/heavy") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(new HeavyResourceAddressPartialUpdate(1, "5th avenue"))) + ).andExpect(status().isOk()); + } + +} \ No newline at end of file From 9c755ee39c9401f32c3592f9dc664e84a6669aad Mon Sep 17 00:00:00 2001 From: Yasin Date: Mon, 20 Mar 2017 23:43:42 +0530 Subject: [PATCH 019/102] BAEL-722 Intro to JSONassert (#1460) * yasin.bhojawala@gmail.com Evaluation article on Different Types of Bean Injection in Spring * Revert "yasin.bhojawala@gmail.com" This reverts commit 963cc51a7a15b75b550108fe4e198cd65a274032. * Fixing compilation error and removing unused import * Introduction to Java9 StackWalking API - yasin.bhojawala@gmail.com Code examples for the article "Introduction to Java9 StackWalking API" * BAEL-608 Introduction to Java9 StackWalking API * BAEL-608 Introduction to Java9 StackWalking API changing the test names to BDD style * BAEL-608 Introduction to Java9 StackWalking API correcting the typo * BAEL-608 Introduction to Java9 StackWalking API improving method names * BAEL-608 Introduction to Java9 StackWalking API test method names improvements * BAEL-718 Quick intro to javatuples * merging pom from master * BAEL-722 Intro to JSONassert * BAEL-722 Intro to JSONassert Updated to 1.5.0 --- libraries/pom.xml | 2 +- .../com/baeldung/jsonassert/JsonAssertTest.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/libraries/pom.xml b/libraries/pom.xml index 85c777b12c..71d0e76c8a 100644 --- a/libraries/pom.xml +++ b/libraries/pom.xml @@ -77,7 +77,7 @@ 1.2 3.21.0-GA 3.6.2 - 1.4.0 + 1.5.0 \ No newline at end of file diff --git a/libraries/src/test/java/com/baeldung/jsonassert/JsonAssertTest.java b/libraries/src/test/java/com/baeldung/jsonassert/JsonAssertTest.java index c169d83897..b70703cc08 100644 --- a/libraries/src/test/java/com/baeldung/jsonassert/JsonAssertTest.java +++ b/libraries/src/test/java/com/baeldung/jsonassert/JsonAssertTest.java @@ -1,5 +1,8 @@ package com.baeldung.jsonassert; + +import static org.assertj.core.api.Assertions.assertThat; + import org.json.JSONException; import org.json.JSONObject; import org.junit.Test; @@ -64,6 +67,17 @@ public class JsonAssertTest { JSONAssert.assertEquals("{id:1,name:\"Juergen\", address:{city:\"Hollywood\", " + "state:\"LA\", zip:91601}}", result, false); } + + @Test + public void whenMessageUsedInAssertion_thenDisplayMessageOnFailure() throws JSONException { + String actual = "{id:123,name:\"John\"}"; + String failureMessage = "Only one field is expected: name"; + try { + JSONAssert.assertEquals(failureMessage, "{name:\"John\"}", actual, JSONCompareMode.STRICT); + } catch (AssertionError ae) { + assertThat(ae.getMessage()).containsIgnoringCase(failureMessage); + } + } @Test public void givenArray_whenComparing_thenOrderMustMatchForStrict() throws JSONException { From b5ef48a1d88085e13e57814edd0b03b296d37e98 Mon Sep 17 00:00:00 2001 From: pivovarit Date: Tue, 21 Mar 2017 10:00:35 +0100 Subject: [PATCH 020/102] Turn off spring-5 module --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d68a7d0749..e0556a7c50 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ handling-spring-static-resources hazelcast - + httpclient hystrix @@ -110,7 +110,7 @@ selenium-junit-testng solr-fulltext-search spark-java - spring-5 + spring-akka spring-amqp spring-all From 07c0e84bf47466dd76eaa3ab3b4c649caf264f04 Mon Sep 17 00:00:00 2001 From: Parth Joshi Date: Tue, 21 Mar 2017 15:54:57 +0530 Subject: [PATCH 021/102] Initial commit for merging modules spring-mvc-forms into (#1222) spring-mvc-simple. --- spring-mvc-forms/README.md | 1 + spring-mvc-simple/pom.xml | 35 ++++++++++++ .../ApplicationConfiguration.java | 37 +++++++++++++ .../spring/configuration/WebInitializer.java | 48 +++++++++++++++++ .../spring/controller/CustomerController.java | 42 +++++++++++++++ .../spring/controller/EmployeeController.java | 47 ++++++++++++++++ .../controller/FileUploadController.java | 52 ++++++++++++++++++ .../com/baeldung/spring/domain/Customer.java | 45 ++++++++++++++++ .../com/baeldung/spring/domain/Employee.java | 46 ++++++++++++++++ .../FileUploadExceptionAdvice.java | 19 +++++++ .../spring/validator/CustomerValidator.java | 28 ++++++++++ ...servlet_AnnotationMethodHandlerAdapter.xml | 50 ++++++++--------- ...g-servlet_RequestMappingHandlerAdapter.xml | 54 +++++++++---------- ...servlet_SimpleControllerHandlerAdapter.xml | 48 ++++++++--------- .../src/main/webapp/WEB-INF/Greeting.jsp | 9 ++-- .../main/webapp/WEB-INF/views/Greeting.jsp | 5 ++ .../webapp/WEB-INF/views/customerHome.jsp | 47 ++++++++++++++++ .../webapp/WEB-INF/views/customerView.jsp | 28 ++++++++++ .../webapp/WEB-INF/views/employeeHome.jsp | 33 ++++++++++++ .../webapp/WEB-INF/views/employeeView.jsp | 24 +++++++++ .../src/main/webapp/WEB-INF/views/error.jsp | 20 +++++++ .../src/main/webapp/WEB-INF/views/file.jsp | 23 ++++++++ 22 files changed, 661 insertions(+), 80 deletions(-) create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/controller/CustomerController.java create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/controller/EmployeeController.java create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Customer.java create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Employee.java create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java create mode 100644 spring-mvc-simple/src/main/java/com/baeldung/spring/validator/CustomerValidator.java create mode 100644 spring-mvc-simple/src/main/webapp/WEB-INF/views/Greeting.jsp create mode 100644 spring-mvc-simple/src/main/webapp/WEB-INF/views/customerHome.jsp create mode 100644 spring-mvc-simple/src/main/webapp/WEB-INF/views/customerView.jsp create mode 100644 spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeHome.jsp create mode 100644 spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeView.jsp create mode 100644 spring-mvc-simple/src/main/webapp/WEB-INF/views/error.jsp create mode 100644 spring-mvc-simple/src/main/webapp/WEB-INF/views/file.jsp diff --git a/spring-mvc-forms/README.md b/spring-mvc-forms/README.md index 51dbef9856..745851a102 100644 --- a/spring-mvc-forms/README.md +++ b/spring-mvc-forms/README.md @@ -2,3 +2,4 @@ ### Relevant Articles - [MaxUploadSizeExceededException in Spring](http://www.baeldung.com/spring-maxuploadsizeexceeded) +- [Getting Started with Forms in Spring MVC](http://www.baeldung.com/spring-mvc-form-tutorial) diff --git a/spring-mvc-simple/pom.xml b/spring-mvc-simple/pom.xml index 4ab5bd9d1e..a004eae4d9 100644 --- a/spring-mvc-simple/pom.xml +++ b/spring-mvc-simple/pom.xml @@ -12,6 +12,13 @@ 4.3.4.RELEASE 3.5.1 2.6 + 1.2 + 2.3.1 + 3.1.0 + 1.8 + 5.3.3.Final + enter-location-of-server + 1.3.2 @@ -40,6 +47,33 @@ spring-core ${springframework.version} + + javax.servlet.jsp + javax.servlet.jsp-api + ${javax.servlet.jsp-api.version} + + + + javax.servlet + jstl + ${jstl.version} + + + + org.hibernate + hibernate-validator + ${hibernate-validator.version} + + + org.springframework + spring-webmvc + ${springframework.version} + + + commons-fileupload + commons-fileupload + ${fileupload.version} + @@ -61,6 +95,7 @@ src/main/webapp springMvcSimple false + ${deploy-path} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java new file mode 100644 index 0000000000..9ace968bbe --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java @@ -0,0 +1,37 @@ +package com.baeldung.spring.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.multipart.MultipartResolver; +import org.springframework.web.multipart.commons.CommonsMultipartResolver; +import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.view.InternalResourceViewResolver; + +@Configuration +@EnableWebMvc +@ComponentScan(basePackages = "com.baeldung.springmvcforms") +class ApplicationConfiguration extends WebMvcConfigurerAdapter { + + @Override + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + configurer.enable(); + } + + @Bean + public InternalResourceViewResolver jspViewResolver() { + InternalResourceViewResolver bean = new InternalResourceViewResolver(); + bean.setPrefix("/WEB-INF/views/"); + bean.setSuffix(".jsp"); + return bean; + } + + @Bean + public MultipartResolver multipartResolver() { + CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); + multipartResolver.setMaxUploadSize(5242880); + return multipartResolver; + } +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java new file mode 100644 index 0000000000..d6bbf5eabd --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/configuration/WebInitializer.java @@ -0,0 +1,48 @@ +package com.baeldung.spring.configuration; + +import org.springframework.web.WebApplicationInitializer; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; + +public class WebInitializer implements WebApplicationInitializer { + + public void onStartup(ServletContext container) throws ServletException { + + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + ctx.register(ApplicationConfiguration.class); + ctx.setServletContext(container); + + // Manage the lifecycle of the root application context + container.addListener(new ContextLoaderListener(ctx)); + + ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(ctx)); + + servlet.setLoadOnStartup(1); + servlet.addMapping("/"); + + } +// @Override +// public void onStartup(ServletContext container) { +// // Create the 'root' Spring application context +// AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); +// rootContext.register(ServiceConfig.class, JPAConfig.class, SecurityConfig.class); +// +// // Manage the lifecycle of the root application context +// container.addListener(new ContextLoaderListener(rootContext)); +// +// // Create the dispatcher servlet's Spring application context +// AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext(); +// dispatcherServlet.register(MvcConfig.class); +// +// // Register and map the dispatcher servlet +// ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet)); +// dispatcher.setLoadOnStartup(1); +// dispatcher.addMapping("/"); +// +// } +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/CustomerController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/CustomerController.java new file mode 100644 index 0000000000..8ecfce58e3 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/CustomerController.java @@ -0,0 +1,42 @@ +package com.baeldung.spring.controller; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.ModelAndView; + +import com.baeldung.spring.domain.Customer; +import com.baeldung.spring.validator.CustomerValidator; + +@Controller +public class CustomerController { + + @Autowired + CustomerValidator validator; + + @RequestMapping(value = "/customer", method = RequestMethod.GET) + public ModelAndView showForm() { + return new ModelAndView("customerHome", "customer", new Customer()); + } + + @PostMapping("/addCustomer") + public String submit(@Valid @ModelAttribute("customer") final Customer customer, final BindingResult result, final ModelMap model) { + validator.validate(customer, result); + if (result.hasErrors()) { + return "customerHome"; + } + model.addAttribute("customerId", customer.getCustomerId()); + model.addAttribute("customerName", customer.getCustomerName()); + model.addAttribute("customerContact", customer.getCustomerContact()); + model.addAttribute("customerEmail", customer.getCustomerEmail()); + return "customerView"; + } + +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/EmployeeController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/EmployeeController.java new file mode 100644 index 0000000000..6543a98af1 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/EmployeeController.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.controller; + +import java.util.HashMap; +import java.util.Map; + +import javax.validation.Valid; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.ModelAttribute; +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.ResponseBody; +import org.springframework.web.servlet.ModelAndView; + +import com.baeldung.spring.domain.Employee; + +@Controller +public class EmployeeController { + + Map employeeMap = new HashMap<>(); + + @RequestMapping(value = "/employee", method = RequestMethod.GET) + public ModelAndView showForm() { + return new ModelAndView("employeeHome", "employee", new Employee()); + } + + @RequestMapping(value = "/employee/{Id}", produces = { "application/json", "application/xml" }, method = RequestMethod.GET) + public @ResponseBody Employee getEmployeeById(@PathVariable final long Id) { + return employeeMap.get(Id); + } + + @RequestMapping(value = "/addEmployee", method = RequestMethod.POST) + public String submit(@Valid @ModelAttribute("employee") final Employee employee, final BindingResult result, final ModelMap model) { + if (result.hasErrors()) { + return "error"; + } + model.addAttribute("name", employee.getName()); + model.addAttribute("contactNumber", employee.getContactNumber()); + model.addAttribute("id", employee.getId()); + employeeMap.put(employee.getId(), employee); + return "employeeView"; + } + +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java new file mode 100644 index 0000000000..47af2ab50d --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/controller/FileUploadController.java @@ -0,0 +1,52 @@ +package com.baeldung.spring.controller; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.multipart.MaxUploadSizeExceededException; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.ModelAndView; + +@Controller +public class FileUploadController implements HandlerExceptionResolver { + + @RequestMapping(value = "/uploadFile", method = RequestMethod.GET) + public String getImageView() { + return "file"; + } + + @RequestMapping(value = "/uploadFile", method = RequestMethod.POST) + public ModelAndView uploadFile(MultipartFile file) throws IOException{ + ModelAndView modelAndView = new ModelAndView("file"); + + InputStream in = file.getInputStream(); + File currDir = new File("."); + String path = currDir.getAbsolutePath(); + FileOutputStream f = new FileOutputStream(path.substring(0, path.length()-1)+ file.getOriginalFilename()); + int ch = 0; + while ((ch = in.read()) != -1) { + f.write(ch); + } + f.flush(); + f.close(); + + modelAndView.getModel().put("message", "File uploaded successfully!"); + return modelAndView; + } + + @Override + public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object object, Exception exc) { + ModelAndView modelAndView = new ModelAndView("file"); + if (exc instanceof MaxUploadSizeExceededException) { + modelAndView.getModel().put("message", "File size exceeds limit!"); + } + return modelAndView; + } +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Customer.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Customer.java new file mode 100644 index 0000000000..09322105f8 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Customer.java @@ -0,0 +1,45 @@ +package com.baeldung.spring.domain; + +public class Customer { + private String customerId; + private String customerName; + private String customerContact; + private String customerEmail; + + public Customer() { + super(); + } + + public String getCustomerId() { + return customerId; + } + + public void setCustomerId(String customerId) { + this.customerId = customerId; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public String getCustomerContact() { + return customerContact; + } + + public void setCustomerContact(String customerContact) { + this.customerContact = customerContact; + } + + public String getCustomerEmail() { + return customerEmail; + } + + public void setCustomerEmail(String customerEmail) { + this.customerEmail = customerEmail; + } + +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Employee.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Employee.java new file mode 100644 index 0000000000..900770b873 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/domain/Employee.java @@ -0,0 +1,46 @@ +package com.baeldung.spring.domain; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +public class Employee { + + private long id; + + @NotNull + @Size(min = 5) + private String name; + + @NotNull + @Size(min = 7) + private String contactNumber; + + public Employee() { + super(); + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(final long id) { + this.id = id; + } + + public String getContactNumber() { + return contactNumber; + } + + public void setContactNumber(final String contactNumber) { + this.contactNumber = contactNumber; + } + +} diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java new file mode 100644 index 0000000000..2f3c44cf12 --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java @@ -0,0 +1,19 @@ +package com.baeldung.spring.interceptor; + +import org.springframework.web.servlet.ModelAndView; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MaxUploadSizeExceededException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class FileUploadExceptionAdvice { + + @ExceptionHandler(MaxUploadSizeExceededException.class) + public ModelAndView handleMaxSizeException(MaxUploadSizeExceededException exc, HttpServletRequest request, HttpServletResponse response){ + ModelAndView modelAndView = new ModelAndView("file"); + modelAndView.getModel().put("message", "File too large!"); + return modelAndView; + } +} \ No newline at end of file diff --git a/spring-mvc-simple/src/main/java/com/baeldung/spring/validator/CustomerValidator.java b/spring-mvc-simple/src/main/java/com/baeldung/spring/validator/CustomerValidator.java new file mode 100644 index 0000000000..2515e8d31f --- /dev/null +++ b/spring-mvc-simple/src/main/java/com/baeldung/spring/validator/CustomerValidator.java @@ -0,0 +1,28 @@ +package com.baeldung.spring.validator; + +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; + +import com.baeldung.spring.domain.Customer; + +@Component +public class CustomerValidator implements Validator { + + @Override + public boolean supports(Class clazz) { + return Customer.class.isAssignableFrom(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "customerId", "error.customerId", "Customer Id is required."); + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "customerName", "error.customerName", "Customer Name is required."); + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "customerContact", "error.customerNumber", "Customer Contact is required."); + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "customerEmail", "error.customerEmail", "Customer Email is required."); + + } + +} diff --git a/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml b/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml index a8071a622f..430b849012 100644 --- a/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml +++ b/spring-mvc-simple/src/main/resources/spring-servlet_AnnotationMethodHandlerAdapter.xml @@ -1,26 +1,26 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml b/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml index b32a213eb3..d3783c2e67 100644 --- a/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml +++ b/spring-mvc-simple/src/main/resources/spring-servlet_RequestMappingHandlerAdapter.xml @@ -1,28 +1,28 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml b/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml index 60c9b9e3df..1d6e5628df 100644 --- a/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml +++ b/spring-mvc-simple/src/main/resources/spring-servlet_SimpleControllerHandlerAdapter.xml @@ -1,25 +1,25 @@ - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/Greeting.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/Greeting.jsp index 820d2f380f..7f7fbccccd 100644 --- a/spring-mvc-simple/src/main/webapp/WEB-INF/Greeting.jsp +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/Greeting.jsp @@ -1,5 +1,6 @@ - - -

Hello ${message}

- + + +

Hello ${message}

+ + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/Greeting.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/Greeting.jsp new file mode 100644 index 0000000000..efd48179f9 --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/Greeting.jsp @@ -0,0 +1,5 @@ + + +

Hello ${message}

+ + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerHome.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerHome.jsp new file mode 100644 index 0000000000..df34a47cc0 --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerHome.jsp @@ -0,0 +1,47 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> + + + +Form Example - Add Customer + + + +

Welcome, Enter The Customer Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Customer Id
Customer Name
Customer Contact
Customer Email
+
+ + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerView.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerView.jsp new file mode 100644 index 0000000000..ab2631bd02 --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/customerView.jsp @@ -0,0 +1,28 @@ +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + +Spring MVC Form Handling + + + +

Submitted Customer Information

+ + + + + + + + + + + + + + + + + +
Customer Id :${customerId}
Customer Name :${customerName}
Customer Contact :${customerContact}
Customer Email :${customerEmail}
+ + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeHome.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeHome.jsp new file mode 100644 index 0000000000..5ed572000a --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeHome.jsp @@ -0,0 +1,33 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> + + + +Form Example - Register an Employee + + +

Welcome, Enter The Employee Details

+ + + + + + + + + + + + + + + + + + +
Name
Id
Contact Number
+
+ + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeView.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeView.jsp new file mode 100644 index 0000000000..1457bc5fc8 --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/employeeView.jsp @@ -0,0 +1,24 @@ +<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> + + +Spring MVC Form Handling + + + +

Submitted Employee Information

+ + + + + + + + + + + + + +
Name :${name}
ID :${id}
Contact Number :${contactNumber}
+ + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/error.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/error.jsp new file mode 100644 index 0000000000..8f3d83af17 --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/error.jsp @@ -0,0 +1,20 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + +SpringMVCExample + + + +

Pleas enter the correct details

+ + + + +
Retry
+ + + + \ No newline at end of file diff --git a/spring-mvc-simple/src/main/webapp/WEB-INF/views/file.jsp b/spring-mvc-simple/src/main/webapp/WEB-INF/views/file.jsp new file mode 100644 index 0000000000..0ed8dae5ed --- /dev/null +++ b/spring-mvc-simple/src/main/webapp/WEB-INF/views/file.jsp @@ -0,0 +1,23 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + +Upload file + + + +
+ + +
+
+${message } +

+
+ +
+ + \ No newline at end of file From 78f87104d66ae0f9d01ba052e58c02cdbec6973f Mon Sep 17 00:00:00 2001 From: Felipe Reis Date: Tue, 21 Mar 2017 08:23:41 -0300 Subject: [PATCH 022/102] BAEL-137 Intro do JHipster (#1427) * refactor: Reorder tests without lambda Moves inner implementations of Answer and ArgumentMatcher to the top of the test classes. Also changes the lambda expression to a regular "pre java 8" expression in one of the tests. Resolves: BAEL-632 * feat: Create basic Monolithic JHipster project Commit just after creating a JHipster project, before making any modifications. Resolves: BAEL-137 * chore: Change the artifactId and name of the project From baeldung to jhipster-monolithic and JHipster Monolithic Application Relates to: BAEL-137 * feat: Create entities Post and Comment Relates to: BAEL-137 * feat: Fix Gatling configuration in pom.xml Relates to: BAEL-137 * feat: Add files for Continuous Integration Relates to: BAEL-137 * feat: Change pom.xml to conform to Baeldung standards - moved the element to the bottom of the file - excluded integration tests in the default surefire configuration - added a new profile, called integration, and added the integration tests there - added Java 8 in the and tags, under maven-compiler solves: BAEL-137 * chore: Add jhipster module to parent pom --- jhipster/.editorconfig | 24 + jhipster/.gitattributes | 22 + jhipster/.gitignore | 143 +++ jhipster/.gitlab-ci.yml | 56 + jhipster/.jhipster/Comment.json | 39 + jhipster/.jhipster/Post.json | 52 + jhipster/.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 49502 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + jhipster/.travis.yml | 41 + jhipster/.yo-rc.json | 36 + jhipster/Jenkinsfile | 50 + jhipster/README.md | 153 +++ jhipster/angular-cli.json | 55 + jhipster/circle.yml | 25 + jhipster/mvnw | 233 ++++ jhipster/mvnw.cmd | 145 +++ jhipster/package.json | 112 ++ jhipster/pom.xml | 1034 +++++++++++++++++ jhipster/postcss.config.js | 3 + jhipster/src/main/docker/Dockerfile | 13 + jhipster/src/main/docker/app.yml | 14 + jhipster/src/main/docker/mysql.yml | 13 + jhipster/src/main/docker/sonar.yml | 7 + .../java/com/baeldung/ApplicationWebXml.java | 21 + .../main/java/com/baeldung/BaeldungApp.java | 84 ++ .../baeldung/aop/logging/LoggingAspect.java | 79 ++ .../config/ApplicationProperties.java | 15 + .../baeldung/config/AsyncConfiguration.java | 46 + .../baeldung/config/CacheConfiguration.java | 48 + .../config/CloudDatabaseConfiguration.java | 23 + .../java/com/baeldung/config/Constants.java | 16 + .../config/DatabaseConfiguration.java | 75 ++ .../config/DateTimeFormatConfiguration.java | 17 + .../baeldung/config/DefaultProfileUtil.java | 48 + .../baeldung/config/LocaleConfiguration.java | 35 + .../config/LoggingAspectConfiguration.java | 19 + .../baeldung/config/LoggingConfiguration.java | 109 ++ .../baeldung/config/MetricsConfiguration.java | 94 ++ .../config/SecurityConfiguration.java | 127 ++ .../config/ThymeleafConfiguration.java | 26 + .../com/baeldung/config/WebConfigurer.java | 186 +++ .../config/audit/AuditEventConverter.java | 91 ++ .../baeldung/config/audit/package-info.java | 4 + .../com/baeldung/config/package-info.java | 4 + .../domain/AbstractAuditingEntity.java | 80 ++ .../java/com/baeldung/domain/Authority.java | 66 ++ .../java/com/baeldung/domain/Comment.java | 114 ++ .../baeldung/domain/PersistentAuditEvent.java | 78 ++ .../main/java/com/baeldung/domain/Post.java | 133 +++ .../main/java/com/baeldung/domain/User.java | 235 ++++ .../com/baeldung/domain/package-info.java | 4 + .../repository/AuthorityRepository.java | 11 + .../repository/CommentRepository.java | 15 + .../CustomAuditEventRepository.java | 81 ++ .../PersistenceAuditEventRepository.java | 26 + .../baeldung/repository/PostRepository.java | 18 + .../baeldung/repository/UserRepository.java | 37 + .../com/baeldung/repository/package-info.java | 4 + .../security/AuthoritiesConstants.java | 16 + .../security/DomainUserDetailsService.java | 51 + .../com/baeldung/security/SecurityUtils.java | 68 ++ .../security/SpringSecurityAuditorAware.java | 19 + .../security/UserNotActivatedException.java | 19 + .../baeldung/security/jwt/JWTConfigurer.java | 23 + .../com/baeldung/security/jwt/JWTFilter.java | 58 + .../baeldung/security/jwt/TokenProvider.java | 109 ++ .../com/baeldung/security/package-info.java | 4 + .../baeldung/service/AuditEventService.java | 50 + .../com/baeldung/service/MailService.java | 108 ++ .../com/baeldung/service/UserService.java | 228 ++++ .../com/baeldung/service/dto/UserDTO.java | 167 +++ .../baeldung/service/dto/package-info.java | 4 + .../baeldung/service/mapper/UserMapper.java | 55 + .../baeldung/service/mapper/package-info.java | 4 + .../com/baeldung/service/package-info.java | 4 + .../com/baeldung/service/util/RandomUtil.java | 41 + .../baeldung/web/rest/AccountResource.java | 202 ++++ .../com/baeldung/web/rest/AuditResource.java | 76 ++ .../baeldung/web/rest/CommentResource.java | 129 ++ .../java/com/baeldung/web/rest/JWTToken.java | 24 + .../com/baeldung/web/rest/LogsResource.java | 39 + .../com/baeldung/web/rest/PostResource.java | 129 ++ .../web/rest/ProfileInfoResource.java | 69 ++ .../baeldung/web/rest/UserJWTController.java | 60 + .../com/baeldung/web/rest/UserResource.java | 187 +++ .../errors/CustomParameterizedException.java | 34 + .../web/rest/errors/ErrorConstants.java | 14 + .../com/baeldung/web/rest/errors/ErrorVM.java | 52 + .../web/rest/errors/ExceptionTranslator.java | 85 ++ .../web/rest/errors/FieldErrorVM.java | 33 + .../web/rest/errors/ParameterizedErrorVM.java | 27 + .../com/baeldung/web/rest/package-info.java | 4 + .../baeldung/web/rest/util/HeaderUtil.java | 45 + .../web/rest/util/PaginationUtil.java | 47 + .../web/rest/vm/KeyAndPasswordVM.java | 27 + .../com/baeldung/web/rest/vm/LoggerVM.java | 48 + .../com/baeldung/web/rest/vm/LoginVM.java | 55 + .../baeldung/web/rest/vm/ManagedUserVM.java | 45 + .../baeldung/web/rest/vm/package-info.java | 4 + .../src/main/resources/.h2.server.properties | 5 + jhipster/src/main/resources/banner.txt | 10 + .../main/resources/config/application-dev.yml | 130 +++ .../resources/config/application-prod.yml | 132 +++ .../src/main/resources/config/application.yml | 106 ++ .../config/liquibase/authorities.csv | 3 + .../00000000000000_initial_schema.xml | 149 +++ .../20170316223211_added_entity_Post.xml | 45 + ...16223211_added_entity_constraints_Post.xml | 18 + .../20170316224021_added_entity_Comment.xml | 41 + ...24021_added_entity_constraints_Comment.xml | 18 + .../resources/config/liquibase/master.xml | 14 + .../main/resources/config/liquibase/users.csv | 5 + .../config/liquibase/users_authorities.csv | 6 + .../main/resources/i18n/messages.properties | 22 + .../resources/i18n/messages_en.properties | 22 + .../src/main/resources/logback-spring.xml | 66 ++ .../main/resources/mails/activationEmail.html | 24 + .../main/resources/mails/creationEmail.html | 24 + .../resources/mails/passwordResetEmail.html | 24 + .../src/main/resources/templates/error.html | 162 +++ jhipster/src/main/webapp/404.html | 60 + .../main/webapp/app/account/account.module.ts | 45 + .../main/webapp/app/account/account.route.ts | 26 + .../account/activate/activate.component.html | 19 + .../account/activate/activate.component.ts | 42 + .../app/account/activate/activate.route.ts | 14 + .../app/account/activate/activate.service.ts | 18 + jhipster/src/main/webapp/app/account/index.ts | 19 + .../password-reset-finish.component.html | 77 ++ .../finish/password-reset-finish.component.ts | 65 ++ .../finish/password-reset-finish.route.ts | 14 + .../finish/password-reset-finish.service.ts | 13 + .../init/password-reset-init.component.html | 47 + .../init/password-reset-init.component.ts | 49 + .../init/password-reset-init.route.ts | 14 + .../init/password-reset-init.service.ts | 13 + .../password-strength-bar.component.ts | 89 ++ .../password/password-strength-bar.scss | 23 + .../account/password/password.component.html | 65 ++ .../account/password/password.component.ts | 49 + .../app/account/password/password.route.ts | 14 + .../app/account/password/password.service.ts | 13 + .../account/register/register.component.html | 122 ++ .../account/register/register.component.ts | 73 ++ .../app/account/register/register.route.ts | 14 + .../app/account/register/register.service.ts | 13 + .../account/settings/settings.component.html | 86 ++ .../account/settings/settings.component.ts | 63 + .../app/account/settings/settings.route.ts | 14 + .../src/main/webapp/app/admin/admin.module.ts | 73 ++ .../src/main/webapp/app/admin/admin.route.ts | 36 + .../app/admin/audits/audit-data.model.ts | 6 + .../webapp/app/admin/audits/audit.model.ts | 10 + .../app/admin/audits/audits.component.html | 45 + .../app/admin/audits/audits.component.ts | 99 ++ .../webapp/app/admin/audits/audits.route.ts | 12 + .../webapp/app/admin/audits/audits.service.ts | 23 + .../configuration.component.html | 46 + .../configuration/configuration.component.ts | 48 + .../configuration/configuration.route.ts | 12 + .../configuration/configuration.service.ts | 53 + .../webapp/app/admin/docs/docs.component.html | 2 + .../webapp/app/admin/docs/docs.component.ts | 14 + .../main/webapp/app/admin/docs/docs.route.ts | 12 + .../admin/health/health-modal.component.html | 36 + .../admin/health/health-modal.component.ts | 37 + .../app/admin/health/health.component.html | 34 + .../app/admin/health/health.component.ts | 69 ++ .../webapp/app/admin/health/health.route.ts | 12 + .../webapp/app/admin/health/health.service.ts | 140 +++ jhipster/src/main/webapp/app/admin/index.ts | 29 + .../main/webapp/app/admin/logs/log.model.ts | 6 + .../webapp/app/admin/logs/logs.component.html | 27 + .../webapp/app/admin/logs/logs.component.ts | 38 + .../main/webapp/app/admin/logs/logs.route.ts | 12 + .../webapp/app/admin/logs/logs.service.ts | 18 + .../metrics/metrics-modal.component.html | 56 + .../admin/metrics/metrics-modal.component.ts | 48 + .../app/admin/metrics/metrics.component.html | 253 ++++ .../app/admin/metrics/metrics.component.ts | 74 ++ .../webapp/app/admin/metrics/metrics.route.ts | 12 + .../app/admin/metrics/metrics.service.ts | 17 + ...er-management-delete-dialog.component.html | 19 + ...user-management-delete-dialog.component.ts | 63 + .../user-management-detail.component.html | 44 + .../user-management-detail.component.ts | 40 + .../user-management-dialog.component.html | 112 ++ .../user-management-dialog.component.ts | 89 ++ .../user-management.component.html | 81 ++ .../user-management.component.ts | 131 +++ .../user-management/user-management.route.ts | 77 ++ .../user-management/user-modal.service.ts | 42 + jhipster/src/main/webapp/app/app.constants.ts | 7 + jhipster/src/main/webapp/app/app.main.ts | 11 + jhipster/src/main/webapp/app/app.module.ts | 58 + jhipster/src/main/webapp/app/app.route.ts | 9 + .../webapp/app/blocks/config/prod.config.ts | 9 + .../blocks/config/uib-pagination.config.ts | 13 + .../interceptor/auth-expired.interceptor.ts | 34 + .../blocks/interceptor/auth.interceptor.ts | 27 + .../interceptor/errorhandler.interceptor.ts | 24 + .../app/blocks/interceptor/http.provider.ts | 45 + .../interceptor/notification.interceptor.ts | 33 + .../comment-delete-dialog.component.html | 19 + .../comment-delete-dialog.component.ts | 67 ++ .../comment/comment-detail.component.html | 35 + .../comment/comment-detail.component.ts | 43 + .../comment/comment-dialog.component.html | 76 ++ .../comment/comment-dialog.component.ts | 107 ++ .../entities/comment/comment-popup.service.ts | 50 + .../entities/comment/comment.component.html | 64 + .../app/entities/comment/comment.component.ts | 110 ++ .../app/entities/comment/comment.model.ts | 10 + .../app/entities/comment/comment.module.ts | 50 + .../app/entities/comment/comment.route.ts | 61 + .../app/entities/comment/comment.service.ts | 78 ++ .../main/webapp/app/entities/comment/index.ts | 8 + .../main/webapp/app/entities/entity.module.ts | 18 + .../main/webapp/app/entities/post/index.ts | 8 + .../post/post-delete-dialog.component.html | 19 + .../post/post-delete-dialog.component.ts | 67 ++ .../entities/post/post-detail.component.html | 37 + .../entities/post/post-detail.component.ts | 43 + .../entities/post/post-dialog.component.html | 96 ++ .../entities/post/post-dialog.component.ts | 107 ++ .../app/entities/post/post-popup.service.ts | 50 + .../app/entities/post/post.component.html | 64 + .../app/entities/post/post.component.ts | 110 ++ .../webapp/app/entities/post/post.model.ts | 11 + .../webapp/app/entities/post/post.module.ts | 52 + .../webapp/app/entities/post/post.route.ts | 61 + .../webapp/app/entities/post/post.service.ts | 78 ++ .../main/webapp/app/home/home.component.html | 42 + .../main/webapp/app/home/home.component.ts | 50 + .../src/main/webapp/app/home/home.module.ts | 23 + .../src/main/webapp/app/home/home.route.ts | 14 + jhipster/src/main/webapp/app/home/home.scss | 26 + jhipster/src/main/webapp/app/home/index.ts | 3 + .../app/layouts/error/error.component.html | 17 + .../app/layouts/error/error.component.ts | 20 + .../webapp/app/layouts/error/error.route.ts | 25 + .../app/layouts/footer/footer.component.html | 4 + .../app/layouts/footer/footer.component.ts | 7 + jhipster/src/main/webapp/app/layouts/index.ts | 10 + .../app/layouts/layout-routing.module.ts | 20 + .../app/layouts/main/main.component.html | 11 + .../webapp/app/layouts/main/main.component.ts | 47 + .../layouts/navbar/active-menu.directive.ts | 26 + .../app/layouts/navbar/navbar.component.html | 168 +++ .../app/layouts/navbar/navbar.component.ts | 81 ++ .../webapp/app/layouts/navbar/navbar.scss | 70 ++ .../layouts/profiles/page-ribbon.component.ts | 25 + .../app/layouts/profiles/page-ribbon.scss | 32 + .../layouts/profiles/profile-info.model.ts | 6 + .../app/layouts/profiles/profile.service.ts | 26 + jhipster/src/main/webapp/app/polyfills.ts | 3 + .../app/shared/alert/alert-error.component.ts | 101 ++ .../app/shared/alert/alert.component.ts | 26 + .../webapp/app/shared/auth/account.service.ts | 16 + .../app/shared/auth/auth-jwt.service.ts | 61 + .../webapp/app/shared/auth/auth.service.ts | 65 ++ .../webapp/app/shared/auth/csrf.service.ts | 13 + .../auth/has-any-authority.directive.ts | 42 + .../app/shared/auth/principal.service.ts | 93 ++ .../app/shared/auth/state-storage.service.ts | 40 + .../shared/auth/user-route-access-service.ts | 15 + .../shared/constants/pagination.constants.ts | 1 + jhipster/src/main/webapp/app/shared/index.ts | 23 + .../app/shared/language/language.constants.ts | 8 + .../app/shared/language/language.helper.ts | 53 + .../app/shared/language/language.pipe.ts | 42 + .../app/shared/login/login-modal.service.ts | 26 + .../app/shared/login/login.component.html | 46 + .../app/shared/login/login.component.ts | 90 ++ .../webapp/app/shared/login/login.service.ts | 45 + .../webapp/app/shared/shared-common.module.ts | 47 + .../webapp/app/shared/shared-libs.module.ts | 27 + .../main/webapp/app/shared/shared.module.ts | 53 + .../webapp/app/shared/user/account.model.ts | 12 + .../main/webapp/app/shared/user/user.model.ts | 44 + .../webapp/app/shared/user/user.service.ts | 45 + jhipster/src/main/webapp/app/vendor.ts | 3 + .../main/webapp/content/images/hipster.png | Bin 0 -> 9499 bytes .../main/webapp/content/images/hipster2x.png | Bin 0 -> 18872 bytes .../webapp/content/images/logo-jhipster.png | Bin 0 -> 4459 bytes .../src/main/webapp/content/scss/global.scss | 211 ++++ .../src/main/webapp/content/scss/vendor.scss | 10 + jhipster/src/main/webapp/favicon.ico | Bin 0 -> 5430 bytes .../src/main/webapp/i18n/en/activate.json | 9 + jhipster/src/main/webapp/i18n/en/audits.json | 27 + jhipster/src/main/webapp/i18n/en/comment.json | 23 + .../main/webapp/i18n/en/configuration.json | 10 + jhipster/src/main/webapp/i18n/en/error.json | 6 + jhipster/src/main/webapp/i18n/en/gateway.json | 15 + jhipster/src/main/webapp/i18n/en/global.json | 133 +++ jhipster/src/main/webapp/i18n/en/health.json | 27 + jhipster/src/main/webapp/i18n/en/home.json | 19 + jhipster/src/main/webapp/i18n/en/login.json | 19 + jhipster/src/main/webapp/i18n/en/logs.json | 11 + jhipster/src/main/webapp/i18n/en/metrics.json | 101 ++ .../src/main/webapp/i18n/en/password.json | 12 + jhipster/src/main/webapp/i18n/en/post.json | 24 + .../src/main/webapp/i18n/en/register.json | 24 + jhipster/src/main/webapp/i18n/en/reset.json | 27 + .../src/main/webapp/i18n/en/sessions.json | 15 + .../src/main/webapp/i18n/en/settings.json | 32 + .../main/webapp/i18n/en/user-management.json | 30 + jhipster/src/main/webapp/index.html | 27 + jhipster/src/main/webapp/robots.txt | 11 + .../webapp/swagger-ui/images/throbber.gif | Bin 0 -> 9257 bytes .../src/main/webapp/swagger-ui/index.html | 177 +++ jhipster/src/test/gatling/conf/gatling.conf | 131 +++ jhipster/src/test/gatling/conf/logback.xml | 22 + .../simulations/CommentGatlingTest.scala | 92 ++ .../gatling/simulations/PostGatlingTest.scala | 92 ++ .../security/SecurityUtilsUnitTest.java | 50 + .../security/jwt/TokenProviderTest.java | 107 ++ .../baeldung/service/UserServiceIntTest.java | 129 ++ .../web/rest/AccountResourceIntTest.java | 395 +++++++ .../web/rest/AuditResourceIntTest.java | 147 +++ .../web/rest/CommentResourceIntTest.java | 279 +++++ .../web/rest/LogsResourceIntTest.java | 59 + .../web/rest/PostResourceIntTest.java | 306 +++++ .../web/rest/ProfileInfoResourceIntTest.java | 86 ++ .../java/com/baeldung/web/rest/TestUtil.java | 120 ++ .../web/rest/UserResourceIntTest.java | 522 +++++++++ .../javascript/e2e/account/account.spec.ts | 108 ++ .../e2e/admin/administration.spec.ts | 80 ++ .../javascript/e2e/entities/comment.spec.ts | 49 + .../test/javascript/e2e/entities/post.spec.ts | 49 + jhipster/src/test/javascript/karma.conf.js | 126 ++ .../src/test/javascript/protractor.conf.js | 48 + .../activate/activate.component.spec.ts | 84 ++ .../password-reset-finish.component.spec.ts | 79 ++ .../password-reset-init.component.spec.ts | 115 ++ .../password-strength-bar.component.spec.ts | 53 + .../password/password.component.spec.ts | 92 ++ .../register/register.component.spec.ts | 138 +++ .../settings/settings.component.spec.ts | 103 ++ .../app/admin/audits/audits.component.spec.ts | 82 ++ .../app/admin/health/health.component.spec.ts | 295 +++++ .../comment/comment-detail.component.spec.ts | 79 ++ .../post/post-detail.component.spec.ts | 79 ++ jhipster/src/test/javascript/spec/entry.ts | 19 + .../spec/helpers/mock-account.service.ts | 26 + .../spec/helpers/mock-language.service.ts | 26 + .../spec/helpers/mock-principal.service.ts | 20 + .../spec/helpers/mock-route.service.ts | 15 + .../test/javascript/spec/helpers/spyobject.ts | 69 ++ .../src/test/javascript/spec/test.module.ts | 24 + .../src/test/resources/config/application.yml | 96 ++ jhipster/src/test/resources/logback-test.xml | 15 + jhipster/tsconfig.json | 22 + jhipster/tslint.json | 107 ++ jhipster/webpack/webpack.common.js | 117 ++ jhipster/webpack/webpack.dev.js | 65 ++ jhipster/webpack/webpack.prod.js | 22 + jhipster/webpack/webpack.vendor.js | 63 + .../ArgumentMatcherWithoutLambdaUnitTest.java | 20 +- .../CustomAnswerWithoutLambdaUnitTest.java | 25 +- pom.xml | 1 + 361 files changed, 20698 insertions(+), 21 deletions(-) create mode 100644 jhipster/.editorconfig create mode 100644 jhipster/.gitattributes create mode 100644 jhipster/.gitignore create mode 100644 jhipster/.gitlab-ci.yml create mode 100644 jhipster/.jhipster/Comment.json create mode 100644 jhipster/.jhipster/Post.json create mode 100644 jhipster/.mvn/wrapper/maven-wrapper.jar create mode 100644 jhipster/.mvn/wrapper/maven-wrapper.properties create mode 100644 jhipster/.travis.yml create mode 100644 jhipster/.yo-rc.json create mode 100644 jhipster/Jenkinsfile create mode 100644 jhipster/README.md create mode 100644 jhipster/angular-cli.json create mode 100644 jhipster/circle.yml create mode 100755 jhipster/mvnw create mode 100644 jhipster/mvnw.cmd create mode 100644 jhipster/package.json create mode 100644 jhipster/pom.xml create mode 100644 jhipster/postcss.config.js create mode 100644 jhipster/src/main/docker/Dockerfile create mode 100644 jhipster/src/main/docker/app.yml create mode 100644 jhipster/src/main/docker/mysql.yml create mode 100644 jhipster/src/main/docker/sonar.yml create mode 100644 jhipster/src/main/java/com/baeldung/ApplicationWebXml.java create mode 100644 jhipster/src/main/java/com/baeldung/BaeldungApp.java create mode 100644 jhipster/src/main/java/com/baeldung/aop/logging/LoggingAspect.java create mode 100644 jhipster/src/main/java/com/baeldung/config/ApplicationProperties.java create mode 100644 jhipster/src/main/java/com/baeldung/config/AsyncConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/CacheConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/CloudDatabaseConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/Constants.java create mode 100644 jhipster/src/main/java/com/baeldung/config/DatabaseConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/DateTimeFormatConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/DefaultProfileUtil.java create mode 100644 jhipster/src/main/java/com/baeldung/config/LocaleConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/LoggingAspectConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/LoggingConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/MetricsConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/SecurityConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/ThymeleafConfiguration.java create mode 100644 jhipster/src/main/java/com/baeldung/config/WebConfigurer.java create mode 100644 jhipster/src/main/java/com/baeldung/config/audit/AuditEventConverter.java create mode 100644 jhipster/src/main/java/com/baeldung/config/audit/package-info.java create mode 100644 jhipster/src/main/java/com/baeldung/config/package-info.java create mode 100644 jhipster/src/main/java/com/baeldung/domain/AbstractAuditingEntity.java create mode 100644 jhipster/src/main/java/com/baeldung/domain/Authority.java create mode 100644 jhipster/src/main/java/com/baeldung/domain/Comment.java create mode 100644 jhipster/src/main/java/com/baeldung/domain/PersistentAuditEvent.java create mode 100644 jhipster/src/main/java/com/baeldung/domain/Post.java create mode 100644 jhipster/src/main/java/com/baeldung/domain/User.java create mode 100644 jhipster/src/main/java/com/baeldung/domain/package-info.java create mode 100644 jhipster/src/main/java/com/baeldung/repository/AuthorityRepository.java create mode 100644 jhipster/src/main/java/com/baeldung/repository/CommentRepository.java create mode 100644 jhipster/src/main/java/com/baeldung/repository/CustomAuditEventRepository.java create mode 100644 jhipster/src/main/java/com/baeldung/repository/PersistenceAuditEventRepository.java create mode 100644 jhipster/src/main/java/com/baeldung/repository/PostRepository.java create mode 100644 jhipster/src/main/java/com/baeldung/repository/UserRepository.java create mode 100644 jhipster/src/main/java/com/baeldung/repository/package-info.java create mode 100644 jhipster/src/main/java/com/baeldung/security/AuthoritiesConstants.java create mode 100644 jhipster/src/main/java/com/baeldung/security/DomainUserDetailsService.java create mode 100644 jhipster/src/main/java/com/baeldung/security/SecurityUtils.java create mode 100644 jhipster/src/main/java/com/baeldung/security/SpringSecurityAuditorAware.java create mode 100644 jhipster/src/main/java/com/baeldung/security/UserNotActivatedException.java create mode 100644 jhipster/src/main/java/com/baeldung/security/jwt/JWTConfigurer.java create mode 100644 jhipster/src/main/java/com/baeldung/security/jwt/JWTFilter.java create mode 100644 jhipster/src/main/java/com/baeldung/security/jwt/TokenProvider.java create mode 100644 jhipster/src/main/java/com/baeldung/security/package-info.java create mode 100644 jhipster/src/main/java/com/baeldung/service/AuditEventService.java create mode 100644 jhipster/src/main/java/com/baeldung/service/MailService.java create mode 100644 jhipster/src/main/java/com/baeldung/service/UserService.java create mode 100644 jhipster/src/main/java/com/baeldung/service/dto/UserDTO.java create mode 100644 jhipster/src/main/java/com/baeldung/service/dto/package-info.java create mode 100644 jhipster/src/main/java/com/baeldung/service/mapper/UserMapper.java create mode 100644 jhipster/src/main/java/com/baeldung/service/mapper/package-info.java create mode 100644 jhipster/src/main/java/com/baeldung/service/package-info.java create mode 100644 jhipster/src/main/java/com/baeldung/service/util/RandomUtil.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/AccountResource.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/AuditResource.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/CommentResource.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/JWTToken.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/LogsResource.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/PostResource.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/ProfileInfoResource.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/UserJWTController.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/UserResource.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/errors/CustomParameterizedException.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorConstants.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorVM.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/errors/ExceptionTranslator.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/errors/FieldErrorVM.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/errors/ParameterizedErrorVM.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/package-info.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/util/HeaderUtil.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/util/PaginationUtil.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/vm/KeyAndPasswordVM.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/vm/LoggerVM.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/vm/LoginVM.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/vm/ManagedUserVM.java create mode 100644 jhipster/src/main/java/com/baeldung/web/rest/vm/package-info.java create mode 100644 jhipster/src/main/resources/.h2.server.properties create mode 100644 jhipster/src/main/resources/banner.txt create mode 100644 jhipster/src/main/resources/config/application-dev.yml create mode 100644 jhipster/src/main/resources/config/application-prod.yml create mode 100644 jhipster/src/main/resources/config/application.yml create mode 100644 jhipster/src/main/resources/config/liquibase/authorities.csv create mode 100644 jhipster/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml create mode 100644 jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_Post.xml create mode 100644 jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_constraints_Post.xml create mode 100644 jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_Comment.xml create mode 100644 jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_constraints_Comment.xml create mode 100644 jhipster/src/main/resources/config/liquibase/master.xml create mode 100644 jhipster/src/main/resources/config/liquibase/users.csv create mode 100644 jhipster/src/main/resources/config/liquibase/users_authorities.csv create mode 100644 jhipster/src/main/resources/i18n/messages.properties create mode 100644 jhipster/src/main/resources/i18n/messages_en.properties create mode 100644 jhipster/src/main/resources/logback-spring.xml create mode 100644 jhipster/src/main/resources/mails/activationEmail.html create mode 100644 jhipster/src/main/resources/mails/creationEmail.html create mode 100644 jhipster/src/main/resources/mails/passwordResetEmail.html create mode 100644 jhipster/src/main/resources/templates/error.html create mode 100644 jhipster/src/main/webapp/404.html create mode 100644 jhipster/src/main/webapp/app/account/account.module.ts create mode 100644 jhipster/src/main/webapp/app/account/account.route.ts create mode 100644 jhipster/src/main/webapp/app/account/activate/activate.component.html create mode 100644 jhipster/src/main/webapp/app/account/activate/activate.component.ts create mode 100644 jhipster/src/main/webapp/app/account/activate/activate.route.ts create mode 100644 jhipster/src/main/webapp/app/account/activate/activate.service.ts create mode 100644 jhipster/src/main/webapp/app/account/index.ts create mode 100644 jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html create mode 100644 jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts create mode 100644 jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts create mode 100644 jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts create mode 100644 jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html create mode 100644 jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts create mode 100644 jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts create mode 100644 jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts create mode 100644 jhipster/src/main/webapp/app/account/password/password-strength-bar.component.ts create mode 100644 jhipster/src/main/webapp/app/account/password/password-strength-bar.scss create mode 100644 jhipster/src/main/webapp/app/account/password/password.component.html create mode 100644 jhipster/src/main/webapp/app/account/password/password.component.ts create mode 100644 jhipster/src/main/webapp/app/account/password/password.route.ts create mode 100644 jhipster/src/main/webapp/app/account/password/password.service.ts create mode 100644 jhipster/src/main/webapp/app/account/register/register.component.html create mode 100644 jhipster/src/main/webapp/app/account/register/register.component.ts create mode 100644 jhipster/src/main/webapp/app/account/register/register.route.ts create mode 100644 jhipster/src/main/webapp/app/account/register/register.service.ts create mode 100644 jhipster/src/main/webapp/app/account/settings/settings.component.html create mode 100644 jhipster/src/main/webapp/app/account/settings/settings.component.ts create mode 100644 jhipster/src/main/webapp/app/account/settings/settings.route.ts create mode 100644 jhipster/src/main/webapp/app/admin/admin.module.ts create mode 100644 jhipster/src/main/webapp/app/admin/admin.route.ts create mode 100644 jhipster/src/main/webapp/app/admin/audits/audit-data.model.ts create mode 100644 jhipster/src/main/webapp/app/admin/audits/audit.model.ts create mode 100644 jhipster/src/main/webapp/app/admin/audits/audits.component.html create mode 100644 jhipster/src/main/webapp/app/admin/audits/audits.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/audits/audits.route.ts create mode 100644 jhipster/src/main/webapp/app/admin/audits/audits.service.ts create mode 100644 jhipster/src/main/webapp/app/admin/configuration/configuration.component.html create mode 100644 jhipster/src/main/webapp/app/admin/configuration/configuration.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/configuration/configuration.route.ts create mode 100644 jhipster/src/main/webapp/app/admin/configuration/configuration.service.ts create mode 100644 jhipster/src/main/webapp/app/admin/docs/docs.component.html create mode 100644 jhipster/src/main/webapp/app/admin/docs/docs.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/docs/docs.route.ts create mode 100644 jhipster/src/main/webapp/app/admin/health/health-modal.component.html create mode 100644 jhipster/src/main/webapp/app/admin/health/health-modal.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/health/health.component.html create mode 100644 jhipster/src/main/webapp/app/admin/health/health.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/health/health.route.ts create mode 100644 jhipster/src/main/webapp/app/admin/health/health.service.ts create mode 100644 jhipster/src/main/webapp/app/admin/index.ts create mode 100644 jhipster/src/main/webapp/app/admin/logs/log.model.ts create mode 100644 jhipster/src/main/webapp/app/admin/logs/logs.component.html create mode 100644 jhipster/src/main/webapp/app/admin/logs/logs.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/logs/logs.route.ts create mode 100644 jhipster/src/main/webapp/app/admin/logs/logs.service.ts create mode 100644 jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.html create mode 100644 jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/metrics/metrics.component.html create mode 100644 jhipster/src/main/webapp/app/admin/metrics/metrics.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/metrics/metrics.route.ts create mode 100644 jhipster/src/main/webapp/app/admin/metrics/metrics.service.ts create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.html create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.html create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-management.component.html create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-management.component.ts create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-management.route.ts create mode 100644 jhipster/src/main/webapp/app/admin/user-management/user-modal.service.ts create mode 100644 jhipster/src/main/webapp/app/app.constants.ts create mode 100644 jhipster/src/main/webapp/app/app.main.ts create mode 100644 jhipster/src/main/webapp/app/app.module.ts create mode 100644 jhipster/src/main/webapp/app/app.route.ts create mode 100644 jhipster/src/main/webapp/app/blocks/config/prod.config.ts create mode 100644 jhipster/src/main/webapp/app/blocks/config/uib-pagination.config.ts create mode 100644 jhipster/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts create mode 100644 jhipster/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts create mode 100644 jhipster/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts create mode 100644 jhipster/src/main/webapp/app/blocks/interceptor/http.provider.ts create mode 100644 jhipster/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.html create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment-detail.component.html create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment-detail.component.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.html create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment-popup.service.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment.component.html create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment.component.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment.model.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment.module.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment.route.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/comment.service.ts create mode 100644 jhipster/src/main/webapp/app/entities/comment/index.ts create mode 100644 jhipster/src/main/webapp/app/entities/entity.module.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/index.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.html create mode 100644 jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/post-detail.component.html create mode 100644 jhipster/src/main/webapp/app/entities/post/post-detail.component.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/post-dialog.component.html create mode 100644 jhipster/src/main/webapp/app/entities/post/post-dialog.component.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/post-popup.service.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/post.component.html create mode 100644 jhipster/src/main/webapp/app/entities/post/post.component.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/post.model.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/post.module.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/post.route.ts create mode 100644 jhipster/src/main/webapp/app/entities/post/post.service.ts create mode 100644 jhipster/src/main/webapp/app/home/home.component.html create mode 100644 jhipster/src/main/webapp/app/home/home.component.ts create mode 100644 jhipster/src/main/webapp/app/home/home.module.ts create mode 100644 jhipster/src/main/webapp/app/home/home.route.ts create mode 100644 jhipster/src/main/webapp/app/home/home.scss create mode 100644 jhipster/src/main/webapp/app/home/index.ts create mode 100644 jhipster/src/main/webapp/app/layouts/error/error.component.html create mode 100644 jhipster/src/main/webapp/app/layouts/error/error.component.ts create mode 100644 jhipster/src/main/webapp/app/layouts/error/error.route.ts create mode 100644 jhipster/src/main/webapp/app/layouts/footer/footer.component.html create mode 100644 jhipster/src/main/webapp/app/layouts/footer/footer.component.ts create mode 100644 jhipster/src/main/webapp/app/layouts/index.ts create mode 100644 jhipster/src/main/webapp/app/layouts/layout-routing.module.ts create mode 100644 jhipster/src/main/webapp/app/layouts/main/main.component.html create mode 100644 jhipster/src/main/webapp/app/layouts/main/main.component.ts create mode 100644 jhipster/src/main/webapp/app/layouts/navbar/active-menu.directive.ts create mode 100644 jhipster/src/main/webapp/app/layouts/navbar/navbar.component.html create mode 100644 jhipster/src/main/webapp/app/layouts/navbar/navbar.component.ts create mode 100644 jhipster/src/main/webapp/app/layouts/navbar/navbar.scss create mode 100644 jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts create mode 100644 jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.scss create mode 100644 jhipster/src/main/webapp/app/layouts/profiles/profile-info.model.ts create mode 100644 jhipster/src/main/webapp/app/layouts/profiles/profile.service.ts create mode 100644 jhipster/src/main/webapp/app/polyfills.ts create mode 100644 jhipster/src/main/webapp/app/shared/alert/alert-error.component.ts create mode 100644 jhipster/src/main/webapp/app/shared/alert/alert.component.ts create mode 100644 jhipster/src/main/webapp/app/shared/auth/account.service.ts create mode 100644 jhipster/src/main/webapp/app/shared/auth/auth-jwt.service.ts create mode 100644 jhipster/src/main/webapp/app/shared/auth/auth.service.ts create mode 100644 jhipster/src/main/webapp/app/shared/auth/csrf.service.ts create mode 100644 jhipster/src/main/webapp/app/shared/auth/has-any-authority.directive.ts create mode 100644 jhipster/src/main/webapp/app/shared/auth/principal.service.ts create mode 100644 jhipster/src/main/webapp/app/shared/auth/state-storage.service.ts create mode 100644 jhipster/src/main/webapp/app/shared/auth/user-route-access-service.ts create mode 100644 jhipster/src/main/webapp/app/shared/constants/pagination.constants.ts create mode 100644 jhipster/src/main/webapp/app/shared/index.ts create mode 100644 jhipster/src/main/webapp/app/shared/language/language.constants.ts create mode 100644 jhipster/src/main/webapp/app/shared/language/language.helper.ts create mode 100644 jhipster/src/main/webapp/app/shared/language/language.pipe.ts create mode 100644 jhipster/src/main/webapp/app/shared/login/login-modal.service.ts create mode 100644 jhipster/src/main/webapp/app/shared/login/login.component.html create mode 100644 jhipster/src/main/webapp/app/shared/login/login.component.ts create mode 100644 jhipster/src/main/webapp/app/shared/login/login.service.ts create mode 100644 jhipster/src/main/webapp/app/shared/shared-common.module.ts create mode 100644 jhipster/src/main/webapp/app/shared/shared-libs.module.ts create mode 100644 jhipster/src/main/webapp/app/shared/shared.module.ts create mode 100644 jhipster/src/main/webapp/app/shared/user/account.model.ts create mode 100644 jhipster/src/main/webapp/app/shared/user/user.model.ts create mode 100644 jhipster/src/main/webapp/app/shared/user/user.service.ts create mode 100644 jhipster/src/main/webapp/app/vendor.ts create mode 100644 jhipster/src/main/webapp/content/images/hipster.png create mode 100644 jhipster/src/main/webapp/content/images/hipster2x.png create mode 100644 jhipster/src/main/webapp/content/images/logo-jhipster.png create mode 100644 jhipster/src/main/webapp/content/scss/global.scss create mode 100644 jhipster/src/main/webapp/content/scss/vendor.scss create mode 100644 jhipster/src/main/webapp/favicon.ico create mode 100644 jhipster/src/main/webapp/i18n/en/activate.json create mode 100644 jhipster/src/main/webapp/i18n/en/audits.json create mode 100644 jhipster/src/main/webapp/i18n/en/comment.json create mode 100644 jhipster/src/main/webapp/i18n/en/configuration.json create mode 100644 jhipster/src/main/webapp/i18n/en/error.json create mode 100644 jhipster/src/main/webapp/i18n/en/gateway.json create mode 100644 jhipster/src/main/webapp/i18n/en/global.json create mode 100644 jhipster/src/main/webapp/i18n/en/health.json create mode 100644 jhipster/src/main/webapp/i18n/en/home.json create mode 100644 jhipster/src/main/webapp/i18n/en/login.json create mode 100644 jhipster/src/main/webapp/i18n/en/logs.json create mode 100644 jhipster/src/main/webapp/i18n/en/metrics.json create mode 100644 jhipster/src/main/webapp/i18n/en/password.json create mode 100644 jhipster/src/main/webapp/i18n/en/post.json create mode 100644 jhipster/src/main/webapp/i18n/en/register.json create mode 100644 jhipster/src/main/webapp/i18n/en/reset.json create mode 100644 jhipster/src/main/webapp/i18n/en/sessions.json create mode 100644 jhipster/src/main/webapp/i18n/en/settings.json create mode 100644 jhipster/src/main/webapp/i18n/en/user-management.json create mode 100644 jhipster/src/main/webapp/index.html create mode 100644 jhipster/src/main/webapp/robots.txt create mode 100644 jhipster/src/main/webapp/swagger-ui/images/throbber.gif create mode 100644 jhipster/src/main/webapp/swagger-ui/index.html create mode 100644 jhipster/src/test/gatling/conf/gatling.conf create mode 100644 jhipster/src/test/gatling/conf/logback.xml create mode 100644 jhipster/src/test/gatling/simulations/CommentGatlingTest.scala create mode 100644 jhipster/src/test/gatling/simulations/PostGatlingTest.scala create mode 100644 jhipster/src/test/java/com/baeldung/security/SecurityUtilsUnitTest.java create mode 100644 jhipster/src/test/java/com/baeldung/security/jwt/TokenProviderTest.java create mode 100644 jhipster/src/test/java/com/baeldung/service/UserServiceIntTest.java create mode 100644 jhipster/src/test/java/com/baeldung/web/rest/AccountResourceIntTest.java create mode 100644 jhipster/src/test/java/com/baeldung/web/rest/AuditResourceIntTest.java create mode 100644 jhipster/src/test/java/com/baeldung/web/rest/CommentResourceIntTest.java create mode 100644 jhipster/src/test/java/com/baeldung/web/rest/LogsResourceIntTest.java create mode 100644 jhipster/src/test/java/com/baeldung/web/rest/PostResourceIntTest.java create mode 100644 jhipster/src/test/java/com/baeldung/web/rest/ProfileInfoResourceIntTest.java create mode 100644 jhipster/src/test/java/com/baeldung/web/rest/TestUtil.java create mode 100644 jhipster/src/test/java/com/baeldung/web/rest/UserResourceIntTest.java create mode 100644 jhipster/src/test/javascript/e2e/account/account.spec.ts create mode 100644 jhipster/src/test/javascript/e2e/admin/administration.spec.ts create mode 100644 jhipster/src/test/javascript/e2e/entities/comment.spec.ts create mode 100644 jhipster/src/test/javascript/e2e/entities/post.spec.ts create mode 100644 jhipster/src/test/javascript/karma.conf.js create mode 100644 jhipster/src/test/javascript/protractor.conf.js create mode 100644 jhipster/src/test/javascript/spec/app/account/activate/activate.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/account/password/password.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/account/register/register.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/account/settings/settings.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/admin/health/health.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/entities/comment/comment-detail.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/app/entities/post/post-detail.component.spec.ts create mode 100644 jhipster/src/test/javascript/spec/entry.ts create mode 100644 jhipster/src/test/javascript/spec/helpers/mock-account.service.ts create mode 100644 jhipster/src/test/javascript/spec/helpers/mock-language.service.ts create mode 100644 jhipster/src/test/javascript/spec/helpers/mock-principal.service.ts create mode 100644 jhipster/src/test/javascript/spec/helpers/mock-route.service.ts create mode 100644 jhipster/src/test/javascript/spec/helpers/spyobject.ts create mode 100644 jhipster/src/test/javascript/spec/test.module.ts create mode 100644 jhipster/src/test/resources/config/application.yml create mode 100644 jhipster/src/test/resources/logback-test.xml create mode 100644 jhipster/tsconfig.json create mode 100644 jhipster/tslint.json create mode 100644 jhipster/webpack/webpack.common.js create mode 100644 jhipster/webpack/webpack.dev.js create mode 100644 jhipster/webpack/webpack.prod.js create mode 100644 jhipster/webpack/webpack.vendor.js diff --git a/jhipster/.editorconfig b/jhipster/.editorconfig new file mode 100644 index 0000000000..a03599dd04 --- /dev/null +++ b/jhipster/.editorconfig @@ -0,0 +1,24 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 4 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[{package,bower}.json] +indent_style = space +indent_size = 2 diff --git a/jhipster/.gitattributes b/jhipster/.gitattributes new file mode 100644 index 0000000000..2a13efa88f --- /dev/null +++ b/jhipster/.gitattributes @@ -0,0 +1,22 @@ +# All text files should have the "lf" (Unix) line endings +* text eol=lf + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.java text +*.js text +*.css text +*.html text + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.jar binary +*.pdf binary +*.eot binary +*.ttf binary +*.gzip binary +*.gz binary +*.ai binary +*.eps binary +*.swf binary diff --git a/jhipster/.gitignore b/jhipster/.gitignore new file mode 100644 index 0000000000..c9f735a496 --- /dev/null +++ b/jhipster/.gitignore @@ -0,0 +1,143 @@ +###################### +# Project Specific +###################### +/src/main/webapp/content/css/main.css +/target/www/** +/src/test/javascript/coverage/ +/src/test/javascript/PhantomJS*/ + +###################### +# Node +###################### +/node/ +node_tmp/ +node_modules/ +npm-debug.log.* + +###################### +# SASS +###################### +.sass-cache/ + +###################### +# Eclipse +###################### +*.pydevproject +.project +.metadata +tmp/ +tmp/**/* +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath +.factorypath +/src/main/resources/rebel.xml + +# External tool builders +.externalToolBuilders/** + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +###################### +# Intellij +###################### +.idea/ +*.iml +*.iws +*.ipr +*.ids +*.orig + +###################### +# Visual Studio Code +###################### +.vscode/ + +###################### +# Maven +###################### +/log/ +/target/ + +###################### +# Gradle +###################### +.gradle/ +/build/ + +###################### +# Package Files +###################### +*.jar +*.war +*.ear +*.db + +###################### +# Windows +###################### +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + +###################### +# Mac OSX +###################### +.DS_Store +.svn + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +###################### +# Directories +###################### +/bin/ +/deploy/ + +###################### +# Logs +###################### +*.log + +###################### +# Others +###################### +*.class +*.*~ +*~ +.merge_file* + +###################### +# Gradle Wrapper +###################### +!gradle/wrapper/gradle-wrapper.jar + +###################### +# Maven Wrapper +###################### +!.mvn/wrapper/maven-wrapper.jar + +###################### +# ESLint +###################### +.eslintcache +/.apt_generated/ diff --git a/jhipster/.gitlab-ci.yml b/jhipster/.gitlab-ci.yml new file mode 100644 index 0000000000..1cf574251a --- /dev/null +++ b/jhipster/.gitlab-ci.yml @@ -0,0 +1,56 @@ + +cache: + key: "$CI_BUILD_REF_NAME" + paths: + - node_modules + - .maven +stages: + - build + - test + - package + +before_script: + - export MAVEN_USER_HOME=`pwd`/.maven + - chmod +x mvnw + - ./mvnw com.github.eirslett:frontend-maven-plugin:install-node-and-npm -DnodeVersion=v6.10.0 -DnpmVersion=4.3.0 + - ./mvnw com.github.eirslett:frontend-maven-plugin:npm + +maven-build: + stage: build + script: ./mvnw compile -Dmaven.repo.local=$MAVEN_USER_HOME + +maven-test: + stage: test + script: + - ./mvnw test -Dmaven.repo.local=$MAVEN_USER_HOME + artifacts: + paths: + - target/surefire-reports/* +maven-front-test: + stage: test + script: + - ./mvnw com.github.eirslett:frontend-maven-plugin:npm -Dfrontend.yarn.arguments=test + artifacts: + paths: + - target/test-results/karma/* +gatling-test: + stage: test + allow_failure: true + script: + - ./mvnw gatling:execute -Dmaven.repo.local=$MAVEN_USER_HOME + before_script: + - export MAVEN_USER_HOME=`pwd`/.maven + - chmod +x mvnw + - ./mvnw com.github.eirslett:frontend-maven-plugin:install-node-and-npm -DnodeVersion=v6.10.0 -DnpmVersion=4.3.0 + - ./mvnw com.github.eirslett:frontend-maven-plugin:npm + - ./mvnw & + artifacts: + paths: + - target/gatling/* +maven-package: + stage: package + script: + - ./mvnw package -Pprod -DskipTests -Dmaven.repo.local=$MAVEN_USER_HOME + artifacts: + paths: + - target/*.war diff --git a/jhipster/.jhipster/Comment.json b/jhipster/.jhipster/Comment.json new file mode 100644 index 0000000000..c6022daa60 --- /dev/null +++ b/jhipster/.jhipster/Comment.json @@ -0,0 +1,39 @@ +{ + "fluentMethods": true, + "relationships": [ + { + "relationshipName": "post", + "otherEntityName": "post", + "relationshipType": "many-to-one", + "relationshipValidateRules": [ + "required" + ], + "otherEntityField": "title" + } + ], + "fields": [ + { + "fieldName": "text", + "fieldType": "String", + "fieldValidateRules": [ + "required", + "minlength", + "maxlength" + ], + "fieldValidateRulesMinlength": "10", + "fieldValidateRulesMaxlength": "100" + }, + { + "fieldName": "creationDate", + "fieldType": "LocalDate", + "fieldValidateRules": [ + "required" + ] + } + ], + "changelogDate": "20170316224021", + "dto": "no", + "service": "no", + "entityTableName": "comment", + "pagination": "infinite-scroll" +} diff --git a/jhipster/.jhipster/Post.json b/jhipster/.jhipster/Post.json new file mode 100644 index 0000000000..595cf43598 --- /dev/null +++ b/jhipster/.jhipster/Post.json @@ -0,0 +1,52 @@ +{ + "fluentMethods": true, + "relationships": [ + { + "relationshipName": "creator", + "otherEntityName": "user", + "relationshipType": "many-to-one", + "relationshipValidateRules": [ + "required" + ], + "otherEntityField": "login", + "ownerSide": true, + "otherEntityRelationshipName": "post" + } + ], + "fields": [ + { + "fieldName": "title", + "fieldType": "String", + "fieldValidateRules": [ + "required", + "minlength", + "maxlength" + ], + "fieldValidateRulesMinlength": "10", + "fieldValidateRulesMaxlength": "100" + }, + { + "fieldName": "content", + "fieldType": "String", + "fieldValidateRules": [ + "required", + "minlength", + "maxlength" + ], + "fieldValidateRulesMinlength": "10", + "fieldValidateRulesMaxlength": "1000" + }, + { + "fieldName": "creationDate", + "fieldType": "LocalDate", + "fieldValidateRules": [ + "required" + ] + } + ], + "changelogDate": "20170316223211", + "dto": "no", + "service": "no", + "entityTableName": "post", + "pagination": "infinite-scroll" +} diff --git a/jhipster/.mvn/wrapper/maven-wrapper.jar b/jhipster/.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/jhipster/.mvn/wrapper/maven-wrapper.properties b/jhipster/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..c954cec91c --- /dev/null +++ b/jhipster/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip diff --git a/jhipster/.travis.yml b/jhipster/.travis.yml new file mode 100644 index 0000000000..c34d1a7da6 --- /dev/null +++ b/jhipster/.travis.yml @@ -0,0 +1,41 @@ +os: + - linux +services: + - docker +language: node_js +node_js: + - "6.10.0" +jdk: + - oraclejdk8 +sudo: false +cache: + directories: + - node + - node_modules + - $HOME/.m2 +env: + global: + - NODE_VERSION=6.10.0 + - SPRING_OUTPUT_ANSI_ENABLED=ALWAYS + - SPRING_JPA_SHOW_SQL=false +before_install: + - jdk_switcher use oraclejdk8 + - java -version + - sudo /etc/init.d/mysql stop + - sudo /etc/init.d/postgresql stop + - nvm install $NODE_VERSION + - npm install -g npm + - node -v + - npm -v +install: + - npm install +script: + - chmod +x mvnw + - ./mvnw clean test + - npm test + - ./mvnw package -Pprod -DskipTests +notifications: + webhooks: + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/jhipster/.yo-rc.json b/jhipster/.yo-rc.json new file mode 100644 index 0000000000..155e33b1c5 --- /dev/null +++ b/jhipster/.yo-rc.json @@ -0,0 +1,36 @@ +{ + "generator-jhipster": { + "jhipsterVersion": "4.0.8", + "baseName": "baeldung", + "packageName": "com.baeldung", + "packageFolder": "com/baeldung", + "serverPort": "8080", + "authenticationType": "jwt", + "hibernateCache": "ehcache", + "clusteredHttpSession": false, + "websocket": false, + "databaseType": "sql", + "devDatabaseType": "h2Disk", + "prodDatabaseType": "mysql", + "searchEngine": false, + "messageBroker": false, + "serviceDiscoveryType": false, + "buildTool": "maven", + "enableSocialSignIn": false, + "jwtSecretKey": "e1d4b69d3f953e3fa622121e882e6f459ca20ca4", + "clientFramework": "angular2", + "useSass": true, + "clientPackageManager": "npm", + "applicationType": "monolith", + "testFrameworks": [ + "gatling", + "protractor" + ], + "jhiPrefix": "jhi", + "enableTranslation": true, + "nativeLanguage": "en", + "languages": [ + "en" + ] + } +} \ No newline at end of file diff --git a/jhipster/Jenkinsfile b/jhipster/Jenkinsfile new file mode 100644 index 0000000000..1f0873a472 --- /dev/null +++ b/jhipster/Jenkinsfile @@ -0,0 +1,50 @@ +#!/usr/bin/env groovy + +node { + stage('checkout') { + checkout scm + } + + stage('check java') { + sh "java -version" + } + + stage('clean') { + sh "chmod +x mvnw" + sh "./mvnw clean" + } + + stage('install tools') { + sh "./mvnw com.github.eirslett:frontend-maven-plugin:install-node-and-npm -DnodeVersion=v6.10.0 -DnpmVersion=4.3.0" + } + + stage('npm install') { + sh "./mvnw com.github.eirslett:frontend-maven-plugin:npm" + } + + stage('backend tests') { + try { + sh "./mvnw test" + } catch(err) { + throw err + } finally { + junit '**/target/surefire-reports/TEST-*.xml' + } + } + + stage('frontend tests') { + try { + sh "./mvnw com.github.eirslett:frontend-maven-plugin:npm -Dfrontend.yarn.arguments=test" + } catch(err) { + throw err + } finally { + junit '**/target/test-results/karma/TESTS-*.xml' + } + } + + stage('packaging') { + sh "./mvnw package -Pprod -DskipTests" + archiveArtifacts artifacts: '**/target/*.war', fingerprint: true + } + +} diff --git a/jhipster/README.md b/jhipster/README.md new file mode 100644 index 0000000000..d45796d867 --- /dev/null +++ b/jhipster/README.md @@ -0,0 +1,153 @@ +# baeldung +This application was generated using JHipster 4.0.8, you can find documentation and help at [https://jhipster.github.io/documentation-archive/v4.0.8](https://jhipster.github.io/documentation-archive/v4.0.8). + +## Development + +Before you can build this project, you must install and configure the following dependencies on your machine: + +1. [Node.js][]: We use Node to run a development web server and build the project. + Depending on your system, you can install Node either from source or as a pre-packaged bundle. + +After installing Node, you should be able to run the following command to install development tools. +You will only need to run this command when dependencies change in [package.json](package.json). + + npm install + +We use npm scripts and [Webpack][] as our build system. + + +Run the following commands in two separate terminals to create a blissful development experience where your browser +auto-refreshes when files change on your hard drive. + + ./mvnw + npm start + +[Npm][] is also used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by +specifying a newer version in [package.json](package.json). You can also run `npm update` and `npm install` to manage dependencies. +Add the `help` flag on any command to see how you can use it. For example, `npm help update`. + +The `npm run` command will list all of the scripts available to run for this project. + +### Managing dependencies + +For example, to add [Leaflet][] library as a runtime dependency of your application, you would run following command: + + npm install --save --save-exact leaflet + +To benefit from TypeScript type definitions from [DefinitelyTyped][] repository in development, you would run following command: + + npm install --save-dev --save-exact @types/leaflet + +Then you would import the JS and CSS files specified in library's installation instructions so that [Webpack][] knows about them: + +Edit [src/main/webapp/app/vendor.ts](src/main/webapp/app/vendor.ts) file: +~~~ +import 'leaflet/dist/leaflet.js'; +~~~ + +Edit [src/main/webapp/content/css/vendor.css](src/main/webapp/content/css/vendor.css) file: +~~~ +@import '~leaflet/dist/leaflet.css'; +~~~ + +Note: there are still few other things remaining to do for Leaflet that we won't detail here. + +For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][]. + +### Using angular-cli + +You can also use [Angular CLI][] to generate some custom client code. + +For example, the following command: + + ng generate component my-component + +will generate few files: + + create src/main/webapp/app/my-component/my-component.component.html + create src/main/webapp/app/my-component/my-component.component.ts + update src/main/webapp/app/app.module.ts + +## Building for production + +To optimize the baeldung application for production, run: + + ./mvnw -Pprod clean package + +This will concatenate and minify the client CSS and JavaScript files. It will also modify `index.html` so it references these new files. +To ensure everything worked, run: + + java -jar target/*.war + +Then navigate to [http://localhost:8080](http://localhost:8080) in your browser. + +Refer to [Using JHipster in production][] for more details. + +## Testing + +To launch your application's tests, run: + + ./mvnw clean test + +### Client tests + +Unit tests are run by [Karma][] and written with [Jasmine][]. They're located in [src/test/javascript/](src/test/javascript/) and can be run with: + + npm test + +UI end-to-end tests are powered by [Protractor][], which is built on top of WebDriverJS. They're located in [src/test/javascript/e2e](src/test/javascript/e2e) +and can be run by starting Spring Boot in one terminal (`./mvnw spring-boot:run`) and running the tests (`gulp itest`) in a second one. +### Other tests + +Performance tests are run by [Gatling][] and written in Scala. They're located in [src/test/gatling](src/test/gatling) and can be run with: + + ./mvnw gatling:execute + +For more information, refer to the [Running tests page][]. + +## Using Docker to simplify development (optional) + +You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services. +For example, to start a mysql database in a docker container, run: + + docker-compose -f src/main/docker/mysql.yml up -d + +To stop it and remove the container, run: + + docker-compose -f src/main/docker/mysql.yml down + +You can also fully dockerize your application and all the services that it depends on. +To achieve this, first build a docker image of your app by running: + + ./mvnw package -Pprod docker:build + +Then run: + + docker-compose -f src/main/docker/app.yml up -d + +For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the docker-compose sub-generator (`yo jhipster:docker-compose`), which is able to generate docker configurations for one or several JHipster applications. + +## Continuous Integration (optional) + +To configure CI for your project, run the ci-cd sub-generator (`yo jhipster:ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information. + +[JHipster Homepage and latest documentation]: https://jhipster.github.io +[JHipster 4.0.8 archive]: https://jhipster.github.io/documentation-archive/v4.0.8 + +[Using JHipster in development]: https://jhipster.github.io/documentation-archive/v4.0.8/development/ +[Using Docker and Docker-Compose]: https://jhipster.github.io/documentation-archive/v4.0.8/docker-compose +[Using JHipster in production]: https://jhipster.github.io/documentation-archive/v4.0.8/production/ +[Running tests page]: https://jhipster.github.io/documentation-archive/v4.0.8/running-tests/ +[Setting up Continuous Integration]: https://jhipster.github.io/documentation-archive/v4.0.8/setting-up-ci/ + +[Gatling]: http://gatling.io/ +[Node.js]: https://nodejs.org/ +[Yarn]: https://yarnpkg.org/ +[Webpack]: https://webpack.github.io/ +[Angular CLI]: https://cli.angular.io/ +[BrowserSync]: http://www.browsersync.io/ +[Karma]: http://karma-runner.github.io/ +[Jasmine]: http://jasmine.github.io/2.0/introduction.html +[Protractor]: https://angular.github.io/protractor/ +[Leaflet]: http://leafletjs.com/ +[DefinitelyTyped]: http://definitelytyped.org/ diff --git a/jhipster/angular-cli.json b/jhipster/angular-cli.json new file mode 100644 index 0000000000..15558a2fae --- /dev/null +++ b/jhipster/angular-cli.json @@ -0,0 +1,55 @@ +{ + "project": { + "version": "1.0.0-beta.24", + "name": "baeldung" + }, + "apps": [ + { + "root": "src/main/webapp/", + "outDir": "target/www/app", + "assets": [ + "content", + "favicon.ico" + ], + "index": "index.html", + "main": "app/app.main.ts", + "test": "", + "tsconfig": "../../../tsconfig.json", + "prefix": "jhi", + "mobile": false, + "styles": [ + "content/css/main.css" + ], + "scripts": [], + "environments": {} + } + ], + "addons": [], + "packages": [], + "e2e": { + "protractor": { + "config": "src/test/javascript/protractor.conf.js" + } + }, + "test": { + "karma": { + "config": "src/test/javascript/karma.conf.js" + } + }, + "defaults": { + "styleExt": "css", + "prefixInterfaces": false, + "inline": { + "style": true, + "template": false + }, + "spec": { + "class": false, + "component": false, + "directive": false, + "module": false, + "pipe": false, + "service": false + } + } +} diff --git a/jhipster/circle.yml b/jhipster/circle.yml new file mode 100644 index 0000000000..bc9371e94f --- /dev/null +++ b/jhipster/circle.yml @@ -0,0 +1,25 @@ +machine: + services: + - docker + java: + version: oraclejdk8 + node: + version: 6.10.0 +dependencies: + cache_directories: + - node + - node_modules + - ~/.m2 + override: + - java -version + - npm install -g npm + - node -v + - npm -v + - java -version + - npm install +test: + override: + - chmod +x mvnw + - ./mvnw clean test + - npm test + - ./mvnw package -Pprod -DskipTests diff --git a/jhipster/mvnw b/jhipster/mvnw new file mode 100755 index 0000000000..a1ba1bf554 --- /dev/null +++ b/jhipster/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/jhipster/mvnw.cmd b/jhipster/mvnw.cmd new file mode 100644 index 0000000000..2b934e89dd --- /dev/null +++ b/jhipster/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/jhipster/package.json b/jhipster/package.json new file mode 100644 index 0000000000..40bbd28799 --- /dev/null +++ b/jhipster/package.json @@ -0,0 +1,112 @@ +{ + "name": "baeldung", + "version": "0.0.0", + "description": "Description for baeldung", + "private": true, + "cacheDirectories": [ + "node_modules" + ], + "dependencies": { + "@angular/common": "2.4.7", + "@angular/compiler": "2.4.7", + "@angular/core": "2.4.7", + "@angular/forms": "2.4.7", + "@angular/http": "2.4.7", + "@angular/platform-browser": "2.4.7", + "@angular/platform-browser-dynamic": "2.4.7", + "@angular/router": "3.4.7", + "@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.20", + "angular2-infinite-scroll": "0.3.0", + "bootstrap": "4.0.0-alpha.6", + "font-awesome": "4.7.0", + "angular2-cookie": "1.2.6", + "core-js": "2.4.1", + "jquery": "3.1.1", + "ng-jhipster": "0.1.9", + "ng2-webstorage": "1.5.0", + "reflect-metadata": "0.1.9", + "rxjs": "5.1.0", + "swagger-ui": "2.2.10", + "tether": "1.4.0", + "zone.js": "0.7.6" + }, + "devDependencies": { + "@angular/cli": "1.0.0-beta.28.3", + "@types/jasmine": "2.5.42", + "@types/node": "7.0.5", + "@types/selenium-webdriver": "2.53.39", + "add-asset-html-webpack-plugin": "1.0.2", + "angular2-template-loader": "0.6.2", + "awesome-typescript-loader": "3.0.7", + "browser-sync": "2.18.7", + "browser-sync-webpack-plugin": "1.1.4", + "codelyzer": "2.0.0", + "copy-webpack-plugin": "4.0.1", + "css-loader": "0.26.1", + "del": "2.2.2", + "event-stream": "3.3.4", + "exports-loader": "0.6.3", + "extract-text-webpack-plugin": "2.0.0-beta.5", + "file-loader": "0.10.0", + "generator-jhipster": "4.0.8", + "html-webpack-plugin": "2.28.0", + "image-webpack-loader": "3.2.0", + "jasmine-core": "2.5.2", + "jasmine-reporters": "2.2.0", + "karma": "1.4.1", + "karma-chrome-launcher": "2.0.0", + "karma-coverage": "1.1.1", + "karma-intl-shim": "1.0.3", + "karma-jasmine": "1.1.0", + "karma-junit-reporter": "1.2.0", + "karma-phantomjs-launcher": "1.0.2", + "karma-remap-istanbul": "0.6.0", + "karma-sourcemap-loader": "0.3.7", + "karma-webpack": "2.0.2", + "lazypipe": "1.0.1", + "lodash": "4.17.4", + "map-stream": "0.0.6", + "phantomjs-prebuilt": "2.1.14", + "protractor": "5.1.1", + "protractor-jasmine2-screenshot-reporter": "0.3.3", + "ts-node": "2.1.0", + "proxy-middleware": "0.15.0", + "raw-loader": "0.5.1", + "run-sequence": "1.2.2", + "sourcemap-istanbul-instrumenter-loader": "0.2.0", + "string-replace-webpack-plugin": "0.0.5", + "style-loader": "0.13.1", + "to-string-loader": "1.1.5", + "tslint": "4.4.2", + "tslint-loader": "3.4.1", + "typescript": "2.1.6", + "webpack": "2.2.1", + "webpack-dev-server": "2.3.0", + "webpack-merge": "2.6.1", + "webpack-visualizer-plugin": "0.1.10", + "write-file-webpack-plugin": "3.4.2", + "xml2js": "0.4.17", + "sass-loader": "5.0.1", + "node-sass": "4.5.0", + "postcss-loader": "1.3.0", + "yargs": "6.6.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "scripts": { + "lint": "tslint 'src/main/webapp/app/**/*.ts' --force", + "lint:fix": "tslint 'src/main/webapp/app/**/*.ts' --fix --force", + "tsc": "tsc", + "tsc:w": "tsc -w", + "start": "npm run webpack:dev", + "webpack:build": "webpack --config webpack/webpack.vendor.js && webpack --config webpack/webpack.dev.js", + "webpack:build:dev": "webpack --config webpack/webpack.dev.js", + "webpack:dev": "webpack-dev-server --config webpack/webpack.dev.js --progress --inline --hot --profile --port=9060", + "webpack:prod": "npm test && webpack -p --config webpack/webpack.vendor.js && webpack -p --config webpack/webpack.prod.js", + "test": "npm run lint && karma start src/test/javascript/karma.conf.js", + "test:watch": "karma start --watch", + "e2e": "protractor src/test/javascript/protractor.conf.js", + "postinstall": "webdriver-manager update && npm run webpack:build" + } +} diff --git a/jhipster/pom.xml b/jhipster/pom.xml new file mode 100644 index 0000000000..ba78ad4e2b --- /dev/null +++ b/jhipster/pom.xml @@ -0,0 +1,1034 @@ + + + 4.0.0 + + + spring-boot-starter-parent + org.springframework.boot + 1.5.2.RELEASE + + + + com.baeldung + jhipster-monolithic + 0.0.1-SNAPSHOT + war + JHipster Monolithic Application + + + ${maven.version} + + + + + com.fasterxml.jackson.datatype + jackson-datatype-hibernate5 + + + com.fasterxml.jackson.datatype + jackson-datatype-hppc + + + com.fasterxml.jackson.datatype + jackson-datatype-json-org + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.h2database + h2 + + + com.jayway.jsonpath + json-path + test + + + + com.jcraft + jzlib + ${jzlib.version} + + + com.mattbertolini + liquibase-slf4j + ${liquibase-slf4j.version} + + + com.ryantenney.metrics + metrics-spring + ${metrics-spring.version} + + + metrics-annotation + com.codahale.metrics + + + metrics-core + com.codahale.metrics + + + metrics-healthchecks + com.codahale.metrics + + + + + com.zaxxer + HikariCP + + + tools + com.sun + + + + + + commons-io + commons-io + ${commons-io.version} + + + io.dropwizard.metrics + metrics-annotation + ${dropwizard-metrics.version} + + + io.dropwizard.metrics + metrics-core + + + io.dropwizard.metrics + metrics-json + ${dropwizard-metrics.version} + + + io.dropwizard.metrics + metrics-jvm + ${dropwizard-metrics.version} + + + io.dropwizard.metrics + metrics-servlet + ${dropwizard-metrics.version} + + + io.dropwizard.metrics + metrics-servlets + + + io.gatling.highcharts + gatling-charts-highcharts + ${gatling.version} + test + + + io.github.jhipster + jhipster + ${jhipster.server.version} + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + io.springfox + springfox-bean-validators + ${springfox.version} + + + io.springfox + springfox-swagger2 + ${springfox.version} + + + mapstruct + org.mapstruct + + + + + javax.cache + cache-api + + + mysql + mysql-connector-java + + + + net.logstash.logback + logstash-logback-encoder + ${logstash-logback-encoder.version} + + + logback-core + ch.qos.logback + + + logback-classic + ch.qos.logback + + + logback-access + ch.qos.logback + + + + + org.apache.commons + commons-lang3 + ${commons-lang.version} + + + org.assertj + assertj-core + test + + + org.awaitility + awaitility + ${awaitility.version} + test + + + org.ehcache + ehcache + + + org.hibernate + hibernate-envers + + + org.hibernate + hibernate-jcache + ${hibernate.version} + + + org.hibernate + hibernate-validator + + + org.liquibase + liquibase-core + + + jetty-servlet + org.eclipse.jetty + + + + + org.mapstruct + mapstruct-jdk8 + ${mapstruct.version} + + + org.springframework + spring-context-support + + + org.springframework.boot + spring-boot-actuator + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-loader-tools + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-cloud-connectors + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-logging + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + spring-boot-starter-tomcat + org.springframework.boot + + + + + org.springframework.boot + spring-boot-test + test + + + + org.springframework.security + spring-security-data + + + org.springframework.security + spring-security-test + test + + + + + spring-boot:run + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + prepare-agent + + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + install-node-and-npm + npm + + + + + + + + + + + + + + + com.github.ekryd.sortpom + sortpom-maven-plugin + ${sortpom-maven-plugin.version} + + + verify + + sort + + + + + true + 4 + groupId,artifactId + groupId,artifactId + true + false + + + + com.spotify + docker-maven-plugin + ${docker-maven-plugin.version} + + baeldung + src/main/docker + + + / + ${project.build.directory} + ${project.build.finalName}.war + + + + + + io.gatling + gatling-maven-plugin + ${gatling-maven-plugin.version} + + src/test/gatling/conf + src/test/gatling/data + target/gatling/results + src/test/gatling/bodies + src/test/gatling/simulations + + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + org.apache.maven.plugins + maven-eclipse-plugin + + true + true + + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-versions + + enforce + + + + + + + You are running an older version of + Maven. JHipster requires at least Maven + ${maven.version} + [${maven.version},) + + + You are running an older version of + Java. JHipster requires at least JDK + ${java.version} + [${java.version}.0,) + + + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + + default-resources + validate + + copy-resources + + + target/classes + false + + # + + + + src/main/resources/ + true + + **/*.xml + **/*.yml + + + + src/main/resources/ + false + + **/*.xml + **/*.yml + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + alphabetical + + **/*IntTest.java + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + pre-unit-tests + + prepare-agent + + + + ${project.testresult.directory}/coverage/jacoco/jacoco.exec + + + + + post-unit-test + test + + report + + + ${project.testresult.directory}/coverage/jacoco/jacoco.exec + ${project.testresult.directory}/coverage/jacoco + + + + + + org.liquibase + liquibase-maven-plugin + ${liquibase.version} + + + javax.validation + validation-api + ${validation-api.version} + + + org.javassist + javassist + ${javassist.version} + + + org.liquibase.ext + liquibase-hibernate5 + ${liquibase-hibernate5.version} + + + org.springframework.boot + spring-boot-starter-data-jpa + ${project.parent.version} + + + + src/main/resources/config/liquibase/master.xml + src/main/resources/config/liquibase/changelog/${maven.build.timestamp}_changelog.xml + org.h2.Driver + jdbc:h2:file:./target/h2db/db/baeldung + + baeldung + + hibernate:spring:com.baeldung.domain?dialect=org.hibernate.dialect.H2Dialect&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy + true + debug + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${sonar-maven-plugin.version} + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + + + + + + + + + no-liquibase + + ,no-liquibase + + + + swagger + + ,swagger + + + + webpack + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + ${node.version} + ${npm.version} + + + + webpack build dev + generate-resources + + npm + + + run webpack:build:dev + + + + + + + + + dev + + true + + + + + org.apache.maven.plugins + maven-war-plugin + + src/main/webapp/ + + + + + + + DEBUG + + dev${profile.no-liquibase} + + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-starter-undertow + + + + + prod + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + ${node.version} + ${npm.version} + + + + npm install + + npm + + + install + + + + npm rebuild node-sass + + npm + + + rebuild node-sass + + + + webpack build prod + generate-resources + + npm + + + run webpack:prod + + + + + + maven-clean-plugin + + + + target/www/ + + + + + + org.apache.maven.plugins + maven-war-plugin + + target/www/ + + + + org.springframework.boot + spring-boot-maven-plugin + + + + build-info + + + + + true + + + + + + + INFO + + prod${profile.swagger}${profile.no-liquibase} + + + + org.springframework.boot + spring-boot-starter-undertow + + + + + + cc + + + + net.alchim31.maven + scala-maven-plugin + ${scala-maven-plugin.version} + + + compile + compile + + add-source + compile + + + + test-compile + test-compile + + add-source + testCompile + + + + + incremental + true + ${scala.version} + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + none + + + default-testCompile + none + + + + + org.apache.maven.plugins + maven-war-plugin + + src/main/webapp/ + + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + true + + + + + + + + DEBUG + + dev,swagger + + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-starter-undertow + + + + + + graphite + + + io.dropwizard.metrics + metrics-graphite + + + + + + prometheus + + + io.prometheus + simpleclient + ${prometheus-simpleclient.version} + + + io.prometheus + simpleclient_dropwizard + ${prometheus-simpleclient.version} + + + io.prometheus + simpleclient_servlet + ${prometheus-simpleclient.version} + + + + + + IDE + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + integration + + true + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*IntTest.java + + + + + + + + + + + + -Djava.security.egd=file:/dev/./urandom -Xmx256m + 3.6.2 + 2.0.0 + 2.5 + 3.5 + 0.4.13 + 1.3 + 2.2.1 + 2.2.3 + 5.2.8.Final + 2.6.0 + 0.7.9 + 1.8 + 3.21.0-GA + 1.0.0 + 1.1.0 + 0.7.0 + 1.1.3 + 3.6 + 2.0.0 + 4.8 + jdt_apt + 1.1.0.Final + 3.6.0 + 1.4.1 + 3.0.1 + yyyyMMddHHmmss + ${java.version} + ${java.version} + 3.0.0 + 3.1.3 + v6.10.0 + 4.3.0 + + + + + ${project.build.directory}/test-results + 0.0.20 + false + 3.2.2 + 2.12.1 + 3.2 + + src/main/webapp/content/**/*.*, + src/main/webapp/bower_components/**/*.*, + src/main/webapp/i18n/*.js, target/www/**/*.* + + S3437,UndocumentedApi,BoldAndItalicTagsCheck + + + src/main/webapp/app/**/*.* + Web:BoldAndItalicTagsCheck + + src/main/java/**/* + squid:S3437 + + src/main/java/**/* + squid:UndocumentedApi + + ${project.testresult.directory}/coverage/jacoco/jacoco-it.exec + ${project.testresult.directory}/coverage/jacoco/jacoco.exec + jacoco + + ${project.testresult.directory}/karma + + ${project.testresult.directory}/coverage/report-lcov/lcov.info + + ${project.testresult.directory}/coverage/report-lcov/lcov.info + + ${project.basedir}/src/main/ + ${project.testresult.directory}/surefire-reports + ${project.basedir}/src/test/ + + 2.5.0 + + 2.6.1 + 1.4.10.Final + 1.1.0.Final + + diff --git a/jhipster/postcss.config.js b/jhipster/postcss.config.js new file mode 100644 index 0000000000..f549c034d5 --- /dev/null +++ b/jhipster/postcss.config.js @@ -0,0 +1,3 @@ +module.exports = { + plugins: [] +} diff --git a/jhipster/src/main/docker/Dockerfile b/jhipster/src/main/docker/Dockerfile new file mode 100644 index 0000000000..66991b2998 --- /dev/null +++ b/jhipster/src/main/docker/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:8-jre-alpine + +ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \ + JHIPSTER_SLEEP=0 + +# add directly the war +ADD *.war /app.war + +VOLUME /tmp +EXPOSE 8080 +CMD echo "The application will start in ${JHIPSTER_SLEEP}s..." && \ + sleep ${JHIPSTER_SLEEP} && \ + java -Djava.security.egd=file:/dev/./urandom -jar /app.war diff --git a/jhipster/src/main/docker/app.yml b/jhipster/src/main/docker/app.yml new file mode 100644 index 0000000000..78cd2c1602 --- /dev/null +++ b/jhipster/src/main/docker/app.yml @@ -0,0 +1,14 @@ +version: '2' +services: + baeldung-app: + image: baeldung + environment: + - SPRING_PROFILES_ACTIVE=prod,swagger + - SPRING_DATASOURCE_URL=jdbc:mysql://baeldung-mysql:3306/baeldung?useUnicode=true&characterEncoding=utf8&useSSL=false + - JHIPSTER_SLEEP=10 # gives time for the database to boot before the application + ports: + - 8080:8080 + baeldung-mysql: + extends: + file: mysql.yml + service: baeldung-mysql diff --git a/jhipster/src/main/docker/mysql.yml b/jhipster/src/main/docker/mysql.yml new file mode 100644 index 0000000000..5038d09504 --- /dev/null +++ b/jhipster/src/main/docker/mysql.yml @@ -0,0 +1,13 @@ +version: '2' +services: + baeldung-mysql: + image: mysql:5.7.13 + # volumes: + # - ~/volumes/jhipster/baeldung/mysql/:/var/lib/mysql/ + environment: + - MYSQL_USER=root + - MYSQL_ALLOW_EMPTY_PASSWORD=yes + - MYSQL_DATABASE=baeldung + ports: + - 3306:3306 + command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8 diff --git a/jhipster/src/main/docker/sonar.yml b/jhipster/src/main/docker/sonar.yml new file mode 100644 index 0000000000..718be83b4d --- /dev/null +++ b/jhipster/src/main/docker/sonar.yml @@ -0,0 +1,7 @@ +version: '2' +services: + baeldung-sonar: + image: sonarqube:6.2-alpine + ports: + - 9000:9000 + - 9092:9092 diff --git a/jhipster/src/main/java/com/baeldung/ApplicationWebXml.java b/jhipster/src/main/java/com/baeldung/ApplicationWebXml.java new file mode 100644 index 0000000000..762d18420c --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/ApplicationWebXml.java @@ -0,0 +1,21 @@ +package com.baeldung; + +import com.baeldung.config.DefaultProfileUtil; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; + +/** + * This is a helper Java class that provides an alternative to creating a web.xml. + * This will be invoked only when the application is deployed to a servlet container like Tomcat, JBoss etc. + */ +public class ApplicationWebXml extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + /** + * set a default to use when no profile is configured. + */ + DefaultProfileUtil.addDefaultProfile(application.application()); + return application.sources(BaeldungApp.class); + } +} diff --git a/jhipster/src/main/java/com/baeldung/BaeldungApp.java b/jhipster/src/main/java/com/baeldung/BaeldungApp.java new file mode 100644 index 0000000000..13b6b2b3fa --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/BaeldungApp.java @@ -0,0 +1,84 @@ +package com.baeldung; + +import com.baeldung.config.ApplicationProperties; +import com.baeldung.config.DefaultProfileUtil; + +import io.github.jhipster.config.JHipsterConstants; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.actuate.autoconfigure.*; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.core.env.Environment; + +import javax.annotation.PostConstruct; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collection; + +@ComponentScan +@EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class}) +@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class}) +public class BaeldungApp { + + private static final Logger log = LoggerFactory.getLogger(BaeldungApp.class); + + private final Environment env; + + public BaeldungApp(Environment env) { + this.env = env; + } + + /** + * Initializes baeldung. + *

+ * Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile + *

+ * You can find more information on how profiles work with JHipster on http://jhipster.github.io/profiles/. + */ + @PostConstruct + public void initApplication() { + Collection activeProfiles = Arrays.asList(env.getActiveProfiles()); + if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { + log.error("You have misconfigured your application! It should not run " + + "with both the 'dev' and 'prod' profiles at the same time."); + } + if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) { + log.error("You have misconfigured your application! It should not" + + "run with both the 'dev' and 'cloud' profiles at the same time."); + } + } + + /** + * Main method, used to run the application. + * + * @param args the command line arguments + * @throws UnknownHostException if the local host name could not be resolved into an address + */ + public static void main(String[] args) throws UnknownHostException { + SpringApplication app = new SpringApplication(BaeldungApp.class); + DefaultProfileUtil.addDefaultProfile(app); + Environment env = app.run(args).getEnvironment(); + String protocol = "http"; + if (env.getProperty("server.ssl.key-store") != null) { + protocol = "https"; + } + log.info("\n----------------------------------------------------------\n\t" + + "Application '{}' is running! Access URLs:\n\t" + + "Local: \t\t{}://localhost:{}\n\t" + + "External: \t{}://{}:{}\n\t" + + "Profile(s): \t{}\n----------------------------------------------------------", + env.getProperty("spring.application.name"), + protocol, + env.getProperty("server.port"), + protocol, + InetAddress.getLocalHost().getHostAddress(), + env.getProperty("server.port"), + env.getActiveProfiles()); + } +} diff --git a/jhipster/src/main/java/com/baeldung/aop/logging/LoggingAspect.java b/jhipster/src/main/java/com/baeldung/aop/logging/LoggingAspect.java new file mode 100644 index 0000000000..7fbd352820 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/aop/logging/LoggingAspect.java @@ -0,0 +1,79 @@ +package com.baeldung.aop.logging; + +import io.github.jhipster.config.JHipsterConstants; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import java.util.Arrays; + +/** + * Aspect for logging execution of service and repository Spring components. + * + * By default, it only runs with the "dev" profile. + */ +@Aspect +public class LoggingAspect { + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private final Environment env; + + public LoggingAspect(Environment env) { + this.env = env; + } + + /** + * Pointcut that matches all repositories, services and Web REST endpoints. + */ + @Pointcut("within(com.baeldung.repository..*) || within(com.baeldung.service..*) || within(com.baeldung.web.rest..*)") + public void loggingPointcut() { + // Method is empty as this is just a Pointcut, the implementations are in the advices. + } + + /** + * Advice that logs methods throwing exceptions. + */ + @AfterThrowing(pointcut = "loggingPointcut()", throwing = "e") + public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { + if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) { + log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL", e.getMessage(), e); + + } else { + log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL"); + } + } + + /** + * Advice that logs when a method is entered and exited. + */ + @Around("loggingPointcut()") + public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { + if (log.isDebugEnabled()) { + log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs())); + } + try { + Object result = joinPoint.proceed(); + if (log.isDebugEnabled()) { + log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(), + joinPoint.getSignature().getName(), result); + } + return result; + } catch (IllegalArgumentException e) { + log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()), + joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); + + throw e; + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/ApplicationProperties.java b/jhipster/src/main/java/com/baeldung/config/ApplicationProperties.java new file mode 100644 index 0000000000..add1782678 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/ApplicationProperties.java @@ -0,0 +1,15 @@ +package com.baeldung.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Properties specific to JHipster. + * + *

+ * Properties are configured in the application.yml file. + *

+ */ +@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false) +public class ApplicationProperties { + +} diff --git a/jhipster/src/main/java/com/baeldung/config/AsyncConfiguration.java b/jhipster/src/main/java/com/baeldung/config/AsyncConfiguration.java new file mode 100644 index 0000000000..dc1ba37514 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/AsyncConfiguration.java @@ -0,0 +1,46 @@ +package com.baeldung.config; + +import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor; +import io.github.jhipster.config.JHipsterProperties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.*; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@Configuration +@EnableAsync +@EnableScheduling +public class AsyncConfiguration implements AsyncConfigurer { + + private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class); + + private final JHipsterProperties jHipsterProperties; + + public AsyncConfiguration(JHipsterProperties jHipsterProperties) { + this.jHipsterProperties = jHipsterProperties; + } + + @Override + @Bean(name = "taskExecutor") + public Executor getAsyncExecutor() { + log.debug("Creating Async Task Executor"); + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize()); + executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize()); + executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity()); + executor.setThreadNamePrefix("baeldung-Executor-"); + return new ExceptionHandlingAsyncTaskExecutor(executor); + } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return new SimpleAsyncUncaughtExceptionHandler(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/CacheConfiguration.java b/jhipster/src/main/java/com/baeldung/config/CacheConfiguration.java new file mode 100644 index 0000000000..4323fa076a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/CacheConfiguration.java @@ -0,0 +1,48 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterProperties; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.expiry.Duration; +import org.ehcache.expiry.Expirations; +import org.ehcache.jsr107.Eh107Configuration; + +import java.util.concurrent.TimeUnit; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.*; + +@Configuration +@EnableCaching +@AutoConfigureAfter(value = { MetricsConfiguration.class }) +@AutoConfigureBefore(value = { WebConfigurer.class, DatabaseConfiguration.class }) +public class CacheConfiguration { + + private final javax.cache.configuration.Configuration jcacheConfiguration; + + public CacheConfiguration(JHipsterProperties jHipsterProperties) { + JHipsterProperties.Cache.Ehcache ehcache = + jHipsterProperties.getCache().getEhcache(); + + jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration( + CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class, + ResourcePoolsBuilder.heap(ehcache.getMaxEntries())) + .withExpiry(Expirations.timeToLiveExpiration(Duration.of(ehcache.getTimeToLiveSeconds(), TimeUnit.SECONDS))) + .build()); + } + + @Bean + public JCacheManagerCustomizer cacheManagerCustomizer() { + return cm -> { + cm.createCache(com.baeldung.domain.User.class.getName(), jcacheConfiguration); + cm.createCache(com.baeldung.domain.Authority.class.getName(), jcacheConfiguration); + cm.createCache(com.baeldung.domain.User.class.getName() + ".authorities", jcacheConfiguration); + cm.createCache(com.baeldung.domain.Post.class.getName(), jcacheConfiguration); + cm.createCache(com.baeldung.domain.Comment.class.getName(), jcacheConfiguration); + // jhipster-needle-ehcache-add-entry + }; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/CloudDatabaseConfiguration.java b/jhipster/src/main/java/com/baeldung/config/CloudDatabaseConfiguration.java new file mode 100644 index 0000000000..cae9ec1e82 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/CloudDatabaseConfiguration.java @@ -0,0 +1,23 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterConstants; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.config.java.AbstractCloudConfig; +import org.springframework.context.annotation.*; + +import javax.sql.DataSource; + +@Configuration +@Profile(JHipsterConstants.SPRING_PROFILE_CLOUD) +public class CloudDatabaseConfiguration extends AbstractCloudConfig { + + private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class); + + @Bean + public DataSource dataSource() { + log.info("Configuring JDBC datasource from a cloud provider"); + return connectionFactory().dataSource(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/Constants.java b/jhipster/src/main/java/com/baeldung/config/Constants.java new file mode 100644 index 0000000000..9d797c1934 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/Constants.java @@ -0,0 +1,16 @@ +package com.baeldung.config; + +/** + * Application constants. + */ +public final class Constants { + + //Regex for acceptable logins + public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$"; + + public static final String SYSTEM_ACCOUNT = "system"; + public static final String ANONYMOUS_USER = "anonymoususer"; + + private Constants() { + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/DatabaseConfiguration.java b/jhipster/src/main/java/com/baeldung/config/DatabaseConfiguration.java new file mode 100644 index 0000000000..b64fd7cc82 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/DatabaseConfiguration.java @@ -0,0 +1,75 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterConstants; +import io.github.jhipster.config.liquibase.AsyncSpringLiquibase; + +import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; +import liquibase.integration.spring.SpringLiquibase; +import org.h2.tools.Server; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; +import org.springframework.core.task.TaskExecutor; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; +import java.sql.SQLException; + +@Configuration +@EnableJpaRepositories("com.baeldung.repository") +@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware") +@EnableTransactionManagement +public class DatabaseConfiguration { + + private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class); + + private final Environment env; + + public DatabaseConfiguration(Environment env) { + this.env = env; + } + + /** + * Open the TCP port for the H2 database, so it is available remotely. + * + * @return the H2 database TCP server + * @throws SQLException if the server failed to start + */ + @Bean(initMethod = "start", destroyMethod = "stop") + @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) + public Server h2TCPServer() throws SQLException { + return Server.createTcpServer("-tcp","-tcpAllowOthers"); + } + + @Bean + public SpringLiquibase liquibase(@Qualifier("taskExecutor") TaskExecutor taskExecutor, + DataSource dataSource, LiquibaseProperties liquibaseProperties) { + + // Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously + SpringLiquibase liquibase = new AsyncSpringLiquibase(taskExecutor, env); + liquibase.setDataSource(dataSource); + liquibase.setChangeLog("classpath:config/liquibase/master.xml"); + liquibase.setContexts(liquibaseProperties.getContexts()); + liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema()); + liquibase.setDropFirst(liquibaseProperties.isDropFirst()); + if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) { + liquibase.setShouldRun(false); + } else { + liquibase.setShouldRun(liquibaseProperties.isEnabled()); + log.debug("Configuring Liquibase"); + } + return liquibase; + } + + @Bean + public Hibernate5Module hibernate5Module() { + return new Hibernate5Module(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/DateTimeFormatConfiguration.java b/jhipster/src/main/java/com/baeldung/config/DateTimeFormatConfiguration.java new file mode 100644 index 0000000000..aab57adfb0 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/DateTimeFormatConfiguration.java @@ -0,0 +1,17 @@ +package com.baeldung.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@Configuration +public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter { + + @Override + public void addFormatters(FormatterRegistry registry) { + DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); + registrar.setUseIsoFormat(true); + registrar.registerFormatters(registry); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/DefaultProfileUtil.java b/jhipster/src/main/java/com/baeldung/config/DefaultProfileUtil.java new file mode 100644 index 0000000000..7918fb49a4 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/DefaultProfileUtil.java @@ -0,0 +1,48 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterConstants; + +import org.springframework.boot.SpringApplication; +import org.springframework.core.env.Environment; + +import java.util.*; + +/** + * Utility class to load a Spring profile to be used as default + * when there is no spring.profiles.active set in the environment or as command line argument. + * If the value is not available in application.yml then dev profile will be used as default. + */ +public final class DefaultProfileUtil { + + private static final String SPRING_PROFILE_DEFAULT = "spring.profiles.default"; + + private DefaultProfileUtil() { + } + + /** + * Set a default to use when no profile is configured. + * + * @param app the Spring application + */ + public static void addDefaultProfile(SpringApplication app) { + Map defProperties = new HashMap<>(); + /* + * The default profile to use when no other profiles are defined + * This cannot be set in the application.yml file. + * See https://github.com/spring-projects/spring-boot/issues/1219 + */ + defProperties.put(SPRING_PROFILE_DEFAULT, JHipsterConstants.SPRING_PROFILE_DEVELOPMENT); + app.setDefaultProperties(defProperties); + } + + /** + * Get the profiles that are applied else get default profiles. + */ + public static String[] getActiveProfiles(Environment env) { + String[] profiles = env.getActiveProfiles(); + if (profiles.length == 0) { + return env.getDefaultProfiles(); + } + return profiles; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/LocaleConfiguration.java b/jhipster/src/main/java/com/baeldung/config/LocaleConfiguration.java new file mode 100644 index 0000000000..dd3a499d56 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/LocaleConfiguration.java @@ -0,0 +1,35 @@ +package com.baeldung.config; + +import io.github.jhipster.config.locale.AngularCookieLocaleResolver; + +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; + +@Configuration +public class LocaleConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware { + + @Override + public void setEnvironment(Environment environment) { + // unused + } + + @Bean(name = "localeResolver") + public LocaleResolver localeResolver() { + AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver(); + cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY"); + return cookieLocaleResolver; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); + localeChangeInterceptor.setParamName("language"); + registry.addInterceptor(localeChangeInterceptor); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/LoggingAspectConfiguration.java b/jhipster/src/main/java/com/baeldung/config/LoggingAspectConfiguration.java new file mode 100644 index 0000000000..936f54709a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/LoggingAspectConfiguration.java @@ -0,0 +1,19 @@ +package com.baeldung.config; + +import com.baeldung.aop.logging.LoggingAspect; + +import io.github.jhipster.config.JHipsterConstants; + +import org.springframework.context.annotation.*; +import org.springframework.core.env.Environment; + +@Configuration +@EnableAspectJAutoProxy +public class LoggingAspectConfiguration { + + @Bean + @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) + public LoggingAspect loggingAspect(Environment env) { + return new LoggingAspect(env); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/LoggingConfiguration.java b/jhipster/src/main/java/com/baeldung/config/LoggingConfiguration.java new file mode 100644 index 0000000000..3ca6a48821 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/LoggingConfiguration.java @@ -0,0 +1,109 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterProperties; + +import ch.qos.logback.classic.AsyncAppender; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggerContextListener; +import ch.qos.logback.core.spi.ContextAwareBase; +import net.logstash.logback.appender.LogstashSocketAppender; +import net.logstash.logback.stacktrace.ShortenedThrowableConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class LoggingConfiguration { + + private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class); + + private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + @Value("${spring.application.name}") + private String appName; + + @Value("${server.port}") + private String serverPort; + + private final JHipsterProperties jHipsterProperties; + + public LoggingConfiguration(JHipsterProperties jHipsterProperties) { + this.jHipsterProperties = jHipsterProperties; + if (jHipsterProperties.getLogging().getLogstash().isEnabled()) { + addLogstashAppender(context); + + // Add context listener + LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener(); + loggerContextListener.setContext(context); + context.addListener(loggerContextListener); + } + } + + public void addLogstashAppender(LoggerContext context) { + log.info("Initializing Logstash logging"); + + LogstashSocketAppender logstashAppender = new LogstashSocketAppender(); + logstashAppender.setName("LOGSTASH"); + logstashAppender.setContext(context); + String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"}"; + + // Set the Logstash appender config from JHipster properties + logstashAppender.setSyslogHost(jHipsterProperties.getLogging().getLogstash().getHost()); + logstashAppender.setPort(jHipsterProperties.getLogging().getLogstash().getPort()); + logstashAppender.setCustomFields(customFields); + + // Limit the maximum length of the forwarded stacktrace so that it won't exceed the 8KB UDP limit of logstash + ShortenedThrowableConverter throwableConverter = new ShortenedThrowableConverter(); + throwableConverter.setMaxLength(7500); + throwableConverter.setRootCauseFirst(true); + logstashAppender.setThrowableConverter(throwableConverter); + + logstashAppender.start(); + + // Wrap the appender in an Async appender for performance + AsyncAppender asyncLogstashAppender = new AsyncAppender(); + asyncLogstashAppender.setContext(context); + asyncLogstashAppender.setName("ASYNC_LOGSTASH"); + asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize()); + asyncLogstashAppender.addAppender(logstashAppender); + asyncLogstashAppender.start(); + + context.getLogger("ROOT").addAppender(asyncLogstashAppender); + } + + /** + * Logback configuration is achieved by configuration file and API. + * When configuration file change is detected, the configuration is reset. + * This listener ensures that the programmatic configuration is also re-applied after reset. + */ + class LogbackLoggerContextListener extends ContextAwareBase implements LoggerContextListener { + + @Override + public boolean isResetResistant() { + return true; + } + + @Override + public void onStart(LoggerContext context) { + addLogstashAppender(context); + } + + @Override + public void onReset(LoggerContext context) { + addLogstashAppender(context); + } + + @Override + public void onStop(LoggerContext context) { + // Nothing to do. + } + + @Override + public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) { + // Nothing to do. + } + } + +} diff --git a/jhipster/src/main/java/com/baeldung/config/MetricsConfiguration.java b/jhipster/src/main/java/com/baeldung/config/MetricsConfiguration.java new file mode 100644 index 0000000000..94a50bed91 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/MetricsConfiguration.java @@ -0,0 +1,94 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterProperties; +import io.github.jhipster.config.jcache.JCacheGaugeSet; + +import com.codahale.metrics.JmxReporter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Slf4jReporter; +import com.codahale.metrics.health.HealthCheckRegistry; +import com.codahale.metrics.jvm.*; +import com.ryantenney.metrics.spring.config.annotation.EnableMetrics; +import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter; +import com.zaxxer.hikari.HikariDataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.*; + +import javax.annotation.PostConstruct; +import java.lang.management.ManagementFactory; +import java.util.concurrent.TimeUnit; + +@Configuration +@EnableMetrics(proxyTargetClass = true) +public class MetricsConfiguration extends MetricsConfigurerAdapter { + + private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory"; + private static final String PROP_METRIC_REG_JVM_GARBAGE = "jvm.garbage"; + private static final String PROP_METRIC_REG_JVM_THREADS = "jvm.threads"; + private static final String PROP_METRIC_REG_JVM_FILES = "jvm.files"; + private static final String PROP_METRIC_REG_JVM_BUFFERS = "jvm.buffers"; + + private static final String PROP_METRIC_REG_JCACHE_STATISTICS = "jcache.statistics"; + private final Logger log = LoggerFactory.getLogger(MetricsConfiguration.class); + + private MetricRegistry metricRegistry = new MetricRegistry(); + + private HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); + + private final JHipsterProperties jHipsterProperties; + + private HikariDataSource hikariDataSource; + + public MetricsConfiguration(JHipsterProperties jHipsterProperties) { + this.jHipsterProperties = jHipsterProperties; + } + + @Autowired(required = false) + public void setHikariDataSource(HikariDataSource hikariDataSource) { + this.hikariDataSource = hikariDataSource; + } + + @Override + @Bean + public MetricRegistry getMetricRegistry() { + return metricRegistry; + } + + @Override + @Bean + public HealthCheckRegistry getHealthCheckRegistry() { + return healthCheckRegistry; + } + + @PostConstruct + public void init() { + log.debug("Registering JVM gauges"); + metricRegistry.register(PROP_METRIC_REG_JVM_MEMORY, new MemoryUsageGaugeSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_GARBAGE, new GarbageCollectorMetricSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_THREADS, new ThreadStatesGaugeSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_FILES, new FileDescriptorRatioGauge()); + metricRegistry.register(PROP_METRIC_REG_JVM_BUFFERS, new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer())); + + metricRegistry.register(PROP_METRIC_REG_JCACHE_STATISTICS, new JCacheGaugeSet()); + if (hikariDataSource != null) { + log.debug("Monitoring the datasource"); + hikariDataSource.setMetricRegistry(metricRegistry); + } + if (jHipsterProperties.getMetrics().getJmx().isEnabled()) { + log.debug("Initializing Metrics JMX reporting"); + JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).build(); + jmxReporter.start(); + } + if (jHipsterProperties.getMetrics().getLogs().isEnabled()) { + log.info("Initializing Metrics Log reporting"); + final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry) + .outputTo(LoggerFactory.getLogger("metrics")) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build(); + reporter.start(jHipsterProperties.getMetrics().getLogs().getReportFrequency(), TimeUnit.SECONDS); + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/SecurityConfiguration.java b/jhipster/src/main/java/com/baeldung/config/SecurityConfiguration.java new file mode 100644 index 0000000000..118d302fcf --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/SecurityConfiguration.java @@ -0,0 +1,127 @@ +package com.baeldung.config; + +import com.baeldung.security.*; +import com.baeldung.security.jwt.*; + +import io.github.jhipster.security.*; + +import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +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; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.filter.CorsFilter; + +import javax.annotation.PostConstruct; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + private final AuthenticationManagerBuilder authenticationManagerBuilder; + + private final UserDetailsService userDetailsService; + + private final TokenProvider tokenProvider; + + private final CorsFilter corsFilter; + + public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService, + TokenProvider tokenProvider, + CorsFilter corsFilter) { + + this.authenticationManagerBuilder = authenticationManagerBuilder; + this.userDetailsService = userDetailsService; + this.tokenProvider = tokenProvider; + this.corsFilter = corsFilter; + } + + @PostConstruct + public void init() { + try { + authenticationManagerBuilder + .userDetailsService(userDetailsService) + .passwordEncoder(passwordEncoder()); + } catch (Exception e) { + throw new BeanInitializationException("Security configuration failed", e); + } + } + + @Bean + public Http401UnauthorizedEntryPoint http401UnauthorizedEntryPoint() { + return new Http401UnauthorizedEntryPoint(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring() + .antMatchers(HttpMethod.OPTIONS, "/**") + .antMatchers("/app/**/*.{js,html}") + .antMatchers("/bower_components/**") + .antMatchers("/i18n/**") + .antMatchers("/content/**") + .antMatchers("/swagger-ui/index.html") + .antMatchers("/test/**") + .antMatchers("/h2-console/**"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) + .exceptionHandling() + .authenticationEntryPoint(http401UnauthorizedEntryPoint()) + .and() + .csrf() + .disable() + .headers() + .frameOptions() + .disable() + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + .antMatchers("/api/register").permitAll() + .antMatchers("/api/activate").permitAll() + .antMatchers("/api/authenticate").permitAll() + .antMatchers("/api/account/reset_password/init").permitAll() + .antMatchers("/api/account/reset_password/finish").permitAll() + .antMatchers("/api/profile-info").permitAll() + .antMatchers("/api/**").authenticated() + .antMatchers("/management/health").permitAll() + .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN) + .antMatchers("/v2/api-docs/**").permitAll() + .antMatchers("/swagger-resources/configuration/ui").permitAll() + .antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN) + .and() + .apply(securityConfigurerAdapter()); + + } + + private JWTConfigurer securityConfigurerAdapter() { + return new JWTConfigurer(tokenProvider); + } + + @Bean + public SecurityEvaluationContextExtension securityEvaluationContextExtension() { + return new SecurityEvaluationContextExtension(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/ThymeleafConfiguration.java b/jhipster/src/main/java/com/baeldung/config/ThymeleafConfiguration.java new file mode 100644 index 0000000000..68afcae71a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/ThymeleafConfiguration.java @@ -0,0 +1,26 @@ +package com.baeldung.config; + +import org.apache.commons.lang3.CharEncoding; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.*; +import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; + +@Configuration +public class ThymeleafConfiguration { + + @SuppressWarnings("unused") + private final Logger log = LoggerFactory.getLogger(ThymeleafConfiguration.class); + + @Bean + @Description("Thymeleaf template resolver serving HTML 5 emails") + public ClassLoaderTemplateResolver emailTemplateResolver() { + ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver(); + emailTemplateResolver.setPrefix("mails/"); + emailTemplateResolver.setSuffix(".html"); + emailTemplateResolver.setTemplateMode("HTML5"); + emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8); + emailTemplateResolver.setOrder(1); + return emailTemplateResolver; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/WebConfigurer.java b/jhipster/src/main/java/com/baeldung/config/WebConfigurer.java new file mode 100644 index 0000000000..bd80a063eb --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/WebConfigurer.java @@ -0,0 +1,186 @@ +package com.baeldung.config; + +import io.github.jhipster.config.JHipsterConstants; +import io.github.jhipster.config.JHipsterProperties; +import io.github.jhipster.web.filter.CachingHttpHeadersFilter; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.servlet.InstrumentedFilter; +import com.codahale.metrics.servlets.MetricsServlet; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.*; +import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory; +import io.undertow.UndertowOptions; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import java.io.File; +import java.nio.file.Paths; +import java.util.*; +import javax.servlet.*; + +/** + * Configuration of web application with Servlet 3.0 APIs. + */ +@Configuration +public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer { + + private final Logger log = LoggerFactory.getLogger(WebConfigurer.class); + + private final Environment env; + + private final JHipsterProperties jHipsterProperties; + + private MetricRegistry metricRegistry; + + public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) { + + this.env = env; + this.jHipsterProperties = jHipsterProperties; + } + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + if (env.getActiveProfiles().length != 0) { + log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles()); + } + EnumSet disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC); + initMetrics(servletContext, disps); + if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { + initCachingHttpHeadersFilter(servletContext, disps); + } + if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) { + initH2Console(servletContext); + } + log.info("Web application fully configured"); + } + + /** + * Customize the Servlet engine: Mime types, the document root, the cache. + */ + @Override + public void customize(ConfigurableEmbeddedServletContainer container) { + MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT); + // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711 + mappings.add("html", "text/html;charset=utf-8"); + // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64 + mappings.add("json", "text/html;charset=utf-8"); + container.setMimeMappings(mappings); + // When running in an IDE or with ./mvnw spring-boot:run, set location of the static web assets. + setLocationForStaticAssets(container); + + /* + * Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288 + * HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1. + * See the JHipsterProperties class and your application-*.yml configuration files + * for more information. + */ + if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) && + container instanceof UndertowEmbeddedServletContainerFactory) { + + ((UndertowEmbeddedServletContainerFactory) container) + .addBuilderCustomizers(builder -> + builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true)); + } + } + + private void setLocationForStaticAssets(ConfigurableEmbeddedServletContainer container) { + File root; + String prefixPath = resolvePathPrefix(); + root = new File(prefixPath + "target/www/"); + if (root.exists() && root.isDirectory()) { + container.setDocumentRoot(root); + } + } + + /** + * Resolve path prefix to static resources. + */ + private String resolvePathPrefix() { + String fullExecutablePath = this.getClass().getResource("").getPath(); + String rootPath = Paths.get(".").toUri().normalize().getPath(); + String extractedPath = fullExecutablePath.replace(rootPath, ""); + int extractionEndIndex = extractedPath.indexOf("target/"); + if(extractionEndIndex <= 0) { + return ""; + } + return extractedPath.substring(0, extractionEndIndex); + } + + /** + * Initializes the caching HTTP Headers Filter. + */ + private void initCachingHttpHeadersFilter(ServletContext servletContext, + EnumSet disps) { + log.debug("Registering Caching HTTP Headers Filter"); + FilterRegistration.Dynamic cachingHttpHeadersFilter = + servletContext.addFilter("cachingHttpHeadersFilter", + new CachingHttpHeadersFilter(jHipsterProperties)); + + cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/content/*"); + cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/app/*"); + cachingHttpHeadersFilter.setAsyncSupported(true); + } + + /** + * Initializes Metrics. + */ + private void initMetrics(ServletContext servletContext, EnumSet disps) { + log.debug("Initializing Metrics registries"); + servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE, + metricRegistry); + servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY, + metricRegistry); + + log.debug("Registering Metrics Filter"); + FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter", + new InstrumentedFilter()); + + metricsFilter.addMappingForUrlPatterns(disps, true, "/*"); + metricsFilter.setAsyncSupported(true); + + log.debug("Registering Metrics Servlet"); + ServletRegistration.Dynamic metricsAdminServlet = + servletContext.addServlet("metricsServlet", new MetricsServlet()); + + metricsAdminServlet.addMapping("/management/metrics/*"); + metricsAdminServlet.setAsyncSupported(true); + metricsAdminServlet.setLoadOnStartup(2); + } + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = jHipsterProperties.getCors(); + if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) { + log.debug("Registering CORS filter"); + source.registerCorsConfiguration("/api/**", config); + source.registerCorsConfiguration("/v2/api-docs", config); + } + return new CorsFilter(source); + } + + /** + * Initializes H2 console. + */ + private void initH2Console(ServletContext servletContext) { + log.debug("Initialize H2 console"); + ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet()); + h2ConsoleServlet.addMapping("/h2-console/*"); + h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/"); + h2ConsoleServlet.setLoadOnStartup(1); + } + + @Autowired(required = false) + public void setMetricRegistry(MetricRegistry metricRegistry) { + this.metricRegistry = metricRegistry; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/audit/AuditEventConverter.java b/jhipster/src/main/java/com/baeldung/config/audit/AuditEventConverter.java new file mode 100644 index 0000000000..2062320751 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/audit/AuditEventConverter.java @@ -0,0 +1,91 @@ +package com.baeldung.config.audit; + +import com.baeldung.domain.PersistentAuditEvent; + +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.security.web.authentication.WebAuthenticationDetails; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.time.ZoneId; +import java.util.*; + +@Component +public class AuditEventConverter { + + /** + * Convert a list of PersistentAuditEvent to a list of AuditEvent + * + * @param persistentAuditEvents the list to convert + * @return the converted list. + */ + public List convertToAuditEvent(Iterable persistentAuditEvents) { + if (persistentAuditEvents == null) { + return Collections.emptyList(); + } + List auditEvents = new ArrayList<>(); + for (PersistentAuditEvent persistentAuditEvent : persistentAuditEvents) { + auditEvents.add(convertToAuditEvent(persistentAuditEvent)); + } + return auditEvents; + } + + /** + * Convert a PersistentAuditEvent to an AuditEvent + * + * @param persistentAuditEvent the event to convert + * @return the converted list. + */ + public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) { + Instant instant = persistentAuditEvent.getAuditEventDate().atZone(ZoneId.systemDefault()).toInstant(); + return new AuditEvent(Date.from(instant), persistentAuditEvent.getPrincipal(), + persistentAuditEvent.getAuditEventType(), convertDataToObjects(persistentAuditEvent.getData())); + } + + /** + * Internal conversion. This is needed to support the current SpringBoot actuator AuditEventRepository interface + * + * @param data the data to convert + * @return a map of String, Object + */ + public Map convertDataToObjects(Map data) { + Map results = new HashMap<>(); + + if (data != null) { + for (Map.Entry entry : data.entrySet()) { + results.put(entry.getKey(), entry.getValue()); + } + } + return results; + } + + /** + * Internal conversion. This method will allow to save additional data. + * By default, it will save the object as string + * + * @param data the data to convert + * @return a map of String, String + */ + public Map convertDataToStrings(Map data) { + Map results = new HashMap<>(); + + if (data != null) { + for (Map.Entry entry : data.entrySet()) { + Object object = entry.getValue(); + + // Extract the data that will be saved. + if (object instanceof WebAuthenticationDetails) { + WebAuthenticationDetails authenticationDetails = (WebAuthenticationDetails) object; + results.put("remoteAddress", authenticationDetails.getRemoteAddress()); + results.put("sessionId", authenticationDetails.getSessionId()); + } else if (object != null) { + results.put(entry.getKey(), object.toString()); + } else { + results.put(entry.getKey(), "null"); + } + } + } + + return results; + } +} diff --git a/jhipster/src/main/java/com/baeldung/config/audit/package-info.java b/jhipster/src/main/java/com/baeldung/config/audit/package-info.java new file mode 100644 index 0000000000..8ed4f851f0 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/audit/package-info.java @@ -0,0 +1,4 @@ +/** + * Audit specific code. + */ +package com.baeldung.config.audit; diff --git a/jhipster/src/main/java/com/baeldung/config/package-info.java b/jhipster/src/main/java/com/baeldung/config/package-info.java new file mode 100644 index 0000000000..5e54ad6d03 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/config/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Framework configuration files. + */ +package com.baeldung.config; diff --git a/jhipster/src/main/java/com/baeldung/domain/AbstractAuditingEntity.java b/jhipster/src/main/java/com/baeldung/domain/AbstractAuditingEntity.java new file mode 100644 index 0000000000..2d181e5dc8 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/AbstractAuditingEntity.java @@ -0,0 +1,80 @@ +package com.baeldung.domain; + +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.envers.Audited; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; + +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import java.time.ZonedDateTime; +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; + +/** + * Base abstract class for entities which will hold definitions for created, last modified by and created, + * last modified by date. + */ +@MappedSuperclass +@Audited +@EntityListeners(AuditingEntityListener.class) +public abstract class AbstractAuditingEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @CreatedBy + @Column(name = "created_by", nullable = false, length = 50, updatable = false) + @JsonIgnore + private String createdBy; + + @CreatedDate + @Column(name = "created_date", nullable = false) + @JsonIgnore + private ZonedDateTime createdDate = ZonedDateTime.now(); + + @LastModifiedBy + @Column(name = "last_modified_by", length = 50) + @JsonIgnore + private String lastModifiedBy; + + @LastModifiedDate + @Column(name = "last_modified_date") + @JsonIgnore + private ZonedDateTime lastModifiedDate = ZonedDateTime.now(); + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public ZonedDateTime getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(ZonedDateTime createdDate) { + this.createdDate = createdDate; + } + + public String getLastModifiedBy() { + return lastModifiedBy; + } + + public void setLastModifiedBy(String lastModifiedBy) { + this.lastModifiedBy = lastModifiedBy; + } + + public ZonedDateTime getLastModifiedDate() { + return lastModifiedDate; + } + + public void setLastModifiedDate(ZonedDateTime lastModifiedDate) { + this.lastModifiedDate = lastModifiedDate; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/Authority.java b/jhipster/src/main/java/com/baeldung/domain/Authority.java new file mode 100644 index 0000000000..8a94ad0bb2 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/Authority.java @@ -0,0 +1,66 @@ +package com.baeldung.domain; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Column; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; + +/** + * An authority (a security role) used by Spring Security. + */ +@Entity +@Table(name = "jhi_authority") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class Authority implements Serializable { + + private static final long serialVersionUID = 1L; + + @NotNull + @Size(min = 0, max = 50) + @Id + @Column(length = 50) + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Authority authority = (Authority) o; + + if (name != null ? !name.equals(authority.name) : authority.name != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return name != null ? name.hashCode() : 0; + } + + @Override + public String toString() { + return "Authority{" + + "name='" + name + '\'' + + "}"; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/Comment.java b/jhipster/src/main/java/com/baeldung/domain/Comment.java new file mode 100644 index 0000000000..c4587b6231 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/Comment.java @@ -0,0 +1,114 @@ +package com.baeldung.domain; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +import javax.persistence.*; +import javax.validation.constraints.*; +import java.io.Serializable; +import java.time.LocalDate; +import java.util.Objects; + +/** + * A Comment. + */ +@Entity +@Table(name = "comment") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class Comment implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Size(min = 10, max = 100) + @Column(name = "text", length = 100, nullable = false) + private String text; + + @NotNull + @Column(name = "creation_date", nullable = false) + private LocalDate creationDate; + + @ManyToOne(optional = false) + @NotNull + private Post post; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public Comment text(String text) { + this.text = text; + return this; + } + + public void setText(String text) { + this.text = text; + } + + public LocalDate getCreationDate() { + return creationDate; + } + + public Comment creationDate(LocalDate creationDate) { + this.creationDate = creationDate; + return this; + } + + public void setCreationDate(LocalDate creationDate) { + this.creationDate = creationDate; + } + + public Post getPost() { + return post; + } + + public Comment post(Post post) { + this.post = post; + return this; + } + + public void setPost(Post post) { + this.post = post; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Comment comment = (Comment) o; + if (comment.id == null || id == null) { + return false; + } + return Objects.equals(id, comment.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "Comment{" + + "id=" + id + + ", text='" + text + "'" + + ", creationDate='" + creationDate + "'" + + '}'; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/PersistentAuditEvent.java b/jhipster/src/main/java/com/baeldung/domain/PersistentAuditEvent.java new file mode 100644 index 0000000000..c0fb0cb146 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/PersistentAuditEvent.java @@ -0,0 +1,78 @@ +package com.baeldung.domain; + + +import java.io.Serializable; +import java.time.LocalDateTime; +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.util.HashMap; +import java.util.Map; + +/** + * Persist AuditEvent managed by the Spring Boot actuator + * @see org.springframework.boot.actuate.audit.AuditEvent + */ +@Entity +@Table(name = "jhi_persistent_audit_event") +public class PersistentAuditEvent implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "event_id") + private Long id; + + @NotNull + @Column(nullable = false) + private String principal; + + @Column(name = "event_date") + private LocalDateTime auditEventDate; + @Column(name = "event_type") + private String auditEventType; + + @ElementCollection + @MapKeyColumn(name = "name") + @Column(name = "value") + @CollectionTable(name = "jhi_persistent_audit_evt_data", joinColumns=@JoinColumn(name="event_id")) + private Map data = new HashMap<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getPrincipal() { + return principal; + } + + public void setPrincipal(String principal) { + this.principal = principal; + } + + public LocalDateTime getAuditEventDate() { + return auditEventDate; + } + + public void setAuditEventDate(LocalDateTime auditEventDate) { + this.auditEventDate = auditEventDate; + } + + public String getAuditEventType() { + return auditEventType; + } + + public void setAuditEventType(String auditEventType) { + this.auditEventType = auditEventType; + } + + public Map getData() { + return data; + } + + public void setData(Map data) { + this.data = data; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/Post.java b/jhipster/src/main/java/com/baeldung/domain/Post.java new file mode 100644 index 0000000000..7f084dc177 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/Post.java @@ -0,0 +1,133 @@ +package com.baeldung.domain; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +import javax.persistence.*; +import javax.validation.constraints.*; +import java.io.Serializable; +import java.time.LocalDate; +import java.util.Objects; + +/** + * A Post. + */ +@Entity +@Table(name = "post") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class Post implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Size(min = 10, max = 100) + @Column(name = "title", length = 100, nullable = false) + private String title; + + @NotNull + @Size(min = 10, max = 1000) + @Column(name = "content", length = 1000, nullable = false) + private String content; + + @NotNull + @Column(name = "creation_date", nullable = false) + private LocalDate creationDate; + + @ManyToOne(optional = false) + @NotNull + private User creator; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public Post title(String title) { + this.title = title; + return this; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public Post content(String content) { + this.content = content; + return this; + } + + public void setContent(String content) { + this.content = content; + } + + public LocalDate getCreationDate() { + return creationDate; + } + + public Post creationDate(LocalDate creationDate) { + this.creationDate = creationDate; + return this; + } + + public void setCreationDate(LocalDate creationDate) { + this.creationDate = creationDate; + } + + public User getCreator() { + return creator; + } + + public Post creator(User user) { + this.creator = user; + return this; + } + + public void setCreator(User user) { + this.creator = user; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Post post = (Post) o; + if (post.id == null || id == null) { + return false; + } + return Objects.equals(id, post.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "Post{" + + "id=" + id + + ", title='" + title + "'" + + ", content='" + content + "'" + + ", creationDate='" + creationDate + "'" + + '}'; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/User.java b/jhipster/src/main/java/com/baeldung/domain/User.java new file mode 100644 index 0000000000..76aa3a5209 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/User.java @@ -0,0 +1,235 @@ +package com.baeldung.domain; + +import com.baeldung.config.Constants; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.validator.constraints.Email; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.time.ZonedDateTime; + +/** + * A user. + */ +@Entity +@Table(name = "jhi_user") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class User extends AbstractAuditingEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Pattern(regexp = Constants.LOGIN_REGEX) + @Size(min = 1, max = 50) + @Column(length = 50, unique = true, nullable = false) + private String login; + + @JsonIgnore + @NotNull + @Size(min = 60, max = 60) + @Column(name = "password_hash",length = 60) + private String password; + + @Size(max = 50) + @Column(name = "first_name", length = 50) + private String firstName; + + @Size(max = 50) + @Column(name = "last_name", length = 50) + private String lastName; + + @Email + @Size(max = 100) + @Column(length = 100, unique = true) + private String email; + + @NotNull + @Column(nullable = false) + private boolean activated = false; + + @Size(min = 2, max = 5) + @Column(name = "lang_key", length = 5) + private String langKey; + + @Size(max = 256) + @Column(name = "image_url", length = 256) + private String imageUrl; + + @Size(max = 20) + @Column(name = "activation_key", length = 20) + @JsonIgnore + private String activationKey; + + @Size(max = 20) + @Column(name = "reset_key", length = 20) + private String resetKey; + + @Column(name = "reset_date") + private ZonedDateTime resetDate = null; + + @JsonIgnore + @ManyToMany + @JoinTable( + name = "jhi_user_authority", + joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")}, + inverseJoinColumns = {@JoinColumn(name = "authority_name", referencedColumnName = "name")}) + @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) + @BatchSize(size = 20) + private Set authorities = new HashSet<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + //Lowercase the login before saving it in database + public void setLogin(String login) { + this.login = login.toLowerCase(Locale.ENGLISH); + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public boolean getActivated() { + return activated; + } + + public void setActivated(boolean activated) { + this.activated = activated; + } + + public String getActivationKey() { + return activationKey; + } + + public void setActivationKey(String activationKey) { + this.activationKey = activationKey; + } + + public String getResetKey() { + return resetKey; + } + + public void setResetKey(String resetKey) { + this.resetKey = resetKey; + } + + public ZonedDateTime getResetDate() { + return resetDate; + } + + public void setResetDate(ZonedDateTime resetDate) { + this.resetDate = resetDate; + } + + public String getLangKey() { + return langKey; + } + + public void setLangKey(String langKey) { + this.langKey = langKey; + } + + public Set getAuthorities() { + return authorities; + } + + public void setAuthorities(Set authorities) { + this.authorities = authorities; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + User user = (User) o; + + if (!login.equals(user.login)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return login.hashCode(); + } + + @Override + public String toString() { + return "User{" + + "login='" + login + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email='" + email + '\'' + + ", imageUrl='" + imageUrl + '\'' + + ", activated='" + activated + '\'' + + ", langKey='" + langKey + '\'' + + ", activationKey='" + activationKey + '\'' + + "}"; + } +} diff --git a/jhipster/src/main/java/com/baeldung/domain/package-info.java b/jhipster/src/main/java/com/baeldung/domain/package-info.java new file mode 100644 index 0000000000..2492048c77 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/domain/package-info.java @@ -0,0 +1,4 @@ +/** + * JPA domain objects. + */ +package com.baeldung.domain; diff --git a/jhipster/src/main/java/com/baeldung/repository/AuthorityRepository.java b/jhipster/src/main/java/com/baeldung/repository/AuthorityRepository.java new file mode 100644 index 0000000000..fab63174aa --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/AuthorityRepository.java @@ -0,0 +1,11 @@ +package com.baeldung.repository; + +import com.baeldung.domain.Authority; + +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Spring Data JPA repository for the Authority entity. + */ +public interface AuthorityRepository extends JpaRepository { +} diff --git a/jhipster/src/main/java/com/baeldung/repository/CommentRepository.java b/jhipster/src/main/java/com/baeldung/repository/CommentRepository.java new file mode 100644 index 0000000000..d2d2618c36 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/CommentRepository.java @@ -0,0 +1,15 @@ +package com.baeldung.repository; + +import com.baeldung.domain.Comment; + +import org.springframework.data.jpa.repository.*; + +import java.util.List; + +/** + * Spring Data JPA repository for the Comment entity. + */ +@SuppressWarnings("unused") +public interface CommentRepository extends JpaRepository { + +} diff --git a/jhipster/src/main/java/com/baeldung/repository/CustomAuditEventRepository.java b/jhipster/src/main/java/com/baeldung/repository/CustomAuditEventRepository.java new file mode 100644 index 0000000000..fcfa9baeec --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/CustomAuditEventRepository.java @@ -0,0 +1,81 @@ +package com.baeldung.repository; + +import com.baeldung.config.Constants; +import com.baeldung.config.audit.AuditEventConverter; +import com.baeldung.domain.PersistentAuditEvent; + +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.boot.actuate.audit.AuditEventRepository; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.List; + +/** + * An implementation of Spring Boot's AuditEventRepository. + */ +@Repository +public class CustomAuditEventRepository implements AuditEventRepository { + + private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE"; + + private final PersistenceAuditEventRepository persistenceAuditEventRepository; + + private final AuditEventConverter auditEventConverter; + + public CustomAuditEventRepository(PersistenceAuditEventRepository persistenceAuditEventRepository, + AuditEventConverter auditEventConverter) { + + this.persistenceAuditEventRepository = persistenceAuditEventRepository; + this.auditEventConverter = auditEventConverter; + } + + @Override + public List find(Date after) { + Iterable persistentAuditEvents = + persistenceAuditEventRepository.findByAuditEventDateAfter(LocalDateTime.from(after.toInstant())); + return auditEventConverter.convertToAuditEvent(persistentAuditEvents); + } + + @Override + public List find(String principal, Date after) { + Iterable persistentAuditEvents; + if (principal == null && after == null) { + persistentAuditEvents = persistenceAuditEventRepository.findAll(); + } else if (after == null) { + persistentAuditEvents = persistenceAuditEventRepository.findByPrincipal(principal); + } else { + persistentAuditEvents = + persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfter(principal, LocalDateTime.from(after.toInstant())); + } + return auditEventConverter.convertToAuditEvent(persistentAuditEvents); + } + + @Override + public List find(String principal, Date after, String type) { + Iterable persistentAuditEvents = + persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfterAndAuditEventType(principal, LocalDateTime.from(after.toInstant()), type); + return auditEventConverter.convertToAuditEvent(persistentAuditEvents); + } + + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void add(AuditEvent event) { + if (!AUTHORIZATION_FAILURE.equals(event.getType()) && + !Constants.ANONYMOUS_USER.equals(event.getPrincipal())) { + + PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent(); + persistentAuditEvent.setPrincipal(event.getPrincipal()); + persistentAuditEvent.setAuditEventType(event.getType()); + Instant instant = Instant.ofEpochMilli(event.getTimestamp().getTime()); + persistentAuditEvent.setAuditEventDate(LocalDateTime.ofInstant(instant, ZoneId.systemDefault())); + persistentAuditEvent.setData(auditEventConverter.convertDataToStrings(event.getData())); + persistenceAuditEventRepository.save(persistentAuditEvent); + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/repository/PersistenceAuditEventRepository.java b/jhipster/src/main/java/com/baeldung/repository/PersistenceAuditEventRepository.java new file mode 100644 index 0000000000..f4b7f1c5bf --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/PersistenceAuditEventRepository.java @@ -0,0 +1,26 @@ +package com.baeldung.repository; + +import com.baeldung.domain.PersistentAuditEvent; + +import java.time.LocalDateTime; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +/** + * Spring Data JPA repository for the PersistentAuditEvent entity. + */ +public interface PersistenceAuditEventRepository extends JpaRepository { + + List findByPrincipal(String principal); + + List findByAuditEventDateAfter(LocalDateTime after); + + List findByPrincipalAndAuditEventDateAfter(String principal, LocalDateTime after); + + List findByPrincipalAndAuditEventDateAfterAndAuditEventType(String principle, LocalDateTime after, String type); + + Page findAllByAuditEventDateBetween(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable); +} diff --git a/jhipster/src/main/java/com/baeldung/repository/PostRepository.java b/jhipster/src/main/java/com/baeldung/repository/PostRepository.java new file mode 100644 index 0000000000..22e8aa1372 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/PostRepository.java @@ -0,0 +1,18 @@ +package com.baeldung.repository; + +import com.baeldung.domain.Post; + +import org.springframework.data.jpa.repository.*; + +import java.util.List; + +/** + * Spring Data JPA repository for the Post entity. + */ +@SuppressWarnings("unused") +public interface PostRepository extends JpaRepository { + + @Query("select post from Post post where post.creator.login = ?#{principal.username}") + List findByCreatorIsCurrentUser(); + +} diff --git a/jhipster/src/main/java/com/baeldung/repository/UserRepository.java b/jhipster/src/main/java/com/baeldung/repository/UserRepository.java new file mode 100644 index 0000000000..a8f8149910 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/UserRepository.java @@ -0,0 +1,37 @@ +package com.baeldung.repository; + +import com.baeldung.domain.User; + +import java.time.ZonedDateTime; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; + +/** + * Spring Data JPA repository for the User entity. + */ +public interface UserRepository extends JpaRepository { + + Optional findOneByActivationKey(String activationKey); + + List findAllByActivatedIsFalseAndCreatedDateBefore(ZonedDateTime dateTime); + + Optional findOneByResetKey(String resetKey); + + Optional findOneByEmail(String email); + + Optional findOneByLogin(String login); + + @EntityGraph(attributePaths = "authorities") + User findOneWithAuthoritiesById(Long id); + + @EntityGraph(attributePaths = "authorities") + Optional findOneWithAuthoritiesByLogin(String login); + + Page findAllByLoginNot(Pageable pageable, String login); +} diff --git a/jhipster/src/main/java/com/baeldung/repository/package-info.java b/jhipster/src/main/java/com/baeldung/repository/package-info.java new file mode 100644 index 0000000000..9d1fb837c3 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/repository/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Data JPA repositories. + */ +package com.baeldung.repository; diff --git a/jhipster/src/main/java/com/baeldung/security/AuthoritiesConstants.java b/jhipster/src/main/java/com/baeldung/security/AuthoritiesConstants.java new file mode 100644 index 0000000000..40cf54e4f6 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/AuthoritiesConstants.java @@ -0,0 +1,16 @@ +package com.baeldung.security; + +/** + * Constants for Spring Security authorities. + */ +public final class AuthoritiesConstants { + + public static final String ADMIN = "ROLE_ADMIN"; + + public static final String USER = "ROLE_USER"; + + public static final String ANONYMOUS = "ROLE_ANONYMOUS"; + + private AuthoritiesConstants() { + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/DomainUserDetailsService.java b/jhipster/src/main/java/com/baeldung/security/DomainUserDetailsService.java new file mode 100644 index 0000000000..6ce07739f7 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/DomainUserDetailsService.java @@ -0,0 +1,51 @@ +package com.baeldung.security; + +import com.baeldung.domain.User; +import com.baeldung.repository.UserRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Authenticate a user from the database. + */ +@Component("userDetailsService") +public class DomainUserDetailsService implements UserDetailsService { + + private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class); + + private final UserRepository userRepository; + + public DomainUserDetailsService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + @Transactional + public UserDetails loadUserByUsername(final String login) { + log.debug("Authenticating {}", login); + String lowercaseLogin = login.toLowerCase(Locale.ENGLISH); + Optional userFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin); + return userFromDatabase.map(user -> { + if (!user.getActivated()) { + throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated"); + } + List grantedAuthorities = user.getAuthorities().stream() + .map(authority -> new SimpleGrantedAuthority(authority.getName())) + .collect(Collectors.toList()); + return new org.springframework.security.core.userdetails.User(lowercaseLogin, + user.getPassword(), + grantedAuthorities); + }).orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the " + + "database")); + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/SecurityUtils.java b/jhipster/src/main/java/com/baeldung/security/SecurityUtils.java new file mode 100644 index 0000000000..96aaef3805 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/SecurityUtils.java @@ -0,0 +1,68 @@ +package com.baeldung.security; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * Utility class for Spring Security. + */ +public final class SecurityUtils { + + private SecurityUtils() { + } + + /** + * Get the login of the current user. + * + * @return the login of the current user + */ + public static String getCurrentUserLogin() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + Authentication authentication = securityContext.getAuthentication(); + String userName = null; + if (authentication != null) { + if (authentication.getPrincipal() instanceof UserDetails) { + UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal(); + userName = springSecurityUser.getUsername(); + } else if (authentication.getPrincipal() instanceof String) { + userName = (String) authentication.getPrincipal(); + } + } + return userName; + } + + /** + * Check if a user is authenticated. + * + * @return true if the user is authenticated, false otherwise + */ + public static boolean isAuthenticated() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + Authentication authentication = securityContext.getAuthentication(); + if (authentication != null) { + return authentication.getAuthorities().stream() + .noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS)); + } + return false; + } + + /** + * If the current user has a specific authority (security role). + * + *

The name of this method comes from the isUserInRole() method in the Servlet API

+ * + * @param authority the authority to check + * @return true if the current user has the authority, false otherwise + */ + public static boolean isCurrentUserInRole(String authority) { + SecurityContext securityContext = SecurityContextHolder.getContext(); + Authentication authentication = securityContext.getAuthentication(); + if (authentication != null) { + return authentication.getAuthorities().stream() + .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority)); + } + return false; + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/SpringSecurityAuditorAware.java b/jhipster/src/main/java/com/baeldung/security/SpringSecurityAuditorAware.java new file mode 100644 index 0000000000..d59917153c --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/SpringSecurityAuditorAware.java @@ -0,0 +1,19 @@ +package com.baeldung.security; + +import com.baeldung.config.Constants; + +import org.springframework.data.domain.AuditorAware; +import org.springframework.stereotype.Component; + +/** + * Implementation of AuditorAware based on Spring Security. + */ +@Component +public class SpringSecurityAuditorAware implements AuditorAware { + + @Override + public String getCurrentAuditor() { + String userName = SecurityUtils.getCurrentUserLogin(); + return userName != null ? userName : Constants.SYSTEM_ACCOUNT; + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/UserNotActivatedException.java b/jhipster/src/main/java/com/baeldung/security/UserNotActivatedException.java new file mode 100644 index 0000000000..7265188cad --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/UserNotActivatedException.java @@ -0,0 +1,19 @@ +package com.baeldung.security; + +import org.springframework.security.core.AuthenticationException; + +/** + * This exception is thrown in case of a not activated user trying to authenticate. + */ +public class UserNotActivatedException extends AuthenticationException { + + private static final long serialVersionUID = 1L; + + public UserNotActivatedException(String message) { + super(message); + } + + public UserNotActivatedException(String message, Throwable t) { + super(message, t); + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/jwt/JWTConfigurer.java b/jhipster/src/main/java/com/baeldung/security/jwt/JWTConfigurer.java new file mode 100644 index 0000000000..5720812b9f --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/jwt/JWTConfigurer.java @@ -0,0 +1,23 @@ +package com.baeldung.security.jwt; + +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +public class JWTConfigurer extends SecurityConfigurerAdapter { + + public static final String AUTHORIZATION_HEADER = "Authorization"; + + private TokenProvider tokenProvider; + + public JWTConfigurer(TokenProvider tokenProvider) { + this.tokenProvider = tokenProvider; + } + + @Override + public void configure(HttpSecurity http) throws Exception { + JWTFilter customFilter = new JWTFilter(tokenProvider); + http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/jwt/JWTFilter.java b/jhipster/src/main/java/com/baeldung/security/jwt/JWTFilter.java new file mode 100644 index 0000000000..89b1947e05 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/jwt/JWTFilter.java @@ -0,0 +1,58 @@ +package com.baeldung.security.jwt; + +import java.io.IOException; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.GenericFilterBean; + +import io.jsonwebtoken.ExpiredJwtException; + +/** + * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is + * found. + */ +public class JWTFilter extends GenericFilterBean { + + private final Logger log = LoggerFactory.getLogger(JWTFilter.class); + + private TokenProvider tokenProvider; + + public JWTFilter(TokenProvider tokenProvider) { + this.tokenProvider = tokenProvider; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + try { + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + String jwt = resolveToken(httpServletRequest); + if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) { + Authentication authentication = this.tokenProvider.getAuthentication(jwt); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(servletRequest, servletResponse); + } catch (ExpiredJwtException eje) { + log.info("Security exception for user {} - {}", + eje.getClaims().getSubject(), eje.getMessage()); + + log.trace("Security exception trace: {}", eje); + ((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } + } + + private String resolveToken(HttpServletRequest request){ + String bearerToken = request.getHeader(JWTConfigurer.AUTHORIZATION_HEADER); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7, bearerToken.length()); + } + return null; + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/jwt/TokenProvider.java b/jhipster/src/main/java/com/baeldung/security/jwt/TokenProvider.java new file mode 100644 index 0000000000..3ba4d7c793 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/jwt/TokenProvider.java @@ -0,0 +1,109 @@ +package com.baeldung.security.jwt; + +import io.github.jhipster.config.JHipsterProperties; + +import java.util.*; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.stereotype.Component; + +import io.jsonwebtoken.*; + +@Component +public class TokenProvider { + + private final Logger log = LoggerFactory.getLogger(TokenProvider.class); + + private static final String AUTHORITIES_KEY = "auth"; + + private String secretKey; + + private long tokenValidityInMilliseconds; + + private long tokenValidityInMillisecondsForRememberMe; + + private final JHipsterProperties jHipsterProperties; + + public TokenProvider(JHipsterProperties jHipsterProperties) { + this.jHipsterProperties = jHipsterProperties; + } + + @PostConstruct + public void init() { + this.secretKey = + jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret(); + + this.tokenValidityInMilliseconds = + 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds(); + this.tokenValidityInMillisecondsForRememberMe = + 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe(); + } + + public String createToken(Authentication authentication, Boolean rememberMe) { + String authorities = authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.joining(",")); + + long now = (new Date()).getTime(); + Date validity; + if (rememberMe) { + validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe); + } else { + validity = new Date(now + this.tokenValidityInMilliseconds); + } + + return Jwts.builder() + .setSubject(authentication.getName()) + .claim(AUTHORITIES_KEY, authorities) + .signWith(SignatureAlgorithm.HS512, secretKey) + .setExpiration(validity) + .compact(); + } + + public Authentication getAuthentication(String token) { + Claims claims = Jwts.parser() + .setSigningKey(secretKey) + .parseClaimsJws(token) + .getBody(); + + Collection authorities = + Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + + User principal = new User(claims.getSubject(), "", authorities); + + return new UsernamePasswordAuthenticationToken(principal, "", authorities); + } + + public boolean validateToken(String authToken) { + try { + Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken); + return true; + } catch (SignatureException e) { + log.info("Invalid JWT signature."); + log.trace("Invalid JWT signature trace: {}", e); + } catch (MalformedJwtException e) { + log.info("Invalid JWT token."); + log.trace("Invalid JWT token trace: {}", e); + } catch (ExpiredJwtException e) { + log.info("Expired JWT token."); + log.trace("Expired JWT token trace: {}", e); + } catch (UnsupportedJwtException e) { + log.info("Unsupported JWT token."); + log.trace("Unsupported JWT token trace: {}", e); + } catch (IllegalArgumentException e) { + log.info("JWT token compact of handler are invalid."); + log.trace("JWT token compact of handler are invalid trace: {}", e); + } + return false; + } +} diff --git a/jhipster/src/main/java/com/baeldung/security/package-info.java b/jhipster/src/main/java/com/baeldung/security/package-info.java new file mode 100644 index 0000000000..1e29c15b4e --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/security/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Security configuration. + */ +package com.baeldung.security; diff --git a/jhipster/src/main/java/com/baeldung/service/AuditEventService.java b/jhipster/src/main/java/com/baeldung/service/AuditEventService.java new file mode 100644 index 0000000000..8701854922 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/AuditEventService.java @@ -0,0 +1,50 @@ +package com.baeldung.service; + +import com.baeldung.config.audit.AuditEventConverter; +import com.baeldung.repository.PersistenceAuditEventRepository; +import java.time.LocalDateTime; +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +/** + * Service for managing audit events. + *

+ * This is the default implementation to support SpringBoot Actuator AuditEventRepository + *

+ */ +@Service +@Transactional +public class AuditEventService { + + private final PersistenceAuditEventRepository persistenceAuditEventRepository; + + private final AuditEventConverter auditEventConverter; + + public AuditEventService( + PersistenceAuditEventRepository persistenceAuditEventRepository, + AuditEventConverter auditEventConverter) { + + this.persistenceAuditEventRepository = persistenceAuditEventRepository; + this.auditEventConverter = auditEventConverter; + } + + public Page findAll(Pageable pageable) { + return persistenceAuditEventRepository.findAll(pageable) + .map(auditEventConverter::convertToAuditEvent); + } + + public Page findByDates(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable) { + return persistenceAuditEventRepository.findAllByAuditEventDateBetween(fromDate, toDate, pageable) + .map(auditEventConverter::convertToAuditEvent); + } + + public Optional find(Long id) { + return Optional.ofNullable(persistenceAuditEventRepository.findOne(id)).map + (auditEventConverter::convertToAuditEvent); + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/MailService.java b/jhipster/src/main/java/com/baeldung/service/MailService.java new file mode 100644 index 0000000000..9f85531da7 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/MailService.java @@ -0,0 +1,108 @@ +package com.baeldung.service; + +import com.baeldung.domain.User; + +import io.github.jhipster.config.JHipsterProperties; + +import org.apache.commons.lang3.CharEncoding; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.thymeleaf.context.Context; +import org.thymeleaf.spring4.SpringTemplateEngine; + +import javax.mail.internet.MimeMessage; +import java.util.Locale; + +/** + * Service for sending e-mails. + *

+ * We use the @Async annotation to send e-mails asynchronously. + *

+ */ +@Service +public class MailService { + + private final Logger log = LoggerFactory.getLogger(MailService.class); + + private static final String USER = "user"; + + private static final String BASE_URL = "baseUrl"; + + private final JHipsterProperties jHipsterProperties; + + private final JavaMailSender javaMailSender; + + private final MessageSource messageSource; + + private final SpringTemplateEngine templateEngine; + + public MailService(JHipsterProperties jHipsterProperties, JavaMailSender javaMailSender, + MessageSource messageSource, SpringTemplateEngine templateEngine) { + + this.jHipsterProperties = jHipsterProperties; + this.javaMailSender = javaMailSender; + this.messageSource = messageSource; + this.templateEngine = templateEngine; + } + + @Async + public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) { + log.debug("Send e-mail[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}", + isMultipart, isHtml, to, subject, content); + + // Prepare message using a Spring helper + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + try { + MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, CharEncoding.UTF_8); + message.setTo(to); + message.setFrom(jHipsterProperties.getMail().getFrom()); + message.setSubject(subject); + message.setText(content, isHtml); + javaMailSender.send(mimeMessage); + log.debug("Sent e-mail to User '{}'", to); + } catch (Exception e) { + log.warn("E-mail could not be sent to user '{}'", to, e); + } + } + + @Async + public void sendActivationEmail(User user) { + log.debug("Sending activation e-mail to '{}'", user.getEmail()); + Locale locale = Locale.forLanguageTag(user.getLangKey()); + Context context = new Context(locale); + context.setVariable(USER, user); + context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl()); + String content = templateEngine.process("activationEmail", context); + String subject = messageSource.getMessage("email.activation.title", null, locale); + sendEmail(user.getEmail(), subject, content, false, true); + } + + @Async + public void sendCreationEmail(User user) { + log.debug("Sending creation e-mail to '{}'", user.getEmail()); + Locale locale = Locale.forLanguageTag(user.getLangKey()); + Context context = new Context(locale); + context.setVariable(USER, user); + context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl()); + String content = templateEngine.process("creationEmail", context); + String subject = messageSource.getMessage("email.activation.title", null, locale); + sendEmail(user.getEmail(), subject, content, false, true); + } + + @Async + public void sendPasswordResetMail(User user) { + log.debug("Sending password reset e-mail to '{}'", user.getEmail()); + Locale locale = Locale.forLanguageTag(user.getLangKey()); + Context context = new Context(locale); + context.setVariable(USER, user); + context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl()); + String content = templateEngine.process("passwordResetEmail", context); + String subject = messageSource.getMessage("email.reset.title", null, locale); + sendEmail(user.getEmail(), subject, content, false, true); + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/UserService.java b/jhipster/src/main/java/com/baeldung/service/UserService.java new file mode 100644 index 0000000000..7b9096e0da --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/UserService.java @@ -0,0 +1,228 @@ +package com.baeldung.service; + +import com.baeldung.domain.Authority; +import com.baeldung.domain.User; +import com.baeldung.repository.AuthorityRepository; +import com.baeldung.config.Constants; +import com.baeldung.repository.UserRepository; +import com.baeldung.security.AuthoritiesConstants; +import com.baeldung.security.SecurityUtils; +import com.baeldung.service.util.RandomUtil; +import com.baeldung.service.dto.UserDTO; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.ZonedDateTime; +import java.util.*; + +/** + * Service class for managing users. + */ +@Service +@Transactional +public class UserService { + + private final Logger log = LoggerFactory.getLogger(UserService.class); + + private final UserRepository userRepository; + + private final PasswordEncoder passwordEncoder; + + private final AuthorityRepository authorityRepository; + + public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthorityRepository authorityRepository) { + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; + this.authorityRepository = authorityRepository; + } + + public Optional activateRegistration(String key) { + log.debug("Activating user for activation key {}", key); + return userRepository.findOneByActivationKey(key) + .map(user -> { + // activate given user for the registration key. + user.setActivated(true); + user.setActivationKey(null); + log.debug("Activated user: {}", user); + return user; + }); + } + + public Optional completePasswordReset(String newPassword, String key) { + log.debug("Reset user password for reset key {}", key); + + return userRepository.findOneByResetKey(key) + .filter(user -> { + ZonedDateTime oneDayAgo = ZonedDateTime.now().minusHours(24); + return user.getResetDate().isAfter(oneDayAgo); + }) + .map(user -> { + user.setPassword(passwordEncoder.encode(newPassword)); + user.setResetKey(null); + user.setResetDate(null); + return user; + }); + } + + public Optional requestPasswordReset(String mail) { + return userRepository.findOneByEmail(mail) + .filter(User::getActivated) + .map(user -> { + user.setResetKey(RandomUtil.generateResetKey()); + user.setResetDate(ZonedDateTime.now()); + return user; + }); + } + + public User createUser(String login, String password, String firstName, String lastName, String email, + String imageUrl, String langKey) { + + User newUser = new User(); + Authority authority = authorityRepository.findOne(AuthoritiesConstants.USER); + Set authorities = new HashSet<>(); + String encryptedPassword = passwordEncoder.encode(password); + newUser.setLogin(login); + // new user gets initially a generated password + newUser.setPassword(encryptedPassword); + newUser.setFirstName(firstName); + newUser.setLastName(lastName); + newUser.setEmail(email); + newUser.setImageUrl(imageUrl); + newUser.setLangKey(langKey); + // new user is not active + newUser.setActivated(false); + // new user gets registration key + newUser.setActivationKey(RandomUtil.generateActivationKey()); + authorities.add(authority); + newUser.setAuthorities(authorities); + userRepository.save(newUser); + log.debug("Created Information for User: {}", newUser); + return newUser; + } + + public User createUser(UserDTO userDTO) { + User user = new User(); + user.setLogin(userDTO.getLogin()); + user.setFirstName(userDTO.getFirstName()); + user.setLastName(userDTO.getLastName()); + user.setEmail(userDTO.getEmail()); + user.setImageUrl(userDTO.getImageUrl()); + if (userDTO.getLangKey() == null) { + user.setLangKey("en"); // default language + } else { + user.setLangKey(userDTO.getLangKey()); + } + if (userDTO.getAuthorities() != null) { + Set authorities = new HashSet<>(); + userDTO.getAuthorities().forEach( + authority -> authorities.add(authorityRepository.findOne(authority)) + ); + user.setAuthorities(authorities); + } + String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword()); + user.setPassword(encryptedPassword); + user.setResetKey(RandomUtil.generateResetKey()); + user.setResetDate(ZonedDateTime.now()); + user.setActivated(true); + userRepository.save(user); + log.debug("Created Information for User: {}", user); + return user; + } + + /** + * Update basic information (first name, last name, email, language) for the current user. + */ + public void updateUser(String firstName, String lastName, String email, String langKey) { + userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(user -> { + user.setFirstName(firstName); + user.setLastName(lastName); + user.setEmail(email); + user.setLangKey(langKey); + log.debug("Changed Information for User: {}", user); + }); + } + + /** + * Update all information for a specific user, and return the modified user. + */ + public Optional updateUser(UserDTO userDTO) { + return Optional.of(userRepository + .findOne(userDTO.getId())) + .map(user -> { + user.setLogin(userDTO.getLogin()); + user.setFirstName(userDTO.getFirstName()); + user.setLastName(userDTO.getLastName()); + user.setEmail(userDTO.getEmail()); + user.setImageUrl(userDTO.getImageUrl()); + user.setActivated(userDTO.isActivated()); + user.setLangKey(userDTO.getLangKey()); + Set managedAuthorities = user.getAuthorities(); + managedAuthorities.clear(); + userDTO.getAuthorities().stream() + .map(authorityRepository::findOne) + .forEach(managedAuthorities::add); + log.debug("Changed Information for User: {}", user); + return user; + }) + .map(UserDTO::new); + } + + public void deleteUser(String login) { + userRepository.findOneByLogin(login).ifPresent(user -> { + userRepository.delete(user); + log.debug("Deleted User: {}", user); + }); + } + + public void changePassword(String password) { + userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(user -> { + String encryptedPassword = passwordEncoder.encode(password); + user.setPassword(encryptedPassword); + log.debug("Changed password for User: {}", user); + }); + } + + @Transactional(readOnly = true) + public Page getAllManagedUsers(Pageable pageable) { + return userRepository.findAllByLoginNot(pageable, Constants.ANONYMOUS_USER).map(UserDTO::new); + } + + @Transactional(readOnly = true) + public Optional getUserWithAuthoritiesByLogin(String login) { + return userRepository.findOneWithAuthoritiesByLogin(login); + } + + @Transactional(readOnly = true) + public User getUserWithAuthorities(Long id) { + return userRepository.findOneWithAuthoritiesById(id); + } + + @Transactional(readOnly = true) + public User getUserWithAuthorities() { + return userRepository.findOneWithAuthoritiesByLogin(SecurityUtils.getCurrentUserLogin()).orElse(null); + } + + + /** + * Not activated users should be automatically deleted after 3 days. + *

+ * This is scheduled to get fired everyday, at 01:00 (am). + *

+ */ + @Scheduled(cron = "0 0 1 * * ?") + public void removeNotActivatedUsers() { + ZonedDateTime now = ZonedDateTime.now(); + List users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minusDays(3)); + for (User user : users) { + log.debug("Deleting not activated user {}", user.getLogin()); + userRepository.delete(user); + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/dto/UserDTO.java b/jhipster/src/main/java/com/baeldung/service/dto/UserDTO.java new file mode 100644 index 0000000000..1080bab7b8 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/dto/UserDTO.java @@ -0,0 +1,167 @@ +package com.baeldung.service.dto; + +import com.baeldung.config.Constants; + +import com.baeldung.domain.Authority; +import com.baeldung.domain.User; + +import org.hibernate.validator.constraints.Email; + +import javax.validation.constraints.*; +import java.time.ZonedDateTime; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A DTO representing a user, with his authorities. + */ +public class UserDTO { + + private Long id; + + @Pattern(regexp = Constants.LOGIN_REGEX) + @Size(min = 1, max = 50) + private String login; + + @Size(max = 50) + private String firstName; + + @Size(max = 50) + private String lastName; + + @Email + @Size(min = 5, max = 100) + private String email; + + @Size(max = 256) + private String imageUrl; + + private boolean activated = false; + + @Size(min = 2, max = 5) + private String langKey; + + private String createdBy; + + private ZonedDateTime createdDate; + + private String lastModifiedBy; + + private ZonedDateTime lastModifiedDate; + + private Set authorities; + + public UserDTO() { + // Empty constructor needed for MapStruct. + } + + public UserDTO(User user) { + this(user.getId(), user.getLogin(), user.getFirstName(), user.getLastName(), + user.getEmail(), user.getActivated(), user.getImageUrl(), user.getLangKey(), + user.getCreatedBy(), user.getCreatedDate(), user.getLastModifiedBy(), user.getLastModifiedDate(), + user.getAuthorities().stream().map(Authority::getName) + .collect(Collectors.toSet())); + } + + public UserDTO(Long id, String login, String firstName, String lastName, + String email, boolean activated, String imageUrl, String langKey, + String createdBy, ZonedDateTime createdDate, String lastModifiedBy, ZonedDateTime lastModifiedDate, + Set authorities) { + + this.id = id; + this.login = login; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.activated = activated; + this.imageUrl = imageUrl; + this.langKey = langKey; + this.createdBy = createdBy; + this.createdDate = createdDate; + this.lastModifiedBy = lastModifiedBy; + this.lastModifiedDate = lastModifiedDate; + this.authorities = authorities; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getEmail() { + return email; + } + + public String getImageUrl() { + return imageUrl; + } + + public boolean isActivated() { + return activated; + } + + public String getLangKey() { + return langKey; + } + + public String getCreatedBy() { + return createdBy; + } + + public ZonedDateTime getCreatedDate() { + return createdDate; + } + + public String getLastModifiedBy() { + return lastModifiedBy; + } + + public ZonedDateTime getLastModifiedDate() { + return lastModifiedDate; + } + + public void setLastModifiedDate(ZonedDateTime lastModifiedDate) { + this.lastModifiedDate = lastModifiedDate; + } + + public Set getAuthorities() { + return authorities; + } + + @Override + public String toString() { + return "UserDTO{" + + "login='" + login + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email='" + email + '\'' + + ", imageUrl='" + imageUrl + '\'' + + ", activated=" + activated + + ", langKey='" + langKey + '\'' + + ", createdBy=" + createdBy + + ", createdDate=" + createdDate + + ", lastModifiedBy='" + lastModifiedBy + '\'' + + ", lastModifiedDate=" + lastModifiedDate + + ", authorities=" + authorities + + "}"; + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/dto/package-info.java b/jhipster/src/main/java/com/baeldung/service/dto/package-info.java new file mode 100644 index 0000000000..12d048eb7a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/dto/package-info.java @@ -0,0 +1,4 @@ +/** + * Data Transfer Objects. + */ +package com.baeldung.service.dto; diff --git a/jhipster/src/main/java/com/baeldung/service/mapper/UserMapper.java b/jhipster/src/main/java/com/baeldung/service/mapper/UserMapper.java new file mode 100644 index 0000000000..aaada37162 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/mapper/UserMapper.java @@ -0,0 +1,55 @@ +package com.baeldung.service.mapper; + +import com.baeldung.domain.Authority; +import com.baeldung.domain.User; +import com.baeldung.service.dto.UserDTO; +import org.mapstruct.*; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Mapper for the entity User and its DTO UserDTO. + */ +@Mapper(componentModel = "spring", uses = {}) +public interface UserMapper { + + UserDTO userToUserDTO(User user); + + List usersToUserDTOs(List users); + + @Mapping(target = "createdBy", ignore = true) + @Mapping(target = "createdDate", ignore = true) + @Mapping(target = "lastModifiedBy", ignore = true) + @Mapping(target = "lastModifiedDate", ignore = true) + @Mapping(target = "activationKey", ignore = true) + @Mapping(target = "resetKey", ignore = true) + @Mapping(target = "resetDate", ignore = true) + @Mapping(target = "password", ignore = true) + User userDTOToUser(UserDTO userDTO); + + List userDTOsToUsers(List userDTOs); + + default User userFromId(Long id) { + if (id == null) { + return null; + } + User user = new User(); + user.setId(id); + return user; + } + + default Set stringsFromAuthorities (Set authorities) { + return authorities.stream().map(Authority::getName) + .collect(Collectors.toSet()); + } + + default Set authoritiesFromStrings(Set strings) { + return strings.stream().map(string -> { + Authority auth = new Authority(); + auth.setName(string); + return auth; + }).collect(Collectors.toSet()); + } +} diff --git a/jhipster/src/main/java/com/baeldung/service/mapper/package-info.java b/jhipster/src/main/java/com/baeldung/service/mapper/package-info.java new file mode 100644 index 0000000000..75f8d24f08 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/mapper/package-info.java @@ -0,0 +1,4 @@ +/** + * MapStruct mappers for mapping domain objects and Data Transfer Objects. + */ +package com.baeldung.service.mapper; diff --git a/jhipster/src/main/java/com/baeldung/service/package-info.java b/jhipster/src/main/java/com/baeldung/service/package-info.java new file mode 100644 index 0000000000..0695bc1473 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/package-info.java @@ -0,0 +1,4 @@ +/** + * Service layer beans. + */ +package com.baeldung.service; diff --git a/jhipster/src/main/java/com/baeldung/service/util/RandomUtil.java b/jhipster/src/main/java/com/baeldung/service/util/RandomUtil.java new file mode 100644 index 0000000000..1f4d9f8b5d --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/service/util/RandomUtil.java @@ -0,0 +1,41 @@ +package com.baeldung.service.util; + +import org.apache.commons.lang3.RandomStringUtils; + +/** + * Utility class for generating random Strings. + */ +public final class RandomUtil { + + private static final int DEF_COUNT = 20; + + private RandomUtil() { + } + + /** + * Generate a password. + * + * @return the generated password + */ + public static String generatePassword() { + return RandomStringUtils.randomAlphanumeric(DEF_COUNT); + } + + /** + * Generate an activation key. + * + * @return the generated activation key + */ + public static String generateActivationKey() { + return RandomStringUtils.randomNumeric(DEF_COUNT); + } + + /** + * Generate a reset key. + * + * @return the generated reset key + */ + public static String generateResetKey() { + return RandomStringUtils.randomNumeric(DEF_COUNT); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/AccountResource.java b/jhipster/src/main/java/com/baeldung/web/rest/AccountResource.java new file mode 100644 index 0000000000..c1009fc918 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/AccountResource.java @@ -0,0 +1,202 @@ +package com.baeldung.web.rest; + +import com.codahale.metrics.annotation.Timed; + +import com.baeldung.domain.User; +import com.baeldung.repository.UserRepository; +import com.baeldung.security.SecurityUtils; +import com.baeldung.service.MailService; +import com.baeldung.service.UserService; +import com.baeldung.service.dto.UserDTO; +import com.baeldung.web.rest.vm.KeyAndPasswordVM; +import com.baeldung.web.rest.vm.ManagedUserVM; +import com.baeldung.web.rest.util.HeaderUtil; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; +import java.util.*; + +/** + * REST controller for managing the current user's account. + */ +@RestController +@RequestMapping("/api") +public class AccountResource { + + private final Logger log = LoggerFactory.getLogger(AccountResource.class); + + private final UserRepository userRepository; + + private final UserService userService; + + private final MailService mailService; + + public AccountResource(UserRepository userRepository, UserService userService, + MailService mailService) { + + this.userRepository = userRepository; + this.userService = userService; + this.mailService = mailService; + } + + /** + * POST /register : register the user. + * + * @param managedUserVM the managed user View Model + * @return the ResponseEntity with status 201 (Created) if the user is registered or 400 (Bad Request) if the login or e-mail is already in use + */ + @PostMapping(path = "/register", + produces={MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE}) + @Timed + public ResponseEntity registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) { + + HttpHeaders textPlainHeaders = new HttpHeaders(); + textPlainHeaders.setContentType(MediaType.TEXT_PLAIN); + + return userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()) + .map(user -> new ResponseEntity<>("login already in use", textPlainHeaders, HttpStatus.BAD_REQUEST)) + .orElseGet(() -> userRepository.findOneByEmail(managedUserVM.getEmail()) + .map(user -> new ResponseEntity<>("e-mail address already in use", textPlainHeaders, HttpStatus.BAD_REQUEST)) + .orElseGet(() -> { + User user = userService + .createUser(managedUserVM.getLogin(), managedUserVM.getPassword(), + managedUserVM.getFirstName(), managedUserVM.getLastName(), + managedUserVM.getEmail().toLowerCase(), managedUserVM.getImageUrl(), managedUserVM.getLangKey()); + + mailService.sendActivationEmail(user); + return new ResponseEntity<>(HttpStatus.CREATED); + }) + ); + } + + /** + * GET /activate : activate the registered user. + * + * @param key the activation key + * @return the ResponseEntity with status 200 (OK) and the activated user in body, or status 500 (Internal Server Error) if the user couldn't be activated + */ + @GetMapping("/activate") + @Timed + public ResponseEntity activateAccount(@RequestParam(value = "key") String key) { + return userService.activateRegistration(key) + .map(user -> new ResponseEntity(HttpStatus.OK)) + .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + + /** + * GET /authenticate : check if the user is authenticated, and return its login. + * + * @param request the HTTP request + * @return the login if the user is authenticated + */ + @GetMapping("/authenticate") + @Timed + public String isAuthenticated(HttpServletRequest request) { + log.debug("REST request to check if the current user is authenticated"); + return request.getRemoteUser(); + } + + /** + * GET /account : get the current user. + * + * @return the ResponseEntity with status 200 (OK) and the current user in body, or status 500 (Internal Server Error) if the user couldn't be returned + */ + @GetMapping("/account") + @Timed + public ResponseEntity getAccount() { + return Optional.ofNullable(userService.getUserWithAuthorities()) + .map(user -> new ResponseEntity<>(new UserDTO(user), HttpStatus.OK)) + .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + + /** + * POST /account : update the current user information. + * + * @param userDTO the current user information + * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) or 500 (Internal Server Error) if the user couldn't be updated + */ + @PostMapping("/account") + @Timed + public ResponseEntity saveAccount(@Valid @RequestBody UserDTO userDTO) { + Optional existingUser = userRepository.findOneByEmail(userDTO.getEmail()); + if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userDTO.getLogin()))) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")).body(null); + } + return userRepository + .findOneByLogin(SecurityUtils.getCurrentUserLogin()) + .map(u -> { + userService.updateUser(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(), + userDTO.getLangKey()); + return new ResponseEntity(HttpStatus.OK); + }) + .orElseGet(() -> new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + + /** + * POST /account/change_password : changes the current user's password + * + * @param password the new password + * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) if the new password is not strong enough + */ + @PostMapping(path = "/account/change_password", + produces = MediaType.TEXT_PLAIN_VALUE) + @Timed + public ResponseEntity changePassword(@RequestBody String password) { + if (!checkPasswordLength(password)) { + return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST); + } + userService.changePassword(password); + return new ResponseEntity<>(HttpStatus.OK); + } + + /** + * POST /account/reset_password/init : Send an e-mail to reset the password of the user + * + * @param mail the mail of the user + * @return the ResponseEntity with status 200 (OK) if the e-mail was sent, or status 400 (Bad Request) if the e-mail address is not registered + */ + @PostMapping(path = "/account/reset_password/init", + produces = MediaType.TEXT_PLAIN_VALUE) + @Timed + public ResponseEntity requestPasswordReset(@RequestBody String mail) { + return userService.requestPasswordReset(mail) + .map(user -> { + mailService.sendPasswordResetMail(user); + return new ResponseEntity<>("e-mail was sent", HttpStatus.OK); + }).orElse(new ResponseEntity<>("e-mail address not registered", HttpStatus.BAD_REQUEST)); + } + + /** + * POST /account/reset_password/finish : Finish to reset the password of the user + * + * @param keyAndPassword the generated key and the new password + * @return the ResponseEntity with status 200 (OK) if the password has been reset, + * or status 400 (Bad Request) or 500 (Internal Server Error) if the password could not be reset + */ + @PostMapping(path = "/account/reset_password/finish", + produces = MediaType.TEXT_PLAIN_VALUE) + @Timed + public ResponseEntity finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) { + if (!checkPasswordLength(keyAndPassword.getNewPassword())) { + return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST); + } + return userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey()) + .map(user -> new ResponseEntity(HttpStatus.OK)) + .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + + private boolean checkPasswordLength(String password) { + return !StringUtils.isEmpty(password) && + password.length() >= ManagedUserVM.PASSWORD_MIN_LENGTH && + password.length() <= ManagedUserVM.PASSWORD_MAX_LENGTH; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/AuditResource.java b/jhipster/src/main/java/com/baeldung/web/rest/AuditResource.java new file mode 100644 index 0000000000..3101d134ed --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/AuditResource.java @@ -0,0 +1,76 @@ +package com.baeldung.web.rest; + +import com.baeldung.service.AuditEventService; +import com.baeldung.web.rest.util.PaginationUtil; + +import io.github.jhipster.web.util.ResponseUtil; +import io.swagger.annotations.ApiParam; +import org.springframework.boot.actuate.audit.AuditEvent; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.net.URISyntaxException; +import java.time.LocalDate; +import java.util.List; + +/** + * REST controller for getting the audit events. + */ +@RestController +@RequestMapping("/management/audits") +public class AuditResource { + + private final AuditEventService auditEventService; + + public AuditResource(AuditEventService auditEventService) { + this.auditEventService = auditEventService; + } + + /** + * GET /audits : get a page of AuditEvents. + * + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body + */ + @GetMapping + public ResponseEntity> getAll(@ApiParam Pageable pageable) { + Page page = auditEventService.findAll(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /audits : get a page of AuditEvents between the fromDate and toDate. + * + * @param fromDate the start of the time period of AuditEvents to get + * @param toDate the end of the time period of AuditEvents to get + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body + */ + + @GetMapping(params = {"fromDate", "toDate"}) + public ResponseEntity> getByDates( + @RequestParam(value = "fromDate") LocalDate fromDate, + @RequestParam(value = "toDate") LocalDate toDate, + @ApiParam Pageable pageable) { + + Page page = auditEventService.findByDates(fromDate.atTime(0, 0), toDate.atTime(23, 59), pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /audits/:id : get an AuditEvent by id. + * + * @param id the id of the entity to get + * @return the ResponseEntity with status 200 (OK) and the AuditEvent in body, or status 404 (Not Found) + */ + @GetMapping("/{id:.+}") + public ResponseEntity get(@PathVariable Long id) { + return ResponseUtil.wrapOrNotFound(auditEventService.find(id)); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/CommentResource.java b/jhipster/src/main/java/com/baeldung/web/rest/CommentResource.java new file mode 100644 index 0000000000..3e3f13b5e9 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/CommentResource.java @@ -0,0 +1,129 @@ +package com.baeldung.web.rest; + +import com.codahale.metrics.annotation.Timed; +import com.baeldung.domain.Comment; + +import com.baeldung.repository.CommentRepository; +import com.baeldung.web.rest.util.HeaderUtil; +import com.baeldung.web.rest.util.PaginationUtil; +import io.swagger.annotations.ApiParam; +import io.github.jhipster.web.util.ResponseUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Optional; + +/** + * REST controller for managing Comment. + */ +@RestController +@RequestMapping("/api") +public class CommentResource { + + private final Logger log = LoggerFactory.getLogger(CommentResource.class); + + private static final String ENTITY_NAME = "comment"; + + private final CommentRepository commentRepository; + + public CommentResource(CommentRepository commentRepository) { + this.commentRepository = commentRepository; + } + + /** + * POST /comments : Create a new comment. + * + * @param comment the comment to create + * @return the ResponseEntity with status 201 (Created) and with body the new comment, or with status 400 (Bad Request) if the comment has already an ID + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PostMapping("/comments") + @Timed + public ResponseEntity createComment(@Valid @RequestBody Comment comment) throws URISyntaxException { + log.debug("REST request to save Comment : {}", comment); + if (comment.getId() != null) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new comment cannot already have an ID")).body(null); + } + Comment result = commentRepository.save(comment); + return ResponseEntity.created(new URI("/api/comments/" + result.getId())) + .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) + .body(result); + } + + /** + * PUT /comments : Updates an existing comment. + * + * @param comment the comment to update + * @return the ResponseEntity with status 200 (OK) and with body the updated comment, + * or with status 400 (Bad Request) if the comment is not valid, + * or with status 500 (Internal Server Error) if the comment couldnt be updated + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PutMapping("/comments") + @Timed + public ResponseEntity updateComment(@Valid @RequestBody Comment comment) throws URISyntaxException { + log.debug("REST request to update Comment : {}", comment); + if (comment.getId() == null) { + return createComment(comment); + } + Comment result = commentRepository.save(comment); + return ResponseEntity.ok() + .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, comment.getId().toString())) + .body(result); + } + + /** + * GET /comments : get all the comments. + * + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and the list of comments in body + * @throws URISyntaxException if there is an error to generate the pagination HTTP headers + */ + @GetMapping("/comments") + @Timed + public ResponseEntity> getAllComments(@ApiParam Pageable pageable) { + log.debug("REST request to get a page of Comments"); + Page page = commentRepository.findAll(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/comments"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /comments/:id : get the "id" comment. + * + * @param id the id of the comment to retrieve + * @return the ResponseEntity with status 200 (OK) and with body the comment, or with status 404 (Not Found) + */ + @GetMapping("/comments/{id}") + @Timed + public ResponseEntity getComment(@PathVariable Long id) { + log.debug("REST request to get Comment : {}", id); + Comment comment = commentRepository.findOne(id); + return ResponseUtil.wrapOrNotFound(Optional.ofNullable(comment)); + } + + /** + * DELETE /comments/:id : delete the "id" comment. + * + * @param id the id of the comment to delete + * @return the ResponseEntity with status 200 (OK) + */ + @DeleteMapping("/comments/{id}") + @Timed + public ResponseEntity deleteComment(@PathVariable Long id) { + log.debug("REST request to delete Comment : {}", id); + commentRepository.delete(id); + return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build(); + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/JWTToken.java b/jhipster/src/main/java/com/baeldung/web/rest/JWTToken.java new file mode 100644 index 0000000000..c0804851f3 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/JWTToken.java @@ -0,0 +1,24 @@ +package com.baeldung.web.rest; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Object to return as body in JWT Authentication. + */ +public class JWTToken { + + private String idToken; + + public JWTToken(String idToken) { + this.idToken = idToken; + } + + @JsonProperty("id_token") + public String getIdToken() { + return idToken; + } + + public void setIdToken(String idToken) { + this.idToken = idToken; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/LogsResource.java b/jhipster/src/main/java/com/baeldung/web/rest/LogsResource.java new file mode 100644 index 0000000000..3d556e2609 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/LogsResource.java @@ -0,0 +1,39 @@ +package com.baeldung.web.rest; + +import com.baeldung.web.rest.vm.LoggerVM; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import com.codahale.metrics.annotation.Timed; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Controller for view and managing Log Level at runtime. + */ +@RestController +@RequestMapping("/management") +public class LogsResource { + + @GetMapping("/logs") + @Timed + public List getList() { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + return context.getLoggerList() + .stream() + .map(LoggerVM::new) + .collect(Collectors.toList()); + } + + @PutMapping("/logs") + @ResponseStatus(HttpStatus.NO_CONTENT) + @Timed + public void changeLevel(@RequestBody LoggerVM jsonLogger) { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + context.getLogger(jsonLogger.getName()).setLevel(Level.valueOf(jsonLogger.getLevel())); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/PostResource.java b/jhipster/src/main/java/com/baeldung/web/rest/PostResource.java new file mode 100644 index 0000000000..7505e1fa46 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/PostResource.java @@ -0,0 +1,129 @@ +package com.baeldung.web.rest; + +import com.codahale.metrics.annotation.Timed; +import com.baeldung.domain.Post; + +import com.baeldung.repository.PostRepository; +import com.baeldung.web.rest.util.HeaderUtil; +import com.baeldung.web.rest.util.PaginationUtil; +import io.swagger.annotations.ApiParam; +import io.github.jhipster.web.util.ResponseUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Optional; + +/** + * REST controller for managing Post. + */ +@RestController +@RequestMapping("/api") +public class PostResource { + + private final Logger log = LoggerFactory.getLogger(PostResource.class); + + private static final String ENTITY_NAME = "post"; + + private final PostRepository postRepository; + + public PostResource(PostRepository postRepository) { + this.postRepository = postRepository; + } + + /** + * POST /posts : Create a new post. + * + * @param post the post to create + * @return the ResponseEntity with status 201 (Created) and with body the new post, or with status 400 (Bad Request) if the post has already an ID + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PostMapping("/posts") + @Timed + public ResponseEntity createPost(@Valid @RequestBody Post post) throws URISyntaxException { + log.debug("REST request to save Post : {}", post); + if (post.getId() != null) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new post cannot already have an ID")).body(null); + } + Post result = postRepository.save(post); + return ResponseEntity.created(new URI("/api/posts/" + result.getId())) + .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString())) + .body(result); + } + + /** + * PUT /posts : Updates an existing post. + * + * @param post the post to update + * @return the ResponseEntity with status 200 (OK) and with body the updated post, + * or with status 400 (Bad Request) if the post is not valid, + * or with status 500 (Internal Server Error) if the post couldnt be updated + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PutMapping("/posts") + @Timed + public ResponseEntity updatePost(@Valid @RequestBody Post post) throws URISyntaxException { + log.debug("REST request to update Post : {}", post); + if (post.getId() == null) { + return createPost(post); + } + Post result = postRepository.save(post); + return ResponseEntity.ok() + .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, post.getId().toString())) + .body(result); + } + + /** + * GET /posts : get all the posts. + * + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and the list of posts in body + * @throws URISyntaxException if there is an error to generate the pagination HTTP headers + */ + @GetMapping("/posts") + @Timed + public ResponseEntity> getAllPosts(@ApiParam Pageable pageable) { + log.debug("REST request to get a page of Posts"); + Page page = postRepository.findAll(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/posts"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /posts/:id : get the "id" post. + * + * @param id the id of the post to retrieve + * @return the ResponseEntity with status 200 (OK) and with body the post, or with status 404 (Not Found) + */ + @GetMapping("/posts/{id}") + @Timed + public ResponseEntity getPost(@PathVariable Long id) { + log.debug("REST request to get Post : {}", id); + Post post = postRepository.findOne(id); + return ResponseUtil.wrapOrNotFound(Optional.ofNullable(post)); + } + + /** + * DELETE /posts/:id : delete the "id" post. + * + * @param id the id of the post to delete + * @return the ResponseEntity with status 200 (OK) + */ + @DeleteMapping("/posts/{id}") + @Timed + public ResponseEntity deletePost(@PathVariable Long id) { + log.debug("REST request to delete Post : {}", id); + postRepository.delete(id); + return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build(); + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/ProfileInfoResource.java b/jhipster/src/main/java/com/baeldung/web/rest/ProfileInfoResource.java new file mode 100644 index 0000000000..b5e26c8281 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/ProfileInfoResource.java @@ -0,0 +1,69 @@ +package com.baeldung.web.rest; + +import com.baeldung.config.DefaultProfileUtil; + +import io.github.jhipster.config.JHipsterProperties; + +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Resource to return information about the currently running Spring profiles. + */ +@RestController +@RequestMapping("/api") +public class ProfileInfoResource { + + private final Environment env; + + private final JHipsterProperties jHipsterProperties; + + public ProfileInfoResource(Environment env, JHipsterProperties jHipsterProperties) { + this.env = env; + this.jHipsterProperties = jHipsterProperties; + } + + @GetMapping("/profile-info") + public ProfileInfoVM getActiveProfiles() { + String[] activeProfiles = DefaultProfileUtil.getActiveProfiles(env); + return new ProfileInfoVM(activeProfiles, getRibbonEnv(activeProfiles)); + } + + private String getRibbonEnv(String[] activeProfiles) { + String[] displayOnActiveProfiles = jHipsterProperties.getRibbon().getDisplayOnActiveProfiles(); + if (displayOnActiveProfiles == null) { + return null; + } + List ribbonProfiles = new ArrayList<>(Arrays.asList(displayOnActiveProfiles)); + List springBootProfiles = Arrays.asList(activeProfiles); + ribbonProfiles.retainAll(springBootProfiles); + if (!ribbonProfiles.isEmpty()) { + return ribbonProfiles.get(0); + } + return null; + } + + class ProfileInfoVM { + + private String[] activeProfiles; + + private String ribbonEnv; + + ProfileInfoVM(String[] activeProfiles, String ribbonEnv) { + this.activeProfiles = activeProfiles; + this.ribbonEnv = ribbonEnv; + } + + public String[] getActiveProfiles() { + return activeProfiles; + } + + public String getRibbonEnv() { + return ribbonEnv; + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/UserJWTController.java b/jhipster/src/main/java/com/baeldung/web/rest/UserJWTController.java new file mode 100644 index 0000000000..02b77c51a6 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/UserJWTController.java @@ -0,0 +1,60 @@ +package com.baeldung.web.rest; + +import com.baeldung.security.jwt.JWTConfigurer; +import com.baeldung.security.jwt.TokenProvider; +import com.baeldung.web.rest.vm.LoginVM; + +import java.util.Collections; + +import com.codahale.metrics.annotation.Timed; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; + +@RestController +@RequestMapping("/api") +public class UserJWTController { + + private final Logger log = LoggerFactory.getLogger(UserJWTController.class); + + private final TokenProvider tokenProvider; + + private final AuthenticationManager authenticationManager; + + public UserJWTController(TokenProvider tokenProvider, AuthenticationManager authenticationManager) { + this.tokenProvider = tokenProvider; + this.authenticationManager = authenticationManager; + } + + @PostMapping("/authenticate") + @Timed + public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletResponse response) { + + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword()); + + try { + Authentication authentication = this.authenticationManager.authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe(); + String jwt = tokenProvider.createToken(authentication, rememberMe); + response.addHeader(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt); + return ResponseEntity.ok(new JWTToken(jwt)); + } catch (AuthenticationException ae) { + log.trace("Authentication exception trace: {}", ae); + return new ResponseEntity<>(Collections.singletonMap("AuthenticationException", + ae.getLocalizedMessage()), HttpStatus.UNAUTHORIZED); + } + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/UserResource.java b/jhipster/src/main/java/com/baeldung/web/rest/UserResource.java new file mode 100644 index 0000000000..296d3e30ba --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/UserResource.java @@ -0,0 +1,187 @@ +package com.baeldung.web.rest; + +import com.baeldung.config.Constants; +import com.codahale.metrics.annotation.Timed; +import com.baeldung.domain.User; +import com.baeldung.repository.UserRepository; +import com.baeldung.security.AuthoritiesConstants; +import com.baeldung.service.MailService; +import com.baeldung.service.UserService; +import com.baeldung.service.dto.UserDTO; +import com.baeldung.web.rest.vm.ManagedUserVM; +import com.baeldung.web.rest.util.HeaderUtil; +import com.baeldung.web.rest.util.PaginationUtil; +import io.github.jhipster.web.util.ResponseUtil; +import io.swagger.annotations.ApiParam; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.*; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; + +/** + * REST controller for managing users. + * + *

This class accesses the User entity, and needs to fetch its collection of authorities.

+ *

+ * For a normal use-case, it would be better to have an eager relationship between User and Authority, + * and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join + * which would be good for performance. + *

+ *

+ * We use a View Model and a DTO for 3 reasons: + *

    + *
  • We want to keep a lazy association between the user and the authorities, because people will + * quite often do relationships with the user, and we don't want them to get the authorities all + * the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users' + * application because of this use-case.
  • + *
  • Not having an outer join causes n+1 requests to the database. This is not a real issue as + * we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests, + * but then all authorities come from the cache, so in fact it's much better than doing an outer join + * (which will get lots of data from the database, for each HTTP call).
  • + *
  • As this manages users, for security reasons, we'd rather have a DTO layer.
  • + *
+ *

Another option would be to have a specific JPA entity graph to handle this case.

+ */ +@RestController +@RequestMapping("/api") +public class UserResource { + + private final Logger log = LoggerFactory.getLogger(UserResource.class); + + private static final String ENTITY_NAME = "userManagement"; + + private final UserRepository userRepository; + + private final MailService mailService; + + private final UserService userService; + + public UserResource(UserRepository userRepository, MailService mailService, + UserService userService) { + + this.userRepository = userRepository; + this.mailService = mailService; + this.userService = userService; + } + + /** + * POST /users : Creates a new user. + *

+ * Creates a new user if the login and email are not already used, and sends an + * mail with an activation link. + * The user needs to be activated on creation. + *

+ * + * @param managedUserVM the user to create + * @return the ResponseEntity with status 201 (Created) and with body the new user, or with status 400 (Bad Request) if the login or email is already in use + * @throws URISyntaxException if the Location URI syntax is incorrect + */ + @PostMapping("/users") + @Timed + @Secured(AuthoritiesConstants.ADMIN) + public ResponseEntity createUser(@RequestBody ManagedUserVM managedUserVM) throws URISyntaxException { + log.debug("REST request to save User : {}", managedUserVM); + + if (managedUserVM.getId() != null) { + return ResponseEntity.badRequest() + .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new user cannot already have an ID")) + .body(null); + // Lowercase the user login before comparing with database + } else if (userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()).isPresent()) { + return ResponseEntity.badRequest() + .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "userexists", "Login already in use")) + .body(null); + } else if (userRepository.findOneByEmail(managedUserVM.getEmail()).isPresent()) { + return ResponseEntity.badRequest() + .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "emailexists", "Email already in use")) + .body(null); + } else { + User newUser = userService.createUser(managedUserVM); + mailService.sendCreationEmail(newUser); + return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin())) + .headers(HeaderUtil.createAlert( "userManagement.created", newUser.getLogin())) + .body(newUser); + } + } + + /** + * PUT /users : Updates an existing User. + * + * @param managedUserVM the user to update + * @return the ResponseEntity with status 200 (OK) and with body the updated user, + * or with status 400 (Bad Request) if the login or email is already in use, + * or with status 500 (Internal Server Error) if the user couldn't be updated + */ + @PutMapping("/users") + @Timed + @Secured(AuthoritiesConstants.ADMIN) + public ResponseEntity updateUser(@RequestBody ManagedUserVM managedUserVM) { + log.debug("REST request to update User : {}", managedUserVM); + Optional existingUser = userRepository.findOneByEmail(managedUserVM.getEmail()); + if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserVM.getId()))) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "emailexists", "E-mail already in use")).body(null); + } + existingUser = userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()); + if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserVM.getId()))) { + return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "userexists", "Login already in use")).body(null); + } + Optional updatedUser = userService.updateUser(managedUserVM); + + return ResponseUtil.wrapOrNotFound(updatedUser, + HeaderUtil.createAlert("userManagement.updated", managedUserVM.getLogin())); + } + + /** + * GET /users : get all users. + * + * @param pageable the pagination information + * @return the ResponseEntity with status 200 (OK) and with body all users + */ + @GetMapping("/users") + @Timed + public ResponseEntity> getAllUsers(@ApiParam Pageable pageable) { + final Page page = userService.getAllManagedUsers(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users"); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + /** + * GET /users/:login : get the "login" user. + * + * @param login the login of the user to find + * @return the ResponseEntity with status 200 (OK) and with body the "login" user, or with status 404 (Not Found) + */ + @GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}") + @Timed + public ResponseEntity getUser(@PathVariable String login) { + log.debug("REST request to get User : {}", login); + return ResponseUtil.wrapOrNotFound( + userService.getUserWithAuthoritiesByLogin(login) + .map(UserDTO::new)); + } + + /** + * DELETE /users/:login : delete the "login" User. + * + * @param login the login of the user to delete + * @return the ResponseEntity with status 200 (OK) + */ + @DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}") + @Timed + @Secured(AuthoritiesConstants.ADMIN) + public ResponseEntity deleteUser(@PathVariable String login) { + log.debug("REST request to delete User: {}", login); + userService.deleteUser(login); + return ResponseEntity.ok().headers(HeaderUtil.createAlert( "userManagement.deleted", login)).build(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/CustomParameterizedException.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/CustomParameterizedException.java new file mode 100644 index 0000000000..ab7754476b --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/CustomParameterizedException.java @@ -0,0 +1,34 @@ +package com.baeldung.web.rest.errors; + +/** + * Custom, parameterized exception, which can be translated on the client side. + * For example: + * + *
+ * throw new CustomParameterizedException("myCustomError", "hello", "world");
+ * 
+ * + * Can be translated with: + * + *
+ * "error.myCustomError" :  "The server says {{params[0]}} to {{params[1]}}"
+ * 
+ */ +public class CustomParameterizedException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + private final String message; + private final String[] params; + + public CustomParameterizedException(String message, String... params) { + super(message); + this.message = message; + this.params = params; + } + + public ParameterizedErrorVM getErrorVM() { + return new ParameterizedErrorVM(message, params); + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorConstants.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorConstants.java new file mode 100644 index 0000000000..69f21ed96c --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorConstants.java @@ -0,0 +1,14 @@ +package com.baeldung.web.rest.errors; + +public final class ErrorConstants { + + public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure"; + public static final String ERR_ACCESS_DENIED = "error.accessDenied"; + public static final String ERR_VALIDATION = "error.validation"; + public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported"; + public static final String ERR_INTERNAL_SERVER_ERROR = "error.internalServerError"; + + private ErrorConstants() { + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorVM.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorVM.java new file mode 100644 index 0000000000..22fb066135 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/ErrorVM.java @@ -0,0 +1,52 @@ +package com.baeldung.web.rest.errors; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * View Model for transferring error message with a list of field errors. + */ +public class ErrorVM implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String message; + private final String description; + + private List fieldErrors; + + public ErrorVM(String message) { + this(message, null); + } + + public ErrorVM(String message, String description) { + this.message = message; + this.description = description; + } + + public ErrorVM(String message, String description, List fieldErrors) { + this.message = message; + this.description = description; + this.fieldErrors = fieldErrors; + } + + public void add(String objectName, String field, String message) { + if (fieldErrors == null) { + fieldErrors = new ArrayList<>(); + } + fieldErrors.add(new FieldErrorVM(objectName, field, message)); + } + + public String getMessage() { + return message; + } + + public String getDescription() { + return description; + } + + public List getFieldErrors() { + return fieldErrors; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/ExceptionTranslator.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/ExceptionTranslator.java new file mode 100644 index 0000000000..51925bfa61 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/ExceptionTranslator.java @@ -0,0 +1,85 @@ +package com.baeldung.web.rest.errors; + +import java.util.List; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.dao.ConcurrencyFailureException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.ResponseEntity.BodyBuilder; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.*; + +/** + * Controller advice to translate the server side exceptions to client-friendly json structures. + */ +@ControllerAdvice +public class ExceptionTranslator { + + @ExceptionHandler(ConcurrencyFailureException.class) + @ResponseStatus(HttpStatus.CONFLICT) + @ResponseBody + public ErrorVM processConcurrencyError(ConcurrencyFailureException ex) { + return new ErrorVM(ErrorConstants.ERR_CONCURRENCY_FAILURE); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseBody + public ErrorVM processValidationError(MethodArgumentNotValidException ex) { + BindingResult result = ex.getBindingResult(); + List fieldErrors = result.getFieldErrors(); + + return processFieldErrors(fieldErrors); + } + + @ExceptionHandler(CustomParameterizedException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseBody + public ParameterizedErrorVM processParameterizedValidationError(CustomParameterizedException ex) { + return ex.getErrorVM(); + } + + @ExceptionHandler(AccessDeniedException.class) + @ResponseStatus(HttpStatus.FORBIDDEN) + @ResponseBody + public ErrorVM processAccessDeniedException(AccessDeniedException e) { + return new ErrorVM(ErrorConstants.ERR_ACCESS_DENIED, e.getMessage()); + } + + private ErrorVM processFieldErrors(List fieldErrors) { + ErrorVM dto = new ErrorVM(ErrorConstants.ERR_VALIDATION); + + for (FieldError fieldError : fieldErrors) { + dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getCode()); + } + + return dto; + } + + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + @ResponseBody + @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) + public ErrorVM processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) { + return new ErrorVM(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage()); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity processRuntimeException(Exception ex) { + BodyBuilder builder; + ErrorVM errorVM; + ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class); + if (responseStatus != null) { + builder = ResponseEntity.status(responseStatus.value()); + errorVM = new ErrorVM("error." + responseStatus.value().value(), responseStatus.reason()); + } else { + builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR); + errorVM = new ErrorVM(ErrorConstants.ERR_INTERNAL_SERVER_ERROR, "Internal server error"); + } + return builder.body(errorVM); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/FieldErrorVM.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/FieldErrorVM.java new file mode 100644 index 0000000000..19ab640ec7 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/FieldErrorVM.java @@ -0,0 +1,33 @@ +package com.baeldung.web.rest.errors; + +import java.io.Serializable; + +public class FieldErrorVM implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String objectName; + + private final String field; + + private final String message; + + public FieldErrorVM(String dto, String field, String message) { + this.objectName = dto; + this.field = field; + this.message = message; + } + + public String getObjectName() { + return objectName; + } + + public String getField() { + return field; + } + + public String getMessage() { + return message; + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/errors/ParameterizedErrorVM.java b/jhipster/src/main/java/com/baeldung/web/rest/errors/ParameterizedErrorVM.java new file mode 100644 index 0000000000..18500c51af --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/errors/ParameterizedErrorVM.java @@ -0,0 +1,27 @@ +package com.baeldung.web.rest.errors; + +import java.io.Serializable; + +/** + * View Model for sending a parameterized error message. + */ +public class ParameterizedErrorVM implements Serializable { + + private static final long serialVersionUID = 1L; + private final String message; + private final String[] params; + + public ParameterizedErrorVM(String message, String... params) { + this.message = message; + this.params = params; + } + + public String getMessage() { + return message; + } + + public String[] getParams() { + return params; + } + +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/package-info.java b/jhipster/src/main/java/com/baeldung/web/rest/package-info.java new file mode 100644 index 0000000000..0a74f6e90c --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring MVC REST controllers. + */ +package com.baeldung.web.rest; diff --git a/jhipster/src/main/java/com/baeldung/web/rest/util/HeaderUtil.java b/jhipster/src/main/java/com/baeldung/web/rest/util/HeaderUtil.java new file mode 100644 index 0000000000..7c643e9323 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/util/HeaderUtil.java @@ -0,0 +1,45 @@ +package com.baeldung.web.rest.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; + +/** + * Utility class for HTTP headers creation. + */ +public final class HeaderUtil { + + private static final Logger log = LoggerFactory.getLogger(HeaderUtil.class); + + private static final String APPLICATION_NAME = "baeldungApp"; + + private HeaderUtil() { + } + + public static HttpHeaders createAlert(String message, String param) { + HttpHeaders headers = new HttpHeaders(); + headers.add("X-baeldungApp-alert", message); + headers.add("X-baeldungApp-params", param); + return headers; + } + + public static HttpHeaders createEntityCreationAlert(String entityName, String param) { + return createAlert(APPLICATION_NAME + "." + entityName + ".created", param); + } + + public static HttpHeaders createEntityUpdateAlert(String entityName, String param) { + return createAlert(APPLICATION_NAME + "." + entityName + ".updated", param); + } + + public static HttpHeaders createEntityDeletionAlert(String entityName, String param) { + return createAlert(APPLICATION_NAME + "." + entityName + ".deleted", param); + } + + public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) { + log.error("Entity creation failed, {}", defaultMessage); + HttpHeaders headers = new HttpHeaders(); + headers.add("X-baeldungApp-error", "error." + errorKey); + headers.add("X-baeldungApp-params", entityName); + return headers; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/util/PaginationUtil.java b/jhipster/src/main/java/com/baeldung/web/rest/util/PaginationUtil.java new file mode 100644 index 0000000000..affcb7842c --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/util/PaginationUtil.java @@ -0,0 +1,47 @@ +package com.baeldung.web.rest.util; + +import org.springframework.data.domain.Page; +import org.springframework.http.HttpHeaders; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URISyntaxException; + +/** + * Utility class for handling pagination. + * + *

+ * Pagination uses the same principles as the Github API, + * and follow RFC 5988 (Link header). + */ +public final class PaginationUtil { + + private PaginationUtil() { + } + + public static HttpHeaders generatePaginationHttpHeaders(Page page, String baseUrl) { + + HttpHeaders headers = new HttpHeaders(); + headers.add("X-Total-Count", "" + Long.toString(page.getTotalElements())); + String link = ""; + if ((page.getNumber() + 1) < page.getTotalPages()) { + link = "<" + generateUri(baseUrl, page.getNumber() + 1, page.getSize()) + ">; rel=\"next\","; + } + // prev link + if ((page.getNumber()) > 0) { + link += "<" + generateUri(baseUrl, page.getNumber() - 1, page.getSize()) + ">; rel=\"prev\","; + } + // last and first link + int lastPage = 0; + if (page.getTotalPages() > 0) { + lastPage = page.getTotalPages() - 1; + } + link += "<" + generateUri(baseUrl, lastPage, page.getSize()) + ">; rel=\"last\","; + link += "<" + generateUri(baseUrl, 0, page.getSize()) + ">; rel=\"first\""; + headers.add(HttpHeaders.LINK, link); + return headers; + } + + private static String generateUri(String baseUrl, int page, int size) { + return UriComponentsBuilder.fromUriString(baseUrl).queryParam("page", page).queryParam("size", size).toUriString(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/KeyAndPasswordVM.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/KeyAndPasswordVM.java new file mode 100644 index 0000000000..94465f309f --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/KeyAndPasswordVM.java @@ -0,0 +1,27 @@ +package com.baeldung.web.rest.vm; + +/** + * View Model object for storing the user's key and password. + */ +public class KeyAndPasswordVM { + + private String key; + + private String newPassword; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getNewPassword() { + return newPassword; + } + + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/LoggerVM.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/LoggerVM.java new file mode 100644 index 0000000000..56f77aec85 --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/LoggerVM.java @@ -0,0 +1,48 @@ +package com.baeldung.web.rest.vm; + +import ch.qos.logback.classic.Logger; +import com.fasterxml.jackson.annotation.JsonCreator; + +/** + * View Model object for storing a Logback logger. + */ +public class LoggerVM { + + private String name; + + private String level; + + public LoggerVM(Logger logger) { + this.name = logger.getName(); + this.level = logger.getEffectiveLevel().toString(); + } + + @JsonCreator + public LoggerVM() { + // Empty public constructor used by Jackson. + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + @Override + public String toString() { + return "LoggerVM{" + + "name='" + name + '\'' + + ", level='" + level + '\'' + + '}'; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/LoginVM.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/LoginVM.java new file mode 100644 index 0000000000..a910d0d59c --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/LoginVM.java @@ -0,0 +1,55 @@ +package com.baeldung.web.rest.vm; + +import com.baeldung.config.Constants; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * View Model object for storing a user's credentials. + */ +public class LoginVM { + + @Pattern(regexp = Constants.LOGIN_REGEX) + @NotNull + @Size(min = 1, max = 50) + private String username; + + @NotNull + @Size(min = ManagedUserVM.PASSWORD_MIN_LENGTH, max = ManagedUserVM.PASSWORD_MAX_LENGTH) + private String password; + + private Boolean rememberMe; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Boolean isRememberMe() { + return rememberMe; + } + + public void setRememberMe(Boolean rememberMe) { + this.rememberMe = rememberMe; + } + + @Override + public String toString() { + return "LoginVM{" + + "username='" + username + '\'' + + ", rememberMe=" + rememberMe + + '}'; + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/ManagedUserVM.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/ManagedUserVM.java new file mode 100644 index 0000000000..0899f0d0aa --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/ManagedUserVM.java @@ -0,0 +1,45 @@ +package com.baeldung.web.rest.vm; + +import com.baeldung.service.dto.UserDTO; +import javax.validation.constraints.Size; + +import java.time.ZonedDateTime; +import java.util.Set; + +/** + * View Model extending the UserDTO, which is meant to be used in the user management UI. + */ +public class ManagedUserVM extends UserDTO { + + public static final int PASSWORD_MIN_LENGTH = 4; + + public static final int PASSWORD_MAX_LENGTH = 100; + + @Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH) + private String password; + + public ManagedUserVM() { + // Empty constructor needed for Jackson. + } + + public ManagedUserVM(Long id, String login, String password, String firstName, String lastName, + String email, boolean activated, String imageUrl, String langKey, + String createdBy, ZonedDateTime createdDate, String lastModifiedBy, ZonedDateTime lastModifiedDate, + Set authorities) { + + super(id, login, firstName, lastName, email, activated, imageUrl, langKey, + createdBy, createdDate, lastModifiedBy, lastModifiedDate, authorities); + + this.password = password; + } + + public String getPassword() { + return password; + } + + @Override + public String toString() { + return "ManagedUserVM{" + + "} " + super.toString(); + } +} diff --git a/jhipster/src/main/java/com/baeldung/web/rest/vm/package-info.java b/jhipster/src/main/java/com/baeldung/web/rest/vm/package-info.java new file mode 100644 index 0000000000..8d56157b6a --- /dev/null +++ b/jhipster/src/main/java/com/baeldung/web/rest/vm/package-info.java @@ -0,0 +1,4 @@ +/** + * View Models used by Spring MVC REST controllers. + */ +package com.baeldung.web.rest.vm; diff --git a/jhipster/src/main/resources/.h2.server.properties b/jhipster/src/main/resources/.h2.server.properties new file mode 100644 index 0000000000..f8b4429902 --- /dev/null +++ b/jhipster/src/main/resources/.h2.server.properties @@ -0,0 +1,5 @@ +#H2 Server Properties +0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/baeldung|baeldung +webAllowOthers=true +webPort=8082 +webSSL=false diff --git a/jhipster/src/main/resources/banner.txt b/jhipster/src/main/resources/banner.txt new file mode 100644 index 0000000000..c3d8cf725d --- /dev/null +++ b/jhipster/src/main/resources/banner.txt @@ -0,0 +1,10 @@ + + ${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗ + ${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗ + ${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╝ ╚█████╗ ██║ ██████╗ ███████╔╝ + ${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔═══██║ ██║ ██╔════╝ ╚═══██╗ ██║ ██╔═══╝ ██╔══██║ + ${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗ + ${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝ + +${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: +:: http://jhipster.github.io ::${AnsiColor.DEFAULT} diff --git a/jhipster/src/main/resources/config/application-dev.yml b/jhipster/src/main/resources/config/application-dev.yml new file mode 100644 index 0000000000..d75c2549c8 --- /dev/null +++ b/jhipster/src/main/resources/config/application-dev.yml @@ -0,0 +1,130 @@ +# =================================================================== +# Spring Boot configuration for the "dev" profile. +# +# This configuration overrides the application.yml file. +# +# More information on profiles: https://jhipster.github.io/profiles/ +# More information on configuration properties: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +spring: + profiles: + active: dev + include: swagger + devtools: + restart: + enabled: true + livereload: + enabled: false # we use gulp + BrowserSync for livereload + jackson: + serialization.indent_output: true + datasource: + type: com.zaxxer.hikari.HikariDataSource + url: jdbc:h2:file:./target/h2db/db/baeldung;DB_CLOSE_DELAY=-1 + username: baeldung + password: + h2: + console: + enabled: false + jpa: + database-platform: io.github.jhipster.domain.util.FixedH2Dialect + database: H2 + show-sql: true + properties: + hibernate.id.new_generator_mappings: true + hibernate.cache.use_second_level_cache: true + hibernate.cache.use_query_cache: false + hibernate.generate_statistics: true + hibernate.cache.region.factory_class: io.github.jhipster.config.jcache.NoDefaultJCacheRegionFactory + mail: + host: localhost + port: 25 + username: + password: + messages: + cache-seconds: 1 + thymeleaf: + cache: false + +liquibase: + contexts: dev + +# =================================================================== +# To enable SSL, generate a certificate using: +# keytool -genkey -alias baeldung -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 +# +# You can also use Let's Encrypt: +# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm +# +# Then, modify the server.ssl properties so your "server" configuration looks like: +# +# server: +# port: 8443 +# ssl: +# key-store: keystore.p12 +# key-store-password: +# keyStoreType: PKCS12 +# keyAlias: baeldung +# =================================================================== +server: + port: 8080 + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +jhipster: + http: + version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration) + cache: # Cache configuration + ehcache: # Ehcache configuration + time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache + max-entries: 100 # Number of objects in each cache entry + security: + authentication: + jwt: + secret: my-secret-token-to-change-in-production + # Token is valid 24 hours + token-validity-in-seconds: 86400 + token-validity-in-seconds-for-remember-me: 2592000 + mail: # specific JHipster mail property, for standard properties see MailProperties + from: baeldung@localhost + base-url: http://127.0.0.1:8080 + metrics: # DropWizard Metrics configuration, used by MetricsConfiguration + jmx.enabled: true + graphite: # Use the "graphite" Maven profile to have the Graphite dependencies + enabled: false + host: localhost + port: 2003 + prefix: baeldung + prometheus: # Use the "prometheus" Maven profile to have the Prometheus dependencies + enabled: false + endpoint: /prometheusMetrics + logs: # Reports Dropwizard metrics in the logs + enabled: false + reportFrequency: 60 # in seconds + logging: + logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration + enabled: false + host: localhost + port: 5000 + queue-size: 512 + +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://jhipster.github.io/common-application-properties/ +# =================================================================== + +application: diff --git a/jhipster/src/main/resources/config/application-prod.yml b/jhipster/src/main/resources/config/application-prod.yml new file mode 100644 index 0000000000..a46fbd1c73 --- /dev/null +++ b/jhipster/src/main/resources/config/application-prod.yml @@ -0,0 +1,132 @@ +# =================================================================== +# Spring Boot configuration for the "prod" profile. +# +# This configuration overrides the application.yml file. +# +# More information on profiles: https://jhipster.github.io/profiles/ +# More information on configuration properties: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +spring: + devtools: + restart: + enabled: false + livereload: + enabled: false + datasource: + type: com.zaxxer.hikari.HikariDataSource + url: jdbc:mysql://localhost:3306/baeldung?useUnicode=true&characterEncoding=utf8&useSSL=false + username: root + password: + hikari: + data-source-properties: + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + jpa: + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + database: MYSQL + show-sql: false + properties: + hibernate.id.new_generator_mappings: true + hibernate.cache.use_second_level_cache: true + hibernate.cache.use_query_cache: false + hibernate.generate_statistics: false + hibernate.cache.region.factory_class: io.github.jhipster.config.jcache.NoDefaultJCacheRegionFactory + mail: + host: localhost + port: 25 + username: + password: + thymeleaf: + cache: true + +liquibase: + contexts: prod + +# =================================================================== +# To enable SSL, generate a certificate using: +# keytool -genkey -alias baeldung -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 +# +# You can also use Let's Encrypt: +# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm +# +# Then, modify the server.ssl properties so your "server" configuration looks like: +# +# server: +# port: 443 +# ssl: +# key-store: keystore.p12 +# key-store-password: +# keyStoreType: PKCS12 +# keyAlias: baeldung +# =================================================================== +server: + port: 8080 + compression: + enabled: true + mime-types: text/html,text/xml,text/plain,text/css, application/javascript, application/json + min-response-size: 1024 + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +jhipster: + http: + version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration) + cache: # Used by the CachingHttpHeadersFilter + timeToLiveInDays: 1461 + cache: # Cache configuration + ehcache: # Ehcache configuration + time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache + max-entries: 1000 # Number of objects in each cache entry + security: + authentication: + jwt: + secret: e1d4b69d3f953e3fa622121e882e6f459ca20ca4 + # Token is valid 24 hours + token-validity-in-seconds: 86400 + token-validity-in-seconds-for-remember-me: 2592000 + mail: # specific JHipster mail property, for standard properties see MailProperties + from: baeldung@localhost + base-url: http://my-server-url-to-change # Modify according to your server's URL + metrics: # DropWizard Metrics configuration, used by MetricsConfiguration + jmx.enabled: true + graphite: + enabled: false + host: localhost + port: 2003 + prefix: baeldung + prometheus: + enabled: false + endpoint: /prometheusMetrics + logs: # Reports Dropwizard metrics in the logs + enabled: false + reportFrequency: 60 # in seconds + logging: + logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration + enabled: false + host: localhost + port: 5000 + queue-size: 512 + +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://jhipster.github.io/common-application-properties/ +# =================================================================== + +application: diff --git a/jhipster/src/main/resources/config/application.yml b/jhipster/src/main/resources/config/application.yml new file mode 100644 index 0000000000..ed48baf853 --- /dev/null +++ b/jhipster/src/main/resources/config/application.yml @@ -0,0 +1,106 @@ +# =================================================================== +# Spring Boot configuration. +# +# This configuration will be overriden by the Spring profile you use, +# for example application-dev.yml if you use the "dev" profile. +# +# More information on profiles: https://jhipster.github.io/profiles/ +# More information on configuration properties: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +management: + security: + roles: ADMIN + context-path: /management + health: + mail: + enabled: false # When using the MailService, configure an SMTP server and set this to true +spring: + application: + name: baeldung + profiles: + # The commented value for `active` can be replaced with valid Spring profiles to load. + # Otherwise, it will be filled in by maven when building the WAR file + # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS` + active: #spring.profiles.active# + jackson: + serialization.write_dates_as_timestamps: false + jpa: + open-in-view: false + hibernate: + ddl-auto: none + naming: + physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy + messages: + basename: i18n/messages + mvc: + favicon: + enabled: false + thymeleaf: + mode: XHTML + +security: + basic: + enabled: false + +server: + session: + cookie: + http-only: true + +info: + project: + version: #project.version# + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +jhipster: + async: + core-pool-size: 2 + max-pool-size: 50 + queue-capacity: 10000 + # By default CORS is disabled. Uncomment to enable. + #cors: + #allowed-origins: "*" + #allowed-methods: GET, PUT, POST, DELETE, OPTIONS + #allowed-headers: "*" + #exposed-headers: + #allow-credentials: true + #max-age: 1800 + mail: + from: baeldung@localhost + swagger: + default-include-pattern: /api/.* + title: baeldung API + description: baeldung API documentation + version: 0.0.1 + terms-of-service-url: + contact-name: + contact-url: + contact-email: + license: + license-url: + ribbon: + display-on-active-profiles: dev + +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://jhipster.github.io/common-application-properties/ +# =================================================================== + +application: diff --git a/jhipster/src/main/resources/config/liquibase/authorities.csv b/jhipster/src/main/resources/config/liquibase/authorities.csv new file mode 100644 index 0000000000..af5c6dfa18 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/authorities.csv @@ -0,0 +1,3 @@ +name +ROLE_ADMIN +ROLE_USER diff --git a/jhipster/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml b/jhipster/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml new file mode 100644 index 0000000000..98ac548808 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_Post.xml b/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_Post.xml new file mode 100644 index 0000000000..ce4773262c --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_Post.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_constraints_Post.xml b/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_constraints_Post.xml new file mode 100644 index 0000000000..2040980371 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/20170316223211_added_entity_constraints_Post.xml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_Comment.xml b/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_Comment.xml new file mode 100644 index 0000000000..d0b26503e1 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_Comment.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_constraints_Comment.xml b/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_constraints_Comment.xml new file mode 100644 index 0000000000..b7670e747c --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/changelog/20170316224021_added_entity_constraints_Comment.xml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/master.xml b/jhipster/src/main/resources/config/liquibase/master.xml new file mode 100644 index 0000000000..32eb479989 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/master.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/jhipster/src/main/resources/config/liquibase/users.csv b/jhipster/src/main/resources/config/liquibase/users.csv new file mode 100644 index 0000000000..b25922b699 --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/users.csv @@ -0,0 +1,5 @@ +id;login;password_hash;first_name;last_name;email;image_url;activated;lang_key;created_by;last_modified_by +1;system;$2a$10$mE.qmcV0mFU5NcKh73TZx.z4ueI/.bDWbj0T1BYyqP481kGGarKLG;System;System;system@localhost;;true;en;system;system +2;anonymoususer;$2a$10$j8S5d7Sr7.8VTOYNviDPOeWX8KcYILUVJBsYV83Y5NtECayypx9lO;Anonymous;User;anonymous@localhost;;true;en;system;system +3;admin;$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC;Administrator;Administrator;admin@localhost;;true;en;system;system +4;user;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;User;User;user@localhost;;true;en;system;system diff --git a/jhipster/src/main/resources/config/liquibase/users_authorities.csv b/jhipster/src/main/resources/config/liquibase/users_authorities.csv new file mode 100644 index 0000000000..06c5feeeea --- /dev/null +++ b/jhipster/src/main/resources/config/liquibase/users_authorities.csv @@ -0,0 +1,6 @@ +user_id;authority_name +1;ROLE_ADMIN +1;ROLE_USER +3;ROLE_ADMIN +3;ROLE_USER +4;ROLE_USER diff --git a/jhipster/src/main/resources/i18n/messages.properties b/jhipster/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000000..1c28002acd --- /dev/null +++ b/jhipster/src/main/resources/i18n/messages.properties @@ -0,0 +1,22 @@ +# Error page +error.title=Your request cannot be processed +error.subtitle=Sorry, an error has occurred. +error.status=Status: +error.message=Message: + +# Activation e-mail +email.activation.title=baeldung account activation +email.activation.greeting=Dear {0} +email.activation.text1=Your baeldung account has been created, please click on the URL below to activate it: +email.activation.text2=Regards, +email.signature=baeldung Team. + +# Creation email +email.creation.text1=Your baeldung account has been created, please click on the URL below to access it: + +# Reset e-mail +email.reset.title=baeldung password reset +email.reset.greeting=Dear {0} +email.reset.text1=For your baeldung account a password reset was requested, please click on the URL below to reset it: +email.reset.text2=Regards, + diff --git a/jhipster/src/main/resources/i18n/messages_en.properties b/jhipster/src/main/resources/i18n/messages_en.properties new file mode 100644 index 0000000000..1c28002acd --- /dev/null +++ b/jhipster/src/main/resources/i18n/messages_en.properties @@ -0,0 +1,22 @@ +# Error page +error.title=Your request cannot be processed +error.subtitle=Sorry, an error has occurred. +error.status=Status: +error.message=Message: + +# Activation e-mail +email.activation.title=baeldung account activation +email.activation.greeting=Dear {0} +email.activation.text1=Your baeldung account has been created, please click on the URL below to activate it: +email.activation.text2=Regards, +email.signature=baeldung Team. + +# Creation email +email.creation.text1=Your baeldung account has been created, please click on the URL below to access it: + +# Reset e-mail +email.reset.title=baeldung password reset +email.reset.greeting=Dear {0} +email.reset.text1=For your baeldung account a password reset was requested, please click on the URL below to reset it: +email.reset.text2=Regards, + diff --git a/jhipster/src/main/resources/logback-spring.xml b/jhipster/src/main/resources/logback-spring.xml new file mode 100644 index 0000000000..3c62a70c31 --- /dev/null +++ b/jhipster/src/main/resources/logback-spring.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + diff --git a/jhipster/src/main/resources/mails/activationEmail.html b/jhipster/src/main/resources/mails/activationEmail.html new file mode 100644 index 0000000000..9fb22a7796 --- /dev/null +++ b/jhipster/src/main/resources/mails/activationEmail.html @@ -0,0 +1,24 @@ + + + + JHipster activation + + + +

+ Dear +

+

+ Your JHipster account has been created, please click on the URL below to activate it: +

+

+ Activation Link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/jhipster/src/main/resources/mails/creationEmail.html b/jhipster/src/main/resources/mails/creationEmail.html new file mode 100644 index 0000000000..a59d91da0c --- /dev/null +++ b/jhipster/src/main/resources/mails/creationEmail.html @@ -0,0 +1,24 @@ + + + + JHipster creation + + + +

+ Dear +

+

+ Your JHipster account has been created, please click on the URL below to access it: +

+

+ login +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/jhipster/src/main/resources/mails/passwordResetEmail.html b/jhipster/src/main/resources/mails/passwordResetEmail.html new file mode 100644 index 0000000000..30d5f62c60 --- /dev/null +++ b/jhipster/src/main/resources/mails/passwordResetEmail.html @@ -0,0 +1,24 @@ + + + + JHipster password reset + + + +

+ Dear +

+

+ For your JHipster account a password reset was requested, please click on the URL below to reset it: +

+

+ Reset Link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/jhipster/src/main/resources/templates/error.html b/jhipster/src/main/resources/templates/error.html new file mode 100644 index 0000000000..774b080d6c --- /dev/null +++ b/jhipster/src/main/resources/templates/error.html @@ -0,0 +1,162 @@ + + + + + Your request cannot be processed + + + +
+

Your request cannot be processed :(

+ +

Sorry, an error has occurred.

+ + Status:  ()
+ + Message: 
+
+ + + +
+ + diff --git a/jhipster/src/main/webapp/404.html b/jhipster/src/main/webapp/404.html new file mode 100644 index 0000000000..8d7925a892 --- /dev/null +++ b/jhipster/src/main/webapp/404.html @@ -0,0 +1,60 @@ + + + + + Page Not Found + + + + +

Page Not Found

+

Sorry, but the page you were trying to view does not exist.

+ + + diff --git a/jhipster/src/main/webapp/app/account/account.module.ts b/jhipster/src/main/webapp/app/account/account.module.ts new file mode 100644 index 0000000000..09b19a7555 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/account.module.ts @@ -0,0 +1,45 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { BaeldungSharedModule } from '../shared'; + +import { + Register, + Activate, + Password, + PasswordResetInit, + PasswordResetFinish, + PasswordStrengthBarComponent, + RegisterComponent, + ActivateComponent, + PasswordComponent, + PasswordResetInitComponent, + PasswordResetFinishComponent, + SettingsComponent, + accountState +} from './'; + +@NgModule({ + imports: [ + BaeldungSharedModule, + RouterModule.forRoot(accountState, { useHash: true }) + ], + declarations: [ + ActivateComponent, + RegisterComponent, + PasswordComponent, + PasswordStrengthBarComponent, + PasswordResetInitComponent, + PasswordResetFinishComponent, + SettingsComponent + ], + providers: [ + Register, + Activate, + Password, + PasswordResetInit, + PasswordResetFinish + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungAccountModule {} diff --git a/jhipster/src/main/webapp/app/account/account.route.ts b/jhipster/src/main/webapp/app/account/account.route.ts new file mode 100644 index 0000000000..4715216cf3 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/account.route.ts @@ -0,0 +1,26 @@ +import { Routes, CanActivate } from '@angular/router'; + +import { UserRouteAccessService } from '../shared'; + +import { + activateRoute, + passwordRoute, + passwordResetFinishRoute, + passwordResetInitRoute, + registerRoute, + settingsRoute +} from './'; + +let ACCOUNT_ROUTES = [ + activateRoute, + passwordRoute, + passwordResetFinishRoute, + passwordResetInitRoute, + registerRoute, + settingsRoute +]; + +export const accountState: Routes = [{ + path: '', + children: ACCOUNT_ROUTES +}]; diff --git a/jhipster/src/main/webapp/app/account/activate/activate.component.html b/jhipster/src/main/webapp/app/account/activate/activate.component.html new file mode 100644 index 0000000000..25e3f23417 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/activate/activate.component.html @@ -0,0 +1,19 @@ +
+
+
+

Activation

+ +
+ + Your user has been activated. Please + sign in. + +
+ +
+ Your user could not be activated. Please use the registration form to sign up. +
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/account/activate/activate.component.ts b/jhipster/src/main/webapp/app/account/activate/activate.component.ts new file mode 100644 index 0000000000..dbaaa7d676 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/activate/activate.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Activate } from './activate.service'; +import { LoginModalService } from '../../shared'; + +@Component({ + selector: 'jhi-activate', + templateUrl: './activate.component.html' +}) +export class ActivateComponent implements OnInit { + error: string; + success: string; + modalRef: NgbModalRef; + + constructor( + private jhiLanguageService: JhiLanguageService, + private activate: Activate, + private loginModalService: LoginModalService, + private route: ActivatedRoute + ) { + this.jhiLanguageService.setLocations(['activate']); + } + + ngOnInit () { + this.route.queryParams.subscribe(params => { + this.activate.get(params['key']).subscribe(() => { + this.error = null; + this.success = 'OK'; + }, () => { + this.success = null; + this.error = 'ERROR'; + }); + }); + } + + login() { + this.modalRef = this.loginModalService.open(); + } +} diff --git a/jhipster/src/main/webapp/app/account/activate/activate.route.ts b/jhipster/src/main/webapp/app/account/activate/activate.route.ts new file mode 100644 index 0000000000..8f1bb32b58 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/activate/activate.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { ActivateComponent } from './activate.component'; + +export const activateRoute: Route = { + path: 'activate', + component: ActivateComponent, + data: { + authorities: [], + pageTitle: 'activate.title' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/activate/activate.service.ts b/jhipster/src/main/webapp/app/account/activate/activate.service.ts new file mode 100644 index 0000000000..f877a4d50e --- /dev/null +++ b/jhipster/src/main/webapp/app/account/activate/activate.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class Activate { + + constructor (private http: Http) {} + + get(key: string): Observable { + let params: URLSearchParams = new URLSearchParams(); + params.set('key', key); + + return this.http.get('api/activate', { + search: params + }).map((res: Response) => res); + } +} diff --git a/jhipster/src/main/webapp/app/account/index.ts b/jhipster/src/main/webapp/app/account/index.ts new file mode 100644 index 0000000000..aeada0551c --- /dev/null +++ b/jhipster/src/main/webapp/app/account/index.ts @@ -0,0 +1,19 @@ +export * from './activate/activate.component'; +export * from './activate/activate.service'; +export * from './activate/activate.route'; +export * from './password/password.component'; +export * from './password/password-strength-bar.component'; +export * from './password/password.service'; +export * from './password/password.route'; +export * from './password-reset/finish/password-reset-finish.component'; +export * from './password-reset/finish/password-reset-finish.service'; +export * from './password-reset/finish/password-reset-finish.route'; +export * from './password-reset/init/password-reset-init.component'; +export * from './password-reset/init/password-reset-init.service'; +export * from './password-reset/init/password-reset-init.route'; +export * from './register/register.component'; +export * from './register/register.service'; +export * from './register/register.route'; +export * from './settings/settings.component'; +export * from './settings/settings.route'; +export * from './account.route'; diff --git a/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html new file mode 100644 index 0000000000..a1dfde055c --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html @@ -0,0 +1,77 @@ +
+
+
+

Reset password

+ +
+ The password reset key is missing. +
+ +
+

Choose a new password

+
+ +
+

Your password couldn't be reset. Remember a password request is only valid for 24 hours.

+
+ +

+ Your password has been reset. Please + sign in. +

+ +
+ The password and its confirmation do not match! +
+ +
+
+
+ + +
+ + Your password is required. + + + Your password is required to be at least 4 characters. + + + Your password cannot be longer than 50 characters. + +
+ +
+ +
+ + +
+ + Your password confirmation is required. + + + Your password confirmation is required to be at least 4 characters. + + + Your password confirmation cannot be longer than 50 characters. + +
+
+ +
+
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts new file mode 100644 index 0000000000..f1889920bd --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts @@ -0,0 +1,65 @@ +import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { PasswordResetFinish } from './password-reset-finish.service'; +import { LoginModalService } from '../../../shared'; + +@Component({ + selector: 'jhi-password-reset-finish', + templateUrl: './password-reset-finish.component.html' +}) +export class PasswordResetFinishComponent implements OnInit, AfterViewInit { + confirmPassword: string; + doNotMatch: string; + error: string; + keyMissing: boolean; + resetAccount: any; + success: string; + modalRef: NgbModalRef; + key: string; + + constructor( + private jhiLanguageService: JhiLanguageService, + private passwordResetFinish: PasswordResetFinish, + private loginModalService: LoginModalService, + private route: ActivatedRoute, + private elementRef: ElementRef, private renderer: Renderer + ) { + this.jhiLanguageService.setLocations(['reset']); + } + + ngOnInit() { + this.route.queryParams.subscribe(params => { + this.key = params['key']; + }); + this.resetAccount = {}; + this.keyMissing = !this.key; + } + + ngAfterViewInit() { + if (this.elementRef.nativeElement.querySelector('#password') != null) { + this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#password'), 'focus', []); + } + } + + finishReset() { + this.doNotMatch = null; + this.error = null; + if (this.resetAccount.password !== this.confirmPassword) { + this.doNotMatch = 'ERROR'; + } else { + this.passwordResetFinish.save({key: this.key, newPassword: this.resetAccount.password}).subscribe(() => { + this.success = 'OK'; + }, () => { + this.success = null; + this.error = 'ERROR'; + }); + } + } + + login() { + this.modalRef = this.loginModalService.open(); + } +} diff --git a/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts new file mode 100644 index 0000000000..7b02653ed5 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../../shared'; +import { PasswordResetFinishComponent } from './password-reset-finish.component'; + +export const passwordResetFinishRoute: Route = { + path: 'reset/finish', + component: PasswordResetFinishComponent, + data: { + authorities: [], + pageTitle: 'global.menu.account.password' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts new file mode 100644 index 0000000000..abd81374b0 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class PasswordResetFinish { + + constructor (private http: Http) {} + + save(keyAndPassword: any): Observable { + return this.http.post('api/account/reset_password/finish', keyAndPassword); + } +} diff --git a/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html new file mode 100644 index 0000000000..2503433275 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html @@ -0,0 +1,47 @@ +
+
+
+

Reset your password

+ +
+ E-Mail address isn't registered! Please check and try again. +
+ +
+

Enter the e-mail address you used to register.

+
+ +
+

Check your e-mails for details on how to reset your password.

+
+ +
+
+ + +
+ + Your e-mail is required. + + + Your e-mail is invalid. + + + Your e-mail is required to be at least 5 characters. + + + Your e-mail cannot be longer than 100 characters. + +
+
+ +
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts new file mode 100644 index 0000000000..eba521dc05 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts @@ -0,0 +1,49 @@ +import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { PasswordResetInit } from './password-reset-init.service'; + +@Component({ + selector: 'jhi-password-reset-init', + templateUrl: './password-reset-init.component.html' +}) +export class PasswordResetInitComponent implements OnInit, AfterViewInit { + error: string; + errorEmailNotExists: string; + resetAccount: any; + success: string; + + constructor( + private jhiLanguageService: JhiLanguageService, + private passwordResetInit: PasswordResetInit, + private elementRef: ElementRef, + private renderer: Renderer + ) { + this.jhiLanguageService.setLocations(['reset']); + } + + ngOnInit() { + this.resetAccount = {}; + } + + ngAfterViewInit() { + this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#email'), 'focus', []); + } + + requestReset () { + + this.error = null; + this.errorEmailNotExists = null; + + this.passwordResetInit.save(this.resetAccount.email).subscribe(() => { + this.success = 'OK'; + }, (response) => { + this.success = null; + if (response.status === 400 && response.data === 'e-mail address not registered') { + this.errorEmailNotExists = 'ERROR'; + } else { + this.error = 'ERROR'; + } + }); + } +} diff --git a/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts new file mode 100644 index 0000000000..37f7e8102d --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../../shared'; +import { PasswordResetInitComponent } from './password-reset-init.component'; + +export const passwordResetInitRoute: Route = { + path: 'reset/request', + component: PasswordResetInitComponent, + data: { + authorities: [], + pageTitle: 'global.menu.account.password' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts new file mode 100644 index 0000000000..fa466cd6cc --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class PasswordResetInit { + + constructor (private http: Http) {} + + save(mail: string): Observable { + return this.http.post('api/account/reset_password/init', mail); + } +} diff --git a/jhipster/src/main/webapp/app/account/password/password-strength-bar.component.ts b/jhipster/src/main/webapp/app/account/password/password-strength-bar.component.ts new file mode 100644 index 0000000000..2a61d133d6 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password-strength-bar.component.ts @@ -0,0 +1,89 @@ +import { Component, ElementRef, Input, Renderer } from '@angular/core'; + +@Component({ + selector: 'jhi-password-strength-bar', + template: ` +
+ Password strength: +
    +
  • +
  • +
  • +
  • +
  • +
+
`, + styleUrls: [ + 'password-strength-bar.scss' + ] +}) +export class PasswordStrengthBarComponent { + + colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0']; + + constructor(private renderer: Renderer, private elementRef: ElementRef) { } + + measureStrength(p: string): number { + + let force = 0; + let regex = /[$-/:-?{-~!"^_`\[\]]/g; // " + + let lowerLetters = /[a-z]+/.test(p); + let upperLetters = /[A-Z]+/.test(p); + let numbers = /[0-9]+/.test(p); + let symbols = regex.test(p); + + let flags = [lowerLetters, upperLetters, numbers, symbols]; + let passedMatches = flags.filter( (isMatchedFlag: boolean) => { + return isMatchedFlag === true; + }).length; + + force += 2 * p.length + ((p.length >= 10) ? 1 : 0); + force += passedMatches * 10; + + // penality (short password) + force = (p.length <= 6) ? Math.min(force, 10) : force; + + // penality (poor variety of characters) + force = (passedMatches === 1) ? Math.min(force, 10) : force; + force = (passedMatches === 2) ? Math.min(force, 20) : force; + force = (passedMatches === 3) ? Math.min(force, 40) : force; + + return force; + }; + + getColor(s: number): any { + let idx = 0; + if (s <= 10) { + idx = 0; + } else if (s <= 20) { + idx = 1; + } else if (s <= 30) { + idx = 2; + } else if (s <= 40) { + idx = 3; + } else { + idx = 4; + } + return {idx: idx + 1, col: this.colors[idx]}; + }; + + @Input() + set passwordToCheck(password: string) { + if (password) { + let c = this.getColor(this.measureStrength(password)); + let element = this.elementRef.nativeElement; + if ( element.className ) { + this.renderer.setElementClass(element, element.className , false); + } + let lis = element.getElementsByTagName('li'); + for (let i = 0; i < lis.length; i++) { + if (i < c.idx) { + this.renderer.setElementStyle(lis[i], 'backgroundColor', c.col); + } else { + this.renderer.setElementStyle(lis[i], 'backgroundColor', '#DDD'); + } + } + } + } +} diff --git a/jhipster/src/main/webapp/app/account/password/password-strength-bar.scss b/jhipster/src/main/webapp/app/account/password/password-strength-bar.scss new file mode 100644 index 0000000000..8f8effcb60 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password-strength-bar.scss @@ -0,0 +1,23 @@ +/* ========================================================================== +start Password strength bar style +========================================================================== */ +ul#strength { + display:inline; + list-style:none; + margin:0; + margin-left:15px; + padding:0; + vertical-align:2px; +} + +.point { + background:#DDD; + border-radius:2px; + display:inline-block; + height:5px; + margin-right:1px; + width:20px; + &:last { + margin:0 !important; + } +} diff --git a/jhipster/src/main/webapp/app/account/password/password.component.html b/jhipster/src/main/webapp/app/account/password/password.component.html new file mode 100644 index 0000000000..2534bccdfc --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password.component.html @@ -0,0 +1,65 @@ +
+
+
+

Password for [{{account.login}}]

+ +
+ Password changed! +
+
+ An error has occurred! The password could not be changed. +
+ +
+ The password and its confirmation do not match! +
+ +
+ +
+ + +
+ + Your password is required. + + + Your password is required to be at least 4 characters. + + + Your password cannot be longer than 50 characters. + +
+ +
+
+ + +
+ + Your confirmation password is required. + + + Your confirmation password is required to be at least 4 characters. + + + Your confirmation password cannot be longer than 50 characters. + +
+
+ + +
+
+
+
diff --git a/jhipster/src/main/webapp/app/account/password/password.component.ts b/jhipster/src/main/webapp/app/account/password/password.component.ts new file mode 100644 index 0000000000..f34eae423c --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password.component.ts @@ -0,0 +1,49 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Principal } from '../../shared'; +import { Password } from './password.service'; + +@Component({ + selector: 'jhi-password', + templateUrl: './password.component.html' +}) +export class PasswordComponent implements OnInit { + doNotMatch: string; + error: string; + success: string; + account: any; + password: string; + confirmPassword: string; + + constructor( + private jhiLanguageService: JhiLanguageService, + private passwordService: Password, + private principal: Principal + ) { + this.jhiLanguageService.setLocations(['password']); + } + + ngOnInit () { + this.principal.identity().then((account) => { + this.account = account; + }); + } + + changePassword () { + if (this.password !== this.confirmPassword) { + this.error = null; + this.success = null; + this.doNotMatch = 'ERROR'; + } else { + this.doNotMatch = null; + this.passwordService.save(this.password).subscribe(() => { + this.error = null; + this.success = 'OK'; + }, () => { + this.success = null; + this.error = 'ERROR'; + }); + } + } +} diff --git a/jhipster/src/main/webapp/app/account/password/password.route.ts b/jhipster/src/main/webapp/app/account/password/password.route.ts new file mode 100644 index 0000000000..d5a7118458 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { PasswordComponent } from './password.component'; + +export const passwordRoute: Route = { + path: 'password', + component: PasswordComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'global.menu.account.password' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/password/password.service.ts b/jhipster/src/main/webapp/app/account/password/password.service.ts new file mode 100644 index 0000000000..0c220d8816 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/password/password.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class Password { + + constructor (private http: Http) {} + + save(newPassword: string): Observable { + return this.http.post('api/account/change_password', newPassword); + } +} diff --git a/jhipster/src/main/webapp/app/account/register/register.component.html b/jhipster/src/main/webapp/app/account/register/register.component.html new file mode 100644 index 0000000000..14a0c851e2 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/register/register.component.html @@ -0,0 +1,122 @@ +
+
+
+

Registration

+ +
+ Registration saved! Please check your email for confirmation. +
+ +
+ Registration failed! Please try again later. +
+ +
+ Login name already registered! Please choose another one. +
+ +
+ E-mail is already in use! Please choose another one. +
+ +
+ The password and its confirmation do not match! +
+
+
+
+
+ + +
+ + Your username is required. + + + Your username is required to be at least 1 character. + + + Your username cannot be longer than 50 characters. + + + Your username can only contain lower-case letters and digits. + +
+
+
+ + +
+ + Your e-mail is required. + + + Your e-mail is invalid. + + + Your e-mail is required to be at least 5 characters. + + + Your e-mail cannot be longer than 100 characters. + +
+
+
+ + +
+ + Your password is required. + + + Your password is required to be at least 4 characters. + + + Your password cannot be longer than 50 characters. + +
+ +
+
+ + +
+ + Your confirmation password is required. + + + Your confirmation password is required to be at least 4 characters. + + + Your confirmation password cannot be longer than 50 characters. + +
+
+ + +
+

+
+ If you want to + sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and password="user").
+
+
+
+
diff --git a/jhipster/src/main/webapp/app/account/register/register.component.ts b/jhipster/src/main/webapp/app/account/register/register.component.ts new file mode 100644 index 0000000000..dc5c4ef2f4 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/register/register.component.ts @@ -0,0 +1,73 @@ +import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Register } from './register.service'; +import { LoginModalService } from '../../shared'; + +@Component({ + selector: 'jhi-register', + templateUrl: './register.component.html' +}) +export class RegisterComponent implements OnInit, AfterViewInit { + + confirmPassword: string; + doNotMatch: string; + error: string; + errorEmailExists: string; + errorUserExists: string; + registerAccount: any; + success: boolean; + modalRef: NgbModalRef; + + constructor( + private languageService: JhiLanguageService, + private loginModalService: LoginModalService, + private registerService: Register, + private elementRef: ElementRef, + private renderer: Renderer + ) { + this.languageService.setLocations(['register']); + } + + ngOnInit() { + this.success = false; + this.registerAccount = {}; + } + + ngAfterViewInit() { + this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#login'), 'focus', []); + } + + register() { + if (this.registerAccount.password !== this.confirmPassword) { + this.doNotMatch = 'ERROR'; + } else { + this.doNotMatch = null; + this.error = null; + this.errorUserExists = null; + this.errorEmailExists = null; + this.languageService.getCurrent().then(key => { + this.registerAccount.langKey = key; + this.registerService.save(this.registerAccount).subscribe(() => { + this.success = true; + }, (response) => this.processError(response)); + }); + } + } + + openLogin() { + this.modalRef = this.loginModalService.open(); + } + + private processError(response) { + this.success = null; + if (response.status === 400 && response._body === 'login already in use') { + this.errorUserExists = 'ERROR'; + } else if (response.status === 400 && response._body === 'e-mail address already in use') { + this.errorEmailExists = 'ERROR'; + } else { + this.error = 'ERROR'; + } + } +} diff --git a/jhipster/src/main/webapp/app/account/register/register.route.ts b/jhipster/src/main/webapp/app/account/register/register.route.ts new file mode 100644 index 0000000000..3dc47f1e58 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/register/register.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { RegisterComponent } from './register.component'; + +export const registerRoute: Route = { + path: 'register', + component: RegisterComponent, + data: { + authorities: [], + pageTitle: 'register.title' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/account/register/register.service.ts b/jhipster/src/main/webapp/app/account/register/register.service.ts new file mode 100644 index 0000000000..87bbe9d37b --- /dev/null +++ b/jhipster/src/main/webapp/app/account/register/register.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class Register { + + constructor (private http: Http) {} + + save(account: any): Observable { + return this.http.post('api/register', account); + } +} diff --git a/jhipster/src/main/webapp/app/account/settings/settings.component.html b/jhipster/src/main/webapp/app/account/settings/settings.component.html new file mode 100644 index 0000000000..82fca9e804 --- /dev/null +++ b/jhipster/src/main/webapp/app/account/settings/settings.component.html @@ -0,0 +1,86 @@ +
+
+
+

User settings for [{{settingsAccount.login}}]

+ +
+ Settings saved! +
+ + + +
+ +
+ + +
+ + Your first name is required. + + + Your first name is required to be at least 1 character. + + + Your first name cannot be longer than 50 characters. + +
+
+
+ + +
+ + Your last name is required. + + + Your last name is required to be at least 1 character. + + + Your last name cannot be longer than 50 characters. + +
+
+
+ + +
+ + Your e-mail is required. + + + Your e-mail is invalid. + + + Your e-mail is required to be at least 5 characters. + + + Your e-mail cannot be longer than 100 characters. + +
+
+
+ + +
+ +
+
+
+ +
diff --git a/jhipster/src/main/webapp/app/account/settings/settings.component.ts b/jhipster/src/main/webapp/app/account/settings/settings.component.ts new file mode 100644 index 0000000000..37f4c70e3b --- /dev/null +++ b/jhipster/src/main/webapp/app/account/settings/settings.component.ts @@ -0,0 +1,63 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Principal, AccountService, JhiLanguageHelper } from '../../shared'; + +@Component({ + selector: 'jhi-settings', + templateUrl: './settings.component.html' +}) +export class SettingsComponent implements OnInit { + error: string; + success: string; + settingsAccount: any; + languages: any[]; + + constructor( + private account: AccountService, + private principal: Principal, + private languageService: JhiLanguageService, + private languageHelper: JhiLanguageHelper + ) { + this.languageService.setLocations(['settings']); + } + + ngOnInit () { + this.principal.identity().then((account) => { + this.settingsAccount = this.copyAccount(account); + }); + this.languageHelper.getAll().then((languages) => { + this.languages = languages; + }); + } + + save () { + this.account.save(this.settingsAccount).subscribe(() => { + this.error = null; + this.success = 'OK'; + this.principal.identity(true).then((account) => { + this.settingsAccount = this.copyAccount(account); + }); + this.languageService.getCurrent().then((current) => { + if (this.settingsAccount.langKey !== current) { + this.languageService.changeLanguage(this.settingsAccount.langKey); + } + }); + }, () => { + this.success = null; + this.error = 'ERROR'; + }); + } + + copyAccount (account) { + return { + activated: account.activated, + email: account.email, + firstName: account.firstName, + langKey: account.langKey, + lastName: account.lastName, + login: account.login, + imageUrl: account.imageUrl + }; + } +} diff --git a/jhipster/src/main/webapp/app/account/settings/settings.route.ts b/jhipster/src/main/webapp/app/account/settings/settings.route.ts new file mode 100644 index 0000000000..9c9a852c7b --- /dev/null +++ b/jhipster/src/main/webapp/app/account/settings/settings.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { SettingsComponent } from './settings.component'; + +export const settingsRoute: Route = { + path: 'settings', + component: SettingsComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'global.menu.account.settings' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/admin/admin.module.ts b/jhipster/src/main/webapp/app/admin/admin.module.ts new file mode 100644 index 0000000000..4ac749bc78 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/admin.module.ts @@ -0,0 +1,73 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { ParseLinks } from 'ng-jhipster'; + +import { BaeldungSharedModule } from '../shared'; + +import { + adminState, + AuditsComponent, + UserMgmtComponent, + UserDialogComponent, + UserDeleteDialogComponent, + UserMgmtDetailComponent, + UserMgmtDialogComponent, + UserMgmtDeleteDialogComponent, + LogsComponent, + JhiMetricsMonitoringModalComponent, + JhiMetricsMonitoringComponent, + JhiHealthModalComponent, + JhiHealthCheckComponent, + JhiConfigurationComponent, + JhiDocsComponent, + AuditsService, + JhiConfigurationService, + JhiHealthService, + JhiMetricsService, + LogsService, + UserResolvePagingParams, + UserResolve, + UserModalService +} from './'; + + +@NgModule({ + imports: [ + BaeldungSharedModule, + RouterModule.forRoot(adminState, { useHash: true }) + ], + declarations: [ + AuditsComponent, + UserMgmtComponent, + UserDialogComponent, + UserDeleteDialogComponent, + UserMgmtDetailComponent, + UserMgmtDialogComponent, + UserMgmtDeleteDialogComponent, + LogsComponent, + JhiConfigurationComponent, + JhiHealthCheckComponent, + JhiHealthModalComponent, + JhiDocsComponent, + JhiMetricsMonitoringComponent, + JhiMetricsMonitoringModalComponent + ], + entryComponents: [ + UserMgmtDialogComponent, + UserMgmtDeleteDialogComponent, + JhiHealthModalComponent, + JhiMetricsMonitoringModalComponent, + ], + providers: [ + AuditsService, + JhiConfigurationService, + JhiHealthService, + JhiMetricsService, + LogsService, + UserResolvePagingParams, + UserResolve, + UserModalService + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungAdminModule {} diff --git a/jhipster/src/main/webapp/app/admin/admin.route.ts b/jhipster/src/main/webapp/app/admin/admin.route.ts new file mode 100644 index 0000000000..8e64d2d5c0 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/admin.route.ts @@ -0,0 +1,36 @@ +import { Routes, CanActivate } from '@angular/router'; + +import { + auditsRoute, + configurationRoute, + docsRoute, + healthRoute, + logsRoute, + metricsRoute, + userMgmtRoute, + userDialogRoute +} from './'; + +import { UserRouteAccessService } from '../shared'; + +let ADMIN_ROUTES = [ + auditsRoute, + configurationRoute, + docsRoute, + healthRoute, + logsRoute, + ...userMgmtRoute, + metricsRoute +]; + + +export const adminState: Routes = [{ + path: '', + data: { + authorities: ['ROLE_ADMIN'] + }, + canActivate: [UserRouteAccessService], + children: ADMIN_ROUTES +}, + ...userDialogRoute +]; diff --git a/jhipster/src/main/webapp/app/admin/audits/audit-data.model.ts b/jhipster/src/main/webapp/app/admin/audits/audit-data.model.ts new file mode 100644 index 0000000000..55a5e9c819 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audit-data.model.ts @@ -0,0 +1,6 @@ +export class AuditData { + constructor( + public remoteAddress: string, + public sessionId: string + ) { } +} diff --git a/jhipster/src/main/webapp/app/admin/audits/audit.model.ts b/jhipster/src/main/webapp/app/admin/audits/audit.model.ts new file mode 100644 index 0000000000..9ca753795c --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audit.model.ts @@ -0,0 +1,10 @@ +import { AuditData } from './audit-data.model'; + +export class Audit { + constructor( + public data: AuditData, + public principal: string, + public timestamp: string, + public type: string + ) { } +} diff --git a/jhipster/src/main/webapp/app/admin/audits/audits.component.html b/jhipster/src/main/webapp/app/admin/audits/audits.component.html new file mode 100644 index 0000000000..9fcbaf7ecd --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audits.component.html @@ -0,0 +1,45 @@ +
+

Audits

+ +
+
+

Filter by date

+

+ from + + to + +

+
+
+ +
+ + + + + + + + + + + + + + + +
DateUserStateExtra data
{{audit.timestamp| date:'medium'}}{{audit.principal}}{{audit.type}} + {{audit.data.message}} + Remote Address {{audit.data.remoteAddress}} +
+
+
+
+ +
+
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/admin/audits/audits.component.ts b/jhipster/src/main/webapp/app/admin/audits/audits.component.ts new file mode 100644 index 0000000000..84e6b40fe8 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audits.component.ts @@ -0,0 +1,99 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { ParseLinks, JhiLanguageService} from 'ng-jhipster'; + +import { Audit } from './audit.model'; +import { AuditsService } from './audits.service'; +import { ITEMS_PER_PAGE } from '../../shared'; +import { PaginationConfig } from '../../blocks/config/uib-pagination.config'; + +@Component({ + selector: 'jhi-audit', + templateUrl: './audits.component.html' +}) +export class AuditsComponent implements OnInit { + audits: Audit[]; + fromDate: string; + itemsPerPage: any; + links: any; + page: number; + orderProp: string; + reverse: boolean; + toDate: string; + totalItems: number; + + constructor( + private jhiLanguageService: JhiLanguageService, + private auditsService: AuditsService, + private parseLinks: ParseLinks, + private paginationConfig: PaginationConfig, + private datePipe: DatePipe + ) { + this.jhiLanguageService.setLocations(['audits']); + this.itemsPerPage = ITEMS_PER_PAGE; + this.page = 1; + this.reverse = false; + this.orderProp = 'timestamp'; + } + + getAudits() { + return this.sortAudits(this.audits); + } + + loadPage(page: number) { + this.page = page; + this.onChangeDate(); + } + + ngOnInit() { + this.today(); + this.previousMonth(); + this.onChangeDate(); + } + + onChangeDate() { + this.auditsService.query({page: this.page - 1, size: this.itemsPerPage, + fromDate: this.fromDate, toDate: this.toDate}).subscribe(res => { + + this.audits = res.json(); + this.links = this.parseLinks.parse(res.headers.get('link')); + this.totalItems = + res.headers.get('X-Total-Count'); + }); + } + + previousMonth() { + let dateFormat = 'yyyy-MM-dd'; + let fromDate: Date = new Date(); + + if (fromDate.getMonth() === 0) { + fromDate = new Date(fromDate.getFullYear() - 1, 11, fromDate.getDate()); + } else { + fromDate = new Date(fromDate.getFullYear(), fromDate.getMonth() - 1, fromDate.getDate()); + } + + this.fromDate = this.datePipe.transform(fromDate, dateFormat); + } + + today() { + let dateFormat = 'yyyy-MM-dd'; + // Today + 1 day - needed if the current day must be included + let today: Date = new Date(); + + let date = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1); + this.toDate = this.datePipe.transform(date, dateFormat); + } + + private sortAudits(audits: Audit[]) { + audits = audits.slice(0).sort((a, b) => { + if (a[this.orderProp] < b[this.orderProp]) { + return -1; + } else if ([b[this.orderProp] < a[this.orderProp]]) { + return 1; + } else { + return 0; + } + }); + + return this.reverse ? audits.reverse() : audits; + } +} diff --git a/jhipster/src/main/webapp/app/admin/audits/audits.route.ts b/jhipster/src/main/webapp/app/admin/audits/audits.route.ts new file mode 100644 index 0000000000..f0e0fa7be9 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audits.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { AuditsComponent } from './audits.component'; + +export const auditsRoute: Route = { + path: 'audits', + component: AuditsComponent, + data: { + pageTitle: 'audits.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/audits/audits.service.ts b/jhipster/src/main/webapp/app/admin/audits/audits.service.ts new file mode 100644 index 0000000000..b373b8915c --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/audits/audits.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class AuditsService { + constructor(private http: Http) { } + + query(req: any): Observable { + let params: URLSearchParams = new URLSearchParams(); + params.set('fromDate', req.fromDate); + params.set('toDate', req.toDate); + params.set('page', req.page); + params.set('size', req.size); + params.set('sort', req.sort); + + let options = { + search: params + }; + + return this.http.get('management/audits', options); + } +} diff --git a/jhipster/src/main/webapp/app/admin/configuration/configuration.component.html b/jhipster/src/main/webapp/app/admin/configuration/configuration.component.html new file mode 100644 index 0000000000..1d64b41c29 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/configuration/configuration.component.html @@ -0,0 +1,46 @@ +
+

Configuration

+ + Filter (by prefix) + + + + + + + + + + + + + + +
PrefixProperties
{{entry.prefix}} +
+
{{key}}
+
+ {{entry.properties[key]}} +
+
+
+
+ + + + + + + + + + + + + + +
PropertyValue
{{item.key}} + {{item.val}} +
+
+
diff --git a/jhipster/src/main/webapp/app/admin/configuration/configuration.component.ts b/jhipster/src/main/webapp/app/admin/configuration/configuration.component.ts new file mode 100644 index 0000000000..88fbc32250 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/configuration/configuration.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { JhiConfigurationService } from './configuration.service'; + +@Component({ + selector: 'jhi-configuration', + templateUrl: './configuration.component.html' +}) +export class JhiConfigurationComponent implements OnInit { + allConfiguration: any = null; + configuration: any = null; + configKeys: any[]; + filter: string; + orderProp: string; + reverse: boolean; + + constructor( + private jhiLanguageService: JhiLanguageService, + private configurationService: JhiConfigurationService + ) { + this.jhiLanguageService.setLocations(['configuration']); + this.configKeys = []; + this.filter = ''; + this.orderProp = 'prefix'; + this.reverse = false; + } + + keys(dict): Array { + return (dict === undefined) ? [] : Object.keys(dict); + } + + ngOnInit() { + this.configurationService.get().subscribe((configuration) => { + this.configuration = configuration; + + for (let config of configuration) { + if (config.properties !== undefined) { + this.configKeys.push(Object.keys(config.properties)); + } + } + }); + + this.configurationService.getEnv().subscribe((configuration) => { + this.allConfiguration = configuration; + }); + } +} diff --git a/jhipster/src/main/webapp/app/admin/configuration/configuration.route.ts b/jhipster/src/main/webapp/app/admin/configuration/configuration.route.ts new file mode 100644 index 0000000000..06866237b0 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/configuration/configuration.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { JhiConfigurationComponent } from './configuration.component'; + +export const configurationRoute: Route = { + path: 'jhi-configuration', + component: JhiConfigurationComponent, + data: { + pageTitle: 'configuration.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/configuration/configuration.service.ts b/jhipster/src/main/webapp/app/admin/configuration/configuration.service.ts new file mode 100644 index 0000000000..e239ed3097 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/configuration/configuration.service.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class JhiConfigurationService { + + constructor(private http: Http) { + } + + get(): Observable { + return this.http.get('management/configprops').map((res: Response) => { + let properties: any[] = []; + + const propertiesObject = res.json(); + + for (let key in propertiesObject) { + if (propertiesObject.hasOwnProperty(key)) { + properties.push(propertiesObject[key]); + } + } + + return properties.sort((propertyA, propertyB) => { + return (propertyA.prefix === propertyB.prefix) ? 0 : + (propertyA.prefix < propertyB.prefix) ? -1 : 1; + }); + }); + } + + getEnv(): Observable { + return this.http.get('management/env').map((res: Response) => { + let properties: any = {}; + + const propertiesObject = res.json(); + + for (let key in propertiesObject) { + if (propertiesObject.hasOwnProperty(key)) { + let valsObject = propertiesObject[key]; + let vals: any[] = []; + + for (let valKey in valsObject) { + if (valsObject.hasOwnProperty(valKey)) { + vals.push({key: valKey, val: valsObject[valKey]}); + } + } + properties[key] = vals; + } + } + + return properties; + }); + } +} diff --git a/jhipster/src/main/webapp/app/admin/docs/docs.component.html b/jhipster/src/main/webapp/app/admin/docs/docs.component.html new file mode 100644 index 0000000000..30efbbb93e --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/docs/docs.component.html @@ -0,0 +1,2 @@ + diff --git a/jhipster/src/main/webapp/app/admin/docs/docs.component.ts b/jhipster/src/main/webapp/app/admin/docs/docs.component.ts new file mode 100644 index 0000000000..4bd5bf2329 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/docs/docs.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +@Component({ + selector: 'jhi-docs', + templateUrl: './docs.component.html' +}) +export class JhiDocsComponent { + constructor ( + private jhiLanguageService: JhiLanguageService + ) { + this.jhiLanguageService.setLocations(['global']); + } +} diff --git a/jhipster/src/main/webapp/app/admin/docs/docs.route.ts b/jhipster/src/main/webapp/app/admin/docs/docs.route.ts new file mode 100644 index 0000000000..6e99618f1d --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/docs/docs.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { JhiDocsComponent } from './docs.component'; + +export const docsRoute: Route = { + path: 'docs', + component: JhiDocsComponent, + data: { + pageTitle: 'global.menu.admin.apidocs' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/health/health-modal.component.html b/jhipster/src/main/webapp/app/admin/health/health-modal.component.html new file mode 100644 index 0000000000..1be6271f3b --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health-modal.component.html @@ -0,0 +1,36 @@ + + + diff --git a/jhipster/src/main/webapp/app/admin/health/health-modal.component.ts b/jhipster/src/main/webapp/app/admin/health/health-modal.component.ts new file mode 100644 index 0000000000..1486f20dbf --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health-modal.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { JhiHealthService } from './health.service'; + +@Component({ + selector: 'jhi-health-modal', + templateUrl: './health-modal.component.html' +}) +export class JhiHealthModalComponent { + + currentHealth: any; + + constructor(private healthService: JhiHealthService, public activeModal: NgbActiveModal) {} + + baseName(name) { + return this.healthService.getBaseName(name); + } + + subSystemName(name) { + return this.healthService.getSubSystemName(name); + } + + readableValue(value: number) { + if (this.currentHealth.name !== 'diskSpace') { + return value.toString(); + } + + // Should display storage space in an human readable unit + let val = value / 1073741824; + if (val > 1) { // Value + return val.toFixed(2) + ' GB'; + } else { + return (value / 1048576).toFixed(2) + ' MB'; + } + } +} diff --git a/jhipster/src/main/webapp/app/admin/health/health.component.html b/jhipster/src/main/webapp/app/admin/health/health.component.html new file mode 100644 index 0000000000..6fea72af96 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health.component.html @@ -0,0 +1,34 @@ +
+

+ Health Checks + +

+
+ + + + + + + + + + + + + + + +
Service NameStatusDetails
{{'health.indicator.' + baseName(health.name) | translate}} {{subSystemName(health.name)}} + + {{health.status}} + + + + + +
+
+
diff --git a/jhipster/src/main/webapp/app/admin/health/health.component.ts b/jhipster/src/main/webapp/app/admin/health/health.component.ts new file mode 100644 index 0000000000..ab1be4271f --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health.component.ts @@ -0,0 +1,69 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { JhiHealthService } from './health.service'; +import { JhiHealthModalComponent } from './health-modal.component'; + +@Component({ + selector: 'jhi-health', + templateUrl: './health.component.html', +}) +export class JhiHealthCheckComponent implements OnInit { + healthData: any; + updatingHealth: boolean; + + constructor( + private jhiLanguageService: JhiLanguageService, + private modalService: NgbModal, + private healthService: JhiHealthService + ) { + this.jhiLanguageService.setLocations(['health']); + + } + + ngOnInit() { + this.refresh(); + } + + baseName(name: string) { + return this.healthService.getBaseName(name); + } + + getBadgeClass(statusState) { + if (statusState === 'UP') { + return 'badge-success'; + } else { + return 'badge-danger'; + } + } + + refresh() { + this.updatingHealth = true; + + this.healthService.checkHealth().subscribe(health => { + this.healthData = this.healthService.transformHealthData(health); + this.updatingHealth = false; + }, error => { + if (error.status === 503) { + this.healthData = this.healthService.transformHealthData(error.json()); + this.updatingHealth = false; + } + }); + } + + showHealth(health: any) { + const modalRef = this.modalService.open(JhiHealthModalComponent); + modalRef.componentInstance.currentHealth = health; + modalRef.result.then((result) => { + // Left blank intentionally, nothing to do here + }, (reason) => { + // Left blank intentionally, nothing to do here + }); + } + + subSystemName(name: string) { + return this.healthService.getSubSystemName(name); + } + +} diff --git a/jhipster/src/main/webapp/app/admin/health/health.route.ts b/jhipster/src/main/webapp/app/admin/health/health.route.ts new file mode 100644 index 0000000000..b47d07a0c0 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { JhiHealthCheckComponent } from './health.component'; + +export const healthRoute: Route = { + path: 'jhi-health', + component: JhiHealthCheckComponent, + data: { + pageTitle: 'health.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/health/health.service.ts b/jhipster/src/main/webapp/app/admin/health/health.service.ts new file mode 100644 index 0000000000..1bf00dab08 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/health/health.service.ts @@ -0,0 +1,140 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class JhiHealthService { + + separator: string; + + constructor (private http: Http) { + this.separator = '.'; + } + + checkHealth(): Observable { + return this.http.get('management/health').map((res: Response) => res.json()); + } + + transformHealthData(data): any { + let response = []; + this.flattenHealthData(response, null, data); + return response; + } + + getBaseName(name): string { + if (name) { + let split = name.split('.'); + return split[0]; + } + } + + getSubSystemName(name): string { + if (name) { + let split = name.split('.'); + split.splice(0, 1); + let remainder = split.join('.'); + return remainder ? ' - ' + remainder : ''; + } + } + + /* private methods */ + private addHealthObject(result, isLeaf, healthObject, name): any { + + let status: any; + let error: any; + let healthData: any = { + 'name': name, + 'error': error, + 'status': status + }; + + let details = {}; + let hasDetails = false; + + for (let key in healthObject) { + if (healthObject.hasOwnProperty(key)) { + let value = healthObject[key]; + if (key === 'status' || key === 'error') { + healthData[key] = value; + } else { + if (!this.isHealthObject(value)) { + details[key] = value; + hasDetails = true; + } + } + } + } + + // Add the details + if (hasDetails) { + healthData.details = details; + } + + // Only add nodes if they provide additional information + if (isLeaf || hasDetails || healthData.error) { + result.push(healthData); + } + return healthData; + } + + private flattenHealthData (result, path, data): any { + for (let key in data) { + if (data.hasOwnProperty(key)) { + let value = data[key]; + if (this.isHealthObject(value)) { + if (this.hasSubSystem(value)) { + this.addHealthObject(result, false, value, this.getModuleName(path, key)); + this.flattenHealthData(result, this.getModuleName(path, key), value); + } else { + this.addHealthObject(result, true, value, this.getModuleName(path, key)); + } + } + } + } + + return result; + } + + private getModuleName (path, name): string { + let result; + if (path && name) { + result = path + this.separator + name; + } else if (path) { + result = path; + } else if (name) { + result = name; + } else { + result = ''; + } + return result; + } + + private hasSubSystem (healthObject): boolean { + let result = false; + + for (let key in healthObject) { + if (healthObject.hasOwnProperty(key)) { + let value = healthObject[key]; + if (value && value.status) { + result = true; + } + } + } + + return result; + } + + private isHealthObject (healthObject): boolean { + let result = false; + + for (let key in healthObject) { + if (healthObject.hasOwnProperty(key)) { + if (key === 'status') { + result = true; + } + } + } + + return result; + } +} diff --git a/jhipster/src/main/webapp/app/admin/index.ts b/jhipster/src/main/webapp/app/admin/index.ts new file mode 100644 index 0000000000..2273c8af12 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/index.ts @@ -0,0 +1,29 @@ +export * from './audits/audits.component'; +export * from './audits/audits.service'; +export * from './audits/audits.route'; +export * from './audits/audit.model'; +export * from './audits/audit-data.model'; +export * from './configuration/configuration.component'; +export * from './configuration/configuration.service'; +export * from './configuration/configuration.route'; +export * from './docs/docs.component'; +export * from './docs/docs.route'; +export * from './health/health.component'; +export * from './health/health-modal.component'; +export * from './health/health.service'; +export * from './health/health.route'; +export * from './logs/logs.component'; +export * from './logs/logs.service'; +export * from './logs/logs.route'; +export * from './logs/log.model'; +export * from './metrics/metrics.component'; +export * from './metrics/metrics-modal.component'; +export * from './metrics/metrics.service'; +export * from './metrics/metrics.route'; +export * from './user-management/user-management-dialog.component'; +export * from './user-management/user-management-delete-dialog.component'; +export * from './user-management/user-management-detail.component'; +export * from './user-management/user-management.component'; +export * from './user-management/user-management.route'; +export * from './user-management/user-modal.service'; +export * from './admin.route'; diff --git a/jhipster/src/main/webapp/app/admin/logs/log.model.ts b/jhipster/src/main/webapp/app/admin/logs/log.model.ts new file mode 100644 index 0000000000..79cbd34808 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/log.model.ts @@ -0,0 +1,6 @@ +export class Log { + constructor( + public name: string, + public level: string + ) { } +} diff --git a/jhipster/src/main/webapp/app/admin/logs/logs.component.html b/jhipster/src/main/webapp/app/admin/logs/logs.component.html new file mode 100644 index 0000000000..242ef5563a --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/logs.component.html @@ -0,0 +1,27 @@ +
+

Logs

+ +

There are {{ loggers.length }} loggers.

+ + Filter + + + + + + + + + + + + + +
NameLevel
{{logger.name | slice:0:140}} + + + + + +
+
diff --git a/jhipster/src/main/webapp/app/admin/logs/logs.component.ts b/jhipster/src/main/webapp/app/admin/logs/logs.component.ts new file mode 100644 index 0000000000..7655749018 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/logs.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Log } from './log.model'; +import { LogsService } from './logs.service'; + +@Component({ + selector: 'jhi-logs', + templateUrl: './logs.component.html', +}) +export class LogsComponent implements OnInit { + + loggers: Log[]; + filter: string; + orderProp: string; + reverse: boolean; + + constructor ( + private jhiLanguageService: JhiLanguageService, + private logsService: LogsService + ) { + this.filter = ''; + this.orderProp = 'name'; + this.reverse = false; + this.jhiLanguageService.setLocations(['logs']); + } + + ngOnInit() { + this.logsService.findAll().subscribe(loggers => this.loggers = loggers); + } + + changeLevel (name: string, level: string) { + let log = new Log(name, level); + this.logsService.changeLevel(log).subscribe(() => { + this.logsService.findAll().subscribe(loggers => this.loggers = loggers); + }); + } +} diff --git a/jhipster/src/main/webapp/app/admin/logs/logs.route.ts b/jhipster/src/main/webapp/app/admin/logs/logs.route.ts new file mode 100644 index 0000000000..1077f5da32 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/logs.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { LogsComponent } from './logs.component'; + +export const logsRoute: Route = { + path: 'logs', + component: LogsComponent, + data: { + pageTitle: 'logs.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/logs/logs.service.ts b/jhipster/src/main/webapp/app/admin/logs/logs.service.ts new file mode 100644 index 0000000000..c937c10a42 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/logs/logs.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { Log } from './log.model'; + +@Injectable() +export class LogsService { + constructor(private http: Http) { } + + changeLevel(log: Log): Observable { + return this.http.put('management/logs', log); + } + + findAll(): Observable { + return this.http.get('management/logs').map((res: Response) => res.json()); + } +} diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.html b/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.html new file mode 100644 index 0000000000..b8f0391acf --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.html @@ -0,0 +1,56 @@ + + + + diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.ts b/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.ts new file mode 100644 index 0000000000..4a3034dd94 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics-modal.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'jhi-metrics-modal', + templateUrl: './metrics-modal.component.html' +}) +export class JhiMetricsMonitoringModalComponent implements OnInit { + + threadDumpFilter: any; + threadDump: any; + threadDumpAll = 0; + threadDumpBlocked = 0; + threadDumpRunnable = 0; + threadDumpTimedWaiting = 0; + threadDumpWaiting = 0; + + constructor(public activeModal: NgbActiveModal) {} + + ngOnInit() { + this.threadDump.forEach((value) => { + if (value.threadState === 'RUNNABLE') { + this.threadDumpRunnable += 1; + } else if (value.threadState === 'WAITING') { + this.threadDumpWaiting += 1; + } else if (value.threadState === 'TIMED_WAITING') { + this.threadDumpTimedWaiting += 1; + } else if (value.threadState === 'BLOCKED') { + this.threadDumpBlocked += 1; + } + }); + + this.threadDumpAll = this.threadDumpRunnable + this.threadDumpWaiting + + this.threadDumpTimedWaiting + this.threadDumpBlocked; + } + + getBadgeClass (threadState) { + if (threadState === 'RUNNABLE') { + return 'badge-success'; + } else if (threadState === 'WAITING') { + return 'badge-info'; + } else if (threadState === 'TIMED_WAITING') { + return 'badge-warning'; + } else if (threadState === 'BLOCKED') { + return 'badge-danger'; + } + } +} diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics.component.html b/jhipster/src/main/webapp/app/admin/metrics/metrics.component.html new file mode 100644 index 0000000000..800ca21ef9 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics.component.html @@ -0,0 +1,253 @@ +
+

+ Application Metrics + +

+ +

JVM Metrics

+
+
+ Memory +

Total Memory ({{metrics.gauges['jvm.memory.total.used'].value / 1000000 | number:'1.0-0'}}M / {{metrics.gauges['jvm.memory.total.max'].value / 1000000 | number:'1.0-0'}}M)

+ + {{metrics.gauges['jvm.memory.total.used'].value * 100 / metrics.gauges['jvm.memory.total.max'].value | number:'1.0-0'}}% + +

Heap Memory ({{metrics.gauges['jvm.memory.heap.used'].value / 1000000 | number:'1.0-0'}}M / {{metrics.gauges['jvm.memory.heap.max'].value / 1000000 | number:'1.0-0'}}M)

+ + {{metrics.gauges['jvm.memory.heap.used'].value * 100 / metrics.gauges['jvm.memory.heap.max'].value | number:'1.0-0'}}% + +

Non-Heap Memory ({{metrics.gauges['jvm.memory.non-heap.used'].value / 1000000 | number:'1.0-0'}}M / {{metrics.gauges['jvm.memory.non-heap.committed'].value / 1000000 | number:'1.0-0'}}M)

+ + {{metrics.gauges['jvm.memory.non-heap.used'].value * 100 / metrics.gauges['jvm.memory.non-heap.committed'].value | number:'1.0-0'}}% + +
+
+ Threads (Total: {{metrics.gauges['jvm.threads.count'].value}}) +

Runnable {{metrics.gauges['jvm.threads.runnable.count'].value}}

+ + {{metrics.gauges['jvm.threads.runnable.count'].value * 100 / metrics.gauges['jvm.threads.count'].value | number:'1.0-0'}}% + +

Timed Waiting ({{metrics.gauges['jvm.threads.timed_waiting.count'].value}})

+ + {{metrics.gauges['jvm.threads.timed_waiting.count'].value * 100 / metrics.gauges['jvm.threads.count'].value | number:'1.0-0'}}% + +

Waiting ({{metrics.gauges['jvm.threads.waiting.count'].value}})

+ + {{metrics.gauges['jvm.threads.waiting.count'].value * 100 / metrics.gauges['jvm.threads.count'].value | number:'1.0-0'}}% + +

Blocked ({{metrics.gauges['jvm.threads.blocked.count'].value}})

+ + {{metrics.gauges['jvm.threads.blocked.count'].value * 100 / metrics.gauges['jvm.threads.count'].value | number:'1.0-0'}}% + +
+
+ Garbage collections +
+
Mark Sweep count
+
{{metrics.gauges['jvm.garbage.PS-MarkSweep.count'].value}}
+
+
+
Mark Sweep time
+
{{metrics.gauges['jvm.garbage.PS-MarkSweep.time'].value}}ms
+
+
+
Scavenge count
+
{{metrics.gauges['jvm.garbage.PS-Scavenge.count'].value}}
+
+
+
Scavenge time
+
{{metrics.gauges['jvm.garbage.PS-Scavenge.time'].value}}ms
+
+
+
+
Updating...
+ +

HTTP requests (events per second)

+

+ Active requests {{metrics.counters['com.codahale.metrics.servlet.InstrumentedFilter.activeRequests'].count | number:'1.0-0'}} - Total requests {{metrics.timers['com.codahale.metrics.servlet.InstrumentedFilter.requests'].count | number:'1.0-0'}} +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeCountMeanAverage (1 min)Average (5 min)Average (15 min)
OK + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].count}} + + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].mean_rate | number:'1.0-2'}} + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].m1_rate | number:'1.0-2'}} + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].m5_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.ok'].m15_rate | number:'1.0-2'}} +
Not Found + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].count}} + + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].mean_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].m1_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].m5_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.notFound'].m15_rate | number:'1.0-2'}} +
Server error + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].count}} + + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].mean_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].m1_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].m5_rate | number:'1.0-2'}} + + {{metrics.meters['com.codahale.metrics.servlet.InstrumentedFilter.responseCodes.serverError'].m15_rate | number:'1.0-2'}} +
+
+ +

Services statistics (time in millisecond)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Service nameCountMeanMinp50p75p95p99Max
{{entry.key}}{{entry.value.count}}{{entry.value.mean * 1000 | number:'1.0-0'}}{{entry.value.min * 1000 | number:'1.0-0'}}{{entry.value.p50 * 1000 | number:'1.0-0'}}{{entry.value.p75 * 1000 | number:'1.0-0'}}{{entry.value.p95 * 1000 | number:'1.0-0'}}{{entry.value.p99 * 1000 | number:'1.0-0'}}{{entry.value.max * 1000 | number:'1.0-0'}}
+
+ +

Cache statistics

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cache nameCache HitsCache MissesCache GetsCache PutsCache RemovalsCache EvictionsCache Hit %Cache Miss %Average get time (µs)Average put time (µs)Average remove time (µs)
{{entry.key}}{{metrics.gauges[entry.key + '.cache-hits'].value}}{{metrics.gauges[entry.key + '.cache-misses'].value}}{{metrics.gauges[entry.key + '.cache-gets'].value}}{{metrics.gauges[entry.key + '.cache-puts'].value}}{{metrics.gauges[entry.key + '.cache-removals'].value}}{{metrics.gauges[entry.key + '.cache-evictions'].value}}{{metrics.gauges[entry.key + '.cache-hit-percentage'].value}}{{metrics.gauges[entry.key + '.cache-miss-percentage'].value }}{{metrics.gauges[entry.key + '.average-get-time'].value | number : '1.2-2' }}{{metrics.gauges[entry.key + '.average-put-time'].value | number : '1.2-2'}}{{metrics.gauges[entry.key + '.average-remove-time'].value | number : '1.2-2' }}
+
+ +

DataSource statistics (time in millisecond)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Usage ({{metrics.gauges['HikariPool-1.pool.ActiveConnections'].value}} / {{metrics.gauges['HikariPool-1.pool.TotalConnections'].value}})CountMeanMinp50p75p95p99Max
+
+ + {{metrics.gauges['HikariPool-1.pool.ActiveConnections'].value * 100 / metrics.gauges['HikariPool-1.pool.TotalConnections'].value | number:'1.0-0'}}% + +
+
{{metrics.histograms['HikariPool-1.pool.Usage'].count}}{{metrics.histograms['HikariPool-1.pool.Usage'].mean | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].min | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].p50 | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].p75 | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].p95 | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].p99 | number:'1.0-2'}}{{metrics.histograms['HikariPool-1.pool.Usage'].max | number:'1.0-2'}}
+
+
diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics.component.ts b/jhipster/src/main/webapp/app/admin/metrics/metrics.component.ts new file mode 100644 index 0000000000..21c39a3deb --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics.component.ts @@ -0,0 +1,74 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { JhiMetricsMonitoringModalComponent } from './metrics-modal.component'; +import { JhiMetricsService } from './metrics.service'; + +@Component({ + selector: 'jhi-metrics', + templateUrl: './metrics.component.html', +}) +export class JhiMetricsMonitoringComponent implements OnInit { + metrics: any = {}; + cachesStats: any = {}; + servicesStats: any = {}; + updatingMetrics = true; + JCACHE_KEY: string ; + + constructor( + private jhiLanguageService: JhiLanguageService, + private modalService: NgbModal, + private metricsService: JhiMetricsService + ) { + this.JCACHE_KEY = 'jcache.statistics'; + this.jhiLanguageService.setLocations(['metrics']); + } + + ngOnInit() { + this.refresh(); + } + + refresh () { + this.updatingMetrics = true; + this.metricsService.getMetrics().subscribe((metrics) => { + this.metrics = metrics; + this.updatingMetrics = false; + this.servicesStats = {}; + this.cachesStats = {}; + Object.keys(metrics.timers).forEach((key) => { + let value = metrics.timers[key]; + if (key.indexOf('web.rest') !== -1 || key.indexOf('service') !== -1) { + this.servicesStats[key] = value; + } + }); + Object.keys(metrics.gauges).forEach((key) => { + if (key.indexOf('jcache.statistics') !== -1) { + let value = metrics.gauges[key].value; + // remove gets or puts + let index = key.lastIndexOf('.'); + let newKey = key.substr(0, index); + + // Keep the name of the domain + this.cachesStats[newKey] = { + 'name': this.JCACHE_KEY.length, + 'value': value + }; + } + }); + }); + } + + refreshThreadDumpData () { + this.metricsService.threadDump().subscribe((data) => { + const modalRef = this.modalService.open(JhiMetricsMonitoringModalComponent, { size: 'lg'}); + modalRef.componentInstance.threadDump = data; + modalRef.result.then((result) => { + // Left blank intentionally, nothing to do here + }, (reason) => { + // Left blank intentionally, nothing to do here + }); + }); + } + +} diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics.route.ts b/jhipster/src/main/webapp/app/admin/metrics/metrics.route.ts new file mode 100644 index 0000000000..cc13284dc8 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics.route.ts @@ -0,0 +1,12 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { JhiMetricsMonitoringComponent } from './metrics.component'; + +export const metricsRoute: Route = { + path: 'jhi-metrics', + component: JhiMetricsMonitoringComponent, + data: { + pageTitle: 'metrics.title' + } +}; diff --git a/jhipster/src/main/webapp/app/admin/metrics/metrics.service.ts b/jhipster/src/main/webapp/app/admin/metrics/metrics.service.ts new file mode 100644 index 0000000000..2b31947339 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/metrics/metrics.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class JhiMetricsService { + + constructor (private http: Http) {} + + getMetrics(): Observable { + return this.http.get('management/metrics').map((res: Response) => res.json()); + } + + threadDump(): Observable { + return this.http.get('management/dump').map((res: Response) => res.json()); + } +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html b/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html new file mode 100644 index 0000000000..452f1612c8 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.html @@ -0,0 +1,19 @@ +
+ + + +
diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts new file mode 100644 index 0000000000..2c5c2a6a01 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-delete-dialog.component.ts @@ -0,0 +1,63 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { User, UserService } from '../../shared'; +import { UserModalService } from './user-modal.service'; + +@Component({ + selector: 'jhi-user-mgmt-delete-dialog', + templateUrl: './user-management-delete-dialog.component.html' +}) +export class UserMgmtDeleteDialogComponent { + + user: User; + + constructor( + private jhiLanguageService: JhiLanguageService, + private userService: UserService, + public activeModal: NgbActiveModal, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['user-management']); + } + + clear () { + this.activeModal.dismiss('cancel'); + } + + confirmDelete (login) { + this.userService.delete(login).subscribe(response => { + this.eventManager.broadcast({ name: 'userListModification', + content: 'Deleted a user'}); + this.activeModal.dismiss(true); + }); + } + +} + +@Component({ + selector: 'jhi-user-delete-dialog', + template: '' +}) +export class UserDeleteDialogComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private userModalService: UserModalService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + this.modalRef = this.userModalService.open(UserMgmtDeleteDialogComponent, params['login']); + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.html b/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.html new file mode 100644 index 0000000000..d5deb8fa24 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.html @@ -0,0 +1,44 @@ +
+

+ User [{{user.login}}] +

+
+
Login
+
+ {{user.login}} + Deactivated + Activated +
+
First Name
+
{{user.firstName}}
+
Last Name
+
{{user.lastName}}
+
Email
+
{{user.email}}
+
Lang Key
+
{{user.langKey}}
+
Created By
+
{{user.createdBy}}
+
Created Date
+
{{user.createdDate | date:'dd/MM/yy HH:mm' }}
+
Last Modified By
+
{{user.lastModifiedBy}}
+
Last Modified Date
+
{{user.lastModifiedDate | date:'dd/MM/yy HH:mm'}}
+
Profiles
+
+
    +
  • + {{authority}} +
  • +
+
+
+ +
diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.ts new file mode 100644 index 0000000000..564b1daf3d --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-detail.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { User, UserService } from '../../shared'; + +@Component({ + selector: 'jhi-user-mgmt-detail', + templateUrl: './user-management-detail.component.html' +}) +export class UserMgmtDetailComponent implements OnInit, OnDestroy { + + user: User; + private subscription: any; + + constructor( + private jhiLanguageService: JhiLanguageService, + private userService: UserService, + private route: ActivatedRoute + ) { + this.jhiLanguageService.setLocations(['user-management']); + } + + ngOnInit() { + this.subscription = this.route.params.subscribe(params => { + this.load(params['login']); + }); + } + + load (login) { + this.userService.find(login).subscribe(user => { + this.user = user; + }); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } + +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.html b/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.html new file mode 100644 index 0000000000..6359527df7 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.html @@ -0,0 +1,112 @@ +
+ + + + +
diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.ts new file mode 100644 index 0000000000..ebbf073df4 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management-dialog.component.ts @@ -0,0 +1,89 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { UserModalService } from './user-modal.service'; +import { JhiLanguageHelper, User, UserService } from '../../shared'; + +@Component({ + selector: 'jhi-user-mgmt-dialog', + templateUrl: './user-management-dialog.component.html' +}) +export class UserMgmtDialogComponent implements OnInit { + + user: User; + languages: any[]; + authorities: any[]; + isSaving: Boolean; + + constructor ( + public activeModal: NgbActiveModal, + private languageHelper: JhiLanguageHelper, + private jhiLanguageService: JhiLanguageService, + private userService: UserService, + private eventManager: EventManager + ) {} + + ngOnInit() { + this.isSaving = false; + this.authorities = ['ROLE_USER', 'ROLE_ADMIN']; + this.languageHelper.getAll().then((languages) => { + this.languages = languages; + }); + this.jhiLanguageService.setLocations(['user-management']); + } + + clear() { + this.activeModal.dismiss('cancel'); + } + + save() { + this.isSaving = true; + if (this.user.id !== null) { + this.userService.update(this.user).subscribe(response => this.onSaveSuccess(response), () => this.onSaveError()); + } else { + this.userService.create(this.user).subscribe(response => this.onSaveSuccess(response), () => this.onSaveError()); + } + } + + private onSaveSuccess(result) { + this.eventManager.broadcast({ name: 'userListModification', content: 'OK' }); + this.isSaving = false; + this.activeModal.dismiss(result); + } + + private onSaveError() { + this.isSaving = false; + } +} + +@Component({ + selector: 'jhi-user-dialog', + template: '' +}) +export class UserDialogComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private userModalService: UserModalService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + if ( params['login'] ) { + this.modalRef = this.userModalService.open(UserMgmtDialogComponent, params['login']); + } else { + this.modalRef = this.userModalService.open(UserMgmtDialogComponent); + } + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management.component.html b/jhipster/src/main/webapp/app/admin/user-management/user-management.component.html new file mode 100644 index 0000000000..73fc18469a --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management.component.html @@ -0,0 +1,81 @@ +
+

+ Users + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDLogin Email Lang Key ProfilesCreated Date Last Modified By Last Modified Date
{{user.id}}{{user.login}}{{user.email}} + Deactivated + Activated + {{user.langKey}} +
+ {{ authority }} +
+
{{user.createdDate | date:'dd/MM/yy HH:mm'}}{{user.lastModifiedBy}}{{user.lastModifiedDate | date:'dd/MM/yy HH:mm'}} +
+ + + +
+
+
+
+
+ +
+
+ +
+
+
diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management.component.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management.component.ts new file mode 100644 index 0000000000..213bb24923 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management.component.ts @@ -0,0 +1,131 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Response } from '@angular/http'; +import { ActivatedRoute, Router } from '@angular/router'; +import { EventManager, PaginationUtil, ParseLinks, AlertService, JhiLanguageService } from 'ng-jhipster'; + +import { ITEMS_PER_PAGE, Principal, User, UserService } from '../../shared'; +import { PaginationConfig } from '../../blocks/config/uib-pagination.config'; + +@Component({ + selector: 'jhi-user-mgmt', + templateUrl: './user-management.component.html' +}) +export class UserMgmtComponent implements OnInit, OnDestroy { + + currentAccount: any; + users: User[]; + error: any; + success: any; + routeData: any; + links: any; + totalItems: any; + queryCount: any; + itemsPerPage: any; + page: any; + predicate: any; + previousPage: any; + reverse: any; + + constructor( + private jhiLanguageService: JhiLanguageService, + private userService: UserService, + private parseLinks: ParseLinks, + private alertService: AlertService, + private principal: Principal, + private eventManager: EventManager, private paginationUtil: PaginationUtil, + private paginationConfig: PaginationConfig, + private activatedRoute: ActivatedRoute, + private router: Router + ) { + this.itemsPerPage = ITEMS_PER_PAGE; + this.routeData = this.activatedRoute.data.subscribe(data => { + this.page = data['pagingParams'].page; + this.previousPage = data['pagingParams'].page; + this.reverse = data['pagingParams'].ascending; + this.predicate = data['pagingParams'].predicate; + }); + this.jhiLanguageService.setLocations(['user-management']); + } + + ngOnInit() { + this.principal.identity().then((account) => { + this.currentAccount = account; + this.loadAll(); + this.registerChangeInUsers(); + }); + } + + ngOnDestroy() { + this.routeData.unsubscribe(); + } + + registerChangeInUsers() { + this.eventManager.subscribe('userListModification', (response) => this.loadAll()); + } + + setActive (user, isActivated) { + user.activated = isActivated; + + this.userService.update(user).subscribe( + response => { + if (response.status === 200) { + this.error = null; + this.success = 'OK'; + this.loadAll(); + } else { + this.success = null; + this.error = 'ERROR'; + } + }); + } + + loadAll () { + this.userService.query({ + page: this.page - 1, + size: this.itemsPerPage, + sort: this.sort()}).subscribe( + (res: Response) => this.onSuccess(res.json(), res.headers), + (res: Response) => this.onError(res.json()) + ); + } + + trackIdentity (index, item: User) { + return item.id; + } + + sort () { + let result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; + if (this.predicate !== 'id') { + result.push('id'); + } + return result; + } + + loadPage (page: number) { + if (page !== this.previousPage) { + this.previousPage = page; + this.transition(); + } + } + + transition () { + this.router.navigate(['/user-management'], { queryParams: + { + page: this.page, + sort: this.predicate + ',' + (this.reverse ? 'asc' : 'desc') + } + }); + this.loadAll(); + } + + private onSuccess(data, headers) { + this.links = this.parseLinks.parse(headers.get('link')); + this.totalItems = headers.get('X-Total-Count'); + this.queryCount = this.totalItems; + this.users = data; + } + + private onError(error) { + this.alertService.error(error.error, error.message, null); + } +} diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-management.route.ts b/jhipster/src/main/webapp/app/admin/user-management/user-management.route.ts new file mode 100644 index 0000000000..6aca0cdeb1 --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-management.route.ts @@ -0,0 +1,77 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes, CanActivate } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { PaginationUtil } from 'ng-jhipster'; + +import { UserMgmtComponent } from './user-management.component'; +import { UserMgmtDetailComponent } from './user-management-detail.component'; +import { UserDialogComponent } from './user-management-dialog.component'; +import { UserDeleteDialogComponent } from './user-management-delete-dialog.component'; + +import { Principal } from '../../shared'; + + +@Injectable() +export class UserResolve implements CanActivate { + + constructor(private principal: Principal) { } + + canActivate() { + return this.principal.identity().then(account => this.principal.hasAnyAuthority(['ROLE_ADMIN'])); + } +} + +@Injectable() +export class UserResolvePagingParams implements Resolve { + + constructor(private paginationUtil: PaginationUtil) {} + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + let page = route.queryParams['page'] ? route.queryParams['page'] : '1'; + let sort = route.queryParams['sort'] ? route.queryParams['sort'] : 'id,asc'; + return { + page: this.paginationUtil.parsePage(page), + predicate: this.paginationUtil.parsePredicate(sort), + ascending: this.paginationUtil.parseAscending(sort) + }; + } +} + +export const userMgmtRoute: Routes = [ + { + path: 'user-management', + component: UserMgmtComponent, + resolve: { + 'pagingParams': UserResolvePagingParams + }, + data: { + pageTitle: 'userManagement.home.title' + } + }, + { + path: 'user-management/:login', + component: UserMgmtDetailComponent, + data: { + pageTitle: 'userManagement.home.title' + } + } +]; + +export const userDialogRoute: Routes = [ + { + path: 'user-management-new', + component: UserDialogComponent, + outlet: 'popup' + }, + { + path: 'user-management/:login/edit', + component: UserDialogComponent, + outlet: 'popup' + }, + { + path: 'user-management/:login/delete', + component: UserDeleteDialogComponent, + outlet: 'popup' + } +]; diff --git a/jhipster/src/main/webapp/app/admin/user-management/user-modal.service.ts b/jhipster/src/main/webapp/app/admin/user-management/user-modal.service.ts new file mode 100644 index 0000000000..006bc2a53c --- /dev/null +++ b/jhipster/src/main/webapp/app/admin/user-management/user-modal.service.ts @@ -0,0 +1,42 @@ +import { Injectable, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; + +import { UserMgmtDialogComponent } from './user-management-dialog.component'; +import { User, UserService } from '../../shared'; + +@Injectable() +export class UserModalService { + private isOpen = false; + constructor ( + private modalService: NgbModal, + private router: Router, + private userService: UserService + ) {} + + open (component: Component, login?: string): NgbModalRef { + if (this.isOpen) { + return; + } + this.isOpen = true; + + if (login) { + this.userService.find(login).subscribe(user => this.userModalRef(component, user)); + } else { + return this.userModalRef(component, new User()); + } + } + + userModalRef(component: Component, user: User): NgbModalRef { + let modalRef = this.modalService.open(component, { size: 'lg', backdrop: 'static'}); + modalRef.componentInstance.user = user; + modalRef.result.then(result => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }, (reason) => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }); + return modalRef; + } +} diff --git a/jhipster/src/main/webapp/app/app.constants.ts b/jhipster/src/main/webapp/app/app.constants.ts new file mode 100644 index 0000000000..8f53b6f2e8 --- /dev/null +++ b/jhipster/src/main/webapp/app/app.constants.ts @@ -0,0 +1,7 @@ +// DO NOT EDIT THIS FILE, EDIT THE WEBPACK COMMON CONFIG INSTEAD, WHICH WILL MODIFY THIS FILE +let _VERSION = '0.0.0'; // This value will be overwritten by webpack +let _DEBUG_INFO_ENABLED = true; // This value will be overwritten by webpack +/* @toreplace VERSION */ +/* @toreplace DEBUG_INFO_ENABLED */ +export const VERSION = _VERSION; +export const DEBUG_INFO_ENABLED = _DEBUG_INFO_ENABLED; diff --git a/jhipster/src/main/webapp/app/app.main.ts b/jhipster/src/main/webapp/app/app.main.ts new file mode 100644 index 0000000000..301e5ffb81 --- /dev/null +++ b/jhipster/src/main/webapp/app/app.main.ts @@ -0,0 +1,11 @@ +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { ProdConfig } from './blocks/config/prod.config'; +import { BaeldungAppModule } from './app.module'; + +ProdConfig(); + +if (module['hot']) { + module['hot'].accept(); +} + +platformBrowserDynamic().bootstrapModule(BaeldungAppModule); diff --git a/jhipster/src/main/webapp/app/app.module.ts b/jhipster/src/main/webapp/app/app.module.ts new file mode 100644 index 0000000000..dc82c6cdce --- /dev/null +++ b/jhipster/src/main/webapp/app/app.module.ts @@ -0,0 +1,58 @@ +import './vendor.ts'; + +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { BrowserModule } from '@angular/platform-browser'; +import { Ng2Webstorage } from 'ng2-webstorage'; + +import { BaeldungSharedModule, UserRouteAccessService } from './shared'; +import { BaeldungHomeModule } from './home/home.module'; +import { BaeldungAdminModule } from './admin/admin.module'; +import { BaeldungAccountModule } from './account/account.module'; +import { BaeldungEntityModule } from './entities/entity.module'; + +import { LayoutRoutingModule } from './layouts'; +import { customHttpProvider } from './blocks/interceptor/http.provider'; +import { PaginationConfig } from './blocks/config/uib-pagination.config'; + +import { + JhiMainComponent, + NavbarComponent, + FooterComponent, + ProfileService, + PageRibbonComponent, + ActiveMenuDirective, + ErrorComponent +} from './layouts'; + + +@NgModule({ + imports: [ + BrowserModule, + LayoutRoutingModule, + Ng2Webstorage.forRoot({ prefix: 'jhi', separator: '-'}), + BaeldungSharedModule, + BaeldungHomeModule, + BaeldungAdminModule, + BaeldungAccountModule, + BaeldungEntityModule + ], + declarations: [ + JhiMainComponent, + NavbarComponent, + ErrorComponent, + PageRibbonComponent, + ActiveMenuDirective, + FooterComponent + ], + providers: [ + ProfileService, + { provide: Window, useValue: window }, + { provide: Document, useValue: document }, + customHttpProvider(), + PaginationConfig, + UserRouteAccessService + ], + bootstrap: [ JhiMainComponent ] +}) +export class BaeldungAppModule {} diff --git a/jhipster/src/main/webapp/app/app.route.ts b/jhipster/src/main/webapp/app/app.route.ts new file mode 100644 index 0000000000..bfddd9bb7e --- /dev/null +++ b/jhipster/src/main/webapp/app/app.route.ts @@ -0,0 +1,9 @@ +import { Route } from '@angular/router'; + +import { NavbarComponent } from './layouts'; + +export const navbarRoute: Route = { + path: '', + component: NavbarComponent, + outlet: 'navbar' + }; diff --git a/jhipster/src/main/webapp/app/blocks/config/prod.config.ts b/jhipster/src/main/webapp/app/blocks/config/prod.config.ts new file mode 100644 index 0000000000..cc0050de27 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/config/prod.config.ts @@ -0,0 +1,9 @@ +import { enableProdMode } from '@angular/core'; +import { DEBUG_INFO_ENABLED } from '../../app.constants'; + +export function ProdConfig() { + // disable debug data on prod profile to improve performance + if (!DEBUG_INFO_ENABLED) { + enableProdMode(); + } +} diff --git a/jhipster/src/main/webapp/app/blocks/config/uib-pagination.config.ts b/jhipster/src/main/webapp/app/blocks/config/uib-pagination.config.ts new file mode 100644 index 0000000000..245e1f6bf1 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/config/uib-pagination.config.ts @@ -0,0 +1,13 @@ +import { ITEMS_PER_PAGE } from '../../shared'; +import { Injectable } from '@angular/core'; +import { NgbPaginationConfig} from '@ng-bootstrap/ng-bootstrap'; + +@Injectable() +export class PaginationConfig { + constructor(private config: NgbPaginationConfig) { + config.boundaryLinks = true; + config.maxSize = 5; + config.pageSize = ITEMS_PER_PAGE; + config.size = 'sm'; + } +} diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts b/jhipster/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts new file mode 100644 index 0000000000..49f517148c --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/auth-expired.interceptor.ts @@ -0,0 +1,34 @@ +import { HttpInterceptor } from 'ng-jhipster'; +import { RequestOptionsArgs, Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; +import { Injector } from '@angular/core'; +import { AuthService } from '../../shared/auth/auth.service'; +import { Principal } from '../../shared/auth/principal.service'; +import { AuthServerProvider } from '../../shared/auth/auth-jwt.service'; + +export class AuthExpiredInterceptor extends HttpInterceptor { + + constructor(private injector: Injector) { + super(); + } + + requestIntercept(options?: RequestOptionsArgs): RequestOptionsArgs { + return options; + } + + responseIntercept(observable: Observable): Observable { + let self = this; + + return > observable.catch((error, source) => { + if (error.status === 401) { + let principal: Principal = self.injector.get(Principal); + + if (principal.isAuthenticated()) { + let auth: AuthService = self.injector.get(AuthService); + auth.authorize(true); + } + } + return Observable.throw(error); + }); + } +} diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts b/jhipster/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts new file mode 100644 index 0000000000..cf90284116 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/auth.interceptor.ts @@ -0,0 +1,27 @@ +import { Observable } from 'rxjs/Observable'; +import { RequestOptionsArgs, Response } from '@angular/http'; +import { LocalStorageService, SessionStorageService } from 'ng2-webstorage'; +import { HttpInterceptor } from 'ng-jhipster'; + +export class AuthInterceptor extends HttpInterceptor { + + constructor( + private localStorage: LocalStorageService, + private sessionStorage: SessionStorageService + ) { + super(); + } + + requestIntercept(options?: RequestOptionsArgs): RequestOptionsArgs { + let token = this.localStorage.retrieve('authenticationToken') || this.sessionStorage.retrieve('authenticationToken'); + if (!!token) { + options.headers.append('Authorization', 'Bearer ' + token); + } + return options; + } + + responseIntercept(observable: Observable): Observable { + return observable; // by pass + } + +} diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts b/jhipster/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts new file mode 100644 index 0000000000..6e22557d05 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/errorhandler.interceptor.ts @@ -0,0 +1,24 @@ +import { HttpInterceptor, EventManager } from 'ng-jhipster'; +import { RequestOptionsArgs, Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; + +export class ErrorHandlerInterceptor extends HttpInterceptor { + + constructor(private eventManager: EventManager) { + super(); + } + + requestIntercept(options?: RequestOptionsArgs): RequestOptionsArgs { + return options; + } + + responseIntercept(observable: Observable): Observable { + return > observable.catch(error => { + if (!(error.status === 401 && (error.text() === '' || + (error.json().path && error.json().path.indexOf('/api/account') === 0 )))) { + this.eventManager.broadcast( {name: 'baeldungApp.httpError', content: error}); + } + return Observable.throw(error); + }); + } +} diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/http.provider.ts b/jhipster/src/main/webapp/app/blocks/interceptor/http.provider.ts new file mode 100644 index 0000000000..a9689662a3 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/http.provider.ts @@ -0,0 +1,45 @@ +import { Injector } from '@angular/core'; +import { Http, XHRBackend, RequestOptions } from '@angular/http'; +import { EventManager, InterceptableHttp } from 'ng-jhipster'; + +import { AuthInterceptor } from './auth.interceptor'; +import { LocalStorageService, SessionStorageService } from 'ng2-webstorage'; +import { AuthExpiredInterceptor } from './auth-expired.interceptor'; +import { ErrorHandlerInterceptor } from './errorhandler.interceptor'; +import { NotificationInterceptor } from './notification.interceptor'; + +export function interceptableFactory( + backend: XHRBackend, + defaultOptions: RequestOptions, + localStorage: LocalStorageService, + sessionStorage: SessionStorageService, + injector: Injector, + eventManager: EventManager +) { + return new InterceptableHttp( + backend, + defaultOptions, + [ + new AuthInterceptor(localStorage, sessionStorage), + new AuthExpiredInterceptor(injector), + // Other interceptors can be added here + new ErrorHandlerInterceptor(eventManager), + new NotificationInterceptor() + ] + ); +}; + +export function customHttpProvider() { + return { + provide: Http, + useFactory: interceptableFactory, + deps: [ + XHRBackend, + RequestOptions, + LocalStorageService, + SessionStorageService, + Injector, + EventManager + ] + }; +}; diff --git a/jhipster/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts b/jhipster/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts new file mode 100644 index 0000000000..ebee7688c5 --- /dev/null +++ b/jhipster/src/main/webapp/app/blocks/interceptor/notification.interceptor.ts @@ -0,0 +1,33 @@ +import { HttpInterceptor } from 'ng-jhipster'; +import { RequestOptionsArgs, Response } from '@angular/http'; +import { Observable } from 'rxjs/Observable'; + +export class NotificationInterceptor extends HttpInterceptor { + + constructor() { + super(); + } + + requestIntercept(options?: RequestOptionsArgs): RequestOptionsArgs { + return options; + } + + responseIntercept(observable: Observable): Observable { + return > observable.catch((error) => { + let arr = Array.from(error.headers._headers); + let headers = []; + let i; + for (i = 0; i < arr.length; i++) { + if (arr[i][0].endsWith('app-alert') || arr[i][0].endsWith('app-params')) { + headers.push(arr[i][0]); + } + } + headers.sort(); + let alertKey = headers.length >= 1 ? error.headers.get(headers[0]) : null; + if (typeof alertKey === 'string') { + // AlertService.success(alertKey, { param: response.headers(headers[1])}); + } + return Observable.throw(error); + }); + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.html b/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.html new file mode 100644 index 0000000000..63540ec6bd --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.html @@ -0,0 +1,19 @@ +
+ + + +
diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.ts b/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.ts new file mode 100644 index 0000000000..29ef818ddf --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-delete-dialog.component.ts @@ -0,0 +1,67 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { Comment } from './comment.model'; +import { CommentPopupService } from './comment-popup.service'; +import { CommentService } from './comment.service'; + +@Component({ + selector: 'jhi-comment-delete-dialog', + templateUrl: './comment-delete-dialog.component.html' +}) +export class CommentDeleteDialogComponent { + + comment: Comment; + + constructor( + private jhiLanguageService: JhiLanguageService, + private commentService: CommentService, + public activeModal: NgbActiveModal, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['comment']); + } + + clear () { + this.activeModal.dismiss('cancel'); + } + + confirmDelete (id: number) { + this.commentService.delete(id).subscribe(response => { + this.eventManager.broadcast({ + name: 'commentListModification', + content: 'Deleted an comment' + }); + this.activeModal.dismiss(true); + }); + } +} + +@Component({ + selector: 'jhi-comment-delete-popup', + template: '' +}) +export class CommentDeletePopupComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private commentPopupService: CommentPopupService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + this.modalRef = this.commentPopupService + .open(CommentDeleteDialogComponent, params['id']); + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.html b/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.html new file mode 100644 index 0000000000..add5b9e208 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.html @@ -0,0 +1,35 @@ + +
+

Comment {{comment.id}}

+
+ +
+
Text
+
+ {{comment.text}} +
+
Creation Date
+
+ {{comment.creationDate | date:'mediumDate'}} +
+
Post
+
+ +
+
+ + + + +
diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.ts b/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.ts new file mode 100644 index 0000000000..c2bf7fce0f --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-detail.component.ts @@ -0,0 +1,43 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; +import { Comment } from './comment.model'; +import { CommentService } from './comment.service'; + +@Component({ + selector: 'jhi-comment-detail', + templateUrl: './comment-detail.component.html' +}) +export class CommentDetailComponent implements OnInit, OnDestroy { + + comment: Comment; + private subscription: any; + + constructor( + private jhiLanguageService: JhiLanguageService, + private commentService: CommentService, + private route: ActivatedRoute + ) { + this.jhiLanguageService.setLocations(['comment']); + } + + ngOnInit() { + this.subscription = this.route.params.subscribe(params => { + this.load(params['id']); + }); + } + + load (id) { + this.commentService.find(id).subscribe(comment => { + this.comment = comment; + }); + } + previousState() { + window.history.back(); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } + +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.html b/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.html new file mode 100644 index 0000000000..7e13c33177 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.html @@ -0,0 +1,76 @@ + + +
+ + + + +
diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.ts b/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.ts new file mode 100644 index 0000000000..1a30305c96 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-dialog.component.ts @@ -0,0 +1,107 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Response } from '@angular/http'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, AlertService, JhiLanguageService } from 'ng-jhipster'; + +import { Comment } from './comment.model'; +import { CommentPopupService } from './comment-popup.service'; +import { CommentService } from './comment.service'; +import { Post, PostService } from '../post'; +@Component({ + selector: 'jhi-comment-dialog', + templateUrl: './comment-dialog.component.html' +}) +export class CommentDialogComponent implements OnInit { + + comment: Comment; + authorities: any[]; + isSaving: boolean; + + posts: Post[]; + constructor( + public activeModal: NgbActiveModal, + private jhiLanguageService: JhiLanguageService, + private alertService: AlertService, + private commentService: CommentService, + private postService: PostService, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['comment']); + } + + ngOnInit() { + this.isSaving = false; + this.authorities = ['ROLE_USER', 'ROLE_ADMIN']; + this.postService.query().subscribe( + (res: Response) => { this.posts = res.json(); }, (res: Response) => this.onError(res.json())); + } + clear () { + this.activeModal.dismiss('cancel'); + } + + save () { + this.isSaving = true; + if (this.comment.id !== undefined) { + this.commentService.update(this.comment) + .subscribe((res: Comment) => + this.onSaveSuccess(res), (res: Response) => this.onSaveError(res.json())); + } else { + this.commentService.create(this.comment) + .subscribe((res: Comment) => + this.onSaveSuccess(res), (res: Response) => this.onSaveError(res.json())); + } + } + + private onSaveSuccess (result: Comment) { + this.eventManager.broadcast({ name: 'commentListModification', content: 'OK'}); + this.isSaving = false; + this.activeModal.dismiss(result); + } + + private onSaveError (error) { + this.isSaving = false; + this.onError(error); + } + + private onError (error) { + this.alertService.error(error.message, null, null); + } + + trackPostById(index: number, item: Post) { + return item.id; + } +} + +@Component({ + selector: 'jhi-comment-popup', + template: '' +}) +export class CommentPopupComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private commentPopupService: CommentPopupService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + if ( params['id'] ) { + this.modalRef = this.commentPopupService + .open(CommentDialogComponent, params['id']); + } else { + this.modalRef = this.commentPopupService + .open(CommentDialogComponent); + } + + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment-popup.service.ts b/jhipster/src/main/webapp/app/entities/comment/comment-popup.service.ts new file mode 100644 index 0000000000..a1c0ce11bf --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment-popup.service.ts @@ -0,0 +1,50 @@ +import { Injectable, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { Comment } from './comment.model'; +import { CommentService } from './comment.service'; +@Injectable() +export class CommentPopupService { + private isOpen = false; + constructor ( + private modalService: NgbModal, + private router: Router, + private commentService: CommentService + + ) {} + + open (component: Component, id?: number | any): NgbModalRef { + if (this.isOpen) { + return; + } + this.isOpen = true; + + if (id) { + this.commentService.find(id).subscribe(comment => { + if (comment.creationDate) { + comment.creationDate = { + year: comment.creationDate.getFullYear(), + month: comment.creationDate.getMonth() + 1, + day: comment.creationDate.getDate() + }; + } + this.commentModalRef(component, comment); + }); + } else { + return this.commentModalRef(component, new Comment()); + } + } + + commentModalRef(component: Component, comment: Comment): NgbModalRef { + let modalRef = this.modalService.open(component, { size: 'lg', backdrop: 'static'}); + modalRef.componentInstance.comment = comment; + modalRef.result.then(result => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }, (reason) => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }); + return modalRef; + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.component.html b/jhipster/src/main/webapp/app/entities/comment/comment.component.html new file mode 100644 index 0000000000..1efd1ce379 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.component.html @@ -0,0 +1,64 @@ +
+

+ Comments + +

+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
ID Text Creation Date Post
{{comment.id}}{{comment.text}}{{comment.creationDate | date:'mediumDate'}} + + +
+ + + +
+
+
+
diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.component.ts b/jhipster/src/main/webapp/app/entities/comment/comment.component.ts new file mode 100644 index 0000000000..4e009d4774 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.component.ts @@ -0,0 +1,110 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Response } from '@angular/http'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Subscription } from 'rxjs/Rx'; +import { EventManager, ParseLinks, PaginationUtil, JhiLanguageService, AlertService } from 'ng-jhipster'; + +import { Comment } from './comment.model'; +import { CommentService } from './comment.service'; +import { ITEMS_PER_PAGE, Principal } from '../../shared'; +import { PaginationConfig } from '../../blocks/config/uib-pagination.config'; + +@Component({ + selector: 'jhi-comment', + templateUrl: './comment.component.html' +}) +export class CommentComponent implements OnInit, OnDestroy { + + comments: Comment[]; + currentAccount: any; + eventSubscriber: Subscription; + itemsPerPage: number; + links: any; + page: any; + predicate: any; + queryCount: any; + reverse: any; + totalItems: number; + + constructor( + private jhiLanguageService: JhiLanguageService, + private commentService: CommentService, + private alertService: AlertService, + private eventManager: EventManager, + private parseLinks: ParseLinks, + private principal: Principal + ) { + this.comments = []; + this.itemsPerPage = ITEMS_PER_PAGE; + this.page = 0; + this.links = { + last: 0 + }; + this.predicate = 'id'; + this.reverse = true; + this.jhiLanguageService.setLocations(['comment']); + } + + loadAll () { + this.commentService.query({ + page: this.page, + size: this.itemsPerPage, + sort: this.sort() + }).subscribe( + (res: Response) => this.onSuccess(res.json(), res.headers), + (res: Response) => this.onError(res.json()) + ); + } + + reset () { + this.page = 0; + this.comments = []; + this.loadAll(); + } + + loadPage(page) { + this.page = page; + this.loadAll(); + } + ngOnInit() { + this.loadAll(); + this.principal.identity().then((account) => { + this.currentAccount = account; + }); + this.registerChangeInComments(); + } + + ngOnDestroy() { + this.eventManager.destroy(this.eventSubscriber); + } + + trackId (index: number, item: Comment) { + return item.id; + } + + + + registerChangeInComments() { + this.eventSubscriber = this.eventManager.subscribe('commentListModification', (response) => this.reset()); + } + + sort () { + let result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; + if (this.predicate !== 'id') { + result.push('id'); + } + return result; + } + + private onSuccess(data, headers) { + this.links = this.parseLinks.parse(headers.get('link')); + this.totalItems = headers.get('X-Total-Count'); + for (let i = 0; i < data.length; i++) { + this.comments.push(data[i]); + } + } + + private onError (error) { + this.alertService.error(error.message, null, null); + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.model.ts b/jhipster/src/main/webapp/app/entities/comment/comment.model.ts new file mode 100644 index 0000000000..66df7d0b34 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.model.ts @@ -0,0 +1,10 @@ +import { Post } from '../post'; +export class Comment { + constructor( + public id?: number, + public text?: string, + public creationDate?: any, + public post?: Post, + ) { + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.module.ts b/jhipster/src/main/webapp/app/entities/comment/comment.module.ts new file mode 100644 index 0000000000..1f3167274e --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.module.ts @@ -0,0 +1,50 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { BaeldungSharedModule } from '../../shared'; + +import { + CommentService, + CommentPopupService, + CommentComponent, + CommentDetailComponent, + CommentDialogComponent, + CommentPopupComponent, + CommentDeletePopupComponent, + CommentDeleteDialogComponent, + commentRoute, + commentPopupRoute, +} from './'; + +let ENTITY_STATES = [ + ...commentRoute, + ...commentPopupRoute, +]; + +@NgModule({ + imports: [ + BaeldungSharedModule, + RouterModule.forRoot(ENTITY_STATES, { useHash: true }) + ], + declarations: [ + CommentComponent, + CommentDetailComponent, + CommentDialogComponent, + CommentDeleteDialogComponent, + CommentPopupComponent, + CommentDeletePopupComponent, + ], + entryComponents: [ + CommentComponent, + CommentDialogComponent, + CommentPopupComponent, + CommentDeleteDialogComponent, + CommentDeletePopupComponent, + ], + providers: [ + CommentService, + CommentPopupService, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungCommentModule {} diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.route.ts b/jhipster/src/main/webapp/app/entities/comment/comment.route.ts new file mode 100644 index 0000000000..bb0e5e4141 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.route.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes, CanActivate } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { PaginationUtil } from 'ng-jhipster'; + +import { CommentComponent } from './comment.component'; +import { CommentDetailComponent } from './comment-detail.component'; +import { CommentPopupComponent } from './comment-dialog.component'; +import { CommentDeletePopupComponent } from './comment-delete-dialog.component'; + +import { Principal } from '../../shared'; + + +export const commentRoute: Routes = [ + { + path: 'comment', + component: CommentComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + } + }, { + path: 'comment/:id', + component: CommentDetailComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + } + } +]; + +export const commentPopupRoute: Routes = [ + { + path: 'comment-new', + component: CommentPopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + }, + outlet: 'popup' + }, + { + path: 'comment/:id/edit', + component: CommentPopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + }, + outlet: 'popup' + }, + { + path: 'comment/:id/delete', + component: CommentDeletePopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.comment.home.title' + }, + outlet: 'popup' + } +]; diff --git a/jhipster/src/main/webapp/app/entities/comment/comment.service.ts b/jhipster/src/main/webapp/app/entities/comment/comment.service.ts new file mode 100644 index 0000000000..b6121b3b10 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/comment.service.ts @@ -0,0 +1,78 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams, BaseRequestOptions } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { Comment } from './comment.model'; +import { DateUtils } from 'ng-jhipster'; +@Injectable() +export class CommentService { + + private resourceUrl = 'api/comments'; + + constructor(private http: Http, private dateUtils: DateUtils) { } + + create(comment: Comment): Observable { + let copy: Comment = Object.assign({}, comment); + copy.creationDate = this.dateUtils + .convertLocalDateToServer(comment.creationDate); + return this.http.post(this.resourceUrl, copy).map((res: Response) => { + return res.json(); + }); + } + + update(comment: Comment): Observable { + let copy: Comment = Object.assign({}, comment); + copy.creationDate = this.dateUtils + .convertLocalDateToServer(comment.creationDate); + return this.http.put(this.resourceUrl, copy).map((res: Response) => { + return res.json(); + }); + } + + find(id: number): Observable { + return this.http.get(`${this.resourceUrl}/${id}`).map((res: Response) => { + let jsonResponse = res.json(); + jsonResponse.creationDate = this.dateUtils + .convertLocalDateFromServer(jsonResponse.creationDate); + return jsonResponse; + }); + } + + query(req?: any): Observable { + let options = this.createRequestOption(req); + return this.http.get(this.resourceUrl, options) + .map((res: any) => this.convertResponse(res)) + ; + } + + delete(id: number): Observable { + return this.http.delete(`${this.resourceUrl}/${id}`); + } + + + private convertResponse(res: any): any { + let jsonResponse = res.json(); + for (let i = 0; i < jsonResponse.length; i++) { + jsonResponse[i].creationDate = this.dateUtils + .convertLocalDateFromServer(jsonResponse[i].creationDate); + } + res._body = jsonResponse; + return res; + } + + private createRequestOption(req?: any): BaseRequestOptions { + let options: BaseRequestOptions = new BaseRequestOptions(); + if (req) { + let params: URLSearchParams = new URLSearchParams(); + params.set('page', req.page); + params.set('size', req.size); + if (req.sort) { + params.paramsMap.set('sort', req.sort); + } + params.set('query', req.query); + + options.search = params; + } + return options; + } +} diff --git a/jhipster/src/main/webapp/app/entities/comment/index.ts b/jhipster/src/main/webapp/app/entities/comment/index.ts new file mode 100644 index 0000000000..5e54fb6099 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/comment/index.ts @@ -0,0 +1,8 @@ +export * from './comment.model'; +export * from './comment-popup.service'; +export * from './comment.service'; +export * from './comment-dialog.component'; +export * from './comment-delete-dialog.component'; +export * from './comment-detail.component'; +export * from './comment.component'; +export * from './comment.route'; diff --git a/jhipster/src/main/webapp/app/entities/entity.module.ts b/jhipster/src/main/webapp/app/entities/entity.module.ts new file mode 100644 index 0000000000..ba044e1902 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/entity.module.ts @@ -0,0 +1,18 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; + +import { BaeldungPostModule } from './post/post.module'; +import { BaeldungCommentModule } from './comment/comment.module'; +/* jhipster-needle-add-entity-module-import - JHipster will add entity modules imports here */ + +@NgModule({ + imports: [ + BaeldungPostModule, + BaeldungCommentModule, + /* jhipster-needle-add-entity-module - JHipster will add entity modules here */ + ], + declarations: [], + entryComponents: [], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungEntityModule {} diff --git a/jhipster/src/main/webapp/app/entities/post/index.ts b/jhipster/src/main/webapp/app/entities/post/index.ts new file mode 100644 index 0000000000..9375e11bd8 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/index.ts @@ -0,0 +1,8 @@ +export * from './post.model'; +export * from './post-popup.service'; +export * from './post.service'; +export * from './post-dialog.component'; +export * from './post-delete-dialog.component'; +export * from './post-detail.component'; +export * from './post.component'; +export * from './post.route'; diff --git a/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.html b/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.html new file mode 100644 index 0000000000..901fc1968e --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.html @@ -0,0 +1,19 @@ +
+ + + +
diff --git a/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.ts b/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.ts new file mode 100644 index 0000000000..5e1413fd95 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-delete-dialog.component.ts @@ -0,0 +1,67 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { Post } from './post.model'; +import { PostPopupService } from './post-popup.service'; +import { PostService } from './post.service'; + +@Component({ + selector: 'jhi-post-delete-dialog', + templateUrl: './post-delete-dialog.component.html' +}) +export class PostDeleteDialogComponent { + + post: Post; + + constructor( + private jhiLanguageService: JhiLanguageService, + private postService: PostService, + public activeModal: NgbActiveModal, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['post']); + } + + clear () { + this.activeModal.dismiss('cancel'); + } + + confirmDelete (id: number) { + this.postService.delete(id).subscribe(response => { + this.eventManager.broadcast({ + name: 'postListModification', + content: 'Deleted an post' + }); + this.activeModal.dismiss(true); + }); + } +} + +@Component({ + selector: 'jhi-post-delete-popup', + template: '' +}) +export class PostDeletePopupComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private postPopupService: PostPopupService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + this.modalRef = this.postPopupService + .open(PostDeleteDialogComponent, params['id']); + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post-detail.component.html b/jhipster/src/main/webapp/app/entities/post/post-detail.component.html new file mode 100644 index 0000000000..8edcbee375 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-detail.component.html @@ -0,0 +1,37 @@ + +
+

Post {{post.id}}

+
+ +
+
Title
+
+ {{post.title}} +
+
Content
+
+ {{post.content}} +
+
Creation Date
+
+ {{post.creationDate | date:'mediumDate'}} +
+
Creator
+
+ {{post.creator?.login}} +
+
+ + + + +
diff --git a/jhipster/src/main/webapp/app/entities/post/post-detail.component.ts b/jhipster/src/main/webapp/app/entities/post/post-detail.component.ts new file mode 100644 index 0000000000..679592f3b8 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-detail.component.ts @@ -0,0 +1,43 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { JhiLanguageService } from 'ng-jhipster'; +import { Post } from './post.model'; +import { PostService } from './post.service'; + +@Component({ + selector: 'jhi-post-detail', + templateUrl: './post-detail.component.html' +}) +export class PostDetailComponent implements OnInit, OnDestroy { + + post: Post; + private subscription: any; + + constructor( + private jhiLanguageService: JhiLanguageService, + private postService: PostService, + private route: ActivatedRoute + ) { + this.jhiLanguageService.setLocations(['post']); + } + + ngOnInit() { + this.subscription = this.route.params.subscribe(params => { + this.load(params['id']); + }); + } + + load (id) { + this.postService.find(id).subscribe(post => { + this.post = post; + }); + } + previousState() { + window.history.back(); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } + +} diff --git a/jhipster/src/main/webapp/app/entities/post/post-dialog.component.html b/jhipster/src/main/webapp/app/entities/post/post-dialog.component.html new file mode 100644 index 0000000000..2216dcd451 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-dialog.component.html @@ -0,0 +1,96 @@ + + +
+ + + + +
diff --git a/jhipster/src/main/webapp/app/entities/post/post-dialog.component.ts b/jhipster/src/main/webapp/app/entities/post/post-dialog.component.ts new file mode 100644 index 0000000000..803b76fc78 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-dialog.component.ts @@ -0,0 +1,107 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Response } from '@angular/http'; + +import { NgbActiveModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, AlertService, JhiLanguageService } from 'ng-jhipster'; + +import { Post } from './post.model'; +import { PostPopupService } from './post-popup.service'; +import { PostService } from './post.service'; +import { User, UserService } from '../../shared'; +@Component({ + selector: 'jhi-post-dialog', + templateUrl: './post-dialog.component.html' +}) +export class PostDialogComponent implements OnInit { + + post: Post; + authorities: any[]; + isSaving: boolean; + + users: User[]; + constructor( + public activeModal: NgbActiveModal, + private jhiLanguageService: JhiLanguageService, + private alertService: AlertService, + private postService: PostService, + private userService: UserService, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['post']); + } + + ngOnInit() { + this.isSaving = false; + this.authorities = ['ROLE_USER', 'ROLE_ADMIN']; + this.userService.query().subscribe( + (res: Response) => { this.users = res.json(); }, (res: Response) => this.onError(res.json())); + } + clear () { + this.activeModal.dismiss('cancel'); + } + + save () { + this.isSaving = true; + if (this.post.id !== undefined) { + this.postService.update(this.post) + .subscribe((res: Post) => + this.onSaveSuccess(res), (res: Response) => this.onSaveError(res.json())); + } else { + this.postService.create(this.post) + .subscribe((res: Post) => + this.onSaveSuccess(res), (res: Response) => this.onSaveError(res.json())); + } + } + + private onSaveSuccess (result: Post) { + this.eventManager.broadcast({ name: 'postListModification', content: 'OK'}); + this.isSaving = false; + this.activeModal.dismiss(result); + } + + private onSaveError (error) { + this.isSaving = false; + this.onError(error); + } + + private onError (error) { + this.alertService.error(error.message, null, null); + } + + trackUserById(index: number, item: User) { + return item.id; + } +} + +@Component({ + selector: 'jhi-post-popup', + template: '' +}) +export class PostPopupComponent implements OnInit, OnDestroy { + + modalRef: NgbModalRef; + routeSub: any; + + constructor ( + private route: ActivatedRoute, + private postPopupService: PostPopupService + ) {} + + ngOnInit() { + this.routeSub = this.route.params.subscribe(params => { + if ( params['id'] ) { + this.modalRef = this.postPopupService + .open(PostDialogComponent, params['id']); + } else { + this.modalRef = this.postPopupService + .open(PostDialogComponent); + } + + }); + } + + ngOnDestroy() { + this.routeSub.unsubscribe(); + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post-popup.service.ts b/jhipster/src/main/webapp/app/entities/post/post-popup.service.ts new file mode 100644 index 0000000000..42ea731e07 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post-popup.service.ts @@ -0,0 +1,50 @@ +import { Injectable, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { Post } from './post.model'; +import { PostService } from './post.service'; +@Injectable() +export class PostPopupService { + private isOpen = false; + constructor ( + private modalService: NgbModal, + private router: Router, + private postService: PostService + + ) {} + + open (component: Component, id?: number | any): NgbModalRef { + if (this.isOpen) { + return; + } + this.isOpen = true; + + if (id) { + this.postService.find(id).subscribe(post => { + if (post.creationDate) { + post.creationDate = { + year: post.creationDate.getFullYear(), + month: post.creationDate.getMonth() + 1, + day: post.creationDate.getDate() + }; + } + this.postModalRef(component, post); + }); + } else { + return this.postModalRef(component, new Post()); + } + } + + postModalRef(component: Component, post: Post): NgbModalRef { + let modalRef = this.modalService.open(component, { size: 'lg', backdrop: 'static'}); + modalRef.componentInstance.post = post; + modalRef.result.then(result => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }, (reason) => { + this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true }); + this.isOpen = false; + }); + return modalRef; + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post.component.html b/jhipster/src/main/webapp/app/entities/post/post.component.html new file mode 100644 index 0000000000..cf17c4e6dc --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.component.html @@ -0,0 +1,64 @@ +
+

+ Posts + +

+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
ID Title Content Creation Date Creator
{{post.id}}{{post.title}}{{post.content}}{{post.creationDate | date:'mediumDate'}} + {{post.creator?.login}} + +
+ + + +
+
+
+
diff --git a/jhipster/src/main/webapp/app/entities/post/post.component.ts b/jhipster/src/main/webapp/app/entities/post/post.component.ts new file mode 100644 index 0000000000..e8401a48c7 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.component.ts @@ -0,0 +1,110 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Response } from '@angular/http'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Subscription } from 'rxjs/Rx'; +import { EventManager, ParseLinks, PaginationUtil, JhiLanguageService, AlertService } from 'ng-jhipster'; + +import { Post } from './post.model'; +import { PostService } from './post.service'; +import { ITEMS_PER_PAGE, Principal } from '../../shared'; +import { PaginationConfig } from '../../blocks/config/uib-pagination.config'; + +@Component({ + selector: 'jhi-post', + templateUrl: './post.component.html' +}) +export class PostComponent implements OnInit, OnDestroy { + + posts: Post[]; + currentAccount: any; + eventSubscriber: Subscription; + itemsPerPage: number; + links: any; + page: any; + predicate: any; + queryCount: any; + reverse: any; + totalItems: number; + + constructor( + private jhiLanguageService: JhiLanguageService, + private postService: PostService, + private alertService: AlertService, + private eventManager: EventManager, + private parseLinks: ParseLinks, + private principal: Principal + ) { + this.posts = []; + this.itemsPerPage = ITEMS_PER_PAGE; + this.page = 0; + this.links = { + last: 0 + }; + this.predicate = 'id'; + this.reverse = true; + this.jhiLanguageService.setLocations(['post']); + } + + loadAll () { + this.postService.query({ + page: this.page, + size: this.itemsPerPage, + sort: this.sort() + }).subscribe( + (res: Response) => this.onSuccess(res.json(), res.headers), + (res: Response) => this.onError(res.json()) + ); + } + + reset () { + this.page = 0; + this.posts = []; + this.loadAll(); + } + + loadPage(page) { + this.page = page; + this.loadAll(); + } + ngOnInit() { + this.loadAll(); + this.principal.identity().then((account) => { + this.currentAccount = account; + }); + this.registerChangeInPosts(); + } + + ngOnDestroy() { + this.eventManager.destroy(this.eventSubscriber); + } + + trackId (index: number, item: Post) { + return item.id; + } + + + + registerChangeInPosts() { + this.eventSubscriber = this.eventManager.subscribe('postListModification', (response) => this.reset()); + } + + sort () { + let result = [this.predicate + ',' + (this.reverse ? 'asc' : 'desc')]; + if (this.predicate !== 'id') { + result.push('id'); + } + return result; + } + + private onSuccess(data, headers) { + this.links = this.parseLinks.parse(headers.get('link')); + this.totalItems = headers.get('X-Total-Count'); + for (let i = 0; i < data.length; i++) { + this.posts.push(data[i]); + } + } + + private onError (error) { + this.alertService.error(error.message, null, null); + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post.model.ts b/jhipster/src/main/webapp/app/entities/post/post.model.ts new file mode 100644 index 0000000000..454f7e75f8 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.model.ts @@ -0,0 +1,11 @@ +import { User } from '../../shared'; +export class Post { + constructor( + public id?: number, + public title?: string, + public content?: string, + public creationDate?: any, + public creator?: User, + ) { + } +} diff --git a/jhipster/src/main/webapp/app/entities/post/post.module.ts b/jhipster/src/main/webapp/app/entities/post/post.module.ts new file mode 100644 index 0000000000..53ba982c02 --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.module.ts @@ -0,0 +1,52 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { BaeldungSharedModule } from '../../shared'; +import { BaeldungAdminModule } from '../../admin/admin.module'; + +import { + PostService, + PostPopupService, + PostComponent, + PostDetailComponent, + PostDialogComponent, + PostPopupComponent, + PostDeletePopupComponent, + PostDeleteDialogComponent, + postRoute, + postPopupRoute, +} from './'; + +let ENTITY_STATES = [ + ...postRoute, + ...postPopupRoute, +]; + +@NgModule({ + imports: [ + BaeldungSharedModule, + BaeldungAdminModule, + RouterModule.forRoot(ENTITY_STATES, { useHash: true }) + ], + declarations: [ + PostComponent, + PostDetailComponent, + PostDialogComponent, + PostDeleteDialogComponent, + PostPopupComponent, + PostDeletePopupComponent, + ], + entryComponents: [ + PostComponent, + PostDialogComponent, + PostPopupComponent, + PostDeleteDialogComponent, + PostDeletePopupComponent, + ], + providers: [ + PostService, + PostPopupService, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungPostModule {} diff --git a/jhipster/src/main/webapp/app/entities/post/post.route.ts b/jhipster/src/main/webapp/app/entities/post/post.route.ts new file mode 100644 index 0000000000..27b23bdc0d --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.route.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Routes, CanActivate } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { PaginationUtil } from 'ng-jhipster'; + +import { PostComponent } from './post.component'; +import { PostDetailComponent } from './post-detail.component'; +import { PostPopupComponent } from './post-dialog.component'; +import { PostDeletePopupComponent } from './post-delete-dialog.component'; + +import { Principal } from '../../shared'; + + +export const postRoute: Routes = [ + { + path: 'post', + component: PostComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + } + }, { + path: 'post/:id', + component: PostDetailComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + } + } +]; + +export const postPopupRoute: Routes = [ + { + path: 'post-new', + component: PostPopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + }, + outlet: 'popup' + }, + { + path: 'post/:id/edit', + component: PostPopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + }, + outlet: 'popup' + }, + { + path: 'post/:id/delete', + component: PostDeletePopupComponent, + data: { + authorities: ['ROLE_USER'], + pageTitle: 'baeldungApp.post.home.title' + }, + outlet: 'popup' + } +]; diff --git a/jhipster/src/main/webapp/app/entities/post/post.service.ts b/jhipster/src/main/webapp/app/entities/post/post.service.ts new file mode 100644 index 0000000000..7f7ee969fe --- /dev/null +++ b/jhipster/src/main/webapp/app/entities/post/post.service.ts @@ -0,0 +1,78 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams, BaseRequestOptions } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { Post } from './post.model'; +import { DateUtils } from 'ng-jhipster'; +@Injectable() +export class PostService { + + private resourceUrl = 'api/posts'; + + constructor(private http: Http, private dateUtils: DateUtils) { } + + create(post: Post): Observable { + let copy: Post = Object.assign({}, post); + copy.creationDate = this.dateUtils + .convertLocalDateToServer(post.creationDate); + return this.http.post(this.resourceUrl, copy).map((res: Response) => { + return res.json(); + }); + } + + update(post: Post): Observable { + let copy: Post = Object.assign({}, post); + copy.creationDate = this.dateUtils + .convertLocalDateToServer(post.creationDate); + return this.http.put(this.resourceUrl, copy).map((res: Response) => { + return res.json(); + }); + } + + find(id: number): Observable { + return this.http.get(`${this.resourceUrl}/${id}`).map((res: Response) => { + let jsonResponse = res.json(); + jsonResponse.creationDate = this.dateUtils + .convertLocalDateFromServer(jsonResponse.creationDate); + return jsonResponse; + }); + } + + query(req?: any): Observable { + let options = this.createRequestOption(req); + return this.http.get(this.resourceUrl, options) + .map((res: any) => this.convertResponse(res)) + ; + } + + delete(id: number): Observable { + return this.http.delete(`${this.resourceUrl}/${id}`); + } + + + private convertResponse(res: any): any { + let jsonResponse = res.json(); + for (let i = 0; i < jsonResponse.length; i++) { + jsonResponse[i].creationDate = this.dateUtils + .convertLocalDateFromServer(jsonResponse[i].creationDate); + } + res._body = jsonResponse; + return res; + } + + private createRequestOption(req?: any): BaseRequestOptions { + let options: BaseRequestOptions = new BaseRequestOptions(); + if (req) { + let params: URLSearchParams = new URLSearchParams(); + params.set('page', req.page); + params.set('size', req.size); + if (req.sort) { + params.paramsMap.set('sort', req.sort); + } + params.set('query', req.query); + + options.search = params; + } + return options; + } +} diff --git a/jhipster/src/main/webapp/app/home/home.component.html b/jhipster/src/main/webapp/app/home/home.component.html new file mode 100644 index 0000000000..4c0197228c --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.component.html @@ -0,0 +1,42 @@ +
+
+ +
+
+

Welcome, Java Hipster!

+

This is your homepage

+ +
+
+ You are logged in as user "{{account.login}}". +
+ +
+ If you want to + sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and password="user").
+
+ +
+ You don't have an account yet? + Register a new account +
+
+ +

+ If you have any question on JHipster: +

+ + + +

+ If you like JHipster, don't forget to give us a star on Github! +

+
+
diff --git a/jhipster/src/main/webapp/app/home/home.component.ts b/jhipster/src/main/webapp/app/home/home.component.ts new file mode 100644 index 0000000000..b16838377e --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.component.ts @@ -0,0 +1,50 @@ +import { Component, OnInit } from '@angular/core'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { EventManager, JhiLanguageService } from 'ng-jhipster'; + +import { Account, LoginModalService, Principal } from '../shared'; + +@Component({ + selector: 'jhi-home', + templateUrl: './home.component.html', + styleUrls: [ + 'home.scss' + ] + +}) +export class HomeComponent implements OnInit { + account: Account; + modalRef: NgbModalRef; + + constructor( + private jhiLanguageService: JhiLanguageService, + private principal: Principal, + private loginModalService: LoginModalService, + private eventManager: EventManager + ) { + this.jhiLanguageService.setLocations(['home']); + } + + ngOnInit() { + this.principal.identity().then((account) => { + this.account = account; + }); + this.registerAuthenticationSuccess(); + } + + registerAuthenticationSuccess() { + this.eventManager.subscribe('authenticationSuccess', (message) => { + this.principal.identity().then((account) => { + this.account = account; + }); + }); + } + + isAuthenticated() { + return this.principal.isAuthenticated(); + } + + login() { + this.modalRef = this.loginModalService.open(); + } +} diff --git a/jhipster/src/main/webapp/app/home/home.module.ts b/jhipster/src/main/webapp/app/home/home.module.ts new file mode 100644 index 0000000000..172c605249 --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.module.ts @@ -0,0 +1,23 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { BaeldungSharedModule } from '../shared'; + +import { HOME_ROUTE, HomeComponent } from './'; + + +@NgModule({ + imports: [ + BaeldungSharedModule, + RouterModule.forRoot([ HOME_ROUTE ], { useHash: true }) + ], + declarations: [ + HomeComponent, + ], + entryComponents: [ + ], + providers: [ + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class BaeldungHomeModule {} diff --git a/jhipster/src/main/webapp/app/home/home.route.ts b/jhipster/src/main/webapp/app/home/home.route.ts new file mode 100644 index 0000000000..cba14bf0c6 --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.route.ts @@ -0,0 +1,14 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from '../shared'; +import { HomeComponent } from './'; + +export const HOME_ROUTE: Route = { + path: '', + component: HomeComponent, + data: { + authorities: [], + pageTitle: 'home.title' + }, + canActivate: [UserRouteAccessService] +}; diff --git a/jhipster/src/main/webapp/app/home/home.scss b/jhipster/src/main/webapp/app/home/home.scss new file mode 100644 index 0000000000..578787b1c9 --- /dev/null +++ b/jhipster/src/main/webapp/app/home/home.scss @@ -0,0 +1,26 @@ + +/* ========================================================================== +Main page styles +========================================================================== */ + +.hipster { + display: inline-block; + width: 347px; + height: 497px; + background: url("../../content/images/hipster.png") no-repeat center top; + background-size: contain; +} + +/* wait autoprefixer update to allow simple generation of high pixel density media query */ +@media +only screen and (-webkit-min-device-pixel-ratio: 2), +only screen and ( min--moz-device-pixel-ratio: 2), +only screen and ( -o-min-device-pixel-ratio: 2/1), +only screen and ( min-device-pixel-ratio: 2), +only screen and ( min-resolution: 192dpi), +only screen and ( min-resolution: 2dppx) { + .hipster { + background: url("../../content/images/hipster2x.png") no-repeat center top; + background-size: contain; + } +} diff --git a/jhipster/src/main/webapp/app/home/index.ts b/jhipster/src/main/webapp/app/home/index.ts new file mode 100644 index 0000000000..d76285b277 --- /dev/null +++ b/jhipster/src/main/webapp/app/home/index.ts @@ -0,0 +1,3 @@ +export * from './home.component'; +export * from './home.route'; +export * from './home.module'; diff --git a/jhipster/src/main/webapp/app/layouts/error/error.component.html b/jhipster/src/main/webapp/app/layouts/error/error.component.html new file mode 100644 index 0000000000..92fe2f1512 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/error/error.component.html @@ -0,0 +1,17 @@ +
+
+
+ +
+
+

Error Page!

+ +
+
{{errorMessage}} +
+
+
You are not authorized to access the page. +
+
+
+
diff --git a/jhipster/src/main/webapp/app/layouts/error/error.component.ts b/jhipster/src/main/webapp/app/layouts/error/error.component.ts new file mode 100644 index 0000000000..983809f541 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/error/error.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +@Component({ + selector: 'jhi-error', + templateUrl: './error.component.html' +}) +export class ErrorComponent implements OnInit { + errorMessage: string; + error403: boolean; + + constructor( + private jhiLanguageService: JhiLanguageService + ) { + this.jhiLanguageService.setLocations(['error']); + } + + ngOnInit() { + } +} diff --git a/jhipster/src/main/webapp/app/layouts/error/error.route.ts b/jhipster/src/main/webapp/app/layouts/error/error.route.ts new file mode 100644 index 0000000000..5f6085076c --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/error/error.route.ts @@ -0,0 +1,25 @@ +import { Routes } from '@angular/router'; + +import { UserRouteAccessService } from '../../shared'; +import { ErrorComponent } from './error.component'; + +export const errorRoute: Routes = [ + { + path: 'error', + component: ErrorComponent, + data: { + authorities: [], + pageTitle: 'error.title' + }, + canActivate: [UserRouteAccessService] + }, + { + path: 'accessdenied', + component: ErrorComponent, + data: { + authorities: [], + pageTitle: 'error.title' + }, + canActivate: [UserRouteAccessService] + } +]; diff --git a/jhipster/src/main/webapp/app/layouts/footer/footer.component.html b/jhipster/src/main/webapp/app/layouts/footer/footer.component.html new file mode 100644 index 0000000000..4e4fda05bf --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/footer/footer.component.html @@ -0,0 +1,4 @@ + + diff --git a/jhipster/src/main/webapp/app/layouts/footer/footer.component.ts b/jhipster/src/main/webapp/app/layouts/footer/footer.component.ts new file mode 100644 index 0000000000..37da8bca75 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/footer/footer.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'jhi-footer', + templateUrl: './footer.component.html' +}) +export class FooterComponent {} diff --git a/jhipster/src/main/webapp/app/layouts/index.ts b/jhipster/src/main/webapp/app/layouts/index.ts new file mode 100644 index 0000000000..f25305a0ac --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/index.ts @@ -0,0 +1,10 @@ +export * from './error/error.component'; +export * from './error/error.route'; +export * from './main/main.component'; +export * from './footer/footer.component'; +export * from './navbar/navbar.component'; +export * from './navbar/active-menu.directive'; +export * from './profiles/page-ribbon.component'; +export * from './profiles/profile.service'; +export * from './profiles/profile-info.model'; +export * from './layout-routing.module'; diff --git a/jhipster/src/main/webapp/app/layouts/layout-routing.module.ts b/jhipster/src/main/webapp/app/layouts/layout-routing.module.ts new file mode 100644 index 0000000000..8edbdff26c --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/layout-routing.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes, Resolve } from '@angular/router'; + +import { navbarRoute } from '../app.route'; +import { errorRoute } from './'; + +let LAYOUT_ROUTES = [ + navbarRoute, + ...errorRoute +]; + +@NgModule({ + imports: [ + RouterModule.forRoot(LAYOUT_ROUTES, { useHash: true }) + ], + exports: [ + RouterModule + ] +}) +export class LayoutRoutingModule {} diff --git a/jhipster/src/main/webapp/app/layouts/main/main.component.html b/jhipster/src/main/webapp/app/layouts/main/main.component.html new file mode 100644 index 0000000000..5bcd12ab0b --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/main/main.component.html @@ -0,0 +1,11 @@ + +
+ +
+
+
+ + +
+ +
diff --git a/jhipster/src/main/webapp/app/layouts/main/main.component.ts b/jhipster/src/main/webapp/app/layouts/main/main.component.ts new file mode 100644 index 0000000000..c5982f60ca --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/main/main.component.ts @@ -0,0 +1,47 @@ +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRouteSnapshot, NavigationEnd, RoutesRecognized } from '@angular/router'; + +import { JhiLanguageHelper, StateStorageService } from '../../shared'; + +@Component({ + selector: 'jhi-main', + templateUrl: './main.component.html' +}) +export class JhiMainComponent implements OnInit { + + constructor( + private jhiLanguageHelper: JhiLanguageHelper, + private router: Router, + private $storageService: StateStorageService, + ) {} + + private getPageTitle(routeSnapshot: ActivatedRouteSnapshot) { + let title: string = (routeSnapshot.data && routeSnapshot.data['pageTitle']) ? routeSnapshot.data['pageTitle'] : 'baeldungApp'; + if (routeSnapshot.firstChild) { + title = this.getPageTitle(routeSnapshot.firstChild) || title; + } + return title; + } + + ngOnInit() { + this.router.events.subscribe((event) => { + if (event instanceof NavigationEnd) { + this.jhiLanguageHelper.updateTitle(this.getPageTitle(this.router.routerState.snapshot.root)); + } + if (event instanceof RoutesRecognized) { + let params = {}; + let destinationData = {}; + let destinationName = ''; + let destinationEvent = event.state.root.firstChild.children[0]; + if (destinationEvent !== undefined) { + params = destinationEvent.params; + destinationData = destinationEvent.data; + destinationName = destinationEvent.url[0].path; + } + let from = {name: this.router.url.slice(1)}; + let destination = {name: destinationName, data: destinationData}; + this.$storageService.storeDestinationState(destination, params, from); + } + }); + } +} diff --git a/jhipster/src/main/webapp/app/layouts/navbar/active-menu.directive.ts b/jhipster/src/main/webapp/app/layouts/navbar/active-menu.directive.ts new file mode 100644 index 0000000000..87275c9d57 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/navbar/active-menu.directive.ts @@ -0,0 +1,26 @@ +import { Directive, OnInit, ElementRef, Renderer, Input} from '@angular/core'; +import { TranslateService, LangChangeEvent } from 'ng2-translate'; + +@Directive({ + selector: '[jhiActiveMenu]' +}) +export class ActiveMenuDirective implements OnInit { + @Input() jhiActiveMenu: string; + + constructor(private el: ElementRef, private renderer: Renderer, private translateService: TranslateService) {} + + ngOnInit() { + this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { + this.updateActiveFlag(event.lang); + }); + this.updateActiveFlag(this.translateService.currentLang); + } + + updateActiveFlag(selectedLanguage) { + if (this.jhiActiveMenu === selectedLanguage) { + this.renderer.setElementClass(this.el.nativeElement, 'active', true); + } else { + this.renderer.setElementClass(this.el.nativeElement, 'active', false); + } + } +} diff --git a/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.html b/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.html new file mode 100644 index 0000000000..07b7abb25c --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.html @@ -0,0 +1,168 @@ + diff --git a/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.ts b/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.ts new file mode 100644 index 0000000000..8f58bfebd9 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/navbar/navbar.component.ts @@ -0,0 +1,81 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { ProfileService } from '../profiles/profile.service'; // FIXME barrel doesnt work here +import { JhiLanguageHelper, Principal, LoginModalService, LoginService } from '../../shared'; + +import { VERSION, DEBUG_INFO_ENABLED } from '../../app.constants'; + +@Component({ + selector: 'jhi-navbar', + templateUrl: './navbar.component.html', + styleUrls: [ + 'navbar.scss' + ] +}) +export class NavbarComponent implements OnInit { + + inProduction: boolean; + isNavbarCollapsed: boolean; + languages: any[]; + swaggerEnabled: boolean; + modalRef: NgbModalRef; + version: string; + + constructor( + private loginService: LoginService, + private languageHelper: JhiLanguageHelper, + private languageService: JhiLanguageService, + private principal: Principal, + private loginModalService: LoginModalService, + private profileService: ProfileService, + private router: Router + ) { + this.version = DEBUG_INFO_ENABLED ? 'v' + VERSION : ''; + this.isNavbarCollapsed = true; + this.languageService.addLocation('home'); + } + + ngOnInit() { + this.languageHelper.getAll().then((languages) => { + this.languages = languages; + }); + + this.profileService.getProfileInfo().subscribe(profileInfo => { + this.inProduction = profileInfo.inProduction; + this.swaggerEnabled = profileInfo.swaggerEnabled; + }); + } + + changeLanguage(languageKey: string) { + this.languageService.changeLanguage(languageKey); + } + + collapseNavbar() { + this.isNavbarCollapsed = true; + } + + isAuthenticated() { + return this.principal.isAuthenticated(); + } + + login() { + this.modalRef = this.loginModalService.open(); + } + + logout() { + this.collapseNavbar(); + this.loginService.logout(); + this.router.navigate(['']); + } + + toggleNavbar() { + this.isNavbarCollapsed = !this.isNavbarCollapsed; + } + + getImageUrl() { + return this.isAuthenticated() ? this.principal.getImageUrl() : null; + } +} diff --git a/jhipster/src/main/webapp/app/layouts/navbar/navbar.scss b/jhipster/src/main/webapp/app/layouts/navbar/navbar.scss new file mode 100644 index 0000000000..d48d609543 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/navbar/navbar.scss @@ -0,0 +1,70 @@ + +/* ========================================================================== +Navbar +========================================================================== */ +.navbar-version { + font-size: 10px; + color: #ccc +} + +.jh-navbar { + background-color: #353d47; + padding: .2em 1em; + .profile-image { + margin: -10px 0px; + height: 40px; + width: 40px; + border-radius: 50%; + } + .dropdown-item.active, .dropdown-item.active:focus, .dropdown-item.active:hover { + background-color: #353d47; + } + .dropdown-toggle::after { + margin-left: 0.15em; + } + ul.navbar-nav { + padding: 0.5em; + .nav-item { + margin-left: 1.5rem; + } + } + a.nav-link { + font-weight: 400; + } + .jh-navbar-toggler { + color: #ccc; + font-size: 1.5em; + padding: 10px; + &:hover { + color: #fff; + } + } +} + +@media screen and (max-width: 992px) { + .jh-logo-container { + width: 100%; + } +} + +.navbar-title { + display: inline-block; + vertical-align: middle; +} + +/* ========================================================================== +Logo styles +========================================================================== */ +.navbar-brand { + &.logo { + padding: 5px 15px; + .logo-img { + height: 45px; + width: 70px; + display: inline-block; + vertical-align: middle; + background: url("../../../content/images/logo-jhipster.png") no-repeat center center; + background-size: contain; + } + } +} diff --git a/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts b/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts new file mode 100644 index 0000000000..f7ba492f66 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit } from '@angular/core'; +import { ProfileService } from './profile.service'; +import { ProfileInfo } from './profile-info.model'; + +@Component({ + selector: 'jhi-page-ribbon', + template: ``, + styleUrls: [ + 'page-ribbon.scss' + ] +}) +export class PageRibbonComponent implements OnInit { + + profileInfo: ProfileInfo; + ribbonEnv: string; + + constructor(private profileService: ProfileService) {} + + ngOnInit() { + this.profileService.getProfileInfo().subscribe(profileInfo => { + this.profileInfo = profileInfo; + this.ribbonEnv = profileInfo.ribbonEnv; + }); + } +} diff --git a/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.scss b/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.scss new file mode 100644 index 0000000000..5efd11c03e --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/profiles/page-ribbon.scss @@ -0,0 +1,32 @@ + +/* ========================================================================== +Developement Ribbon +========================================================================== */ +.ribbon { + background-color: rgba(170, 0, 0, 0.5); + left: -3.5em; + moz-transform: rotate(-45deg); + ms-transform: rotate(-45deg); + o-transform: rotate(-45deg); + webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + overflow: hidden; + position: absolute; + top: 40px; + white-space: nowrap; + width: 15em; + z-index: 9999; + pointer-events: none; + opacity: 0.75; + a { + color: #fff; + display: block; + font-weight: 400; + margin: 1px 0; + padding: 10px 50px; + text-align: center; + text-decoration: none; + text-shadow: 0 0 5px #444; + pointer-events: none; + } +} diff --git a/jhipster/src/main/webapp/app/layouts/profiles/profile-info.model.ts b/jhipster/src/main/webapp/app/layouts/profiles/profile-info.model.ts new file mode 100644 index 0000000000..f1adc52c7b --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/profiles/profile-info.model.ts @@ -0,0 +1,6 @@ +export class ProfileInfo { + activeProfiles: string[]; + ribbonEnv: string; + inProduction: boolean; + swaggerEnabled: boolean; +} diff --git a/jhipster/src/main/webapp/app/layouts/profiles/profile.service.ts b/jhipster/src/main/webapp/app/layouts/profiles/profile.service.ts new file mode 100644 index 0000000000..347b84d8e5 --- /dev/null +++ b/jhipster/src/main/webapp/app/layouts/profiles/profile.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { ProfileInfo } from './profile-info.model'; + +@Injectable() +export class ProfileService { + + private profileInfoUrl = 'api/profile-info'; + + constructor(private http: Http) { } + + getProfileInfo(): Observable { + return this.http.get(this.profileInfoUrl) + .map((res: Response) => { + let data = res.json(); + let pi = new ProfileInfo(); + pi.activeProfiles = data.activeProfiles; + pi.ribbonEnv = data.ribbonEnv; + pi.inProduction = data.activeProfiles.indexOf('prod') !== -1; + pi.swaggerEnabled = data.activeProfiles.indexOf('swagger') !== -1; + return pi; + }); + } +} diff --git a/jhipster/src/main/webapp/app/polyfills.ts b/jhipster/src/main/webapp/app/polyfills.ts new file mode 100644 index 0000000000..0771ba0b72 --- /dev/null +++ b/jhipster/src/main/webapp/app/polyfills.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +import 'reflect-metadata/Reflect'; +import 'zone.js/dist/zone'; diff --git a/jhipster/src/main/webapp/app/shared/alert/alert-error.component.ts b/jhipster/src/main/webapp/app/shared/alert/alert-error.component.ts new file mode 100644 index 0000000000..e9e3e2b7a0 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/alert/alert-error.component.ts @@ -0,0 +1,101 @@ +import { Component, OnDestroy } from '@angular/core'; +import { TranslateService } from 'ng2-translate'; +import { EventManager, AlertService } from 'ng-jhipster'; +import { Subscription } from 'rxjs/Rx'; + +@Component({ + selector: 'jhi-alert-error', + template: ` + ` +}) +export class JhiAlertErrorComponent implements OnDestroy { + + alerts: any[]; + cleanHttpErrorListener: Subscription; + + constructor(private alertService: AlertService, private eventManager: EventManager, private translateService: TranslateService) { + this.alerts = []; + + this.cleanHttpErrorListener = eventManager.subscribe('baeldungApp.httpError', (response) => { + let i; + let httpResponse = response.content; + switch (httpResponse.status) { + // connection refused, server not reachable + case 0: + this.addErrorAlert('Server not reachable', 'error.server.not.reachable'); + break; + + case 400: + let arr = Array.from(httpResponse.headers._headers); + let headers = []; + for (i = 0; i < arr.length; i++) { + if (arr[i][0].endsWith('app-error') || arr[i][0].endsWith('app-params')) { + headers.push(arr[i][0]); + } + } + headers.sort(); + let errorHeader = httpResponse.headers.get(headers[0]); + let entityKey = httpResponse.headers.get(headers[1]); + if (errorHeader) { + let entityName = translateService.instant('global.menu.entities.' + entityKey); + this.addErrorAlert(errorHeader, errorHeader, {entityName: entityName}); + } else if (httpResponse.text() !== '' && httpResponse.json() && httpResponse.json().fieldErrors) { + let fieldErrors = httpResponse.json().fieldErrors; + for (i = 0; i < fieldErrors.length; i++) { + let fieldError = fieldErrors[i]; + // convert 'something[14].other[4].id' to 'something[].other[].id' so translations can be written to it + let convertedField = fieldError.field.replace(/\[\d*\]/g, '[]'); + let fieldName = translateService.instant('baeldungApp.' + + fieldError.objectName + '.' + convertedField); + this.addErrorAlert( + 'Field ' + fieldName + ' cannot be empty', 'error.' + fieldError.message, {fieldName: fieldName}); + } + } else if (httpResponse.text() !== '' && httpResponse.json() && httpResponse.json().message) { + this.addErrorAlert(httpResponse.json().message, httpResponse.json().message, httpResponse.json()); + } else { + this.addErrorAlert(httpResponse.text()); + } + break; + + case 404: + this.addErrorAlert('Not found', 'error.url.not.found'); + break; + + default: + if (httpResponse.text() !== '' && httpResponse.json() && httpResponse.json().message) { + this.addErrorAlert(httpResponse.json().message); + } else { + this.addErrorAlert(JSON.stringify(httpResponse)); // Fixme find a way to parse httpResponse + } + } + }); + } + + ngOnDestroy() { + if (this.cleanHttpErrorListener !== undefined && this.cleanHttpErrorListener !== null) { + this.eventManager.destroy(this.cleanHttpErrorListener); + this.alerts = []; + } + } + + addErrorAlert (message, key?, data?) { + key = key && key !== null ? key : message; + this.alerts.push( + this.alertService.addAlert( + { + type: 'danger', + msg: key, + params: data, + timeout: 5000, + toast: this.alertService.isToast(), + scoped: true + }, + this.alerts + ) + ); + } +} diff --git a/jhipster/src/main/webapp/app/shared/alert/alert.component.ts b/jhipster/src/main/webapp/app/shared/alert/alert.component.ts new file mode 100644 index 0000000000..b8aa418ac5 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/alert/alert.component.ts @@ -0,0 +1,26 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { AlertService } from 'ng-jhipster'; + +@Component({ + selector: 'jhi-alert', + template: ` + ` +}) +export class JhiAlertComponent implements OnInit, OnDestroy { + alerts: any[]; + + constructor(private alertService: AlertService) { } + + ngOnInit() { + this.alerts = this.alertService.get(); + } + + ngOnDestroy() { + this.alerts = []; + } + +} diff --git a/jhipster/src/main/webapp/app/shared/auth/account.service.ts b/jhipster/src/main/webapp/app/shared/auth/account.service.ts new file mode 100644 index 0000000000..6d21943d49 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/account.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +@Injectable() +export class AccountService { + constructor(private http: Http) { } + + get(): Observable { + return this.http.get('api/account').map((res: Response) => res.json()); + } + + save(account: any): Observable { + return this.http.post('api/account', account); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/auth-jwt.service.ts b/jhipster/src/main/webapp/app/shared/auth/auth-jwt.service.ts new file mode 100644 index 0000000000..9be418d175 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/auth-jwt.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, Headers, URLSearchParams } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; +import { LocalStorageService, SessionStorageService } from 'ng2-webstorage'; + +@Injectable() +export class AuthServerProvider { + constructor( + private http: Http, + private $localStorage: LocalStorageService, + private $sessionStorage: SessionStorageService + ) {} + + getToken () { + return this.$localStorage.retrieve('authenticationToken') || this.$sessionStorage.retrieve('authenticationToken'); + } + + login (credentials): Observable { + + let data = { + username: credentials.username, + password: credentials.password, + rememberMe: credentials.rememberMe + }; + return this.http.post('api/authenticate', data).map(authenticateSuccess.bind(this)); + + function authenticateSuccess (resp) { + let bearerToken = resp.headers.get('Authorization'); + if (bearerToken && bearerToken.slice(0, 7) === 'Bearer ') { + let jwt = bearerToken.slice(7, bearerToken.length); + this.storeAuthenticationToken(jwt, credentials.rememberMe); + return jwt; + } + } + } + + loginWithToken(jwt, rememberMe) { + if (jwt) { + this.storeAuthenticationToken(jwt, rememberMe); + return Promise.resolve(jwt); + } else { + return Promise.reject('auth-jwt-service Promise reject'); // Put appropriate error message here + } + } + + storeAuthenticationToken(jwt, rememberMe) { + if (rememberMe) { + this.$localStorage.store('authenticationToken', jwt); + } else { + this.$sessionStorage.store('authenticationToken', jwt); + } + } + + logout (): Observable { + return new Observable(observer => { + this.$localStorage.clear('authenticationToken'); + this.$sessionStorage.clear('authenticationToken'); + observer.complete(); + }); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/auth.service.ts b/jhipster/src/main/webapp/app/shared/auth/auth.service.ts new file mode 100644 index 0000000000..9e21fb6737 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/auth.service.ts @@ -0,0 +1,65 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; + +import { LoginModalService } from '../login/login-modal.service'; +import { Principal } from './principal.service'; +import { StateStorageService } from './state-storage.service'; + +@Injectable() +export class AuthService { + + constructor( + private principal: Principal, + private stateStorageService: StateStorageService, + private loginModalService: LoginModalService, + private router: Router + ) {} + + authorize (force) { + let authReturn = this.principal.identity(force).then(authThen.bind(this)); + + return authReturn; + + function authThen () { + let isAuthenticated = this.principal.isAuthenticated(); + let toStateInfo = this.stateStorageService.getDestinationState().destination; + + // an authenticated user can't access to login and register pages + if (isAuthenticated && (toStateInfo.name === 'register' || toStateInfo.name === 'social-auth')) { + this.router.navigate(['']); + return false; + } + + // recover and clear previousState after external login redirect (e.g. oauth2) + let fromStateInfo = this.stateStorageService.getDestinationState().from; + let previousState = this.stateStorageService.getPreviousState(); + if (isAuthenticated && !fromStateInfo.name && previousState) { + this.stateStorageService.resetPreviousState(); + this.router.navigate([previousState.name], { queryParams: previousState.params }); + return false; + } + + if (toStateInfo.data.authorities && toStateInfo.data.authorities.length > 0) { + return this.principal.hasAnyAuthority(toStateInfo.data.authorities).then(hasAnyAuthority => { + if (!hasAnyAuthority) { + if (isAuthenticated) { + // user is signed in but not authorized for desired state + this.router.navigate(['accessdenied']); + } else { + // user is not authenticated. Show the state they wanted before you + // send them to the login service, so you can return them when you're done + let toStateParamsInfo = this.stateStorageService.getDestinationState().params; + this.stateStorageService.storePreviousState(toStateInfo.name, toStateParamsInfo); + // now, send them to the signin state so they can log in + this.router.navigate(['accessdenied']).then(() => { + this.loginModalService.open(); + }); + } + } + return hasAnyAuthority; + }); + } + return true; + } + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/csrf.service.ts b/jhipster/src/main/webapp/app/shared/auth/csrf.service.ts new file mode 100644 index 0000000000..6f1064112a --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/csrf.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; +import { CookieService } from 'angular2-cookie/core'; + +@Injectable() +export class CSRFService { + + constructor(private cookieService: CookieService) {} + + getCSRF(name?: string) { + name = `${name ? name : 'XSRF-TOKEN'}`; + return this.cookieService.get(name); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/has-any-authority.directive.ts b/jhipster/src/main/webapp/app/shared/auth/has-any-authority.directive.ts new file mode 100644 index 0000000000..858c105fd5 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/has-any-authority.directive.ts @@ -0,0 +1,42 @@ +import { Directive, ElementRef, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { Principal } from './principal.service'; + +/** + * @whatItDoes Conditionally includes an HTML element if current user has any + * of the authorities passed as the `expression`. + * + * @howToUse + * ``` + * ... + * + * ... + * ``` + */ +@Directive({ + selector: '[jhiHasAnyAuthority]' +}) +export class HasAnyAuthorityDirective { + + private authorities: string[]; + + constructor(private principal: Principal, private templateRef: TemplateRef, private viewContainerRef: ViewContainerRef) { + } + + @Input() + set jhiHasAnyAuthority(value: string|string[]) { + this.authorities = typeof value === 'string' ? [ value ] : value; + this.updateView(); + // Get notified each time authentication state changes. + this.principal.getAuthenticationState().subscribe(identity => this.updateView()); + } + + private updateView(): void { + this.principal.hasAnyAuthority(this.authorities).then(result => { + if (result) { + this.viewContainerRef.createEmbeddedView(this.templateRef); + } else { + this.viewContainerRef.clear(); + } + }); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/principal.service.ts b/jhipster/src/main/webapp/app/shared/auth/principal.service.ts new file mode 100644 index 0000000000..2c7f05dd56 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/principal.service.ts @@ -0,0 +1,93 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { AccountService } from './account.service'; + +@Injectable() +export class Principal { + private userIdentity: any; + private authenticated = false; + private authenticationState = new Subject(); + + constructor( + private account: AccountService + ) {} + + authenticate (identity) { + this.userIdentity = identity; + this.authenticated = identity !== null; + this.authenticationState.next(this.userIdentity); + } + + hasAnyAuthority (authorities: string[]): Promise { + if (!this.authenticated || !this.userIdentity || !this.userIdentity.authorities) { + return Promise.resolve(false); + } + + for (let i = 0; i < authorities.length; i++) { + if (this.userIdentity.authorities.indexOf(authorities[i]) !== -1) { + return Promise.resolve(true); + } + } + + return Promise.resolve(false); + } + + hasAuthority (authority: string): Promise { + if (!this.authenticated) { + return Promise.resolve(false); + } + + return this.identity().then(id => { + return Promise.resolve(id.authorities && id.authorities.indexOf(authority) !== -1); + }, () => { + return Promise.resolve(false); + }); + } + + identity (force?: boolean): Promise { + if (force === true) { + this.userIdentity = undefined; + } + + // check and see if we have retrieved the userIdentity data from the server. + // if we have, reuse it by immediately resolving + if (this.userIdentity) { + return Promise.resolve(this.userIdentity); + } + + // retrieve the userIdentity data from the server, update the identity object, and then resolve. + return this.account.get().toPromise().then(account => { + if (account) { + this.userIdentity = account; + this.authenticated = true; + } else { + this.userIdentity = null; + this.authenticated = false; + } + this.authenticationState.next(this.userIdentity); + return this.userIdentity; + }).catch(err => { + this.userIdentity = null; + this.authenticated = false; + this.authenticationState.next(this.userIdentity); + return null; + }); + } + + isAuthenticated (): boolean { + return this.authenticated; + } + + isIdentityResolved (): boolean { + return this.userIdentity !== undefined; + } + + getAuthenticationState(): Observable { + return this.authenticationState.asObservable(); + } + + getImageUrl(): String { + return this.isIdentityResolved () ? this.userIdentity.imageUrl : null; + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/state-storage.service.ts b/jhipster/src/main/webapp/app/shared/auth/state-storage.service.ts new file mode 100644 index 0000000000..1fe364b06d --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/state-storage.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { SessionStorageService } from 'ng2-webstorage'; + +@Injectable() +export class StateStorageService { + constructor( + private $sessionStorage: SessionStorageService + ) {} + + getPreviousState() { + return this.$sessionStorage.retrieve('previousState'); + } + + resetPreviousState() { + this.$sessionStorage.clear('previousState'); + } + + storePreviousState(previousStateName, previousStateParams) { + let previousState = { 'name': previousStateName, 'params': previousStateParams }; + this.$sessionStorage.store('previousState', previousState); + } + + getDestinationState() { + return this.$sessionStorage.retrieve('destinationState'); + } + + storeDestinationState(destinationState, destinationStateParams, fromState) { + let destinationInfo = { + 'destination': { + 'name': destinationState.name, + 'data': destinationState.data, + }, + 'params': destinationStateParams, + 'from': { + 'name': fromState.name, + } + }; + this.$sessionStorage.store('destinationState', destinationInfo); + } +} diff --git a/jhipster/src/main/webapp/app/shared/auth/user-route-access-service.ts b/jhipster/src/main/webapp/app/shared/auth/user-route-access-service.ts new file mode 100644 index 0000000000..95eb236672 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/auth/user-route-access-service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, Router, ActivatedRouteSnapshot } from '@angular/router'; + +import { AuthService } from '../'; + +@Injectable() +export class UserRouteAccessService implements CanActivate { + + constructor(private router: Router, private auth: AuthService) { + } + + canActivate(route: ActivatedRouteSnapshot): boolean | Promise { + return this.auth.authorize(false).then( canActivate => canActivate); + } +} diff --git a/jhipster/src/main/webapp/app/shared/constants/pagination.constants.ts b/jhipster/src/main/webapp/app/shared/constants/pagination.constants.ts new file mode 100644 index 0000000000..a148d4579b --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/constants/pagination.constants.ts @@ -0,0 +1 @@ +export const ITEMS_PER_PAGE = 20; diff --git a/jhipster/src/main/webapp/app/shared/index.ts b/jhipster/src/main/webapp/app/shared/index.ts new file mode 100644 index 0000000000..d2687cf884 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/index.ts @@ -0,0 +1,23 @@ +export * from './alert/alert.component'; +export * from './alert/alert-error.component'; +export * from './auth/csrf.service'; +export * from './auth/state-storage.service'; +export * from './auth/account.service'; +export * from './auth/auth-jwt.service'; +export * from './auth/auth.service'; +export * from './auth/principal.service'; +export * from './auth/has-any-authority.directive'; +export * from './language/language.constants'; +export * from './language/language.helper'; +export * from './language/language.pipe'; +export * from './login/login.component'; +export * from './login/login.service'; +export * from './login/login-modal.service'; +export * from './constants/pagination.constants'; +export * from './user/account.model'; +export * from './user/user.model'; +export * from './user/user.service'; +export * from './shared-libs.module'; +export * from './shared-common.module'; +export * from './shared.module'; +export * from './auth/user-route-access-service'; diff --git a/jhipster/src/main/webapp/app/shared/language/language.constants.ts b/jhipster/src/main/webapp/app/shared/language/language.constants.ts new file mode 100644 index 0000000000..2292ef4624 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/language/language.constants.ts @@ -0,0 +1,8 @@ +/* + Languages codes are ISO_639-1 codes, see http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + They are written in English to avoid character encoding issues (not a perfect solution) +*/ +export const LANGUAGES: string[] = [ + 'en' + // jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array +]; diff --git a/jhipster/src/main/webapp/app/shared/language/language.helper.ts b/jhipster/src/main/webapp/app/shared/language/language.helper.ts new file mode 100644 index 0000000000..d8d609bf0c --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/language/language.helper.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core'; +import { Title } from '@angular/platform-browser'; +import { Router, ActivatedRouteSnapshot } from '@angular/router'; +import { TranslateService, TranslationChangeEvent, LangChangeEvent } from 'ng2-translate/ng2-translate'; + +import { LANGUAGES } from './language.constants'; + +@Injectable() +export class JhiLanguageHelper { + + constructor (private translateService: TranslateService, private titleService: Title, private router: Router) { + this.init(); + } + + getAll(): Promise { + return Promise.resolve(LANGUAGES); + } + + /** + * Update the window title using params in the following + * order: + * 1. titleKey parameter + * 2. $state.$current.data.pageTitle (current state page title) + * 3. 'global.title' + */ + updateTitle(titleKey?: string) { + if (!titleKey) { + titleKey = this.getPageTitle(this.router.routerState.snapshot.root); + } + + this.translateService.get(titleKey).subscribe(title => { + this.titleService.setTitle(title); + }); + } + + private init () { + this.translateService.onTranslationChange.subscribe((event: TranslationChangeEvent) => { + this.updateTitle(); + }); + + this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { + this.updateTitle(); + }); + } + + private getPageTitle(routeSnapshot: ActivatedRouteSnapshot) { + let title: string = (routeSnapshot.data && routeSnapshot.data['pageTitle']) ? routeSnapshot.data['pageTitle'] : 'baeldungApp'; + if (routeSnapshot.firstChild) { + title = this.getPageTitle(routeSnapshot.firstChild) || title; + } + return title; + } +} diff --git a/jhipster/src/main/webapp/app/shared/language/language.pipe.ts b/jhipster/src/main/webapp/app/shared/language/language.pipe.ts new file mode 100644 index 0000000000..d271c8e2c2 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/language/language.pipe.ts @@ -0,0 +1,42 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({name: 'findLanguageFromKey'}) +export class FindLanguageFromKeyPipe implements PipeTransform { + private languages: any = { + 'ca': 'Català', + 'cs': 'Český', + 'da': 'Dansk', + 'de': 'Deutsch', + 'el': 'Ελληνικά', + 'en': 'English', + 'es': 'Español', + 'et': 'Eesti', + 'fr': 'Français', + 'gl': 'Galego', + 'hu': 'Magyar', + 'hi': 'हिंदी', + 'hy': 'Հայերեն', + 'it': 'Italiano', + 'ja': '日本語', + 'ko': '한국어', + 'mr': 'मराठी', + 'nl': 'Nederlands', + 'pl': 'Polski', + 'pt-br': 'Português (Brasil)', + 'pt-pt': 'Português', + 'ro': 'Română', + 'ru': 'Русский', + 'sk': 'Slovenský', + 'sr': 'Srpski', + 'sv': 'Svenska', + 'ta': 'தமிழ்', + 'th': 'ไทย', + 'tr': 'Türkçe', + 'vi': 'Tiếng Việt', + 'zh-cn': '中文(简体)', + 'zh-tw': '繁體中文' + }; + transform(lang: string): string { + return this.languages[lang]; + } +} diff --git a/jhipster/src/main/webapp/app/shared/login/login-modal.service.ts b/jhipster/src/main/webapp/app/shared/login/login-modal.service.ts new file mode 100644 index 0000000000..9e435978ea --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/login/login-modal.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; + +import { JhiLoginModalComponent } from './login.component'; + +@Injectable() +export class LoginModalService { + private isOpen = false; + constructor ( + private modalService: NgbModal, + ) {} + + open (): NgbModalRef { + if (this.isOpen) { + return; + } + this.isOpen = true; + let modalRef = this.modalService.open(JhiLoginModalComponent); + modalRef.result.then(result => { + this.isOpen = false; + }, (reason) => { + this.isOpen = false; + }); + return modalRef; + } +} diff --git a/jhipster/src/main/webapp/app/shared/login/login.component.html b/jhipster/src/main/webapp/app/shared/login/login.component.html new file mode 100644 index 0000000000..a6b6b19249 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/login/login.component.html @@ -0,0 +1,46 @@ + + diff --git a/jhipster/src/main/webapp/app/shared/login/login.component.ts b/jhipster/src/main/webapp/app/shared/login/login.component.ts new file mode 100644 index 0000000000..90acbb03ac --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/login/login.component.ts @@ -0,0 +1,90 @@ +import { Component, OnInit, AfterViewInit, Renderer, ElementRef } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { Router } from '@angular/router'; +import { JhiLanguageService, EventManager } from 'ng-jhipster'; + +import { LoginService } from '../login/login.service'; +import { StateStorageService } from '../auth/state-storage.service'; + +@Component({ + selector: 'jhi-login-modal', + templateUrl: './login.component.html' +}) +export class JhiLoginModalComponent implements OnInit, AfterViewInit { + authenticationError: boolean; + password: string; + rememberMe: boolean; + username: string; + credentials: any; + + constructor( + private eventManager: EventManager, + private languageService: JhiLanguageService, + private loginService: LoginService, + private stateStorageService: StateStorageService, + private elementRef: ElementRef, + private renderer: Renderer, + private router: Router, + public activeModal: NgbActiveModal + ) { + this.credentials = {}; + } + + ngOnInit() { + this.languageService.addLocation('login'); + } + + ngAfterViewInit() { + this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#username'), 'focus', []); + } + + cancel () { + this.credentials = { + username: null, + password: null, + rememberMe: true + }; + this.authenticationError = false; + this.activeModal.dismiss('cancel'); + } + + login () { + this.loginService.login({ + username: this.username, + password: this.password, + rememberMe: this.rememberMe + }).then(() => { + this.authenticationError = false; + this.activeModal.dismiss('login success'); + if (this.router.url === '/register' || (/activate/.test(this.router.url)) || + this.router.url === '/finishReset' || this.router.url === '/requestReset') { + this.router.navigate(['']); + } + + this.eventManager.broadcast({ + name: 'authenticationSuccess', + content: 'Sending Authentication Success' + }); + + // // previousState was set in the authExpiredInterceptor before being redirected to login modal. + // // since login is succesful, go to stored previousState and clear previousState + let previousState = this.stateStorageService.getPreviousState(); + if (previousState) { + this.stateStorageService.resetPreviousState(); + this.router.navigate([previousState.name], { queryParams: previousState.params }); + } + }).catch(() => { + this.authenticationError = true; + }); + } + + register () { + this.activeModal.dismiss('to state register'); + this.router.navigate(['/register']); + } + + requestResetPassword () { + this.activeModal.dismiss('to state requestReset'); + this.router.navigate(['/reset', 'request']); + } +} diff --git a/jhipster/src/main/webapp/app/shared/login/login.service.ts b/jhipster/src/main/webapp/app/shared/login/login.service.ts new file mode 100644 index 0000000000..2235299225 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/login/login.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@angular/core'; +import { JhiLanguageService } from 'ng-jhipster'; + +import { Principal } from '../auth/principal.service'; +import { AuthServerProvider } from '../auth/auth-jwt.service'; + +@Injectable() +export class LoginService { + + constructor ( + private languageService: JhiLanguageService, + private principal: Principal, + private authServerProvider: AuthServerProvider + ) {} + + login (credentials, callback?) { + let cb = callback || function() {}; + + return new Promise((resolve, reject) => { + this.authServerProvider.login(credentials).subscribe(data => { + this.principal.identity(true).then(account => { + // After the login the language will be changed to + // the language selected by the user during his registration + if (account !== null) { + this.languageService.changeLanguage(account.langKey); + } + resolve(data); + }); + return cb(); + }, err => { + this.logout(); + reject(err); + return cb(err); + }); + }); + } + loginWithToken(jwt, rememberMe) { + return this.authServerProvider.loginWithToken(jwt, rememberMe); + } + + logout () { + this.authServerProvider.logout().subscribe(); + this.principal.authenticate(null); + } +} diff --git a/jhipster/src/main/webapp/app/shared/shared-common.module.ts b/jhipster/src/main/webapp/app/shared/shared-common.module.ts new file mode 100644 index 0000000000..6f713e216b --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/shared-common.module.ts @@ -0,0 +1,47 @@ +import { NgModule, Sanitizer } from '@angular/core'; +import { Title } from '@angular/platform-browser'; + +import { TranslateService } from 'ng2-translate'; +import { AlertService } from 'ng-jhipster'; + +import { + BaeldungSharedLibsModule, + JhiLanguageHelper, + FindLanguageFromKeyPipe, + JhiAlertComponent, + JhiAlertErrorComponent +} from './'; + + +export function alertServiceProvider(sanitizer: Sanitizer, translateService: TranslateService) { + // set below to true to make alerts look like toast + let isToast = false; + return new AlertService(sanitizer, isToast, translateService); +} + +@NgModule({ + imports: [ + BaeldungSharedLibsModule + ], + declarations: [ + FindLanguageFromKeyPipe, + JhiAlertComponent, + JhiAlertErrorComponent + ], + providers: [ + JhiLanguageHelper, + { + provide: AlertService, + useFactory: alertServiceProvider, + deps: [Sanitizer, TranslateService] + }, + Title + ], + exports: [ + BaeldungSharedLibsModule, + FindLanguageFromKeyPipe, + JhiAlertComponent, + JhiAlertErrorComponent + ] +}) +export class BaeldungSharedCommonModule {} diff --git a/jhipster/src/main/webapp/app/shared/shared-libs.module.ts b/jhipster/src/main/webapp/app/shared/shared-libs.module.ts new file mode 100644 index 0000000000..0bf10eeaa8 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/shared-libs.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; +import { CommonModule } from '@angular/common'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgJhipsterModule } from 'ng-jhipster'; +import { InfiniteScrollModule } from 'angular2-infinite-scroll'; + +@NgModule({ + imports: [ + NgbModule.forRoot(), + NgJhipsterModule.forRoot({ + i18nEnabled: true, + defaultI18nLang: 'en' + }), + InfiniteScrollModule + ], + exports: [ + FormsModule, + HttpModule, + CommonModule, + NgbModule, + NgJhipsterModule, + InfiniteScrollModule + ] +}) +export class BaeldungSharedLibsModule {} diff --git a/jhipster/src/main/webapp/app/shared/shared.module.ts b/jhipster/src/main/webapp/app/shared/shared.module.ts new file mode 100644 index 0000000000..f7af13852b --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/shared.module.ts @@ -0,0 +1,53 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { DatePipe } from '@angular/common'; + +import { CookieService } from 'angular2-cookie/services/cookies.service'; +import { + BaeldungSharedLibsModule, + BaeldungSharedCommonModule, + CSRFService, + AuthService, + AuthServerProvider, + AccountService, + UserService, + StateStorageService, + LoginService, + LoginModalService, + Principal, + HasAnyAuthorityDirective, + JhiLoginModalComponent +} from './'; + +@NgModule({ + imports: [ + BaeldungSharedLibsModule, + BaeldungSharedCommonModule + ], + declarations: [ + JhiLoginModalComponent, + HasAnyAuthorityDirective + ], + providers: [ + CookieService, + LoginService, + LoginModalService, + AccountService, + StateStorageService, + Principal, + CSRFService, + AuthServerProvider, + AuthService, + UserService, + DatePipe + ], + entryComponents: [JhiLoginModalComponent], + exports: [ + BaeldungSharedCommonModule, + JhiLoginModalComponent, + HasAnyAuthorityDirective, + DatePipe + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + +}) +export class BaeldungSharedModule {} diff --git a/jhipster/src/main/webapp/app/shared/user/account.model.ts b/jhipster/src/main/webapp/app/shared/user/account.model.ts new file mode 100644 index 0000000000..c8b9750760 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/user/account.model.ts @@ -0,0 +1,12 @@ +export class Account { + constructor( + public activated: boolean, + public authorities: string[], + public email: string, + public firstName: string, + public langKey: string, + public lastName: string, + public login: string, + public imageUrl: string + ) { } +} diff --git a/jhipster/src/main/webapp/app/shared/user/user.model.ts b/jhipster/src/main/webapp/app/shared/user/user.model.ts new file mode 100644 index 0000000000..374c3ae0ca --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/user/user.model.ts @@ -0,0 +1,44 @@ +export class User { + public id?: any; + public login?: string; + public firstName?: string; + public lastName?: string; + public email?: string; + public activated?: Boolean; + public langKey?: string; + public authorities?: any[]; + public createdBy?: string; + public createdDate?: Date; + public lastModifiedBy?: string; + public lastModifiedDate?: Date; + public password?: string; + constructor( + id?: any, + login?: string, + firstName?: string, + lastName?: string, + email?: string, + activated?: Boolean, + langKey?: string, + authorities?: any[], + createdBy?: string, + createdDate?: Date, + lastModifiedBy?: string, + lastModifiedDate?: Date, + password?: string + ) { + this.id = id ? id : null; + this.login = login ? login : null; + this.firstName = firstName ? firstName : null; + this.lastName = lastName ? lastName : null; + this.email = email ? email : null; + this.activated = activated ? activated : false; + this.langKey = langKey ? langKey : null; + this.authorities = authorities ? authorities : null; + this.createdBy = createdBy ? createdBy : null; + this.createdDate = createdDate ? createdDate : null; + this.lastModifiedBy = lastModifiedBy ? lastModifiedBy : null; + this.lastModifiedDate = lastModifiedDate ? lastModifiedDate : null; + this.password = password ? password : null; + } +} diff --git a/jhipster/src/main/webapp/app/shared/user/user.service.ts b/jhipster/src/main/webapp/app/shared/user/user.service.ts new file mode 100644 index 0000000000..b0e1121215 --- /dev/null +++ b/jhipster/src/main/webapp/app/shared/user/user.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, URLSearchParams } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; + +import { User } from './user.model'; + +@Injectable() +export class UserService { + private resourceUrl = 'api/users'; + + constructor(private http: Http) { } + + create(user: User): Observable { + return this.http.post(this.resourceUrl, user); + } + + update(user: User): Observable { + return this.http.put(this.resourceUrl, user); + } + + find(login: string): Observable { + return this.http.get(`${this.resourceUrl}/${login}`).map((res: Response) => res.json()); + } + + query(req?: any): Observable { + let params: URLSearchParams = new URLSearchParams(); + if (req) { + params.set('page', req.page); + params.set('size', req.size); + if (req.sort) { + params.paramsMap.set('sort', req.sort); + } + } + + let options = { + search: params + }; + + return this.http.get(this.resourceUrl, options); + } + + delete(login: string): Observable { + return this.http.delete(`${this.resourceUrl}/${login}`); + } +} diff --git a/jhipster/src/main/webapp/app/vendor.ts b/jhipster/src/main/webapp/app/vendor.ts new file mode 100644 index 0000000000..f9ccfd5b00 --- /dev/null +++ b/jhipster/src/main/webapp/app/vendor.ts @@ -0,0 +1,3 @@ +/* after changing this file run 'npm install' or 'npm run webpack:build' */ +/* tslint:disable */ +import '../content/scss/vendor.scss'; diff --git a/jhipster/src/main/webapp/content/images/hipster.png b/jhipster/src/main/webapp/content/images/hipster.png new file mode 100644 index 0000000000000000000000000000000000000000..141778d482528a8c6636d56d6683f74b21a34394 GIT binary patch literal 9499 zcmX9^WmFtZ*MtDU3GM_!aQEQB-GeV~!FF*A!QEXJ_u$Shun-d5HRuAt-9040x6k+X zndzyjd%I@(o}P31N5^PsDB@yKVj&?R;VLW1=^!B?0}%Hm1`0yC$viheLVA^@rKT&7 zpxMc(|Nj0pGcuQw*K14)6qC{v{iu%UMI_ZEWOe>W#ih0Gj&~6XA|xTBbGy4Lt)PFi zvx-o}q%>rejjmRwMI=-a4l*i6%_$*>e1uhVO7P3ei?XJrl7|Y& zlY*{Sr~a3K&=H_HHROK@P02ww8}rZ4&xlOKg#ZX00dCf3o}Qi{84-vc;dAqQ4iUOo z97RNrr+fb=B6LV*L`!N2!fCkF?I z5%_d&;PU?)0!zqfBYMQ^5LO6h#8^ax-@S@YNJc-O%iV_xJb$^bx&8=OxM!K~1tYzXSCR@smW(Qg!u-WR&-oD|X>YUJs#G>K~ zLlcLX*i_r%g{%F|slM)6Xk}M-Z(RfAc4uW|bYir=u&=$v+TQcx;=;dXJ~u0Cdv>I` z>YJvfCTw7~sCRR5e6X^#WbNo~dh;@-by>tBdLRC4VEITou=V)r&(-N+!^D9_;k<4P zR4sMN($Z2y$Hm45D5_>-3Z9hHceOuIAx11s6HgtDPe`cGSdb&VYyG(=Pun2MYid!LbQ__J`GqKj3Unh2Patj$-{bhK&vi_ut}W zuAH`-mln^Cf90o#jAhxV!~0IQ)_u%95|NPJEh@`N>-w&oEH}k&^WeX>$Q^L8JQD?8N>X-0Yf?;itE(sXCI`_g<5Tk9!PzoR@1QCiO^$ zVfl4SYurMqucnfgDIICu4u<1V_=7FuKVNhFvLAVD?WW!Fe ze`?L?ft1ip{qU2ib&jGi;ro!xtJZYx5Nh0{)QYSCuVzoUpH-RQk(TXGwKrlnaT(*W zEYrQPx922HBCYn~2LB{~<3fh@sD4<`!~--@-bk}ImAF{03uMpiw72F4CsWE^V*0k+ z_1KHh$2aYl)w3;QjW09R=)VtDK2v#heLC#GN}4Jdt+irXt2m>f%IRe#%V-tv-liu` z%b9a4ge_RI%3`C%N-ag>iW*zT?zJeT;XnI>P^n8PnCvBUZy;{N3-S9-D54+$H?R^NkF)6&C$>e1EtED2Y(QJ`l(CTHl!=HH3k`C!dAV^Mmy!4GLyeD|I3KqY_SX zy!4US_6Baut5i3o02}HP9-ibx!hdI&0uWpVn+FoRLe@{OcK_5ZB`;AbRqYvM1kPQ0e=ntJOeZE3eNrJ3V~oUu%ZkDW%wr7J4C1gHQQHK9BwcGjMx0*}oms@qJSWoO`7-dYDctt1e# z9t*5iR_)Uw3dkW~7)kfi_OuaD1wp?N{XN9Mo~}!Cgl4 z&4*9Er$;_Pd|Z+hwMr~5@wfHC<)Frj28+6*1R7C8wmj&dFItH8uWsC|Ml3Vw{jZ(S zYk6sVU(Bg*{$f4jUwji1weP)E+6B9!H|M(Zg@&+;CWq!W6X!1fEm@ZAH9zn&qqX3i zTF(7x>I)%t7L6w}UqbQ{cNUfO&L&^>B>K|1nW^89kbGt+d)^ZfbT**y34mT&Ew8M9hrOanI8(EEA6pA1ADp9lIJn(|apUW5dQpI&diS$AoiD6CSdVV_zLPK#(}U1J zt|K4gD2MJpwphUsIBfE%clFEr%L(Yj=iJ}xzSb!RR(%W;;h2=2nJL2W(qxU>;aHis z$KXD01A~YIt6sI7a0g?IUEdT~uon9&=W}|4i)3GId!a{3945a5VTH61HX)fBeXc9C zp|R+gwy942S_Ql)yQ=$b! zD1+L!_x?OPsaelKdjtvzCSu8n864=L$vnz)mM^-IV1&-~KK`mPxh0YfYddJN=1P~) zITK%j>gAqDN!c*U*J*=!J7P8M;7AtceD*|zux>bbW) z&wMLE_NFSu04$=!PAp22i7r}y)$IQ?CP{nw{I*M!`k z{TwPtwZ|;Xu6RUuqQBf9O3fr&7DmJ+!s7@F$A4L7 zuYM^pAhMV5oaZSXLW^;NI;i8N0I&;kbtyTe=BimkV8pSU3Y_BHrdtpu;c>`2&MZ-8 zk5c^kqV{p*+FSSqosUgP~cZl}}I{;rW6&TAee0Gx*<)(h=)W|VC8 zP-(p`8vKpBjiRhMCf)Ij&3dprS%_moUn^wu*1SeAy2DNiyya1I#1q@;62eX;*+#|? zlJ0;uRC-*wLN~UEkrSI%+3`n*;AIF7bP5xhkKEK1V*+87qVAwvmeF}(+mvj4iTS%tv-eDQJ(#dLP8qJfEp zfRioWxp0)}V}wyW-`H|_F|YD&WE}`b)1uFRJmIc}9gf3V1IR4mUNw{=U9^)NR`I2L!C_GuY>IwB(-L0nZ+z`3tePWrw6qP>CRHZrb$DZ4V_NzTs(eOyt{vkgFT76VOT zbsT~nc;cBaR|AcW4w1*67hBf8b9ae7|LZS)QoKKvaJ?Abl=uhSf8IMY^UjIxMM|V) zKa7yUB9x{x?YKaW$2Ryr>{DU+6^W6u3zZ~+D@WfcgNc)siZnF`zfT^=qSerdH?{nMyRmK}6gJ znm>1j8!-i*!X|R5I84zcq{jAxyXczh);{!ta=|S2Fv9`zIsh_Rc&DLk*Nd+=1%rJzz#`?_j7(5*XRXH^wGFdgGH82Nk~)td<&FG2}=3ljNrv z{UZLw2rCzKpp|nyRP1%MZom)LQ+K$a8k`OH?0}B%Pskj)4}5{C7rXjcfjGWxq8}IK zn-3PziwN=@JA8rWzx_%sLZAQLuI$K@EXuxgD_Y7D1?cAQ=KYv2&)T=4tLa`rX!~-|@bM$H- z(kG_hR~>)IS&=l;Khl}7gyJb2K?p%>be%EcC_fIvGMcYblx!jw%70zLli0EK#eC}o zCvjQ<|3qdh4Xg5BqX6BtE|9}+sm^nq%N!FSN@~XyoWO2shdcKy9nk{Rq1R&30L#*n9w@tc^-nrmy~@+oc=0rHo0LS-L`k6PmTs?tL9cV7;(T4 zz8&7yO1!RrBTV8u$`x8cdB50jdU`saw41u`yV^DwcFL70VVC9z8v0qq)vIa@XBo=*!`ED;4hwQf3V?BdQFrb~#cXef0UJVUOah0;kT=_- ze>p-6&Fq$c_s$`EuuP-5GANZDB1vAy&yfkl10V!%f7|P^p(o-H8EK$~jHOz!C73_E zE|F%~&$G2UCK(L!xc;$_B;0z~^+|+?nE>zOgKmKc`JIIoGypxQ^ib9FA0X5>HRPuOP^Ds6id1Ch0e;iBu~v5C*?emBGT zDR%hn&O90x(afiTm)|l%RGFz;ar5Y!!%ggS+m^i7WQlfNP9O~q6Q`9=km5ACR7=-l zAf8rUJ|w#5LnG*om(gEf*zC$3Z2FFf?rz@lbIYy|-(F{V!wF>G6JNR0#QrY@2ek|B z>Fnz&32NNO^KD3yWvN$y93idnN9HolR?gXNp#(A)ik{IW7q}(^rija5j*&HQ+I{4q zt*5=8E@vw)?8jCM*mflz?7VmR_cRv}Q%!A@ax16xFhU@Y6Ra*Pdgv{bzq)#TN`V5{ zk_Yq|{iWvM((NGJ#WkaV4S_$#xSqJdiu5JGvANy6l1Z|qv`o3=ai3e-13jyJJIpku znLeD9&p$*HGly9?tJJ|nQpWk#9Sry3`4XceEdaI*!q$P3HAt9TY#8~a^a?Vb^0p(3kw6?&XnymukX$(S!FRT5XY(3|mT4};%v#OXnAnm8 zIf0bpHkEtkV9e4!Ii7}@8hXInm$m3&h|zPaLCy`q&)#Xew?hWB6RD0r#j|rt z&Zy8~z$dxNv%yxuPktHjy0Qz%8;(R&0lDz2pR^;<`R)9@@X2Km6&*b;I@N4RxiU5! zd;s|bfk#Fwnc^yjD_s9`Gmin~LWDsx2blC)NOMACi8Wo#q42$0h&+v4-uKK_^83!w zLt|nbPVL1iiyj~|!8i0I49;1oo(2<&YeDDP_i{J(Q!DQ+jtXE#;JAMX{)+d!G@OfG z?ZaDwn|@HW_*-r)AZ(`y_=dm^W9}$+O=o$~ny&TKe{7o+7hhY{m6reFIdU|0Ds&oB zP#K0;QpDVNfcGt$C(br3s%zfph@X#0TPAx!_$)h(ox7|G<_xqw;2Gpe2&MtFaV3;O4%{`hk#2GiEo7|zwMR)Su|AG`A?Ti0k#KS>8bjLw|oOI7-M6gwacCV&tOf zkuh29U3V3lF^ZXTW0MV>R6pWzrH)JNujavjzyC))CGp|`vTOd8AD6VW6dDWXI%JW( zO9(oeR>s6Z4-(p!n0E7*_lWBs6g$pOSGZrS-ajSxTrIFOfeLQt3Tsxyf0U3AL1BbK zrJRV8J1^DwZCxc^aY7fJq3cjlx6zU`hYlbS9JT*mpLsOr~VZCNvVLEX(9N zLl@BNNXAibwE*dfMV9R~X5-VS=PI7uYGwjiNv2lQyc^uM6%zyG^~$-&UCUjJ)D33& zwJ~Ry7{CbqM$x1G;{xACeznQ8|ox)Fi9mZ$$9qF|2EB=6AW zq2+l#>F>sn?=WblJ^fMDpi0DMj>EcZW}(=yx#$EsO`BiUo!v<2ci%eeC`7j8%9g4- zB{cF^!mB{XA*Yn0QrqLBt{Cp1tXHrjA<&7@NIU%mQjL>@I~)AzgO^V}1Mq`kCC#l1-tWdI+PrTpNu-50QglK7yiN zagys`@751_*Ph>@M0c8EZ(jwh#EwUH!q4fZ*Xi$j$r;2G6a0`oJM}}kcmO(xc5wHHI5bI zNLb`wt7rWPsl!<8m)aY=>LB(R!+pc)ZU3OyT6uK$YIQ015K;mgS?0xf4-SBS)p3Kq zN#BEzQznzBLur4etbswa4)s(E6EsIswie4byK7u*yRUI@l~5{IjfLL3?)-5!+_Y4s z6QCo|`s28aBhhXu=&3I+gVKyFhsTjcbg==c_hN2!zy}>kw3@u$7!E{+s;V#+x6vKk zb?>^L^_|9QDEqGRgn~3VvLJ7uw)tF^LrR?zZE7ka?N{zXdKq0XiA_~}xkS#i_ZOSq zV95^kgW`957(JT999dM{ARllGYM{LoX^6s*`yZx5(wKcSP})j(5Gvhli^-S3)difC z3N?EvD>`>ua9%Ji)ahDsG>88?xcEl+X!@JJf|2ty!=$lV@95_))A0Uivf^D<(+ z-}81b>!=ku#tu+n&;KB`=|uo#_vox|6)bHuka9SkzTbz47mi^n_o?k8Yq1)UnigGD zN5iY%%`!y1{v%Xu?HB*lvBo1uiODE148@ckD@l)m$&(HfnRwf8XgWTW=Ah6unGXZQ zo(a&s?WN9;F`p|d&gX#n-}#(GYP~);;xRmIhZ9WuZhreI-*MbE{lom~>izlBH#mtb zvtzRi3Ly$uGgFoog_66Fq{vy=nP{Q&rc_N*lsZ@ws@J!ryyZ%!8_0wA=gr<}d0Pxi zEXuNtj}0;#(%9@F%&RGR}6XdGclTBnT-=tLylBHb&(^|AZ(=v*=HL zW3y4ED811GW|Cml%XuG;Z$T|Ie_JrH|E6YACj^fR!?E!zTb50@u)f=2Y@w^;+9_y2uQW z@Ke}J&<22nvg32PXw%5Fku#Qx9_C(INx)Q`8R^Wwkwmi%k(Ci+Y1(Zb50U*}?l~STesty8^$_80JT^bT@ zgwa6ZlF9*6V7;V$4%cNqka_Aag3($(akq_Tn^mirGUJ@)iLf_9U2M6@x|k6V@dBI& zi@q#OiPSIgGPwowjJ3hlkAv*wAb)XQi)P!^x2d|c<>ED}hBF(bwd_+1dxRB|AasTW zY1YMUR-`!2ZHS^3Q97HyxUxl)?dn;IZcC)jv0j1Se=JeSk!(PyT3p_uX(u~aQ~5|R zR&GzxoCaMID-{$C0{|$6hW?9Cc?b;wdRxizpy~` zY)6AZ1iE*5{YNYiqc#XjzqS!Pavm94Bd$T|IaXSYx!fp9r(eg3rJP1}E`}3)pUjH) z@>v^VP3Rwr?Ao!sC*5K%6R_Q8$tla>(UOl&N~IwPV-V^^a&~P=2muw-vpJ-!;-hF; zDjYLN!R(FQw%e?B_}! zDu5=t3|Tv3lR~i67j)WH?<;TnrPv zW>$nb_$3>RWD;_0D<}Sj2j%6=0M@ppk2e(psK%AEX=Io9{cIg&S|7s-D`5`P~`*?G7%4fFgBe$q9|^HKTHhvC#35Qs#`yXJhxpEugiPWCyEv!>K? z&^fBOA-1B1mbrkId;oj*#E{X#qmUQwcEG+itgz=1_pZhCcE$AT6mfNzI2AAPh3uEc zpH{Cx3Wmy5Dd1(3P4V4{9!4K?b!}4j1U$q^i`QXK%Iyx#1(PeRWZ3WS{Vc_7?#%rB z){Sbrm=u{+sMgKS0ySkVl&;c-YMIdki{-`iOhZ8v4SGIMwuTR@dfq)o3xud6KTIQE zMmI>Aae58eEwS8EKK+2Hm`*F~%Q&W|Bs_A0|`4%71JW^)TC@Xlw6V2n~h2N^< zii4M793P6!ynpj@+hu6jPU+ybFru3&Ra84a!-hIlx;fjyTM?oGEfs2xOwl-2A%B$l zo^X7p(xhM+_?H5m_-ERQqY-{f#;4%fi$^WQV+w+d4Og?=aSLWNaKe5d^0>qVx0h8 z-rlr!gW&vSEHS>M@(DvR(q@UJA}?kCgIp6$n@m!8igc(Q+FQ0}U#$Kf@h>Gra%4L8 zrKf7q49z1bm#VTt+Q#kQn@;S2~a-kAB0~tcpb7xii3mAGAE%p+-zXbug)h zL~;m7U~zLUQN)rU`}V$p$4yieqm;e{)l}iJbQXgtUhRE0k0m8hf6OeIf~7#2k6?*S zB`8AsP)3nDflZp6F=YMV+qY2KM8!kNDAhd6-AUw4dM5#f=u*e^nOTJeTt*@o#Hk9u zlXQ}~?e0juKh@2+Rf4|ek1ku9G7~>o*G!Ma0C5;2HK<2TRiAl#Rygi{#aeWfTqMRNmel?H~+EIUQ|-cT)1Y z(hB-=%5NXe4iE@9HAGTQS4L4EoErM_^3s$R_HeX$zq6#N|L*VKzl|v&h#=H^BvaY{DszZb%ej!Q>Wh~OYnBV-Yr|HBbdB9dxC?<35^1;n*`x9M_wbe`QRnv{dGv(>ecb6ey@k$yN zrRB9N=Lgqf;MzzuX_Jy8;939KF}4hJKX%=xYUA*}55ytx<;@5 z;$R9>{B@?`Fs@@O=zCuFSCx#lIJ}bz{OGejpPOayQ}^hozPQaLH3iyt*D%#~J?9FW z?(aNWIjw`u*JT}@=dh?azJK*fO;$r$;cdYB#YUqCOn?yGwj3R^I|NA~p0O_<-Tih! z;_0yqC7UJ&|6Np}*V74T0lC&U4QTjw-{N1ZggS&Fd;jJHkLdr~Uicv@=2`7kaH*cjTct?RHAbU?fvA(a44XHU7UyqH z``++#cuuzYEk1USXaM~a=Dq)Eni`@7==@7Lfnb8%0+FlX)GtIPXv-RK@l?bQ=BTx zjA02sRrE?Zt*Z3ts`2+V=C;C> z_m5M&R$zEa-{X;G+6wd2^^MpRFRvHnV%RWM`U3Nljgj?H7Bd;Ip}0GGTx~o)h%ltR zR7DljqJghx?aW{5+Z}40si->hiYf)5n~R)wgf8GpzUlIVh-V@_3##@)FMrOhuadBB z^xKyS>f*RsBLc>!ozN`3+N*m^zN)pnJHyFO0v60I77gshYh}IbyMKA_->M%4Qf0>2 zo!i&3QTO;4T2`twD5%X_S-JU!_iZdJ&>{b@;T>!lUeT$6U9X2`Y4iS>n_HRnAZ+`r zg;PfE=iDvF8k-GMj%XDQHK5AUU=FOy&DJRj5}o z1CX#}`i}knVbb!X5A4F-6F#~x~+PTPNr3`uAM~7QDYDd z)$UazTxL1Y;%y~SZ^6`1lq>NlR!b51a6N2K1$xZUb6h%qpC{BJomFcUiThS{Iv_cP z>p#zY-d5H?0#{UiJl|sLw$3aOS2=TKzNltA%YuG#rCJ`}Zka$8d@59yH40v?N)1h_ zZ6a5)Tv@s`gBYTL31!qVDH&h4P_+b4c9M(SoLWMe7)|{{9PgS0=1TzU8 zK*6Q~UF%&?Co>v2^)9C)M79^`Cedw`i2duQo%`HAuupFOy~OTJi&>n%ILB(4Ntf72 zK(w-@ZkslSAOMdQ+&z03^%#`X`NWfdj9PI20jQuwz$mATvdP9y*bxd?T3yoBacSvLR z@{LB7>}wnN=vyfPdub&#)X3{wBqRQTTt>#kwf9vf^G4l|g=3Pk$~zCGN}y-XI6{U} zXHK1^8;b|-6^AVl?On;1LE3X2&fO{a`SP?WYbN4EguYT#0>}9%<6;8r8%~1E0NpnW&VQxFSUZvWZqsN-hJp4Re>Eg{I?2Xq~uk7Exc{N)pZ;NzP{G*029PAI16rT1KwEF|I(uG-)| zdSL&|yjyXeXuF@KkZ_%BAPD;4-Tiy&fpcPAl){}q<V8kUK&3YLr0>r(FUuU^haBMB=xg|83&sw|54$Q0aMVi-sB2D{;X`FQ$cTt4y>wOK7|fiP7? zLv+~v`-QZW&^V3Ry-voKP28oLy?P2&w@#{xfMFZ{X43Je>h|XKo;yR<{rV%XhnsZH zW%R#pPFjEvMuY(YN)Bz4uIYKy**i>vTyf?!TSB|Jtt;US8dZ3I8r{?b$bPK%ZHq+X z5X-(X1npd*|7(pqrXFcF1le9?!IE$pH4p(d<(FfKZxZdTY!EC<79GXeUh$7ujov&O z;FU&-8%oe4$HiEUR%y@N-Ls0ij8b-n{{jx%GWz8Mj=#go$4I3NInWXLJD=;yO29f^Y%^3=7g z_CVDX{Q%28q|`yQU?iF5AxIa(XKhFDUY3fZ(MF^R^t%i`R2hj~aLoJHxZKAe452GW7N##olk& zR$rO#8^#Uw+J*(a%%W1-mcp`Hf~4I-lTEB+kAR~ms(Hy@hag{S@W1;d!SxS2FX^)o@Ua$3U&@D zO{Dq(DU$=ec&P34R??@b-{5J7&jbR?HV>Cu+4`Q(SAPfC4nFRTQXhw60J&dWI=DI~ zGW4aclH7G66po_{c_J6!i?%+@Up?B1B_d#wC; zlxp9c`Chm~R(34I%(J-3w=DX9!0)bXuEwoVr*m!oQ5WuA7oYC$T#f9hP`+uLtON*g zl4@rzu{*yYz}?ywpb9uuE%7cJ${TS!pNgjiAfRX!S963>M`bl)E4W=E-}IzY0a2B$uo%p|>_9v$a}78@&J8@L zD5?EfVt~&bm};72wZ;x0X&Z(djVP^a%H&nOQ}MLTun&VC!JOT>hujcV=kcCItNoxv zs+yUClR5Tk*}k6tgnMw)0Bz2`V`QvPk+4zn-VbhIDX{IHS`^Ai_Tw z0Vikuo?J#2$m6s%^ySqG&WdQGgb5CpUIk__;`@mF`$;q=xXF@I>h#P&qaeS5a(u1M ze>DL{3AJ25mnqcxE|VSN0Kf^#HLQbT3rEQhj*$=d+VH_$s#a9O*A=Ydp6(NK|fF((H>6I3?lG>#bZ`bNa)-fTP z4Bn%|8j%qTwSi*9m?kn7^1Ed1zETts0?2-bNZ&xunmPxxnU)&lu7@7`#zcotsDCml z{CGncCR@IGvS3nytl0h)=$pv6oHtyX1H6CIs(C|bCp%?xo(%VVxGYq0ql`ldh%=O) z(m}mpCQVg%CleDxztSDCdiP8zF@y;sVEVy9(Iw^FTb1Oe4r|U$=xowneC*`q$({T% zC|3{F1zW4NM8IyXH)Ky1o+!GtxK#2hvI_kUW!7X)7O3!p%iM*zpsFnq42LpL{S5D2 zRmhke8H$~-&Oi84}Ax87|*&1fi3~y+~&FsWwn1Zbd`N1OZQ|Z-ww1L)~7bC7sit2zI-gJ+Nt&kg*gm@OeN~V&UVWU+f z?}H<8;B6!+8uw0NX>`Z=HFZ!%h-`aAy18+>%twTs*)w0h`YP}DzVpa)-3 z+KV;A>Sju&7mGP>pEg9D3q+Rg^rU&T17;{Y%RD*_O$qY>5lKhRg@yL}hR<;?H#zKB z-1q7uN06wb9ii8-b=4b*Tvy-x8&{`?a6ry&82nRFJ6!Wq7}Yeew^k~{aI1%3j zHv%P;sFG|`W|%)?-S;&kU+b+Iuy*pT)ZfIK)Vb6+y~>T-mTZe{vUBO49xl%l-GSc+ z0;67|A>pHX&(s))3L)utTvXzT*%ky=^;VtSG2^vS*B$^Y%Cwb=Pqgz#0uH}OcR{V8 zOFfPbhZ{vaKfjuWl(g|jV<}=@$GQc+I@(EH8W2)lxg-hb+9y)fBj<#?dHI%(hllRB_dbWp&ywro-@}d>$cF4K)S&O-$Oy@eNXI(d z*6_OBu^Av^8Jg}xZm&hzG#UI-VO;2Z^4dr9hZ^1xsI`CNXm0WK6_2l^`Sc?$cc>_U zSO^7&_}8_XKS0&>$;O0kMWB|ljv(l#Ww!Ek-=0SN7Zhi-4TUWWm>U-TI#45_99fs` z%yQu4oRgMaE6lNQI_oEC18y_)uhZ3*b$-;IcnH7A{!(B;g~0c?whmK3;K9YN@~e9!_3u2y$9)76W%V^jwJw zfxFIvGXL};`h6WLP{o@waz~u@)mmmfo^q%U?P#7h$h-);BZ_=r-Fmd-_IIFhE!Iqv z6EN25VLmN6O*zBr=;J+0{=OQZ1L2s=BcrtHVnk{5l`5Dr%Q_UF9%9ePhM+NCRA{@G z^_#Xni6YnN8*s|CkL#65UxQEyzX_2_ z{wgew4WrgkYRc2_?%nUFkO|o*;{WbCP$lAuO|469Tbic5XuytDW|2SAe?$R1iZOlG z^5V_C9W>2hx;mDK0}VU*Rn0Fq{6Y&8?SJCBUHbiUW(szXU;BgXT`l-I&lR>v1rSYV zCl~8wj8D^;CE55BwkN|q=!nX>qejv|AAaEMc#O7UpW>=LrwxU`1#r9D1?*sDWpiCS zg2+GUNrWZh&b$Op-taqH3(*}J{}8(FuB198QT&sf-v zfw@ZCWmLPtxK9^LQ6;#Ijuy2_dS&K2Jgmwho#eb;xLvq=R59oVjh&IC)fnIiz6;8wm5O-jw~iKuJj?A0wz$I|l)& z1+u@;siG1(fn3jE@1Sw=Zn_|;_)GEMi^iNY zRPr@>dCo9Fdxm^WMicYtZqTSb6~YWq9qR>+${OM*F0af(Kxma{sb}XdutuHz-Rj*w zcYu`|EQv$-Ti7zCS5<|(E5K95t?>@6>)X*bq}rZ)ABfuq_iO`|FOa{gUsr{he87Yv z?-;`zV7k6xYl_(S(=D~9gb0iJUZ%Ph+w@-*&D?&Zx*w-!U_M`>!jJ5LyAlePHlh4=TDAmS+NrFL{{ zF?nLC&SZu(8}+ohM^y<3?>W>qYnAbL|eJ;BF&WD!rjMq8_~j2&%|PjcUZ&fbo4lyc;b1r+>S+d$!O--nrlWhSndr6~B{ zMLZoFKg7t(yaUnv%>4)je-AD=%$E2>B=LLU=~{XQCd4%I68Qxm5EZ*jvIN=u<#66a z2Y5VS^?FeWk?YBkc-jbfmd<@{^_VhB__#+C^thO=N*IOo6(mAh!&|D%_bsqNqjyA# zzu#VuII?-TOhYL@B~w{+n!XUTwwk+R?|aUZ`D9HWbEiI~lc>GZm*@xEz0>h@sQpor3$6)C_{T#CLHNOFlwIpxdvvHeX0_$N8KwBPAezH3Em_JP@oU@3}t8FEn9M zPXW17Tap}Wm>k>4q6dUy)2#xlbIsfI$lhYAeitqcd7=5ykg74nJkYJ;-F&+M0!UD2 z)=K@XT0Or+FHvHDK{l^ZQ?uI@H7Gp01-Y+8YYA_5_?KUB-=y#5>`XWn8`YUz=~vu> zL7i&$?hCoRys#o0$s6u)X~TIc(~3vVx0&rAt4u{`O{bQyxHW_~9Mv^US+FrifZw3O z5w=p+C?W;fyq|#d=pd8f%QM#(lXw&^D>ExZ$6!e+JN!gKs|L67w(Rpwf6cu-?v3h6 z;W}eXPjvZlZ6?{2HSv4xI$F$z&v?=%dw)ziJdsi%%CmW8X zuK?~8=)6#^dj2Z*D(#$22BNB^Y~ zh!#-&j&qG2i(J;})Zri&uRo&>=DTwt5>+y`0KpG(8~4LMD84>@Feew68X**z0nvR$ zOgfEVxpF2%Kn<`1<$yc7EoOvZAcEJ{fppT_l>*;P}+RLBvL=fqR!PG;J7rb*2^4}*B(?sg5 zR7OVF0YU~9=u~g~j$Hw-pB|^2HmtUrVXdw3e5vpR7{IN{!;}TdM<|>UI=zjIEiJ>X|ov3LXotp)#$Bg>y&8^XZ=0 zSqXNNY7`P@-;fVy;jy|$dpoSrZgJ0}F+2#f-n(yc5fz~vq;%f(_D6ZXZ0 zMSg%Z-4~nrPsGxJ>D^v}H+BgWGR72XP6RTT=tM|FznVJbL;T&AUASgzq6(ZHr}mG> ze&vrh4PH($wULR$d(PLn2fN=pd}FO<$eJ9^cM_(;0QhKC;T5O0X2x3FO@+SMRX^gk zy?{c5qpdOWsqY*PX?Q56$IVJ{kP9I9B2eysh34SdE`y}A* z4(LXT8R9Tp#3Q|l1Tt%@TC*#S&QuZwK93>gEVq=}Q7G?uzJtV!lzH@~{9=r+wD4aZ zz#pG}Aj;Wv(c+h%dI9m)m-mnO4UC3NVVDbVN|vjh6-d}VD8f!fj%oMY z1+*U+OsQV1d}L6R^W+RSwE?svWj+mMxqHad5A#Hvg_av%B&~TeCik6pLkxhSn!$jk z^U4mAL}~O?qtP&|?(%=MU4iQ(ytpQ^;f^1ugHT5^2*24zC*d$>_Kl0eL~(=3g_0-N zN(b{IzqJQoT8n^+Owg9E%>z(ZGYD}Txa|l?w=wEhH-r=&FD)QS*}7;Xfp1Uf18!z9 zXB@T4P?v{iEJ+9cwtog89NJTsc5+qJ3a$F$st4SK>L=;a5|!(^D&|r zD@Opvp&exVpSRcHFrbK^?Q8g=JU~2je4-o3fxkJ*{j)TJSp3rp^xpPB=;mo?qTtZv zL<`G#$37d*kt0ky<@aMF*CX<8XD5NKRe==^dS-h#G|>KV%4wi+YViE)IAkp(HXW*} z*$(yx{QX|bTMc{`@Dhft72x>jI$nkpgc_+i`=OK!ISbm;c~s6m1-*XcqmQ-pAn31H zSjqMMR|M>y+gtg(1@vYVgb|B7&OZ2(sdCr3<7;ILRg- zis#@$zmN0OLG33Wv>hO7Ni`5L0kLpm7&VhJcr5ewfc7Z=b%7icH_7zTVTks5ynCDbA))19 zEhq`---==KamUOK5^cM@OtqZ4>J~)z!}56OJ6!4@$tkFrLjd+c4NQ0KsD*+0Iw4BJ zA;?%|%LU>Xx=cRd;Q&*Li*S`MWN+Nm4I5UXa)*=?H+B24!1M&FS%XoKk823I9B=eT z^5BQo3YM+UJNM?!i>h%E?(#bYWMvb;kUz$vn8Dd)2da=^a(9T2yt-Iot!{gZIAH=e zAk;LSE_^k1<_7`FBru<~mUQ1G$Gf@o8;hqftSL;KVkbiaCW%`EF3=(CZ#_sd&clDN z+LZZ7bbthQ|I`LwVDKd$CijQL>w`EF?BX#5<7)xWz(YE3fTB|(fvKq}!M%A5pCYR1tq64o(WdPou zkuxlHWgSHev^&YGeqVszAg>Fue`1e!YZ61zIG$iUmz*ZDM-vzFH!aIHl3y02S~|cg zzt(rkSNd+2YfpRrEnSyU$q(j(ZjuCWV#@1$^nw^Yvr8ng1wuU=szmN@_70pn8FcHq z2+wSdSFrNoPH{1gdYf*&#))^m4kFqPqEjONe{RmgEJeD!Abqi5GFf(jB9Dm3j7Rlv zCyC<4*D;YL5yyD^M95yR&m+3Y^3-T~Mf#mSNEFM){Q7qBoS981B2WyBOdcmEQmQXz zulmZbc7tv7^sdz|b{?r5th)jzx{E35EsLr3O2*DP5>z=4x1ly}C~`xD&<|Dm856nh zt_Wl@9Q|Q5_-(5M#b!w7Wd!$kU>{qJ{W2pPa`AoUeG_ky#UdaBfZBtDVt{Q$G2ILs z^aE|$#?9aSF-0?KFW#7w8=wJRhxxyGwC^}`%qw0dZ@qxV{nqNHWqu8;Qh$An{@h5| z+iLTT1E@3E+g9r4m^{v$Jl662y6P_A`OW4;3;AVhygh&ALtg|qQ--~hp=Di(Z1ZE+ zubevEf4@gvny>emH8`xlazPhywPEMzLyAWo zh*mLwnztQ#JQAAklq!c1MF9`D*a2}SAfw^8RK3bx;xKYE{r^*$H=!Q<&``oeI|IQd ze_MO<#=OksZS60?BDCx#85Eoelb<<2ETRi8$4;23mn2N2p%AE&JbsH=UnX2O2{U5w z_UUfEMjA-3-GDi$M64K6FCM}tk!ffBFwrv*YEa^V*R8MP)-2g+@pb|sp9rJe{uf@| zS{5_yIDmNQFblyL$f})EqYzRd44)mq%fj*xdioB!0J0^IUxrf#lY2C1i=Q#c^7F2N zGo{%lnZE8OF^gtn@nbxFuT_AAjXJzr#q2j#0rHXP;t;07<+joeFEPCsibx@uD#y+8 z6~;GWVoh{?vQ;LxIXuwZQQtL0T$oK{yWeFz@m2oUYB7<3@VO^eOunP;P{>?_b;{=$?yKwDG}VsVyLZLvU- z4o8Z4`7e}y$7GZE6!}M`IPlpJ3=i>gHu-m`%-HDIaC|@M^^+qOd3D( zqUrsqMp41d?yi($e!ozJj_$~OZ!t4CzHHb^H(biX3Zs9O(pfVV8rQYsX~xm zAx1Q5JZprOd1DFmnlJei7L?vpAqSlK6lmq))^gC;!KBa$&Tx1pLwpQ)kGkeE3$txr zSKAp<4fG{tV>>fGG?7WMPi_VNETsWfrXE~O&<9oa1^#8bx$lS+SWfFNqt=+J;bDV9 zUJ1PA_Cs{?l2Acx!&n+pJ>v|+=u4?xdjlOzSx(y}8MdGO1^o017aB*GF0(*DG=lR$ z7Qftg12k2*aOU>ahLHAH5YtxBq+O%!*JYz?V_?~hI!M4z+`&8gjVCN+pZM{f@QIbJ zC&x)!lp?sogobzf;J{pb;vY%l;B`M9!&a;(VJ12#{yUqGc~UbLF=o76KBVE!J%dnD zc|-;uZP9vTnpzlYa9&Cr(Eq=rKH3lZQp!12t-wMw8cibMOb`Gq zy2-I)ozX|z&j!IVZ#`EM|H^yrV!CneD*<{ZRqe6*8*zr%m(ABry@7VGEvNYrC-p9* z!qF)+Vu#!%2%6z$LmuhZTsqX?!$1~=qa5ITp@pGoRx^>!s8){*VHvQr+q(4!Xw+#5 zRDKqKnDjkBm6vg&tr`n(Igj|yT|Y>Tl%Q+AjwdhyXe_v9Mewj*~aTb7B<%!(Jw2sc@j5!%+JhD6PEa%F@an?ONQJALc*~JfJRd zoZb!g#^{AN90h;^;@_p-X&{cXw+Cw*0M@8!zNyGGWTwKI#(-eoi~Ek6Ww(7EFK!vk z@drH0IckNi9H0U$i7HEu$Sg{pNTfaitrgaL6d%{0DFV4 zAaoFl_~lAW-YKKjsD5=mYKs3&Wo@KHLuNXN1lL84-9tM|q-rI&s9`(uE6|-$Xkw*u z^_*M>Vt<4aS&NM7DD^7*R#Tj>+gi*S+#q7ZSwry7Xj7|MmCS)0N*rxnKRL+%=3FlK zXG-)BAc4G7R;|?PdqI`np}M)Z_RWm1VWOBykcA^WsN1QH3q0ZDn34%Z#>)(AvH+1q z>)$|*C+Rw27Kj3u6Y%aX>ia>#tOv0H=?LC@iW{l#eigR~-=`?g_ay$pxbwoIe_a=b zggqp21THAm(G@9fU&&S{JS%m#1E_**-d@u9EgQ+(uHG_hOV zn8B=CdB0vJ>trL!e>+zSCqzD2e|=7qSEf5bbQy_Ms0AZRVJWUHZuy#?Rhd2i%fhx= z3x@U|^sVLVNM&U{tWw+7y*(kOJxWC$Amc7EtMjtP(L|(tqBf+>s3@j3YoWU+{_bxr z;ab&#$w~I*QHA;B#|=RbGu$f_fbsealY%VEN#!^-Xg}d&n3S-q`|Ny-OSL2Kajtjl z3y#~u12*x&`dQ_8-Uu&ON{I&S4G;4Anc1X-Q^Wl(PPRFWO4)3^qLkgeIWfraRbKP- ze>q1jW`qXivs@M)kjX=-6owHcq@=uF@WYG`fl#l!*WDn1Wbqi3z%j{@Kx8$C!n$|_ zxzr#E!gr^|Nj{f-lH6R3#Q-Z)WL!4z<8vY_BKf&Tx?mC+t=_mA%C(3j}Vi zUjxATxPk{Ips`OV1~OSiA^0G<{yl5J_m%fKXN{<6J}y#G!`%@2+Czb5d#<>XIX~3+ zAdt)%P0U%Nh`XX%eRdpVHX zoTd#zrRjx=mGKE>^3E*obwn*3EpEBE*fFLza z#S-3k+w!;4S$%lvF|p(a;^(vkR@?Fm$)y$QbL*c~h?qgp-{@&~am%%M7BHqbLf6R^ zF|V(tz!E_Pcd0PO&6%5&dpuh1quNnV{mjA^t3u3Fh{Gq8(Z6PQ^EhpG)Hl~iX+xs` zEkqQps@dm^?e5VprZUDdhskOZx;|eL5s~u0@hmuy!>S?% zLwXTwKU3yT2@x4~R~LJY>8gkuAgi1TrMw2)^7F!7FOOJZ+;z;<>}KKY>b7SzoISw1 ze$ivL*9h9DjKYPia!*?q>UBLJadaGN4V;(t^d%c2{K#cM+tl(eXhQ|-J2N@N@ z^Gr43>-`;G5c^7b+^p$e-77B=w(R$*`EcQZAn3StU71kzhK+L}4ppV=Zi4&!J}%Pq za9IWC-+nnVgCUwAgVF#JKuoF11&<< zCR4=L7$BRq@0~DcXt5a7DiqU2y$3K6 z)hmS90n9c=Glc?P7+w+BqLUzwx|UBD6*#~<0!5h5^a-TTk3r(Gwf|BmY}tNZ)14-b z%dI&V4@j6c9D_)#T&eg231G$YgKyFcB=GU|!*n5d+wr;{6{6p%hiY5@1!Q_Wx(x1If6e9+*?`2l2lXW(|A14-(FWoS|?jbX86zPFPzPQ*J0Q4?k_%ffW2H z2|EIuI6;#}HCBhdv^p>OofZkBLY@;gX^vXS>Y(sN*d*--FnUe+@wS(P&r_X+sgum% z`-VJ>RuXweKU5zg2Bu6qiKalKmc&RO;bLzA9_O?u` z{DmpY=}2Qin%&yV=X?P6J-R4ChoGAUY!!NZd1+rui|Rx@2kGsG8$#ZF4F9wI5F8rS zq-o#F(aa7k%m4)#SO&@s4AiHC+gy6vFHySmCq%C&I4;*$N?$9VXM7fS?-|4?1rFY# zd$0O^oat-Kg?|39`Q|@b11F!A#ZL;Cv4@~F&&QK~1T8#nZ;u3;hKx5|HAiIRm}kOB&qCndqI50I}# z_?pl<&N!_}Ka)Ztb(#>0nT*GZNF#{Cc=uBTsCmZTc|xpt_|te-kqDGrB0LJ@ z{-QI((L5a9MP;DkZ3Ugd7G{RPzs0N7|7}74Z*mx4K^C9%h9PhXfNDkf+y4&x#`D}F za$*7qR>8(+`Y&$`(o1e~j2G7f|7lmf|D~YKh4kFU{_=iRQQp9kpRKdyz)tJePK>u! zFvY#sglHjW-J{OT5WT@|ZxkPW|L@hDg1><-?VMZiUyL1Vf5VecKmCadRClED5!6^7 zDwhaP`{!uc`YDPnWkPg_EA2O@vti-9AaO!aJcb!^B@pFxr6Imqsr+CsYmtGc!FBjo zte5*aus7_+%7Y7A7%wpb72CGl480sUm;lI?DN2v8%}Wj8m`OiJ^rum<17LGIdu`@y?kLX2~~P_u} z_hh;dC4xt>ilyg3=|bI#Gs?R;ys{VnRW%c}pA5?gJ#PfwmhJVot(0qSjn`0c#_xNv z@g+hq-U(SRv}{Lmom~=U{B3N-xUHM!#AhL>wS@w3yQ)g~9geN%5WwiRk<>2EyOeT> z>om_N+s2u}A-Hjl2=9gF@mV{(o}*Old^+8mn;v@&1|k6{b@&3jxNA1#nj z`TZ^)for!~qES!A0uT1|zm1K6jYT}1A5jP5K~KtA+UpI;WDOIdb8T!Z@T;#10n9WV zoIg0g_v4~=gBa!C1=`mTWpwgkpT^>zoKv?(dVItdP9-rHguroXB8(uB-tO|pf(A3T zqN%&J@yZ{XMiY7(ya8VMXLjyFsk=q-NUV;jPVPQ6iOhufC~7rQkMB>w_mJGJE%B zFm)+u8jZQazQ-_o7=r1K6zkU3yKoR}^ZYJomJ9)Y} z2_$w`dK3*6@8s#haix|nr8tB?Y6Uf7v}!43A?th%asa>7*RN@xu9>K?*8-o06Mnt@ zrZm9o#t3;FGkMr2@W{RmdjTzQ(jTa$w`@{wvjE!j6}>u_Hap&jvQOm8C_geBdlmWu zdofzE48h*f`K46STf#C2Ixy3E6Iknep10qO97DUG8qr&mi({; zk2~Eb3^3czd;333Xng5`RyQN5HfrY~4gB=4cfF-4V2Xi;x`1ep7xuRy|E*V$hAi#B z4=&N}{iaL#c=cnZOOCK@dzw?aRz69wzoP9bxwWx@O}iO?hAkm4@A53XI{B`)DQ(8y zkA{E#_QG^F9fhW`9!An)U(MF`lHX}f;WnAXUMHz9 zaKx0HFTbrnVqq4%RbL=F7L1Khaw2 z?5x)drZtjAWi;JJAq$in9>gz*+pAc~XqImKYT>p`o};+7YpPvT#n+;F;V=PJO!(ke zWxD>%^+pkfjfWk^N+IO#=oAf_4OL4bIVMjoRICJhKt6#7hF6B_;zQ>zTtz=_zLKdn zBE@HIF#TZedSihDJX!FppY$dtl= zhq?4Ap8Wa;Sj)b%qYY0jwq`$N8{EFtC}L$bMYKV4o2{Pm>aFRL2}LbE=p_ub+upss zqYZ`Q6qqW4OT6Uk3N)p_qX&H-j7Ef5S~UkWTlIY(;7dF#_zQsQ&Rkx+58e?cIfuV5(-+vZ7<;u6+xojhjf5L^*%DAv$rwfuk!<$SW#dR~4xa zal*EhAyC~ubj(XGk<#o4HtEx`=p7rnfa^ORv>DRFPpS#uA?aYgG&eS!^ zsw@2uxlDxNgIPXsW~4V5 zBSUZby5CSj+D$Ob^%|4A)MgIA9JBDR;2ftmbK{d;zwkYYl0zcPGo}D}%zKR5 zWsU&hkm&C64CXxQl|C%jZ~CL}K|I%Snj#0(wU&YN;xgJ3@N)q)~(BB#StMnhiK0 z5)h92GZbU5G(tuP&0jvjXPz&4TU4KqtwLC{4K2(e-cLdrv54xjN?0={1VvD#6Qge^ z9+{U-6-$ayx6Fk?t4}5LtwF4TB5i~Q861o?{^}b!XXTB>2|f=)vl}=jvzVB=NbB4PZ_^k>l4)c5_A@R;=7Mfj|gy>9Ql};eo>K1IgKgiahpM`@4 zCsR@&Gh&|aFFN^yjvi-Yr+8{#vYstARf!e39@lV*zqaT9K-J)0yl-|Sa6z-y&DgCu z(QkRy`VRiytSKr(MxN(cTFpT(jxASeLo>pFf=Y{qe0^NA{#;B+=r9eSSvy%R#ds zeC1@T`TqiM2$1)@1g2>z{8t6M@C3Q2!;;jx%>$e&Kd##~^CEvO!I9)rc1^m-MFo~& zyJh{vEA56noPk9(73A?Us|MsM$VCIZR_pyP;cU5eZD`XZr^?Q44FC7F1Jo?!q79xJ z)Gd)6u^U${Zd7z(po5W*QBSyEFpzgsMdiDpJiM6 zS829eZb}yOX+4!dESY0|m*7kquhgso5kAYdwhmA^sav1Wv1~?9h0g93eUn~U{!iv) zXLW%cpi)9Ux5w_#zY$V%x1MAS}N6;}m2saP=^rse_ttYd}wOemnUk=a( zyJA!8niFBaY#ncii>TIRx{;kZwHmE%f6(awbUK4Uzu#@u8%^umse$fHD%P>hB00Hx z+Z0|x-{3OaTJ%VDJS%W%r$|liNkS4ul?LDnx2;8b+00}f>t3`-tRVNVck?J{!3pjs zl#-o!#k9o*?-yywy#f|P2dWKlYa69wZ^DE$DqBTba^J{wbmaI6xWVXU4-&hhiY2vC zC{o&!JKN2m6K*GAT)xM@sL91^UGqS=NJ;XS5>k3Hoy(&r*Nws#TzQj=msf5V3B~2U zkWB52fZDz*jd4s}?peIASk~(p9Zx zgfumc4q!c*$z}0B)kY1RY2eDK<^+ydN;mG8Y1EDlL)TO{t%LYF00$U7S|=oL4`Tz` zvPE~w6nY;G!+5Cq{uXfRtbmOcIAP#w$T}Gm&+iWh)sbI%a#$TVUMyuc1FVDAkQnwC_F~3WgVl$N8<4T9$zaCr7X1m?~ zJ^cICw2n)c>ecTO=nmYdW?Q!hX@XfCy~zO^(AY~EuK8VpxSMg9V`JJVIAK@t6=@uO zy~b9cUMJQM4LAHQ!J*(Sm?<(Z%t+};`a{^haIMJsdP&-F#P1T^x*dTT?l_nRPx^>l z-hB<{N9D@9f1N4ecL~nXcM5B66OBAc_)#UzF(*(yZ z;hyGmUASAC?^?QqwB{4G8HRt!|Luzrc3r}uKS*$7Z7c3~wu^}wj`?$hg#~YhVM2+o zXwrLREb5_DvlRhUd-{LayMvs@fglRQw!9l6HY|xoBNha4hgr&)6BQ0Y%z~foTk%r1+1&Nu}esjUMk=Ut1cnw zsD7w`uQ_!INoV`GX(Ghbv^Q6+bf|#qEJC+QpB^hc|86W=sU`@9wKaz{w>{`oMmPbhJ4jt>uRgR%(_SlJ zU3;B;Rl03T$}t7Qb{h4%E~U2Z%lG(qx@-+-6JX&%3n0UYb-id2-0)&euey#l0cH-; zd`uJIVXwKECcs5gOSB0HW~Unwy~X;T^eto)u%$Few5&Jocu{0K{}RyzOtYPTozy(HDDjj@BXnz5DZ1$W80HAew1O%du)5e@i}tlV_S(X0Xg%r zt;8$=Kdjb|;HV54+q5msEhd?78zgcG$Qh??`Avoi_S*)^7CHNEgZQ6MD6soTD6cK9 zvR{&MnS+e~l8i|La`sCylHwQ&Q?{%dkg?zG5){W+fZZ;c1dK%3?TT+D;8UEk<-7rx z><=Kx+oHrjNkiEpAvL%v$kq7}WA_?e)DO(~57z>bFB$9w$n6hQJi(Z(r zWw(nl|CD&mk_-buUR$;exDeyDW!r$EAg?Xk1`I?gTgJ8+h*GwkNpZ8$3ivss=fN_z70{0Jv zvLi#zeN9Gdi=6%7mev+U-lGKKUnnvEO-7CeMb`HX1QPzYbH_LggD@0^Wo2RT<`GXF zaFl!a{#WW=B~_bL0o4CHgZ1=;0UQ{4Pew<;p0Qhk@F6qyjeMJia|9e4cH_V`8r-5d zVT<{g5Vn|)31N%*nCDLjs|MVt1ZTFG&r*eofca?TMb4%H_p`_=oMMYEtVx~~WG#9r z)$Mda)xxv_00000006*0cmhdadc5A5%OU^(002ovPDHLkV1fXQwKxC( literal 0 HcmV?d00001 diff --git a/jhipster/src/main/webapp/content/images/logo-jhipster.png b/jhipster/src/main/webapp/content/images/logo-jhipster.png new file mode 100644 index 0000000000000000000000000000000000000000..d8eb48da05c157571eed9fd7303f8d9566f2ea12 GIT binary patch literal 4459 zcmX|E1yB@V)238FK~j(qNkODUIgmU;(gO(*P${JZ=?3XM8YCs8kB$SRLmH0mJmRPW zjygDwxc}z=zWHYM-FJ7N_u1LmnVoqzLJOo!LC!=@KtMpDs-mckzrWy*J1H?wbcBiIKI{l~?l|FQ8} zGHf6(+!t^B7X_K9f{bfoefMF_h_P-2VhbNfP4`Pj4&zy(j>UKvG2D*N^Cq~ z3ULNT!6=*t3j-aGm&Rya2O$w2`K^=SsxjR0HtuxidS@xe$9fOe){q>6Ma&n5xud3f zXId+p(!xQBBTODckT6%=#XjzMrY9%*WNrvC(g~~1#bM@f$T7!)F>b%%r`|&&MTxDy z!O+COn=9hJxj*L-JH@Zfj;7-=_>W5}WdMaao@(9S|ZqIR7s7ut2 zRO%LPYr1)0d}d-)EOdCaF55pWI_Zazx`G-P2RAV>@!8s_UG^}KO+iXV7MWg(A7sSr z_efP`c@YGXpP!eVlTG&}D`(FLc@R8?9{`YxHb{qn-wCvDW+EGJ zElW?f_TlvtI&`K#aao|+bLQSgwp%O`F+S3m{XYGBjnPednx=>?{N(x zqvhlXI|2%;{U)c9lZfFoHzjk)O$733PeHPHS!wW{oHvFz`@#u8{5?Fo_Gd@U7g-nM z18myb1IANM1va-Q|NL^a%AKL?Tq%C`?ufGBZfY|i@I>U4azx{{#4AfEX=vVtHI8~U zkA!1nWv5N5IV2o*H(&aQCGcq2^;qECmXh-6Q_}#{*z5h+;Kh^O^HWYhH3JEYES%&b zn~`IQ4wS_3-GPLjbm^uQ9`8#*Np5r~?w!#3DUdUjU%W~f(;qWT-KNePOb4pohLdh~ zaW7E_M_L^Cqlm)<0BL8gQS$*Wq|$s|NoiX?30DYG`!#798_Mw8#H1GORusc9N6CUU zP@f%DBQw)FigQ|Xo}2g0b_nJyft<}!g~9G9ACjd}ynGVcO=b3}pvoBd`!%iDpD80M ziDoEmFvDhIWgTUL$1Y!W>lmfcl?8=}_Vg*ymq5WoA|iO!wpMt2h0J3Nv(esXqMh)u zCnGVu8xcw_nQt!piI`~vh8e!XKE>)N=F>wWbsVWk470IAZ7enF*+LsBCIt zrVUJ&R2^m7|er zR`k5JtaQ}dyu&>(#K4Ybfp0R@N8*r4+B~1Pq-6CqR`lxLqA`5k)mNbAJI2~;)mF@? zb;as@o`Sae%~#})Wk^Ylg1%R)qp~P%pU$vS?c`OKpg>7UOw=vhwB8VQ*6$m<^1DRb z=X{oCCc*0A&Ed+Q5?C^Ip|qC#h5FfB^R^31TDut*nRn<^#b3NQMSJw;A&v8T1n}jb zpnwuCrFPuF0%^i0ZlGiC8tr7JjA2#57toQr9M5ucNj|({?XY*0-q>$c>uN>)oBr7? z<{|eMZN@9QzT+KWYpTZtIK8L48w{}b63NmSo_I@cXZ?rY&Qwd0*J`eqCTF>SYfMC? z``;AgDi7}Hr6v06eQFrBrl8S!&G5c(%Fckt$;F`GyO{%Tt05G^7khxZSwZp(68n); z6?*8is~k`Wars8O!dUEElYgCWWyRHiN=58p$C^?>LW;KbG+^Hbs@u5B-cfmrKvwh@ z*&F**D^}nu_gTyNo~+B%c&|T%A!s=&63Q9j_&t!Az@0mJ??eve?u^f+k1pI)39k%B zQ%CRe^4!x-gFqU&K55%+Lp#3Rd#RH)6~slq37;p8LC=k)TeR_hLUPmn^NQ=AQ(+); ziU*JfPHgJ*XgPH=Q~<3uw)LMS=dQZlQxrV-7|x>%rQo9$+yg7)Xll*}TN9L;#g0YyG+5zfcN5Ou)ef_O&HZPzR0Is+goj`iUR;ca)<#R7at8nf>X#8 zfk3V+JN=>L*GO3#b~a{@$i{mU81eAfydg{|>$+R|Y!ee4+sw+@>m-wLS+xGMC{dd# z#8Jr37&Uc%4!Dasduy`mm6A;KfWq;|n0tp$;@cTzH+Iw!wl#;0%VjP5W5$<`{bMAp zjbuzd=sG-apRTJJ18USIeQ%N_RH}b%D6c-OQU$W(ebunRT z>W}VLA`hY-S#1E$^gR?3M*4X6JdOticP#mszBkRTu3`322pW?wJFZfpN8tPJ4%g!+ zD0-H5QPE|G*moD7gbUbW`_1l;5+lCXHQl-P40iwiAdkY+9Ly4vU8Y+))PHM6E5yTL z`EG|{P9--wb|YuoXgb?}n|LqGY2{#K_F6=O^mtmO7~z2YAbTj&)zYeC*`g`QeqM37bl%$mk2m^@@y&WEwtQXhdSL`c6)0J zS9;!6)92o+OptB0cG)f2QenpN3%SK()G}pmOAlw z;V;Jwcr|pc#yOsi56A88BsX<;n2T)0tR0GbJy)a7-P(#?h^R6XWKI%WT7KU6cp-*R z))-sFUI}hMaGi<1v`^3KwoTO_t8^(e3a7-hm!*xWFPbp+EwlW9yJpo7k#tVp&AMy; z#CbKRssTPPgTz{;cG&P;ks zCK=NTkv9~J{(jY(&vi7QzQQ9lx=r>(=q&3?vC>x^g}V9qA>|GH7n1e49$HhxbtU@g zurasC?2 zpSV+LOR8ry;d`rq(;nM7)zD9dFL{I_1L02;h}u;-T540Mdh%O!qAiX#0I033rG@sF zE2bjRr?i0+V&4KPU+*S)8gzLt2{=cb-f5fwSu!NL9kRsxyI5Wy{BH1S*SIUbnj7N5 z)z=`wLHg%o+dEN3XJZ3}XboI|; zw$wN=wz)#G?^5$Q!+%HHMa1(5bUTtD5l$oW;ld;pfXmx1N);-qi09(zj-6xLsoZy# zh|R~HP;R-_X$X7Y8n3}XK5k{?}!pLX)rY{_UUfu#VUb{(7$|a7K169Sg5ld|Up*q5gfl@ChTK{qZb-;uhx{ z%Le-;u|%5UOyw@?F@^HCuF24RY9Tl4+EJz0mxdoQbg^08G$ltWIH#SyQNI=xu5?;>Amv4PB-Q^XbyB^*O>oqurI-2z% z!jbbv%2#2{(TNFfSgsq^=P&*(*RFTQ_h$ra)tsTv`UZg2)!u%L7>I0ns=5mEd%93d z+8wSQITdztdd2Y6zgp|W%g~Pn%SlLH1E{=v;gI}il|Nejy>9U@njE5pAqF7{(B77$ zxCCgZNhcTgmMK(^Ebz7V3mGcw`>pP&!(XFjttWpDNu;5&E=Re+w3=^x{~)19f+~iOopzsOqPCO^+0B(Q-PVeg^emHYpY_2J|xyo{9yA9|~+- zJAJ6^(+%oCu?Z%sbAJ$nxYItG!)yi|5FAz-T-idyryDq%Y2fLrVX#vSEo4bEb=*@VxG&b#1g { + dd { + margin-bottom: 15px; + } +} + +@media screen and (min-width: 768px) { + .row.jh-entity-details > { + dt { + margin-bottom: 15px; + } + dd { + border-bottom: 1px solid #eee; + padding-left: 180px; + margin-left: 0; + } + } +} + +/* ========================================================================== +ui bootstrap tweaks +========================================================================== */ +.nav, .pagination, .carousel, .panel-title a { + cursor: pointer; +} + +.datetime-picker-dropdown > li.date-picker-menu div > table .btn-default, +.uib-datepicker-popup > li > div.uib-datepicker > table .btn-default { + border: 0; +} + +.datetime-picker-dropdown > li.date-picker-menu div > table:focus, +.uib-datepicker-popup > li > div.uib-datepicker > table:focus { + outline: none; +} + + +/* jhipster-needle-scss-add-main JHipster will add new css style */ diff --git a/jhipster/src/main/webapp/content/scss/vendor.scss b/jhipster/src/main/webapp/content/scss/vendor.scss new file mode 100644 index 0000000000..55990bbef5 --- /dev/null +++ b/jhipster/src/main/webapp/content/scss/vendor.scss @@ -0,0 +1,10 @@ +/* after changing this file run 'npm install' or 'npm run webpack:build' */ +$fa-font-path: '~font-awesome/fonts'; + +/*************************** +put Sass variables here: +eg $input-color: red; +****************************/ + +@import 'node_modules/bootstrap/scss/bootstrap'; +@import 'node_modules/font-awesome/scss/font-awesome'; diff --git a/jhipster/src/main/webapp/favicon.ico b/jhipster/src/main/webapp/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..44c7595f58f5a2c52f5e21451e5e6868d6caea7f GIT binary patch literal 5430 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 user has been activated. Please ", + "error": "Your user could not be activated. Please use the registration form to sign up." + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/audits.json b/jhipster/src/main/webapp/i18n/en/audits.json new file mode 100644 index 0000000000..ed5e16d4c5 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/audits.json @@ -0,0 +1,27 @@ +{ + "audits": { + "title": "Audits", + "filter": { + "title": "Filter per date", + "from": "from", + "to": "to", + "button": { + "weeks": "Weeks", + "today": "today", + "clear": "clear", + "close": "close" + } + }, + "table": { + "header": { + "principal": "User", + "date": "Date", + "status": "State", + "data": "Extra data" + }, + "data": { + "remoteAddress": "Remote Address:" + } + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/comment.json b/jhipster/src/main/webapp/i18n/en/comment.json new file mode 100644 index 0000000000..eed148855a --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/comment.json @@ -0,0 +1,23 @@ +{ + "baeldungApp": { + "comment" : { + "home": { + "title": "Comments", + "createLabel": "Create a new Comment", + "createOrEditLabel": "Create or edit a Comment" + }, + "created": "A new Comment is created with identifier {{ param }}", + "updated": "A Comment is updated with identifier {{ param }}", + "deleted": "A Comment is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete Comment {{ id }}?" + }, + "detail": { + "title": "Comment" + }, + "text": "Text", + "creationDate": "Creation Date", + "post": "Post" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/configuration.json b/jhipster/src/main/webapp/i18n/en/configuration.json new file mode 100644 index 0000000000..81e208de5c --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/configuration.json @@ -0,0 +1,10 @@ +{ + "configuration": { + "title": "Configuration", + "filter": "Filter (by prefix)", + "table": { + "prefix": "Prefix", + "properties": "Properties" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/error.json b/jhipster/src/main/webapp/i18n/en/error.json new file mode 100644 index 0000000000..65246c3b03 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/error.json @@ -0,0 +1,6 @@ +{ + "error": { + "title": "Error page!", + "403": "You are not authorized to access the page." + } +} diff --git a/jhipster/src/main/webapp/i18n/en/gateway.json b/jhipster/src/main/webapp/i18n/en/gateway.json new file mode 100644 index 0000000000..8c8bbd7e98 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/gateway.json @@ -0,0 +1,15 @@ +{ + "gateway": { + "title": "Gateway", + "routes": { + "title": "Current routes", + "url": "URL", + "service": "service", + "servers": "Available servers", + "error": "Warning: no server available!" + }, + "refresh": { + "button": "Refresh" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/global.json b/jhipster/src/main/webapp/i18n/en/global.json new file mode 100644 index 0000000000..0c49362336 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/global.json @@ -0,0 +1,133 @@ +{ + "global": { + "title": "Baeldung", + "browsehappy": "You are using an outdated browser. Please upgrade your browser to improve your experience.", + "menu": { + "home": "Home", + "jhipster-needle-menu-add-element": "JHipster will add additional menu entries here (do not translate!)", + "entities": { + "main": "Entities", + "post": "Post", + "comment": "Comment", + "jhipster-needle-menu-add-entry": "JHipster will add additional entities here (do not translate!)" + }, + "account": { + "main": "Account", + "settings": "Settings", + "password": "Password", + "sessions": "Sessions", + "login": "Sign in", + "logout": "Sign out", + "register": "Register" + }, + "admin": { + "main": "Administration", + "userManagement": "User management", + "tracker": "User tracker", + "metrics": "Metrics", + "health": "Health", + "configuration": "Configuration", + "logs": "Logs", + "audits": "Audits", + "apidocs": "API", + "database": "Database", + "jhipster-needle-menu-add-admin-element": "JHipster will add additional menu entries here (do not translate!)" + }, + "language": "Language" + }, + "form": { + "username": "Username", + "username.placeholder": "Your username", + "newpassword": "New password", + "newpassword.placeholder": "New password", + "confirmpassword": "New password confirmation", + "confirmpassword.placeholder": "Confirm the new password", + "email": "E-mail", + "email.placeholder": "Your e-mail" + }, + "messages": { + "info": { + "authenticated": { + "prefix": "If you want to ", + "link": "sign in", + "suffix": ", you can try the default accounts:
- Administrator (login=\"admin\" and password=\"admin\")
- User (login=\"user\" and password=\"user\")." + }, + "register": { + "noaccount": "You don't have an account yet?", + "link": "Register a new account" + } + }, + "error": { + "dontmatch": "The password and its confirmation do not match!" + }, + "validate": { + "newpassword": { + "required": "Your password is required.", + "minlength": "Your password is required to be at least 4 characters.", + "maxlength": "Your password cannot be longer than 50 characters.", + "strength": "Password strength:" + }, + "confirmpassword": { + "required": "Your confirmation password is required.", + "minlength": "Your confirmation password is required to be at least 4 characters.", + "maxlength": "Your confirmation password cannot be longer than 50 characters." + }, + "email": { + "required": "Your e-mail is required.", + "invalid": "Your e-mail is invalid.", + "minlength": "Your e-mail is required to be at least 5 characters.", + "maxlength": "Your e-mail cannot be longer than 50 characters." + } + } + }, + "field": { + "id": "ID" + }, + "ribbon": { + "dev":"Development" + } + }, + "entity": { + "action": { + "addblob": "Add blob", + "addimage": "Add image", + "back": "Back", + "cancel": "Cancel", + "delete": "Delete", + "edit": "Edit", + "open": "Open", + "save": "Save", + "view": "View" + }, + "detail": { + "field": "Field", + "value": "Value" + }, + "delete": { + "title": "Confirm delete operation" + }, + "validation": { + "required": "This field is required.", + "minlength": "This field is required to be at least {{ min }} characters.", + "maxlength": "This field cannot be longer than {{ max }} characters.", + "min": "This field should be at least {{ min }}.", + "max": "This field cannot be more than {{ max }}.", + "minbytes": "This field should be at least {{ min }} bytes.", + "maxbytes": "This field cannot be more than {{ max }} bytes.", + "pattern": "This field should follow pattern {{ pattern }}.", + "number": "This field should be a number.", + "datetimelocal": "This field should be a date and time." + } + }, + "error": { + "internalServerError": "Internal server error", + "server.not.reachable": "Server not reachable", + "url.not.found": "Not found", + "NotNull": "Field {{ fieldName }} cannot be empty!", + "Size": "Field {{ fieldName }} does not meet min/max size requirements!", + "userexists": "Login name already used!", + "emailexists": "E-mail is already in use!", + "idexists": "A new {{ entityName }} cannot already have an ID" + }, + "footer": "This is your footer" +} diff --git a/jhipster/src/main/webapp/i18n/en/health.json b/jhipster/src/main/webapp/i18n/en/health.json new file mode 100644 index 0000000000..868ea3fda4 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/health.json @@ -0,0 +1,27 @@ +{ + "health": { + "title": "Health Checks", + "refresh.button": "Refresh", + "stacktrace": "Stacktrace", + "details": { + "details": "Details", + "properties": "Properties", + "name": "Name", + "value": "Value", + "error": "Error" + }, + "indicator": { + "diskSpace": "Disk space", + "mail": "Email", + "db": "Database" + }, + "table": { + "service": "Service name", + "status": "Status" + }, + "status": { + "UP": "UP", + "DOWN": "DOWN" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/home.json b/jhipster/src/main/webapp/i18n/en/home.json new file mode 100644 index 0000000000..402f18700a --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/home.json @@ -0,0 +1,19 @@ +{ + "home": { + "title": "Welcome, Java Hipster!", + "subtitle": "This is your homepage", + "logged": { + "message": "You are logged in as user \"{{username}}\"." + }, + "question": "If you have any question on JHipster:", + "link": { + "homepage": "JHipster homepage", + "stackoverflow": "JHipster on Stack Overflow", + "bugtracker": "JHipster bug tracker", + "chat": "JHipster public chat room", + "follow": "follow @java_hipster on Twitter" + }, + "like": "If you like JHipster, don't forget to give us a star on", + "github": "GitHub" + } +} diff --git a/jhipster/src/main/webapp/i18n/en/login.json b/jhipster/src/main/webapp/i18n/en/login.json new file mode 100644 index 0000000000..3a000852e6 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/login.json @@ -0,0 +1,19 @@ +{ + "login": { + "title": "Sign in", + "form": { + "password": "Password", + "password.placeholder": "Your password", + "rememberme": "Remember me", + "button": "Sign in" + }, + "messages": { + "error": { + "authentication": "Failed to sign in! Please check your credentials and try again." + } + }, + "password" : { + "forgot": "Did you forget your password?" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/logs.json b/jhipster/src/main/webapp/i18n/en/logs.json new file mode 100644 index 0000000000..a614b128da --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/logs.json @@ -0,0 +1,11 @@ +{ + "logs": { + "title": "Logs", + "nbloggers": "There are {{ total }} loggers.", + "filter": "Filter", + "table": { + "name": "Name", + "level": "Level" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/metrics.json b/jhipster/src/main/webapp/i18n/en/metrics.json new file mode 100644 index 0000000000..9d804947c5 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/metrics.json @@ -0,0 +1,101 @@ +{ + "metrics": { + "title": "Application Metrics", + "refresh.button": "Refresh", + "updating": "Updating...", + "jvm": { + "title": "JVM Metrics", + "memory": { + "title": "Memory", + "total": "Total Memory", + "heap": "Heap Memory", + "nonheap": "Non-Heap Memory" + }, + "threads": { + "title": "Threads", + "all": "All", + "runnable": "Runnable", + "timedwaiting": "Timed waiting", + "waiting": "Waiting", + "blocked": "Blocked", + "dump": { + "title": "Threads dump", + "id": "Id: ", + "blockedtime": "Blocked Time", + "blockedcount": "Blocked Count", + "waitedtime": "Waited Time", + "waitedcount": "Waited Count", + "lockname": "Lock name", + "stacktrace": "Stacktrace", + "show": "Show Stacktrace", + "hide": "Hide Stacktrace" + } + }, + "gc": { + "title": "Garbage collections", + "marksweepcount": "Mark Sweep count", + "marksweeptime": "Mark Sweep time", + "scavengecount": "Scavenge count", + "scavengetime": "Scavenge time" + }, + "http": { + "title": "HTTP requests (events per second)", + "active": "Active requests:", + "total": "Total requests:", + "table": { + "code": "Code", + "count": "Count", + "mean": "Mean", + "average": "Average" + }, + "code": { + "ok": "Ok", + "notfound": "Not found", + "servererror": "Server Error" + } + } + }, + "servicesstats": { + "title": "Services statistics (time in millisecond)", + "table": { + "name": "Service name", + "count": "Count", + "mean": "Mean", + "min": "Min", + "max": "Max", + "p50": "p50", + "p75": "p75", + "p95": "p95", + "p99": "p99" + } + }, + "cache": { + "title": "Cache statistics", + "cachename": "Cache name", + "hits": "Cache Hits", + "misses": "Cache Misses", + "gets": "Cache Gets", + "puts": "Cache Puts", + "removals": "Cache Removals", + "evictions": "Cache Evictions", + "hitPercent": "Cache Hit %", + "missPercent": "Cache Miss %", + "averageGetTime": "Average get time (µs)", + "averagePutTime": "Average put time (µs)", + "averageRemoveTime": "Average remove time (µs)" + }, + "datasource": { + "usage": "Usage", + "title": "DataSource statistics (time in millisecond)", + "name": "Pool usage", + "count": "Count", + "mean": "Mean", + "min": "Min", + "max": "Max", + "p50": "p50", + "p75": "p75", + "p95": "p95", + "p99": "p99" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/password.json b/jhipster/src/main/webapp/i18n/en/password.json new file mode 100644 index 0000000000..46227a7702 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/password.json @@ -0,0 +1,12 @@ +{ + "password": { + "title": "Password for [{{username}}]", + "form": { + "button": "Save" + }, + "messages": { + "error": "An error has occurred! The password could not be changed.", + "success": "Password changed!" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/post.json b/jhipster/src/main/webapp/i18n/en/post.json new file mode 100644 index 0000000000..14c64f3f90 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/post.json @@ -0,0 +1,24 @@ +{ + "baeldungApp": { + "post" : { + "home": { + "title": "Posts", + "createLabel": "Create a new Post", + "createOrEditLabel": "Create or edit a Post" + }, + "created": "A new Post is created with identifier {{ param }}", + "updated": "A Post is updated with identifier {{ param }}", + "deleted": "A Post is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete Post {{ id }}?" + }, + "detail": { + "title": "Post" + }, + "title": "Title", + "content": "Content", + "creationDate": "Creation Date", + "creator": "Creator" + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/register.json b/jhipster/src/main/webapp/i18n/en/register.json new file mode 100644 index 0000000000..df8f6e31b8 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/register.json @@ -0,0 +1,24 @@ +{ + "register": { + "title": "Registration", + "form": { + "button": "Register" + }, + "messages": { + "validate": { + "login": { + "required": "Your username is required.", + "minlength": "Your username is required to be at least 1 character.", + "maxlength": "Your username cannot be longer than 50 characters.", + "pattern": "Your username can only contain lower-case letters and digits." + } + }, + "success": "Registration saved! Please check your email for confirmation.", + "error": { + "fail": "Registration failed! Please try again later.", + "userexists": "Login name already registered! Please choose another one.", + "emailexists": "E-mail is already in use! Please choose another one." + } + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/reset.json b/jhipster/src/main/webapp/i18n/en/reset.json new file mode 100644 index 0000000000..fc61e0070f --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/reset.json @@ -0,0 +1,27 @@ +{ + "reset": { + "request": { + "title": "Reset your password", + "form": { + "button": "Reset password" + }, + "messages": { + "info": "Enter the e-mail address you used to register", + "success": "Check your e-mails for details on how to reset your password.", + "notfound": "E-Mail address isn't registered! Please check and try again" + } + }, + "finish" : { + "title": "Reset password", + "form": { + "button": "Validate new password" + }, + "messages": { + "info": "Choose a new password", + "success": "Your password has been reset. Please ", + "keymissing": "The reset key is missing.", + "error": "Your password couldn't be reset. Remember a password request is only valid for 24 hours." + } + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/sessions.json b/jhipster/src/main/webapp/i18n/en/sessions.json new file mode 100644 index 0000000000..d410035ee7 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/sessions.json @@ -0,0 +1,15 @@ +{ + "sessions": { + "title": "Active sessions for [{{username}}]", + "table": { + "ipaddress": "IP address", + "useragent": "User Agent", + "date": "Date", + "button": "Invalidate" + }, + "messages": { + "success": "Session invalidated!", + "error": "An error has occurred! The session could not be invalidated." + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/settings.json b/jhipster/src/main/webapp/i18n/en/settings.json new file mode 100644 index 0000000000..919ab51cc9 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/settings.json @@ -0,0 +1,32 @@ +{ + "settings": { + "title": "User settings for [{{username}}]", + "form": { + "firstname": "First Name", + "firstname.placeholder": "Your first name", + "lastname": "Last Name", + "lastname.placeholder": "Your last name", + "language": "Language", + "button": "Save" + }, + "messages": { + "error": { + "fail": "An error has occurred! Settings could not be saved.", + "emailexists": "E-mail is already in use! Please choose another one." + }, + "success": "Settings saved!", + "validate": { + "firstname": { + "required": "Your first name is required.", + "minlength": "Your first name is required to be at least 1 character", + "maxlength": "Your first name cannot be longer than 50 characters" + }, + "lastname": { + "required": "Your last name is required.", + "minlength": "Your last name is required to be at least 1 character", + "maxlength": "Your last name cannot be longer than 50 characters" + } + } + } + } +} diff --git a/jhipster/src/main/webapp/i18n/en/user-management.json b/jhipster/src/main/webapp/i18n/en/user-management.json new file mode 100644 index 0000000000..30c125b6d0 --- /dev/null +++ b/jhipster/src/main/webapp/i18n/en/user-management.json @@ -0,0 +1,30 @@ +{ + "userManagement": { + "home": { + "title": "Users", + "createLabel": "Create a new user", + "createOrEditLabel": "Create or edit a user" + }, + "created": "A new user is created with identifier {{ param }}", + "updated": "An user is updated with identifier {{ param }}", + "deleted": "An user is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete user {{ login }}?" + }, + "detail": { + "title": "User" + }, + "login": "Login", + "firstName": "First name", + "lastName": "Last name", + "email": "Email", + "activated": "Activated", + "deactivated": "Deactivated", + "profiles": "Profiles", + "langKey": "Language", + "createdBy": "Created by", + "createdDate": "Created date", + "lastModifiedBy": "Modified by", + "lastModifiedDate": "Modified date" + } +} diff --git a/jhipster/src/main/webapp/index.html b/jhipster/src/main/webapp/index.html new file mode 100644 index 0000000000..6227d62823 --- /dev/null +++ b/jhipster/src/main/webapp/index.html @@ -0,0 +1,27 @@ + + + + + + + baeldung + + + + + + + + + + diff --git a/jhipster/src/main/webapp/robots.txt b/jhipster/src/main/webapp/robots.txt new file mode 100644 index 0000000000..7de2585cf6 --- /dev/null +++ b/jhipster/src/main/webapp/robots.txt @@ -0,0 +1,11 @@ +# robotstxt.org/ + +User-agent: * +Disallow: /api/account +Disallow: /api/account/change_password +Disallow: /api/account/sessions +Disallow: /api/audits/ +Disallow: /api/logs/ +Disallow: /api/users/ +Disallow: /management/ +Disallow: /v2/api-docs/ diff --git a/jhipster/src/main/webapp/swagger-ui/images/throbber.gif b/jhipster/src/main/webapp/swagger-ui/images/throbber.gif new file mode 100644 index 0000000000000000000000000000000000000000..06393889242fb3ea9e0205fa84369ec7bb66d15a GIT binary patch literal 9257 zcmd^^X;@R|x`tQg5wbE8AV3mAn1TjmQ&en2CK8~ENEH<+P_)pZ24y2E+7O0>K^a6u zQ3;5MiU^7p6*M3qDk!2=YEcHMQ>nzEYP;R`e2C@r+U+?#XaC*&gKPcB#k$`o&;7mu zYNhYYXe|Uo84#4ZIko#rcU5K8*yFL{qT47O&^5fZH$ zVZ@%(l~vVHjnm;H@KL8@r%yUHoo;rbHI_4lIH(_nsTT>S2`DFOD~uCb9_dF4`#QgI zy7ldMcLs+A_s%|e1pRPrbX-tpeNP!9(IpMFTce`t_5U%lP99z%&i6`1d~ zWeM!Rxc50<+d$e^9LT`?B+aMK~apR zHm?q;p<7{wN2g|I^aGlSws;VP84j(z%aQwvAWv83Z$}p(% zZ^?2;gxg(ey_`V5J7{;!o;o;KslW@z5EP~JGs|U)J7dF&(ff#A=6vU?cGQ$-4+;Jf z-ggJEa!yStn`_EWvl)#yhm6XVs}UUbsi;+agri;mCfjH^Uy;lH+Zw^h)4N?oZgZz4 zJk(fTZ|Bi^;+s_M=~+d#vyoxEPzTlOS=mX@sbl*uRj>=MaMr}cFIY8i?UM61>86uB zV$DlOUCiUJwbzJMP@D$urzK|lL2-PC!p1l47V-ZG<5Ev0Z5h~Kx?`KOp7gkAjV93A z-Gc7MrlxTf?wF;CbNc@tCHJH{TB3c;#{SVu%97}tyAM2n&|9W_?qv}$*Jt*%7Yxb# zV0;d;7|lDEltJYS+U)#aiJO};?_Jyy_4%syQ(uy?-J-Yx-9O5nKRk@@XSS~X<(2u~ zV-LamWm~!iqtH9wkpf8mAXZhOD&L#aA_%)4h2M;1M5jt zIR>Us+%W-GXa_f^opKg=DSrAs)AXeRa;Hp0aC1OgbxQ%Qr_QvTleM1jkR!2mkcX$3 ztsR8~G9iqh(-FJ@F_rQBIYDXV_6s7G9SxaVF^laZqcx$!D97m|7t16j6@Jt6UdDRy49Qyvs|c>RuA|@b%}`*wU}2^7q;&Vtc6@lb zcXl)T!6nYDzmMJ~%n$KNXyNlCG)GkJ4!82;v6@d3>s5r~E+3!O?049JDr14Y^PeMI02R`0lJ^=oJ zYd|*u9|SU(j7hY?+<=(?fP*mtV*zFhOrz6%{VA?ozdm&(Jf^V zMfPZ?>l`mS3{Uq8IM;e!+1YjJy2!mzK$O|wPeU{*QSbs9m+@`f5KxO3PBnQ=%RsZg%go*fJ`*w9TL{-WgZVIA$!YV}3BRcfeXaR$x#b zW)Tpd#8E4)^MyYdkH;4_;ChJuw%n+Be7Ko4;w-nHvyo$d_0e-YiF78Df&)_)(}fcr_r0mPH(4RRYWIu+d@t0&Ss@O^s! zOKyX&13)%N@83r^;QsgN{rl(!0|RF1FA)b1{CRXAy&1ySz@>olPiR4r$aMdq&_=nK zq|cFs8phWJ1@%dZ-gXd{zDbTILD>)qEvH-NU*Rf1b2J1Ri79`rBFl@ z8E^0I)OqEi{pH(a24b9YPG;Kz@t-qZW;3Mpe`MRlmYx{7bH-XZ&`RQ7Rb^%}gc&X| zd}Q-FZf|RWxHU?PR!(C?80zu(^l>*h{#ulSiid(O!J(8P-41bNM3tnX@U6NS5yo0? zdcF)~xFE&+&|gZ$23dV5t~?$$&ymZ;F8j7GGMncGSsDo%>J`26=&l=X#rSKv_64;0 zr;k6no@=gV`P)K!=kaHl>q?!`X>(A;84tg^Md<`zA%qbRLby1Z=fn*ZRdNqs%Tq|3 zOt}lZu0q9oKJhgz&+^7PCt$=UFW=R*w?a1)ePoL*`R$Gxj?TU@12tTHsT$giHQU+sqf;fS0FpT!< z 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 + + + + Swagger UI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+
+ + diff --git a/jhipster/src/test/gatling/conf/gatling.conf b/jhipster/src/test/gatling/conf/gatling.conf new file mode 100644 index 0000000000..e509853f22 --- /dev/null +++ b/jhipster/src/test/gatling/conf/gatling.conf @@ -0,0 +1,131 @@ +######################### +# Gatling Configuration # +######################### + +# This file contains all the settings configurable for Gatling with their default values + +gatling { + core { + #outputDirectoryBaseName = "" # The prefix for each simulation result folder (then suffixed by the report generation timestamp) + #runDescription = "" # The description for this simulation run, displayed in each report + #encoding = "utf-8" # Encoding to use throughout Gatling for file and string manipulation + #simulationClass = "" # The FQCN of the simulation to run (when used in conjunction with noReports, the simulation for which assertions will be validated) + #mute = false # When set to true, don't ask for simulation name nor run description (currently only used by Gatling SBT plugin) + #elFileBodiesCacheMaxCapacity = 200 # Cache size for request body EL templates, set to 0 to disable + #rawFileBodiesCacheMaxCapacity = 200 # Cache size for request body Raw templates, set to 0 to disable + #rawFileBodiesInMemoryMaxSize = 1000 # Below this limit, raw file bodies will be cached in memory + + extract { + regex { + #cacheMaxCapacity = 200 # Cache size for the compiled regexes, set to 0 to disable caching + } + xpath { + #cacheMaxCapacity = 200 # Cache size for the compiled XPath queries, set to 0 to disable caching + } + jsonPath { + #cacheMaxCapacity = 200 # Cache size for the compiled jsonPath queries, set to 0 to disable caching + #preferJackson = false # When set to true, prefer Jackson over Boon for JSON-related operations + } + css { + #cacheMaxCapacity = 200 # Cache size for the compiled CSS selectors queries, set to 0 to disable caching + } + } + directory { + #data = user-files/data # Folder where user's data (e.g. files used by Feeders) is located + #bodies = user-files/bodies # Folder where bodies are located + #simulations = user-files/simulations # Folder where the bundle's simulations are located + #reportsOnly = "" # If set, name of report folder to look for in order to generate its report + #binaries = "" # If set, name of the folder where compiles classes are located: Defaults to GATLING_HOME/target. + #results = results # Name of the folder where all reports folder are located + } + } + charting { + #noReports = false # When set to true, don't generate HTML reports + #maxPlotPerSeries = 1000 # Number of points per graph in Gatling reports + #useGroupDurationMetric = false # Switch group timings from cumulated response time to group duration. + indicators { + #lowerBound = 800 # Lower bound for the requests' response time to track in the reports and the console summary + #higherBound = 1200 # Higher bound for the requests' response time to track in the reports and the console summary + #percentile1 = 50 # Value for the 1st percentile to track in the reports, the console summary and Graphite + #percentile2 = 75 # Value for the 2nd percentile to track in the reports, the console summary and Graphite + #percentile3 = 95 # Value for the 3rd percentile to track in the reports, the console summary and Graphite + #percentile4 = 99 # Value for the 4th percentile to track in the reports, the console summary and Graphite + } + } + http { + #fetchedCssCacheMaxCapacity = 200 # Cache size for CSS parsed content, set to 0 to disable + #fetchedHtmlCacheMaxCapacity = 200 # Cache size for HTML parsed content, set to 0 to disable + #perUserCacheMaxCapacity = 200 # Per virtual user cache size, set to 0 to disable + #warmUpUrl = "http://gatling.io" # The URL to use to warm-up the HTTP stack (blank means disabled) + #enableGA = true # Very light Google Analytics, please support + ssl { + keyStore { + #type = "" # Type of SSLContext's KeyManagers store + #file = "" # Location of SSLContext's KeyManagers store + #password = "" # Password for SSLContext's KeyManagers store + #algorithm = "" # Algorithm used SSLContext's KeyManagers store + } + trustStore { + #type = "" # Type of SSLContext's TrustManagers store + #file = "" # Location of SSLContext's TrustManagers store + #password = "" # Password for SSLContext's TrustManagers store + #algorithm = "" # Algorithm used by SSLContext's TrustManagers store + } + } + ahc { + #keepAlive = true # Allow pooling HTTP connections (keep-alive header automatically added) + #connectTimeout = 10000 # Timeout when establishing a connection + #handshakeTimeout = 10000 # Timeout when performing TLS hashshake + #pooledConnectionIdleTimeout = 60000 # Timeout when a connection stays unused in the pool + #readTimeout = 60000 # Timeout when a used connection stays idle + #maxRetry = 2 # Number of times that a request should be tried again + #requestTimeout = 60000 # Timeout of the requests + #acceptAnyCertificate = true # When set to true, doesn't validate SSL certificates + #httpClientCodecMaxInitialLineLength = 4096 # Maximum length of the initial line of the response (e.g. "HTTP/1.0 200 OK") + #httpClientCodecMaxHeaderSize = 8192 # Maximum size, in bytes, of each request's headers + #httpClientCodecMaxChunkSize = 8192 # Maximum length of the content or each chunk + #webSocketMaxFrameSize = 10240000 # Maximum frame payload size + #sslEnabledProtocols = [TLSv1.2, TLSv1.1, TLSv1] # Array of enabled protocols for HTTPS, if empty use the JDK defaults + #sslEnabledCipherSuites = [] # Array of enabled cipher suites for HTTPS, if empty use the AHC defaults + #sslSessionCacheSize = 0 # SSLSession cache size, set to 0 to use JDK's default + #sslSessionTimeout = 0 # SSLSession timeout in seconds, set to 0 to use JDK's default (24h) + #useOpenSsl = false # if OpenSSL should be used instead of JSSE (requires tcnative jar) + #useNativeTransport = false # if native transport should be used instead of Java NIO (requires netty-transport-native-epoll, currently Linux only) + #tcpNoDelay = true + #soReuseAddress = false + #soLinger = -1 + #soSndBuf = -1 + #soRcvBuf = -1 + #allocator = "pooled" # switch to unpooled for unpooled ByteBufAllocator + #maxThreadLocalCharBufferSize = 200000 # Netty's default is 16k + } + dns { + #queryTimeout = 5000 # Timeout of each DNS query in millis + #maxQueriesPerResolve = 6 # Maximum allowed number of DNS queries for a given name resolution + } + } + jms { + #acknowledgedMessagesBufferSize = 5000 # size of the buffer used to tracked acknowledged messages and protect against duplicate receives + } + data { + #writers = [console, file] # The list of DataWriters to which Gatling write simulation data (currently supported : console, file, graphite, jdbc) + console { + #light = false # When set to true, displays a light version without detailed request stats + } + file { + #bufferSize = 8192 # FileDataWriter's internal data buffer size, in bytes + } + leak { + #noActivityTimeout = 30 # Period, in seconds, for which Gatling may have no activity before considering a leak may be happening + } + graphite { + #light = false # only send the all* stats + #host = "localhost" # The host where the Carbon server is located + #port = 2003 # The port to which the Carbon server listens to (2003 is default for plaintext, 2004 is default for pickle) + #protocol = "tcp" # The protocol used to send data to Carbon (currently supported : "tcp", "udp") + #rootPathPrefix = "gatling" # The common prefix of all metrics sent to Graphite + #bufferSize = 8192 # GraphiteDataWriter's internal data buffer size, in bytes + #writeInterval = 1 # GraphiteDataWriter's write interval, in seconds + } + } +} diff --git a/jhipster/src/test/gatling/conf/logback.xml b/jhipster/src/test/gatling/conf/logback.xml new file mode 100644 index 0000000000..7b037e6813 --- /dev/null +++ b/jhipster/src/test/gatling/conf/logback.xml @@ -0,0 +1,22 @@ + + + + + + %d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx + false + + + + + + + + + + + + + + + diff --git a/jhipster/src/test/gatling/simulations/CommentGatlingTest.scala b/jhipster/src/test/gatling/simulations/CommentGatlingTest.scala new file mode 100644 index 0000000000..93d066f9bb --- /dev/null +++ b/jhipster/src/test/gatling/simulations/CommentGatlingTest.scala @@ -0,0 +1,92 @@ +import _root_.io.gatling.core.scenario.Simulation +import ch.qos.logback.classic.{Level, LoggerContext} +import io.gatling.core.Predef._ +import io.gatling.http.Predef._ +import org.slf4j.LoggerFactory + +import scala.concurrent.duration._ + +/** + * Performance test for the Comment entity. + */ +class CommentGatlingTest extends Simulation { + + val context: LoggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] + // Log all HTTP requests + //context.getLogger("io.gatling.http").setLevel(Level.valueOf("TRACE")) + // Log failed HTTP requests + //context.getLogger("io.gatling.http").setLevel(Level.valueOf("DEBUG")) + + val baseURL = Option(System.getProperty("baseURL")) getOrElse """http://127.0.0.1:8080""" + + val httpConf = http + .baseURL(baseURL) + .inferHtmlResources() + .acceptHeader("*/*") + .acceptEncodingHeader("gzip, deflate") + .acceptLanguageHeader("fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3") + .connectionHeader("keep-alive") + .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:33.0) Gecko/20100101 Firefox/33.0") + + val headers_http = Map( + "Accept" -> """application/json""" + ) + + val headers_http_authentication = Map( + "Content-Type" -> """application/json""", + "Accept" -> """application/json""" + ) + + val headers_http_authenticated = Map( + "Accept" -> """application/json""", + "Authorization" -> "${access_token}" + ) + + val scn = scenario("Test the Comment entity") + .exec(http("First unauthenticated request") + .get("/api/account") + .headers(headers_http) + .check(status.is(401))).exitHereIfFailed + .pause(10) + .exec(http("Authentication") + .post("/api/authenticate") + .headers(headers_http_authentication) + .body(StringBody("""{"username":"admin", "password":"admin"}""")).asJSON + .check(header.get("Authorization").saveAs("access_token"))).exitHereIfFailed + .pause(1) + .exec(http("Authenticated request") + .get("/api/account") + .headers(headers_http_authenticated) + .check(status.is(200))) + .pause(10) + .repeat(2) { + exec(http("Get all comments") + .get("/api/comments") + .headers(headers_http_authenticated) + .check(status.is(200))) + .pause(10 seconds, 20 seconds) + .exec(http("Create new comment") + .post("/api/comments") + .headers(headers_http_authenticated) + .body(StringBody("""{"id":null, "text":"SAMPLE_TEXT", "creationDate":"2020-01-01T00:00:00.000Z"}""")).asJSON + .check(status.is(201)) + .check(headerRegex("Location", "(.*)").saveAs("new_comment_url"))).exitHereIfFailed + .pause(10) + .repeat(5) { + exec(http("Get created comment") + .get("${new_comment_url}") + .headers(headers_http_authenticated)) + .pause(10) + } + .exec(http("Delete created comment") + .delete("${new_comment_url}") + .headers(headers_http_authenticated)) + .pause(10) + } + + val users = scenario("Users").exec(scn) + + setUp( + users.inject(rampUsers(100) over (1 minutes)) + ).protocols(httpConf) +} diff --git a/jhipster/src/test/gatling/simulations/PostGatlingTest.scala b/jhipster/src/test/gatling/simulations/PostGatlingTest.scala new file mode 100644 index 0000000000..d76198c9ae --- /dev/null +++ b/jhipster/src/test/gatling/simulations/PostGatlingTest.scala @@ -0,0 +1,92 @@ +import _root_.io.gatling.core.scenario.Simulation +import ch.qos.logback.classic.{Level, LoggerContext} +import io.gatling.core.Predef._ +import io.gatling.http.Predef._ +import org.slf4j.LoggerFactory + +import scala.concurrent.duration._ + +/** + * Performance test for the Post entity. + */ +class PostGatlingTest extends Simulation { + + val context: LoggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] + // Log all HTTP requests + //context.getLogger("io.gatling.http").setLevel(Level.valueOf("TRACE")) + // Log failed HTTP requests + //context.getLogger("io.gatling.http").setLevel(Level.valueOf("DEBUG")) + + val baseURL = Option(System.getProperty("baseURL")) getOrElse """http://127.0.0.1:8080""" + + val httpConf = http + .baseURL(baseURL) + .inferHtmlResources() + .acceptHeader("*/*") + .acceptEncodingHeader("gzip, deflate") + .acceptLanguageHeader("fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3") + .connectionHeader("keep-alive") + .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:33.0) Gecko/20100101 Firefox/33.0") + + val headers_http = Map( + "Accept" -> """application/json""" + ) + + val headers_http_authentication = Map( + "Content-Type" -> """application/json""", + "Accept" -> """application/json""" + ) + + val headers_http_authenticated = Map( + "Accept" -> """application/json""", + "Authorization" -> "${access_token}" + ) + + val scn = scenario("Test the Post entity") + .exec(http("First unauthenticated request") + .get("/api/account") + .headers(headers_http) + .check(status.is(401))).exitHereIfFailed + .pause(10) + .exec(http("Authentication") + .post("/api/authenticate") + .headers(headers_http_authentication) + .body(StringBody("""{"username":"admin", "password":"admin"}""")).asJSON + .check(header.get("Authorization").saveAs("access_token"))).exitHereIfFailed + .pause(1) + .exec(http("Authenticated request") + .get("/api/account") + .headers(headers_http_authenticated) + .check(status.is(200))) + .pause(10) + .repeat(2) { + exec(http("Get all posts") + .get("/api/posts") + .headers(headers_http_authenticated) + .check(status.is(200))) + .pause(10 seconds, 20 seconds) + .exec(http("Create new post") + .post("/api/posts") + .headers(headers_http_authenticated) + .body(StringBody("""{"id":null, "title":"SAMPLE_TEXT", "content":"SAMPLE_TEXT", "creationDate":"2020-01-01T00:00:00.000Z"}""")).asJSON + .check(status.is(201)) + .check(headerRegex("Location", "(.*)").saveAs("new_post_url"))).exitHereIfFailed + .pause(10) + .repeat(5) { + exec(http("Get created post") + .get("${new_post_url}") + .headers(headers_http_authenticated)) + .pause(10) + } + .exec(http("Delete created post") + .delete("${new_post_url}") + .headers(headers_http_authenticated)) + .pause(10) + } + + val users = scenario("Users").exec(scn) + + setUp( + users.inject(rampUsers(100) over (1 minutes)) + ).protocols(httpConf) +} diff --git a/jhipster/src/test/java/com/baeldung/security/SecurityUtilsUnitTest.java b/jhipster/src/test/java/com/baeldung/security/SecurityUtilsUnitTest.java new file mode 100644 index 0000000000..b78a18790b --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/security/SecurityUtilsUnitTest.java @@ -0,0 +1,50 @@ +package com.baeldung.security; + +import org.junit.Test; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.util.ArrayList; +import java.util.Collection; + +import static org.assertj.core.api.Assertions.assertThat; + +/** +* Test class for the SecurityUtils utility class. +* +* @see SecurityUtils +*/ +public class SecurityUtilsUnitTest { + + @Test + public void testgetCurrentUserLogin() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); + SecurityContextHolder.setContext(securityContext); + String login = SecurityUtils.getCurrentUserLogin(); + assertThat(login).isEqualTo("admin"); + } + + @Test + public void testIsAuthenticated() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); + SecurityContextHolder.setContext(securityContext); + boolean isAuthenticated = SecurityUtils.isAuthenticated(); + assertThat(isAuthenticated).isTrue(); + } + + @Test + public void testAnonymousIsNotAuthenticated() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities)); + SecurityContextHolder.setContext(securityContext); + boolean isAuthenticated = SecurityUtils.isAuthenticated(); + assertThat(isAuthenticated).isFalse(); + } +} diff --git a/jhipster/src/test/java/com/baeldung/security/jwt/TokenProviderTest.java b/jhipster/src/test/java/com/baeldung/security/jwt/TokenProviderTest.java new file mode 100644 index 0000000000..3fec4bfb88 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/security/jwt/TokenProviderTest.java @@ -0,0 +1,107 @@ +package com.baeldung.security.jwt; + +import com.baeldung.security.AuthoritiesConstants; +import io.github.jhipster.config.JHipsterProperties; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TokenProviderTest { + + private final String secretKey = "e5c9ee274ae87bc031adda32e27fa98b9290da83"; + private final long ONE_MINUTE = 60000; + private JHipsterProperties jHipsterProperties; + private TokenProvider tokenProvider; + + @Before + public void setup() { + jHipsterProperties = Mockito.mock(JHipsterProperties.class); + tokenProvider = new TokenProvider(jHipsterProperties); + ReflectionTestUtils.setField(tokenProvider, "secretKey", secretKey); + ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", ONE_MINUTE); + } + + @Test + public void testReturnFalseWhenJWThasInvalidSignature() { + boolean isTokenValid = tokenProvider.validateToken(createTokenWithDifferentSignature()); + + assertThat(isTokenValid).isEqualTo(false); + } + + @Test + public void testReturnFalseWhenJWTisMalformed() { + Authentication authentication = createAuthentication(); + String token = tokenProvider.createToken(authentication, false); + String invalidToken = token.substring(1); + boolean isTokenValid = tokenProvider.validateToken(invalidToken); + + assertThat(isTokenValid).isEqualTo(false); + } + + @Test + public void testReturnFalseWhenJWTisExpired() { + ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", -ONE_MINUTE); + + Authentication authentication = createAuthentication(); + String token = tokenProvider.createToken(authentication, false); + + boolean isTokenValid = tokenProvider.validateToken(token); + + assertThat(isTokenValid).isEqualTo(false); + } + + @Test + public void testReturnFalseWhenJWTisUnsupported() { + Date expirationDate = new Date(new Date().getTime() + ONE_MINUTE); + + Authentication authentication = createAuthentication(); + + String unsupportedToken = createUnsupportedToken(); + + boolean isTokenValid = tokenProvider.validateToken(unsupportedToken); + + assertThat(isTokenValid).isEqualTo(false); + } + + @Test + public void testReturnFalseWhenJWTisInvalid() { + + boolean isTokenValid = tokenProvider.validateToken(""); + + assertThat(isTokenValid).isEqualTo(false); + } + + private Authentication createAuthentication() { + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); + return new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities); + } + + private String createUnsupportedToken() { + return Jwts.builder() + .setPayload("payload") + .signWith(SignatureAlgorithm.HS512, secretKey) + .compact(); + } + + private String createTokenWithDifferentSignature() { + return Jwts.builder() + .setSubject("anonymous") + .signWith(SignatureAlgorithm.HS512, "e5c9ee274ae87bc031adda32e27fa98b9290da90") + .setExpiration(new Date(new Date().getTime() + ONE_MINUTE)) + .compact(); + } +} diff --git a/jhipster/src/test/java/com/baeldung/service/UserServiceIntTest.java b/jhipster/src/test/java/com/baeldung/service/UserServiceIntTest.java new file mode 100644 index 0000000000..968f0a7f08 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/service/UserServiceIntTest.java @@ -0,0 +1,129 @@ +package com.baeldung.service; + +import com.baeldung.BaeldungApp; +import com.baeldung.domain.User; +import com.baeldung.config.Constants; +import com.baeldung.repository.UserRepository; +import com.baeldung.service.dto.UserDTO; +import java.time.ZonedDateTime; +import com.baeldung.service.util.RandomUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.test.context.junit4.SpringRunner; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import java.util.Optional; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +/** + * Test class for the UserResource REST controller. + * + * @see UserService + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +@Transactional +public class UserServiceIntTest { + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserService userService; + + @Test + public void assertThatUserMustExistToResetPassword() { + Optional maybeUser = userService.requestPasswordReset("john.doe@localhost"); + assertThat(maybeUser.isPresent()).isFalse(); + + maybeUser = userService.requestPasswordReset("admin@localhost"); + assertThat(maybeUser.isPresent()).isTrue(); + + assertThat(maybeUser.get().getEmail()).isEqualTo("admin@localhost"); + assertThat(maybeUser.get().getResetDate()).isNotNull(); + assertThat(maybeUser.get().getResetKey()).isNotNull(); + } + + @Test + public void assertThatOnlyActivatedUserCanRequestPasswordReset() { + User user = userService.createUser("johndoe", "johndoe", "John", "Doe", "john.doe@localhost", "http://placehold.it/50x50", "en-US"); + Optional maybeUser = userService.requestPasswordReset("john.doe@localhost"); + assertThat(maybeUser.isPresent()).isFalse(); + userRepository.delete(user); + } + + @Test + public void assertThatResetKeyMustNotBeOlderThan24Hours() { + User user = userService.createUser("johndoe", "johndoe", "John", "Doe", "john.doe@localhost", "http://placehold.it/50x50", "en-US"); + + ZonedDateTime daysAgo = ZonedDateTime.now().minusHours(25); + String resetKey = RandomUtil.generateResetKey(); + user.setActivated(true); + user.setResetDate(daysAgo); + user.setResetKey(resetKey); + + userRepository.save(user); + + Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); + + assertThat(maybeUser.isPresent()).isFalse(); + + userRepository.delete(user); + } + + @Test + public void assertThatResetKeyMustBeValid() { + User user = userService.createUser("johndoe", "johndoe", "John", "Doe", "john.doe@localhost", "http://placehold.it/50x50", "en-US"); + + ZonedDateTime daysAgo = ZonedDateTime.now().minusHours(25); + user.setActivated(true); + user.setResetDate(daysAgo); + user.setResetKey("1234"); + userRepository.save(user); + Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); + assertThat(maybeUser.isPresent()).isFalse(); + userRepository.delete(user); + } + + @Test + public void assertThatUserCanResetPassword() { + User user = userService.createUser("johndoe", "johndoe", "John", "Doe", "john.doe@localhost", "http://placehold.it/50x50", "en-US"); + String oldPassword = user.getPassword(); + ZonedDateTime daysAgo = ZonedDateTime.now().minusHours(2); + String resetKey = RandomUtil.generateResetKey(); + user.setActivated(true); + user.setResetDate(daysAgo); + user.setResetKey(resetKey); + userRepository.save(user); + Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); + assertThat(maybeUser.isPresent()).isTrue(); + assertThat(maybeUser.get().getResetDate()).isNull(); + assertThat(maybeUser.get().getResetKey()).isNull(); + assertThat(maybeUser.get().getPassword()).isNotEqualTo(oldPassword); + + userRepository.delete(user); + } + + @Test + public void testFindNotActivatedUsersByCreationDateBefore() { + userService.removeNotActivatedUsers(); + ZonedDateTime now = ZonedDateTime.now(); + List users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minusDays(3)); + assertThat(users).isEmpty(); + } + + @Test + public void assertThatAnonymousUserIsNotGet() { + final PageRequest pageable = new PageRequest(0, (int) userRepository.count()); + final Page allManagedUsers = userService.getAllManagedUsers(pageable); + assertThat(allManagedUsers.getContent().stream() + .noneMatch(user -> Constants.ANONYMOUS_USER.equals(user.getLogin()))) + .isTrue(); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/AccountResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/AccountResourceIntTest.java new file mode 100644 index 0000000000..e42ce1c6d4 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/AccountResourceIntTest.java @@ -0,0 +1,395 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; +import com.baeldung.domain.Authority; +import com.baeldung.domain.User; +import com.baeldung.repository.AuthorityRepository; +import com.baeldung.repository.UserRepository; +import com.baeldung.security.AuthoritiesConstants; +import com.baeldung.service.MailService; +import com.baeldung.service.UserService; +import com.baeldung.service.dto.UserDTO; +import com.baeldung.web.rest.vm.ManagedUserVM; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test class for the AccountResource REST controller. + * + * @see AccountResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class AccountResourceIntTest { + + @Autowired + private UserRepository userRepository; + + @Autowired + private AuthorityRepository authorityRepository; + + @Autowired + private UserService userService; + + @Mock + private UserService mockUserService; + + @Mock + private MailService mockMailService; + + private MockMvc restUserMockMvc; + + private MockMvc restMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + doNothing().when(mockMailService).sendActivationEmail(anyObject()); + + AccountResource accountResource = + new AccountResource(userRepository, userService, mockMailService); + + AccountResource accountUserMockResource = + new AccountResource(userRepository, mockUserService, mockMailService); + + this.restMvc = MockMvcBuilders.standaloneSetup(accountResource).build(); + this.restUserMockMvc = MockMvcBuilders.standaloneSetup(accountUserMockResource).build(); + } + + @Test + public void testNonAuthenticatedUser() throws Exception { + restUserMockMvc.perform(get("/api/authenticate") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string("")); + } + + @Test + public void testAuthenticatedUser() throws Exception { + restUserMockMvc.perform(get("/api/authenticate") + .with(request -> { + request.setRemoteUser("test"); + return request; + }) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string("test")); + } + + @Test + public void testGetExistingAccount() throws Exception { + Set authorities = new HashSet<>(); + Authority authority = new Authority(); + authority.setName(AuthoritiesConstants.ADMIN); + authorities.add(authority); + + User user = new User(); + user.setLogin("test"); + user.setFirstName("john"); + user.setLastName("doe"); + user.setEmail("john.doe@jhipster.com"); + user.setImageUrl("http://placehold.it/50x50"); + user.setAuthorities(authorities); + when(mockUserService.getUserWithAuthorities()).thenReturn(user); + + restUserMockMvc.perform(get("/api/account") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.login").value("test")) + .andExpect(jsonPath("$.firstName").value("john")) + .andExpect(jsonPath("$.lastName").value("doe")) + .andExpect(jsonPath("$.email").value("john.doe@jhipster.com")) + .andExpect(jsonPath("$.imageUrl").value("http://placehold.it/50x50")) + .andExpect(jsonPath("$.authorities").value(AuthoritiesConstants.ADMIN)); + } + + @Test + public void testGetUnknownAccount() throws Exception { + when(mockUserService.getUserWithAuthorities()).thenReturn(null); + + restUserMockMvc.perform(get("/api/account") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isInternalServerError()); + } + + @Test + @Transactional + public void testRegisterValid() throws Exception { + ManagedUserVM validUser = new ManagedUserVM( + null, // id + "joe", // login + "password", // password + "Joe", // firstName + "Shmoe", // lastName + "joe@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + Optional user = userRepository.findOneByLogin("joe"); + assertThat(user.isPresent()).isTrue(); + } + + @Test + @Transactional + public void testRegisterInvalidLogin() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM( + null, // id + "funky-log!n", // login <-- invalid + "password", // password + "Funky", // firstName + "One", // lastName + "funky@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + restUserMockMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByEmail("funky@example.com"); + assertThat(user.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterInvalidEmail() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM( + null, // id + "bob", // login + "password", // password + "Bob", // firstName + "Green", // lastName + "invalid", // e-mail <-- invalid + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + restUserMockMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByLogin("bob"); + assertThat(user.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterInvalidPassword() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM( + null, // id + "bob", // login + "123", // password with only 3 digits + "Bob", // firstName + "Green", // lastName + "bob@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + restUserMockMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByLogin("bob"); + assertThat(user.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterDuplicateLogin() throws Exception { + // Good + ManagedUserVM validUser = new ManagedUserVM( + null, // id + "alice", // login + "password", // password + "Alice", // firstName + "Something", // lastName + "alice@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + // Duplicate login, different e-mail + ManagedUserVM duplicatedUser = new ManagedUserVM(validUser.getId(), validUser.getLogin(), validUser.getPassword(), validUser.getLogin(), validUser.getLastName(), + "alicejr@example.com", true, validUser.getImageUrl(), validUser.getLangKey(), validUser.getCreatedBy(), validUser.getCreatedDate(), validUser.getLastModifiedBy(), validUser.getLastModifiedDate(), validUser.getAuthorities()); + + // Good user + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + // Duplicate login + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(duplicatedUser))) + .andExpect(status().is4xxClientError()); + + Optional userDup = userRepository.findOneByEmail("alicejr@example.com"); + assertThat(userDup.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterDuplicateEmail() throws Exception { + // Good + ManagedUserVM validUser = new ManagedUserVM( + null, // id + "john", // login + "password", // password + "John", // firstName + "Doe", // lastName + "john@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER))); + + // Duplicate e-mail, different login + ManagedUserVM duplicatedUser = new ManagedUserVM(validUser.getId(), "johnjr", validUser.getPassword(), validUser.getLogin(), validUser.getLastName(), + validUser.getEmail(), true, validUser.getImageUrl(), validUser.getLangKey(), validUser.getCreatedBy(), validUser.getCreatedDate(), validUser.getLastModifiedBy(), validUser.getLastModifiedDate(), validUser.getAuthorities()); + + // Good user + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + // Duplicate e-mail + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(duplicatedUser))) + .andExpect(status().is4xxClientError()); + + Optional userDup = userRepository.findOneByLogin("johnjr"); + assertThat(userDup.isPresent()).isFalse(); + } + + @Test + @Transactional + public void testRegisterAdminIsIgnored() throws Exception { + ManagedUserVM validUser = new ManagedUserVM( + null, // id + "badguy", // login + "password", // password + "Bad", // firstName + "Guy", // lastName + "badguy@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.ADMIN))); + + restMvc.perform( + post("/api/register") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + Optional userDup = userRepository.findOneByLogin("badguy"); + assertThat(userDup.isPresent()).isTrue(); + assertThat(userDup.get().getAuthorities()).hasSize(1) + .containsExactly(authorityRepository.findOne(AuthoritiesConstants.USER)); + } + + @Test + @Transactional + public void testSaveInvalidLogin() throws Exception { + UserDTO invalidUser = new UserDTO( + null, // id + "funky-log!n", // login <-- invalid + "Funky", // firstName + "One", // lastName + "funky@example.com", // e-mail + true, // activated + "http://placehold.it/50x50", //imageUrl + "en", // langKey + null, // createdBy + null, // createdDate + null, // lastModifiedBy + null, // lastModifiedDate + new HashSet<>(Arrays.asList(AuthoritiesConstants.USER)) + ); + + restUserMockMvc.perform( + post("/api/account") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByEmail("funky@example.com"); + assertThat(user.isPresent()).isFalse(); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/AuditResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/AuditResourceIntTest.java new file mode 100644 index 0000000000..127cb36f07 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/AuditResourceIntTest.java @@ -0,0 +1,147 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; +import com.baeldung.config.audit.AuditEventConverter; +import com.baeldung.domain.PersistentAuditEvent; +import com.baeldung.repository.PersistenceAuditEventRepository; +import com.baeldung.service.AuditEventService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.format.support.FormattingConversionService; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test class for the AuditResource REST controller. + * + * @see AuditResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +@Transactional +public class AuditResourceIntTest { + + private static final String SAMPLE_PRINCIPAL = "SAMPLE_PRINCIPAL"; + private static final String SAMPLE_TYPE = "SAMPLE_TYPE"; + private static final LocalDateTime SAMPLE_TIMESTAMP = LocalDateTime.parse("2015-08-04T10:11:30"); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + @Autowired + private PersistenceAuditEventRepository auditEventRepository; + + @Autowired + private AuditEventConverter auditEventConverter; + + @Autowired + private MappingJackson2HttpMessageConverter jacksonMessageConverter; + + @Autowired + private FormattingConversionService formattingConversionService; + + @Autowired + private PageableHandlerMethodArgumentResolver pageableArgumentResolver; + + private PersistentAuditEvent auditEvent; + + private MockMvc restAuditMockMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + AuditEventService auditEventService = + new AuditEventService(auditEventRepository, auditEventConverter); + AuditResource auditResource = new AuditResource(auditEventService); + this.restAuditMockMvc = MockMvcBuilders.standaloneSetup(auditResource) + .setCustomArgumentResolvers(pageableArgumentResolver) + .setConversionService(formattingConversionService) + .setMessageConverters(jacksonMessageConverter).build(); + } + + @Before + public void initTest() { + auditEventRepository.deleteAll(); + auditEvent = new PersistentAuditEvent(); + auditEvent.setAuditEventType(SAMPLE_TYPE); + auditEvent.setPrincipal(SAMPLE_PRINCIPAL); + auditEvent.setAuditEventDate(SAMPLE_TIMESTAMP); + } + + @Test + public void getAllAudits() throws Exception { + // Initialize the database + auditEventRepository.save(auditEvent); + + // Get all the audits + restAuditMockMvc.perform(get("/management/audits")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].principal").value(hasItem(SAMPLE_PRINCIPAL))); + } + + @Test + public void getAudit() throws Exception { + // Initialize the database + auditEventRepository.save(auditEvent); + + // Get the audit + restAuditMockMvc.perform(get("/management/audits/{id}", auditEvent.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.principal").value(SAMPLE_PRINCIPAL)); + } + + @Test + public void getAuditsByDate() throws Exception { + // Initialize the database + auditEventRepository.save(auditEvent); + + // Generate dates for selecting audits by date, making sure the period will contain the audit + String fromDate = SAMPLE_TIMESTAMP.minusDays(1).format(FORMATTER); + String toDate = SAMPLE_TIMESTAMP.plusDays(1).format(FORMATTER); + + // Get the audit + restAuditMockMvc.perform(get("/management/audits?fromDate="+fromDate+"&toDate="+toDate)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].principal").value(hasItem(SAMPLE_PRINCIPAL))); + } + + @Test + public void getNonExistingAuditsByDate() throws Exception { + // Initialize the database + auditEventRepository.save(auditEvent); + + // Generate dates for selecting audits by date, making sure the period will not contain the sample audit + String fromDate = SAMPLE_TIMESTAMP.minusDays(2).format(FORMATTER); + String toDate = SAMPLE_TIMESTAMP.minusDays(1).format(FORMATTER); + + // Query audits but expect no results + restAuditMockMvc.perform(get("/management/audits?fromDate=" + fromDate + "&toDate=" + toDate)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(header().string("X-Total-Count", "0")); + } + + @Test + public void getNonExistingAudit() throws Exception { + // Get the audit + restAuditMockMvc.perform(get("/management/audits/{id}", Long.MAX_VALUE)) + .andExpect(status().isNotFound()); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/CommentResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/CommentResourceIntTest.java new file mode 100644 index 0000000000..04b16b25f8 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/CommentResourceIntTest.java @@ -0,0 +1,279 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; + +import com.baeldung.domain.Comment; +import com.baeldung.domain.Post; +import com.baeldung.repository.CommentRepository; +import com.baeldung.web.rest.errors.ExceptionTranslator; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test class for the CommentResource REST controller. + * + * @see CommentResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class CommentResourceIntTest { + + private static final String DEFAULT_TEXT = "AAAAAAAAAA"; + private static final String UPDATED_TEXT = "BBBBBBBBBB"; + + private static final LocalDate DEFAULT_CREATION_DATE = LocalDate.ofEpochDay(0L); + private static final LocalDate UPDATED_CREATION_DATE = LocalDate.now(ZoneId.systemDefault()); + + @Autowired + private CommentRepository commentRepository; + + @Autowired + private MappingJackson2HttpMessageConverter jacksonMessageConverter; + + @Autowired + private PageableHandlerMethodArgumentResolver pageableArgumentResolver; + + @Autowired + private ExceptionTranslator exceptionTranslator; + + @Autowired + private EntityManager em; + + private MockMvc restCommentMockMvc; + + private Comment comment; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + CommentResource commentResource = new CommentResource(commentRepository); + this.restCommentMockMvc = MockMvcBuilders.standaloneSetup(commentResource) + .setCustomArgumentResolvers(pageableArgumentResolver) + .setControllerAdvice(exceptionTranslator) + .setMessageConverters(jacksonMessageConverter).build(); + } + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Comment createEntity(EntityManager em) { + Comment comment = new Comment() + .text(DEFAULT_TEXT) + .creationDate(DEFAULT_CREATION_DATE); + // Add required entity + Post post = PostResourceIntTest.createEntity(em); + em.persist(post); + em.flush(); + comment.setPost(post); + return comment; + } + + @Before + public void initTest() { + comment = createEntity(em); + } + + @Test + @Transactional + public void createComment() throws Exception { + int databaseSizeBeforeCreate = commentRepository.findAll().size(); + + // Create the Comment + restCommentMockMvc.perform(post("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isCreated()); + + // Validate the Comment in the database + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeCreate + 1); + Comment testComment = commentList.get(commentList.size() - 1); + assertThat(testComment.getText()).isEqualTo(DEFAULT_TEXT); + assertThat(testComment.getCreationDate()).isEqualTo(DEFAULT_CREATION_DATE); + } + + @Test + @Transactional + public void createCommentWithExistingId() throws Exception { + int databaseSizeBeforeCreate = commentRepository.findAll().size(); + + // Create the Comment with an existing ID + comment.setId(1L); + + // An entity with an existing ID cannot be created, so this API call must fail + restCommentMockMvc.perform(post("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isBadRequest()); + + // Validate the Alice in the database + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void checkTextIsRequired() throws Exception { + int databaseSizeBeforeTest = commentRepository.findAll().size(); + // set the field null + comment.setText(null); + + // Create the Comment, which fails. + + restCommentMockMvc.perform(post("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isBadRequest()); + + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void checkCreationDateIsRequired() throws Exception { + int databaseSizeBeforeTest = commentRepository.findAll().size(); + // set the field null + comment.setCreationDate(null); + + // Create the Comment, which fails. + + restCommentMockMvc.perform(post("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isBadRequest()); + + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void getAllComments() throws Exception { + // Initialize the database + commentRepository.saveAndFlush(comment); + + // Get all the commentList + restCommentMockMvc.perform(get("/api/comments?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(comment.getId().intValue()))) + .andExpect(jsonPath("$.[*].text").value(hasItem(DEFAULT_TEXT.toString()))) + .andExpect(jsonPath("$.[*].creationDate").value(hasItem(DEFAULT_CREATION_DATE.toString()))); + } + + @Test + @Transactional + public void getComment() throws Exception { + // Initialize the database + commentRepository.saveAndFlush(comment); + + // Get the comment + restCommentMockMvc.perform(get("/api/comments/{id}", comment.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.id").value(comment.getId().intValue())) + .andExpect(jsonPath("$.text").value(DEFAULT_TEXT.toString())) + .andExpect(jsonPath("$.creationDate").value(DEFAULT_CREATION_DATE.toString())); + } + + @Test + @Transactional + public void getNonExistingComment() throws Exception { + // Get the comment + restCommentMockMvc.perform(get("/api/comments/{id}", Long.MAX_VALUE)) + .andExpect(status().isNotFound()); + } + + @Test + @Transactional + public void updateComment() throws Exception { + // Initialize the database + commentRepository.saveAndFlush(comment); + int databaseSizeBeforeUpdate = commentRepository.findAll().size(); + + // Update the comment + Comment updatedComment = commentRepository.findOne(comment.getId()); + updatedComment + .text(UPDATED_TEXT) + .creationDate(UPDATED_CREATION_DATE); + + restCommentMockMvc.perform(put("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(updatedComment))) + .andExpect(status().isOk()); + + // Validate the Comment in the database + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeUpdate); + Comment testComment = commentList.get(commentList.size() - 1); + assertThat(testComment.getText()).isEqualTo(UPDATED_TEXT); + assertThat(testComment.getCreationDate()).isEqualTo(UPDATED_CREATION_DATE); + } + + @Test + @Transactional + public void updateNonExistingComment() throws Exception { + int databaseSizeBeforeUpdate = commentRepository.findAll().size(); + + // Create the Comment + + // If the entity doesn't have an ID, it will be created instead of just being updated + restCommentMockMvc.perform(put("/api/comments") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(comment))) + .andExpect(status().isCreated()); + + // Validate the Comment in the database + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeUpdate + 1); + } + + @Test + @Transactional + public void deleteComment() throws Exception { + // Initialize the database + commentRepository.saveAndFlush(comment); + int databaseSizeBeforeDelete = commentRepository.findAll().size(); + + // Get the comment + restCommentMockMvc.perform(delete("/api/comments/{id}", comment.getId()) + .accept(TestUtil.APPLICATION_JSON_UTF8)) + .andExpect(status().isOk()); + + // Validate the database is empty + List commentList = commentRepository.findAll(); + assertThat(commentList).hasSize(databaseSizeBeforeDelete - 1); + } + + @Test + @Transactional + public void equalsVerifier() throws Exception { + TestUtil.equalsVerifier(Comment.class); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/LogsResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/LogsResourceIntTest.java new file mode 100644 index 0000000000..92bb976205 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/LogsResourceIntTest.java @@ -0,0 +1,59 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; +import com.baeldung.web.rest.vm.LoggerVM; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Test class for the LogsResource REST controller. + * + * @see LogsResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class LogsResourceIntTest { + + private MockMvc restLogsMockMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + LogsResource logsResource = new LogsResource(); + this.restLogsMockMvc = MockMvcBuilders + .standaloneSetup(logsResource) + .build(); + } + + @Test + public void getAllLogs()throws Exception { + restLogsMockMvc.perform(get("/management/logs")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); + } + + @Test + public void changeLogs()throws Exception { + LoggerVM logger = new LoggerVM(); + logger.setLevel("INFO"); + logger.setName("ROOT"); + + restLogsMockMvc.perform(put("/management/logs") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(logger))) + .andExpect(status().isNoContent()); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/PostResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/PostResourceIntTest.java new file mode 100644 index 0000000000..2a23452711 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/PostResourceIntTest.java @@ -0,0 +1,306 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; + +import com.baeldung.domain.Post; +import com.baeldung.domain.User; +import com.baeldung.repository.PostRepository; +import com.baeldung.web.rest.errors.ExceptionTranslator; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test class for the PostResource REST controller. + * + * @see PostResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class PostResourceIntTest { + + private static final String DEFAULT_TITLE = "AAAAAAAAAA"; + private static final String UPDATED_TITLE = "BBBBBBBBBB"; + + private static final String DEFAULT_CONTENT = "AAAAAAAAAA"; + private static final String UPDATED_CONTENT = "BBBBBBBBBB"; + + private static final LocalDate DEFAULT_CREATION_DATE = LocalDate.ofEpochDay(0L); + private static final LocalDate UPDATED_CREATION_DATE = LocalDate.now(ZoneId.systemDefault()); + + @Autowired + private PostRepository postRepository; + + @Autowired + private MappingJackson2HttpMessageConverter jacksonMessageConverter; + + @Autowired + private PageableHandlerMethodArgumentResolver pageableArgumentResolver; + + @Autowired + private ExceptionTranslator exceptionTranslator; + + @Autowired + private EntityManager em; + + private MockMvc restPostMockMvc; + + private Post post; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + PostResource postResource = new PostResource(postRepository); + this.restPostMockMvc = MockMvcBuilders.standaloneSetup(postResource) + .setCustomArgumentResolvers(pageableArgumentResolver) + .setControllerAdvice(exceptionTranslator) + .setMessageConverters(jacksonMessageConverter).build(); + } + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Post createEntity(EntityManager em) { + Post post = new Post() + .title(DEFAULT_TITLE) + .content(DEFAULT_CONTENT) + .creationDate(DEFAULT_CREATION_DATE); + // Add required entity + User creator = UserResourceIntTest.createEntity(em); + em.persist(creator); + em.flush(); + post.setCreator(creator); + return post; + } + + @Before + public void initTest() { + post = createEntity(em); + } + + @Test + @Transactional + public void createPost() throws Exception { + int databaseSizeBeforeCreate = postRepository.findAll().size(); + + // Create the Post + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isCreated()); + + // Validate the Post in the database + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeCreate + 1); + Post testPost = postList.get(postList.size() - 1); + assertThat(testPost.getTitle()).isEqualTo(DEFAULT_TITLE); + assertThat(testPost.getContent()).isEqualTo(DEFAULT_CONTENT); + assertThat(testPost.getCreationDate()).isEqualTo(DEFAULT_CREATION_DATE); + } + + @Test + @Transactional + public void createPostWithExistingId() throws Exception { + int databaseSizeBeforeCreate = postRepository.findAll().size(); + + // Create the Post with an existing ID + post.setId(1L); + + // An entity with an existing ID cannot be created, so this API call must fail + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isBadRequest()); + + // Validate the Alice in the database + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void checkTitleIsRequired() throws Exception { + int databaseSizeBeforeTest = postRepository.findAll().size(); + // set the field null + post.setTitle(null); + + // Create the Post, which fails. + + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isBadRequest()); + + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void checkContentIsRequired() throws Exception { + int databaseSizeBeforeTest = postRepository.findAll().size(); + // set the field null + post.setContent(null); + + // Create the Post, which fails. + + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isBadRequest()); + + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void checkCreationDateIsRequired() throws Exception { + int databaseSizeBeforeTest = postRepository.findAll().size(); + // set the field null + post.setCreationDate(null); + + // Create the Post, which fails. + + restPostMockMvc.perform(post("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isBadRequest()); + + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeTest); + } + + @Test + @Transactional + public void getAllPosts() throws Exception { + // Initialize the database + postRepository.saveAndFlush(post); + + // Get all the postList + restPostMockMvc.perform(get("/api/posts?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(post.getId().intValue()))) + .andExpect(jsonPath("$.[*].title").value(hasItem(DEFAULT_TITLE.toString()))) + .andExpect(jsonPath("$.[*].content").value(hasItem(DEFAULT_CONTENT.toString()))) + .andExpect(jsonPath("$.[*].creationDate").value(hasItem(DEFAULT_CREATION_DATE.toString()))); + } + + @Test + @Transactional + public void getPost() throws Exception { + // Initialize the database + postRepository.saveAndFlush(post); + + // Get the post + restPostMockMvc.perform(get("/api/posts/{id}", post.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.id").value(post.getId().intValue())) + .andExpect(jsonPath("$.title").value(DEFAULT_TITLE.toString())) + .andExpect(jsonPath("$.content").value(DEFAULT_CONTENT.toString())) + .andExpect(jsonPath("$.creationDate").value(DEFAULT_CREATION_DATE.toString())); + } + + @Test + @Transactional + public void getNonExistingPost() throws Exception { + // Get the post + restPostMockMvc.perform(get("/api/posts/{id}", Long.MAX_VALUE)) + .andExpect(status().isNotFound()); + } + + @Test + @Transactional + public void updatePost() throws Exception { + // Initialize the database + postRepository.saveAndFlush(post); + int databaseSizeBeforeUpdate = postRepository.findAll().size(); + + // Update the post + Post updatedPost = postRepository.findOne(post.getId()); + updatedPost + .title(UPDATED_TITLE) + .content(UPDATED_CONTENT) + .creationDate(UPDATED_CREATION_DATE); + + restPostMockMvc.perform(put("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(updatedPost))) + .andExpect(status().isOk()); + + // Validate the Post in the database + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeUpdate); + Post testPost = postList.get(postList.size() - 1); + assertThat(testPost.getTitle()).isEqualTo(UPDATED_TITLE); + assertThat(testPost.getContent()).isEqualTo(UPDATED_CONTENT); + assertThat(testPost.getCreationDate()).isEqualTo(UPDATED_CREATION_DATE); + } + + @Test + @Transactional + public void updateNonExistingPost() throws Exception { + int databaseSizeBeforeUpdate = postRepository.findAll().size(); + + // Create the Post + + // If the entity doesn't have an ID, it will be created instead of just being updated + restPostMockMvc.perform(put("/api/posts") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(post))) + .andExpect(status().isCreated()); + + // Validate the Post in the database + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeUpdate + 1); + } + + @Test + @Transactional + public void deletePost() throws Exception { + // Initialize the database + postRepository.saveAndFlush(post); + int databaseSizeBeforeDelete = postRepository.findAll().size(); + + // Get the post + restPostMockMvc.perform(delete("/api/posts/{id}", post.getId()) + .accept(TestUtil.APPLICATION_JSON_UTF8)) + .andExpect(status().isOk()); + + // Validate the database is empty + List postList = postRepository.findAll(); + assertThat(postList).hasSize(databaseSizeBeforeDelete - 1); + } + + @Test + @Transactional + public void equalsVerifier() throws Exception { + TestUtil.equalsVerifier(Post.class); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/ProfileInfoResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/ProfileInfoResourceIntTest.java new file mode 100644 index 0000000000..df3544f344 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/ProfileInfoResourceIntTest.java @@ -0,0 +1,86 @@ +package com.baeldung.web.rest; + +import io.github.jhipster.config.JHipsterProperties; +import com.baeldung.BaeldungApp; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.env.Environment; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Test class for the ProfileInfoResource REST controller. + * + * @see ProfileInfoResource + **/ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class ProfileInfoResourceIntTest { + + @Mock + private Environment environment; + + @Mock + private JHipsterProperties jHipsterProperties; + + private MockMvc restProfileMockMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + String mockProfile[] = {"test"}; + JHipsterProperties.Ribbon ribbon = new JHipsterProperties.Ribbon(); + ribbon.setDisplayOnActiveProfiles(mockProfile); + when(jHipsterProperties.getRibbon()).thenReturn(ribbon); + + String activeProfiles[] = {"test"}; + when(environment.getDefaultProfiles()).thenReturn(activeProfiles); + when(environment.getActiveProfiles()).thenReturn(activeProfiles); + + ProfileInfoResource profileInfoResource = new ProfileInfoResource(environment, jHipsterProperties); + this.restProfileMockMvc = MockMvcBuilders + .standaloneSetup(profileInfoResource) + .build(); + } + + @Test + public void getProfileInfoWithRibbon() throws Exception { + restProfileMockMvc.perform(get("/api/profile-info")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); + } + + @Test + public void getProfileInfoWithoutRibbon() throws Exception { + JHipsterProperties.Ribbon ribbon = new JHipsterProperties.Ribbon(); + ribbon.setDisplayOnActiveProfiles(null); + when(jHipsterProperties.getRibbon()).thenReturn(ribbon); + + restProfileMockMvc.perform(get("/api/profile-info")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); + } + + @Test + public void getProfileInfoWithoutActiveProfiles() throws Exception { + String emptyProfile[] = {}; + when(environment.getDefaultProfiles()).thenReturn(emptyProfile); + when(environment.getActiveProfiles()).thenReturn(emptyProfile); + + restProfileMockMvc.perform(get("/api/profile-info")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/TestUtil.java b/jhipster/src/test/java/com/baeldung/web/rest/TestUtil.java new file mode 100644 index 0000000000..64d092fdf1 --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/TestUtil.java @@ -0,0 +1,120 @@ +package com.baeldung.web.rest; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.http.MediaType; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Utility class for testing REST controllers. + */ +public class TestUtil { + + /** MediaType for JSON UTF8 */ + public static final MediaType APPLICATION_JSON_UTF8 = new MediaType( + MediaType.APPLICATION_JSON.getType(), + MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); + + /** + * Convert an object to JSON byte array. + * + * @param object + * the object to convert + * @return the JSON byte array + * @throws IOException + */ + public static byte[] convertObjectToJsonBytes(Object object) + throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + JavaTimeModule module = new JavaTimeModule(); + mapper.registerModule(module); + + return mapper.writeValueAsBytes(object); + } + + /** + * Create a byte array with a specific size filled with specified data. + * + * @param size the size of the byte array + * @param data the data to put in the byte array + * @return the JSON byte array + */ + public static byte[] createByteArray(int size, String data) { + byte[] byteArray = new byte[size]; + for (int i = 0; i < size; i++) { + byteArray[i] = Byte.parseByte(data, 2); + } + return byteArray; + } + + /** + * A matcher that tests that the examined string represents the same instant as the reference datetime. + */ + public static class ZonedDateTimeMatcher extends TypeSafeDiagnosingMatcher { + + private final ZonedDateTime date; + + public ZonedDateTimeMatcher(ZonedDateTime date) { + this.date = date; + } + + @Override + protected boolean matchesSafely(String item, Description mismatchDescription) { + try { + if (!date.isEqual(ZonedDateTime.parse(item))) { + mismatchDescription.appendText("was ").appendValue(item); + return false; + } + return true; + } catch (DateTimeParseException e) { + mismatchDescription.appendText("was ").appendValue(item) + .appendText(", which could not be parsed as a ZonedDateTime"); + return false; + } + + } + + @Override + public void describeTo(Description description) { + description.appendText("a String representing the same Instant as ").appendValue(date); + } + } + + /** + * Creates a matcher that matches when the examined string reprensents the same instant as the reference datetime + * @param date the reference datetime against which the examined string is checked + */ + public static ZonedDateTimeMatcher sameInstant(ZonedDateTime date) { + return new ZonedDateTimeMatcher(date); + } + + /** + * Verifies the equals/hashcode contract on the domain object. + */ + public static void equalsVerifier(Class clazz) throws Exception { + Object domainObject1 = clazz.getConstructor().newInstance(); + assertThat(domainObject1.toString()).isNotNull(); + assertThat(domainObject1).isEqualTo(domainObject1); + assertThat(domainObject1.hashCode()).isEqualTo(domainObject1.hashCode()); + // Test with an instance of another class + Object testOtherObject = new Object(); + assertThat(domainObject1).isNotEqualTo(testOtherObject); + // Test with an instance of the same class + Object domainObject2 = clazz.getConstructor().newInstance(); + assertThat(domainObject1).isNotEqualTo(domainObject2); + // HashCodes are equals because the objects are not persisted yet + assertThat(domainObject1.hashCode()).isEqualTo(domainObject2.hashCode()); + } +} diff --git a/jhipster/src/test/java/com/baeldung/web/rest/UserResourceIntTest.java b/jhipster/src/test/java/com/baeldung/web/rest/UserResourceIntTest.java new file mode 100644 index 0000000000..74df23283a --- /dev/null +++ b/jhipster/src/test/java/com/baeldung/web/rest/UserResourceIntTest.java @@ -0,0 +1,522 @@ +package com.baeldung.web.rest; + +import com.baeldung.BaeldungApp; +import com.baeldung.domain.User; +import com.baeldung.repository.UserRepository; +import com.baeldung.service.MailService; +import com.baeldung.service.UserService; +import com.baeldung.web.rest.errors.ExceptionTranslator; +import com.baeldung.web.rest.vm.ManagedUserVM; +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Test class for the UserResource REST controller. + * + * @see UserResource + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = BaeldungApp.class) +public class UserResourceIntTest { + + private static final String DEFAULT_LOGIN = "johndoe"; + private static final String UPDATED_LOGIN = "jhipster"; + + private static final String DEFAULT_PASSWORD = "passjohndoe"; + private static final String UPDATED_PASSWORD = "passjhipster"; + + private static final String DEFAULT_EMAIL = "johndoe@localhost"; + private static final String UPDATED_EMAIL = "jhipster@localhost"; + + private static final String DEFAULT_FIRSTNAME = "john"; + private static final String UPDATED_FIRSTNAME = "jhipsterFirstName"; + + private static final String DEFAULT_LASTNAME = "doe"; + private static final String UPDATED_LASTNAME = "jhipsterLastName"; + + private static final String DEFAULT_IMAGEURL = "http://placehold.it/50x50"; + private static final String UPDATED_IMAGEURL = "http://placehold.it/40x40"; + + private static final String DEFAULT_LANGKEY = "en"; + private static final String UPDATED_LANGKEY = "fr"; + + @Autowired + private UserRepository userRepository; + + @Autowired + private MailService mailService; + + @Autowired + private UserService userService; + + @Autowired + private MappingJackson2HttpMessageConverter jacksonMessageConverter; + + @Autowired + private PageableHandlerMethodArgumentResolver pageableArgumentResolver; + + @Autowired + private ExceptionTranslator exceptionTranslator; + + @Autowired + private EntityManager em; + + private MockMvc restUserMockMvc; + + private User user; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + UserResource userResource = new UserResource(userRepository, mailService, userService); + this.restUserMockMvc = MockMvcBuilders.standaloneSetup(userResource) + .setCustomArgumentResolvers(pageableArgumentResolver) + .setControllerAdvice(exceptionTranslator) + .setMessageConverters(jacksonMessageConverter) + .build(); + } + + /** + * Create a User. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which has a required relationship to the User entity. + */ + public static User createEntity(EntityManager em) { + User user = new User(); + user.setLogin(DEFAULT_LOGIN); + user.setPassword(RandomStringUtils.random(60)); + user.setActivated(true); + user.setEmail(DEFAULT_EMAIL); + user.setFirstName(DEFAULT_FIRSTNAME); + user.setLastName(DEFAULT_LASTNAME); + user.setImageUrl(DEFAULT_IMAGEURL); + user.setLangKey(DEFAULT_LANGKEY); + return user; + } + + @Before + public void initTest() { + user = createEntity(em); + } + + @Test + @Transactional + public void createUser() throws Exception { + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + // Create the User + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + null, + DEFAULT_LOGIN, + DEFAULT_PASSWORD, + DEFAULT_FIRSTNAME, + DEFAULT_LASTNAME, + DEFAULT_EMAIL, + true, + DEFAULT_IMAGEURL, + DEFAULT_LANGKEY, + null, + null, + null, + null, + autorities); + + restUserMockMvc.perform(post("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isCreated()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeCreate + 1); + User testUser = userList.get(userList.size() - 1); + assertThat(testUser.getLogin()).isEqualTo(DEFAULT_LOGIN); + assertThat(testUser.getFirstName()).isEqualTo(DEFAULT_FIRSTNAME); + assertThat(testUser.getLastName()).isEqualTo(DEFAULT_LASTNAME); + assertThat(testUser.getEmail()).isEqualTo(DEFAULT_EMAIL); + assertThat(testUser.getImageUrl()).isEqualTo(DEFAULT_IMAGEURL); + assertThat(testUser.getLangKey()).isEqualTo(DEFAULT_LANGKEY); + } + + @Test + @Transactional + public void createUserWithExistingId() throws Exception { + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + 1L, + DEFAULT_LOGIN, + DEFAULT_PASSWORD, + DEFAULT_FIRSTNAME, + DEFAULT_LASTNAME, + DEFAULT_EMAIL, + true, + DEFAULT_IMAGEURL, + DEFAULT_LANGKEY, + null, + null, + null, + null, + autorities); + + // An entity with an existing ID cannot be created, so this API call must fail + restUserMockMvc.perform(post("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void createUserWithExistingLogin() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + null, + DEFAULT_LOGIN, // this login should already be used + DEFAULT_PASSWORD, + DEFAULT_FIRSTNAME, + DEFAULT_LASTNAME, + "anothermail@localhost", + true, + DEFAULT_IMAGEURL, + DEFAULT_LANGKEY, + null, + null, + null, + null, + autorities); + + // Create the User + restUserMockMvc.perform(post("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void createUserWithExistingEmail() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + null, + "anotherlogin", + DEFAULT_PASSWORD, + DEFAULT_FIRSTNAME, + DEFAULT_LASTNAME, + DEFAULT_EMAIL, // this email should already be used + true, + DEFAULT_IMAGEURL, + DEFAULT_LANGKEY, + null, + null, + null, + null, + autorities); + + // Create the User + restUserMockMvc.perform(post("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeCreate); + } + + @Test + @Transactional + public void getAllUsers() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + // Get all the users + restUserMockMvc.perform(get("/api/users?sort=id,desc") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.[*].login").value(hasItem(DEFAULT_LOGIN))) + .andExpect(jsonPath("$.[*].firstName").value(hasItem(DEFAULT_FIRSTNAME))) + .andExpect(jsonPath("$.[*].lastName").value(hasItem(DEFAULT_LASTNAME))) + .andExpect(jsonPath("$.[*].email").value(hasItem(DEFAULT_EMAIL))) + .andExpect(jsonPath("$.[*].imageUrl").value(hasItem(DEFAULT_IMAGEURL))) + .andExpect(jsonPath("$.[*].langKey").value(hasItem(DEFAULT_LANGKEY))); + } + + @Test + @Transactional + public void getUser() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + // Get the user + restUserMockMvc.perform(get("/api/users/{login}", user.getLogin())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(jsonPath("$.login").value(user.getLogin())) + .andExpect(jsonPath("$.firstName").value(DEFAULT_FIRSTNAME)) + .andExpect(jsonPath("$.lastName").value(DEFAULT_LASTNAME)) + .andExpect(jsonPath("$.email").value(DEFAULT_EMAIL)) + .andExpect(jsonPath("$.imageUrl").value(DEFAULT_IMAGEURL)) + .andExpect(jsonPath("$.langKey").value(DEFAULT_LANGKEY)); + } + + @Test + @Transactional + public void getNonExistingUser() throws Exception { + restUserMockMvc.perform(get("/api/users/unknown")) + .andExpect(status().isNotFound()); + } + + @Test + @Transactional + public void updateUser() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findOne(user.getId()); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + updatedUser.getId(), + updatedUser.getLogin(), + UPDATED_PASSWORD, + UPDATED_FIRSTNAME, + UPDATED_LASTNAME, + UPDATED_EMAIL, + updatedUser.getActivated(), + UPDATED_IMAGEURL, + UPDATED_LANGKEY, + updatedUser.getCreatedBy(), + updatedUser.getCreatedDate(), + updatedUser.getLastModifiedBy(), + updatedUser.getLastModifiedDate(), + autorities); + + restUserMockMvc.perform(put("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isOk()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeUpdate); + User testUser = userList.get(userList.size() - 1); + assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME); + assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME); + assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL); + assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL); + assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY); + } + + @Test + @Transactional + public void updateUserLogin() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findOne(user.getId()); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + updatedUser.getId(), + UPDATED_LOGIN, + UPDATED_PASSWORD, + UPDATED_FIRSTNAME, + UPDATED_LASTNAME, + UPDATED_EMAIL, + updatedUser.getActivated(), + UPDATED_IMAGEURL, + UPDATED_LANGKEY, + updatedUser.getCreatedBy(), + updatedUser.getCreatedDate(), + updatedUser.getLastModifiedBy(), + updatedUser.getLastModifiedDate(), + autorities); + + restUserMockMvc.perform(put("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isOk()); + + // Validate the User in the database + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeUpdate); + User testUser = userList.get(userList.size() - 1); + assertThat(testUser.getLogin()).isEqualTo(UPDATED_LOGIN); + assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME); + assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME); + assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL); + assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL); + assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY); + } + + @Test + @Transactional + public void updateUserExistingEmail() throws Exception { + // Initialize the database with 2 users + userRepository.saveAndFlush(user); + + User anotherUser = new User(); + anotherUser.setLogin("jhipster"); + anotherUser.setPassword(RandomStringUtils.random(60)); + anotherUser.setActivated(true); + anotherUser.setEmail("jhipster@localhost"); + anotherUser.setFirstName("java"); + anotherUser.setLastName("hipster"); + anotherUser.setImageUrl(""); + anotherUser.setLangKey("en"); + userRepository.saveAndFlush(anotherUser); + + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findOne(user.getId()); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + updatedUser.getId(), + updatedUser.getLogin(), + updatedUser.getPassword(), + updatedUser.getFirstName(), + updatedUser.getLastName(), + "jhipster@localhost", // this email should already be used by anotherUser + updatedUser.getActivated(), + updatedUser.getImageUrl(), + updatedUser.getLangKey(), + updatedUser.getCreatedBy(), + updatedUser.getCreatedDate(), + updatedUser.getLastModifiedBy(), + updatedUser.getLastModifiedDate(), + autorities); + + restUserMockMvc.perform(put("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + } + + @Test + @Transactional + public void updateUserExistingLogin() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + User anotherUser = new User(); + anotherUser.setLogin("jhipster"); + anotherUser.setPassword(RandomStringUtils.random(60)); + anotherUser.setActivated(true); + anotherUser.setEmail("jhipster@localhost"); + anotherUser.setFirstName("java"); + anotherUser.setLastName("hipster"); + anotherUser.setImageUrl(""); + anotherUser.setLangKey("en"); + userRepository.saveAndFlush(anotherUser); + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findOne(user.getId()); + + Set autorities = new HashSet<>(); + autorities.add("ROLE_USER"); + ManagedUserVM managedUserVM = new ManagedUserVM( + updatedUser.getId(), + "jhipster", // this login should already be used by anotherUser + updatedUser.getPassword(), + updatedUser.getFirstName(), + updatedUser.getLastName(), + updatedUser.getEmail(), + updatedUser.getActivated(), + updatedUser.getImageUrl(), + updatedUser.getLangKey(), + updatedUser.getCreatedBy(), + updatedUser.getCreatedDate(), + updatedUser.getLastModifiedBy(), + updatedUser.getLastModifiedDate(), + autorities); + + restUserMockMvc.perform(put("/api/users") + .contentType(TestUtil.APPLICATION_JSON_UTF8) + .content(TestUtil.convertObjectToJsonBytes(managedUserVM))) + .andExpect(status().isBadRequest()); + } + + @Test + @Transactional + public void deleteUser() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeDelete = userRepository.findAll().size(); + + // Delete the user + restUserMockMvc.perform(delete("/api/users/{login}", user.getLogin()) + .accept(TestUtil.APPLICATION_JSON_UTF8)) + .andExpect(status().isOk()); + + // Validate the database is empty + List userList = userRepository.findAll(); + assertThat(userList).hasSize(databaseSizeBeforeDelete - 1); + } + + @Test + @Transactional + public void equalsVerifier() throws Exception { + User userA = new User(); + userA.setLogin("AAA"); + User userB = new User(); + userB.setLogin("BBB"); + assertThat(userA).isNotEqualTo(userB); + } +} diff --git a/jhipster/src/test/javascript/e2e/account/account.spec.ts b/jhipster/src/test/javascript/e2e/account/account.spec.ts new file mode 100644 index 0000000000..cfa9fae078 --- /dev/null +++ b/jhipster/src/test/javascript/e2e/account/account.spec.ts @@ -0,0 +1,108 @@ +import { browser, element, by, $ } from 'protractor'; + +describe('account', () => { + + const username = element(by.id('username')); + const password = element(by.id('password')); + const accountMenu = element(by.id('account-menu')); + const login = element(by.id('login')); + const logout = element(by.id('logout')); + + beforeAll(() => { + browser.get('/'); + }); + + it('should fail to login with bad password', () => { + const expect1 = /home.title/; + element.all(by.css('h1')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('foo'); + element(by.css('button[type=submit]')).click(); + + const expect2 = /login.messages.error.authentication/; + element.all(by.css('.alert-danger')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect2); + }); + }); + + it('should login successfully with admin account', () => { + const expect1 = /login.title/; + element.all(by.css('.modal-content h1')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + username.clear(); + username.sendKeys('admin'); + password.clear(); + password.sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + + browser.waitForAngular(); + + const expect2 = /home.logged.message/; + element.all(by.css('.alert-success span')).getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect2); + }); + }); + + it('should be able to update settings', () => { + accountMenu.click(); + element(by.css('[routerLink="settings"]')).click(); + + const expect1 = /settings.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + element(by.css('button[type=submit]')).click(); + + const expect2 = /settings.messages.success/; + element.all(by.css('.alert-success')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect2); + }); + }); + + it('should be able to update password', () => { + accountMenu.click(); + element(by.css('[routerLink="password"]')).click(); + + const expect1 = /password.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + password.sendKeys('newpassword'); + element(by.id('confirmPassword')).sendKeys('newpassword'); + element(by.css('button[type=submit]')).click(); + + const expect2 = /password.messages.success/; + element.all(by.css('.alert-success')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect2); + }); + accountMenu.click(); + logout.click(); + + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('newpassword'); + element(by.css('button[type=submit]')).click(); + + accountMenu.click(); + element(by.css('[routerLink="password"]')).click(); + // change back to default + password.clear(); + password.sendKeys('admin'); + element(by.id('confirmPassword')).clear(); + element(by.id('confirmPassword')).sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + }); + + afterAll(() => { + accountMenu.click(); + logout.click(); + }); +}); diff --git a/jhipster/src/test/javascript/e2e/admin/administration.spec.ts b/jhipster/src/test/javascript/e2e/admin/administration.spec.ts new file mode 100644 index 0000000000..0516f7a27d --- /dev/null +++ b/jhipster/src/test/javascript/e2e/admin/administration.spec.ts @@ -0,0 +1,80 @@ +import { browser, element, by, $ } from 'protractor'; + +describe('administration', () => { + + const username = element(by.id('username')); + const password = element(by.id('password')); + const accountMenu = element(by.id('account-menu')); + const adminMenu = element(by.id('admin-menu')); + const login = element(by.id('login')); + const logout = element(by.id('logout')); + + beforeAll(() => { + browser.get('/'); + + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + browser.waitForAngular(); + }); + + beforeEach(() => { + adminMenu.click(); + }); + + it('should load user management', () => { + element(by.css('[routerLink="user-management"]')).click(); + const expect1 = /userManagement.home.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load metrics', () => { + element(by.css('[routerLink="jhi-metrics"]')).click(); + const expect1 = /metrics.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load health', () => { + element(by.css('[routerLink="jhi-health"]')).click(); + const expect1 = /health.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load configuration', () => { + element(by.css('[routerLink="jhi-configuration"]')).click(); + const expect1 = /configuration.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load audits', () => { + element(by.css('[routerLink="audits"]')).click(); + const expect1 = /audits.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + it('should load logs', () => { + element(by.css('[routerLink="logs"]')).click(); + const expect1 = /logs.title/; + element.all(by.css('h2')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expect1); + }); + }); + + afterAll(() => { + accountMenu.click(); + logout.click(); + }); +}); diff --git a/jhipster/src/test/javascript/e2e/entities/comment.spec.ts b/jhipster/src/test/javascript/e2e/entities/comment.spec.ts new file mode 100644 index 0000000000..3032bd1364 --- /dev/null +++ b/jhipster/src/test/javascript/e2e/entities/comment.spec.ts @@ -0,0 +1,49 @@ +import { browser, element, by, $ } from 'protractor'; + +describe('Comment e2e test', () => { + + const username = element(by.id('username')); + const password = element(by.id('password')); + const entityMenu = element(by.id('entity-menu')); + const accountMenu = element(by.id('account-menu')); + const login = element(by.id('login')); + const logout = element(by.id('logout')); + + beforeAll(() => { + browser.get('/'); + + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + browser.waitForAngular(); + }); + + it('should load Comments', () => { + entityMenu.click(); + element.all(by.css('[routerLink="comment"]')).first().click().then(() => { + const expectVal = /baeldungApp.comment.home.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expectVal); + }); + }); + }); + + it('should load create Comment dialog', function () { + element(by.css('button.create-comment')).click().then(() => { + const expectVal = /baeldungApp.comment.home.createOrEditLabel/; + element.all(by.css('h4.modal-title')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expectVal); + }); + + element(by.css('button.close')).click(); + }); + }); + + afterAll(function () { + accountMenu.click(); + logout.click(); + }); +}); diff --git a/jhipster/src/test/javascript/e2e/entities/post.spec.ts b/jhipster/src/test/javascript/e2e/entities/post.spec.ts new file mode 100644 index 0000000000..3c8d04f731 --- /dev/null +++ b/jhipster/src/test/javascript/e2e/entities/post.spec.ts @@ -0,0 +1,49 @@ +import { browser, element, by, $ } from 'protractor'; + +describe('Post e2e test', () => { + + const username = element(by.id('username')); + const password = element(by.id('password')); + const entityMenu = element(by.id('entity-menu')); + const accountMenu = element(by.id('account-menu')); + const login = element(by.id('login')); + const logout = element(by.id('logout')); + + beforeAll(() => { + browser.get('/'); + + accountMenu.click(); + login.click(); + + username.sendKeys('admin'); + password.sendKeys('admin'); + element(by.css('button[type=submit]')).click(); + browser.waitForAngular(); + }); + + it('should load Posts', () => { + entityMenu.click(); + element.all(by.css('[routerLink="post"]')).first().click().then(() => { + const expectVal = /baeldungApp.post.home.title/; + element.all(by.css('h2 span')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expectVal); + }); + }); + }); + + it('should load create Post dialog', function () { + element(by.css('button.create-post')).click().then(() => { + const expectVal = /baeldungApp.post.home.createOrEditLabel/; + element.all(by.css('h4.modal-title')).first().getAttribute('jhiTranslate').then((value) => { + expect(value).toMatch(expectVal); + }); + + element(by.css('button.close')).click(); + }); + }); + + afterAll(function () { + accountMenu.click(); + logout.click(); + }); +}); diff --git a/jhipster/src/test/javascript/karma.conf.js b/jhipster/src/test/javascript/karma.conf.js new file mode 100644 index 0000000000..1b10226955 --- /dev/null +++ b/jhipster/src/test/javascript/karma.conf.js @@ -0,0 +1,126 @@ +'use strict'; + +const path = require('path'); +const webpack = require('webpack'); +const WATCH = process.argv.indexOf('--watch') > -1; +const LoaderOptionsPlugin = require("webpack/lib/LoaderOptionsPlugin"); + +module.exports = function (config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: './', + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine', 'intl-shim'], + + // list of files / patterns to load in the browser + files: [ + 'spec/entry.ts' + ], + + + // list of files to exclude + exclude: ['e2e/**'], + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + 'spec/entry.ts': ['webpack', 'sourcemap'] + }, + + webpack: { + resolve: { + extensions: ['.ts', '.js'] + }, + module: { + rules: [ + { + test: /\.ts$/, enforce: 'pre', loader: 'tslint-loader', exclude: /(test|node_modules)/ + }, + { + test: /\.ts$/, + loaders: ['awesome-typescript-loader', 'angular2-template-loader?keepUrl=true'], + exclude: /node_modules/ + }, + { + test: /\.(html|css)$/, + loader: 'raw-loader', + exclude: /\.async\.(html|css)$/ + }, + { + test: /\.async\.(html|css)$/, + loaders: ['file?name=[name].[hash].[ext]', 'extract'] + }, + { + test: /\.scss$/, + loaders: ['to-string-loader', 'css-loader', 'sass-loader'] + }, + { + test: /src\/main\/webapp\/.+\.ts$/, + enforce: 'post', + exclude: /(test|node_modules)/, + loader: 'sourcemap-istanbul-instrumenter-loader?force-sourcemap=true' + }] + }, + devtool: 'inline-source-map', + plugins: [ + new webpack.ContextReplacementPlugin( + // The (\\|\/) piece accounts for path separators in *nix and Windows + /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, + root('./src') // location of your src + ), + new LoaderOptionsPlugin({ + options: { + tslint: { + emitErrors: !WATCH, + failOnHint: false + } + } + }) + ] + }, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['dots', 'junit', 'progress', 'karma-remap-istanbul'], + + junitReporter: { + outputFile: '../../../../target/test-results/karma/TESTS-results.xml' + }, + + remapIstanbulReporter: { + reports: { // eslint-disable-line + 'html': 'target/test-results/coverage', + 'text-summary': null + } + }, + + // web server port + port: 9876, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: WATCH, + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['PhantomJS'], + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: !WATCH + }); +}; + +function root(__path) { + return path.join(__dirname, __path); +} diff --git a/jhipster/src/test/javascript/protractor.conf.js b/jhipster/src/test/javascript/protractor.conf.js new file mode 100644 index 0000000000..e79b09e17b --- /dev/null +++ b/jhipster/src/test/javascript/protractor.conf.js @@ -0,0 +1,48 @@ +var HtmlScreenshotReporter = require("protractor-jasmine2-screenshot-reporter"); +var JasmineReporters = require('jasmine-reporters'); + +exports.config = { + allScriptsTimeout: 20000, + + specs: [ + './e2e/account/*.spec.ts', + './e2e/admin/*.spec.ts', + './e2e/entities/*.spec.ts' + ], + + capabilities: { + 'browserName': 'chrome', + 'phantomjs.binary.path': require('phantomjs-prebuilt').path, + 'phantomjs.ghostdriver.cli.args': ['--loglevel=DEBUG'] + }, + + directConnect: true, + + baseUrl: 'http://localhost:8080/', + + framework: 'jasmine2', + + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000 + }, + + beforeLaunch: function() { + require('ts-node').register({ + project: '' + }); + }, + + onPrepare: function() { + browser.driver.manage().window().setSize(1280, 1024); + jasmine.getEnv().addReporter(new JasmineReporters.JUnitXmlReporter({ + savePath: 'target/reports/e2e', + consolidateAll: false + })); + jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ + dest: "target/reports/e2e/screenshots" + })); + }, + + useAllAngular2AppRoots: true +}; diff --git a/jhipster/src/test/javascript/spec/app/account/activate/activate.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/activate/activate.component.spec.ts new file mode 100644 index 0000000000..76a6e9f941 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/activate/activate.component.spec.ts @@ -0,0 +1,84 @@ +import { TestBed, async, tick, fakeAsync, inject } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Rx'; +import { BaeldungTestModule } from '../../../test.module'; +import { MockActivatedRoute } from '../../../helpers/mock-route.service'; +import { LoginModalService } from '../../../../../../main/webapp/app/shared'; +import { Activate } from '../../../../../../main/webapp/app/account/activate/activate.service'; +import { ActivateComponent } from '../../../../../../main/webapp/app/account/activate/activate.component'; + +describe('Component Tests', () => { + + describe('ActivateComponent', () => { + + let comp: ActivateComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [ActivateComponent], + providers: [ + Activate, + { + provide: ActivatedRoute, + useValue: new MockActivatedRoute({'key': 'ABC123'}) + }, + { + provide: LoginModalService, + useValue: null + } + ] + }).overrideComponent(ActivateComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + let fixture = TestBed.createComponent(ActivateComponent); + comp = fixture.componentInstance; + }); + + it('calls activate.get with the key from params', + inject([Activate], + fakeAsync((service: Activate) => { + spyOn(service, 'get').and.returnValue(Observable.of()); + + comp.ngOnInit(); + tick(); + + expect(service.get).toHaveBeenCalledWith('ABC123'); + }) + ) + ); + + it('should set set success to OK upon successful activation', + inject([Activate], + fakeAsync((service: Activate) => { + spyOn(service, 'get').and.returnValue(Observable.of({})); + + comp.ngOnInit(); + tick(); + + expect(comp.error).toBe(null); + expect(comp.success).toEqual('OK'); + }) + ) + ); + + it('should set set error to ERROR upon activation failure', + inject([Activate], + fakeAsync((service: Activate) => { + spyOn(service, 'get').and.returnValue(Observable.throw('ERROR')); + + comp.ngOnInit(); + tick(); + + expect(comp.error).toBe('ERROR'); + expect(comp.success).toEqual(null); + }) + ) + ); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts new file mode 100644 index 0000000000..537c58351e --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/password-reset/finish/password-reset-finish.component.spec.ts @@ -0,0 +1,79 @@ +import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; +import { Renderer, ElementRef } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { LoginModalService } from '../../../../../../../main/webapp/app/shared'; +import { Observable } from 'rxjs/Rx'; +import { BaeldungTestModule } from '../../../../test.module'; +import { PasswordResetFinishComponent } from '../../../../../../../main/webapp/app/account/password-reset/finish/password-reset-finish.component'; +import { PasswordResetFinish } from '../../../../../../../main/webapp/app/account/password-reset/finish/password-reset-finish.service'; +import { MockActivatedRoute } from '../../../../helpers/mock-route.service'; + + +describe('Component Tests', () => { + + describe('PasswordResetFinishComponent', () => { + + let fixture: ComponentFixture; + let comp: PasswordResetFinishComponent; + + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [PasswordResetFinishComponent], + providers: [ + PasswordResetFinish, + { + provide: LoginModalService, + useValue: null + }, + { + provide: ActivatedRoute, + useValue: new MockActivatedRoute({'key': 'XYZPDQ'}) + }, + { + provide: Renderer, + useValue: { + invokeElementMethod(renderElement: any, methodName: string, args?: any[]) {} + } + }, + { + provide: ElementRef, + useValue: new ElementRef(null) + } + ] + }).overrideComponent(PasswordResetFinishComponent, { + set: { + template: '' + } + }).createComponent(PasswordResetFinishComponent); + comp = fixture.componentInstance; + }); + + it('should define its initial state', function () { + comp.ngOnInit(); + + expect(comp.keyMissing).toBeFalsy(); + expect(comp.key).toEqual('XYZPDQ'); + expect(comp.resetAccount).toEqual({}); + }); + + it('sets focus after the view has been initialized', + inject([ElementRef], (elementRef: ElementRef) => { + let element = fixture.nativeElement; + let node = { + focus() {} + }; + + elementRef.nativeElement = element; + spyOn(element, 'querySelector').and.returnValue(node); + spyOn(node, 'focus'); + + comp.ngAfterViewInit(); + + expect(element.querySelector).toHaveBeenCalledWith('#password'); + expect(node.focus).toHaveBeenCalled(); + }) + ); + + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts new file mode 100644 index 0000000000..55c0a81922 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/password-reset/init/password-reset-init.component.spec.ts @@ -0,0 +1,115 @@ +import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; +import { Renderer, ElementRef } from '@angular/core'; +import { Observable } from 'rxjs/Rx'; +import { BaeldungTestModule } from '../../../../test.module'; +import { PasswordResetInitComponent } from '../../../../../../../main/webapp/app/account/password-reset/init/password-reset-init.component'; +import { PasswordResetInit } from '../../../../../../../main/webapp/app/account/password-reset/init/password-reset-init.service'; + + +describe('Component Tests', () => { + + describe('PasswordResetInitComponent', function () { + let fixture: ComponentFixture; + let comp: PasswordResetInitComponent; + + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [PasswordResetInitComponent], + providers: [ + PasswordResetInit, + { + provide: Renderer, + useValue: { + invokeElementMethod(renderElement: any, methodName: string, args?: any[]) {} + } + }, + { + provide: ElementRef, + useValue: new ElementRef(null) + } + ] + }).overrideComponent(PasswordResetInitComponent, { + set: { + template: '' + } + }).createComponent(PasswordResetInitComponent); + comp = fixture.componentInstance; + comp.ngOnInit(); + }); + + it('should define its initial state', function () { + expect(comp.success).toBeUndefined(); + expect(comp.error).toBeUndefined(); + expect(comp.errorEmailNotExists).toBeUndefined(); + expect(comp.resetAccount).toEqual({}); + }); + + it('sets focus after the view has been initialized', + inject([ElementRef], (elementRef: ElementRef) => { + let element = fixture.nativeElement; + let node = { + focus() {} + }; + + elementRef.nativeElement = element; + spyOn(element, 'querySelector').and.returnValue(node); + spyOn(node, 'focus'); + + comp.ngAfterViewInit(); + + expect(element.querySelector).toHaveBeenCalledWith('#email'); + expect(node.focus).toHaveBeenCalled(); + }) + ); + + it('notifies of success upon successful requestReset', + inject([PasswordResetInit], (service: PasswordResetInit) => { + spyOn(service, 'save').and.returnValue(Observable.of({})); + comp.resetAccount.email = 'user@domain.com'; + + comp.requestReset(); + + expect(service.save).toHaveBeenCalledWith('user@domain.com'); + expect(comp.success).toEqual('OK'); + expect(comp.error).toBeNull(); + expect(comp.errorEmailNotExists).toBeNull(); + }) + ); + + it('notifies of unknown email upon e-mail address not registered/400', + inject([PasswordResetInit], (service: PasswordResetInit) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 400, + data: 'e-mail address not registered' + })); + comp.resetAccount.email = 'user@domain.com'; + + comp.requestReset(); + + expect(service.save).toHaveBeenCalledWith('user@domain.com'); + expect(comp.success).toBeNull(); + expect(comp.error).toBeNull(); + expect(comp.errorEmailNotExists).toEqual('ERROR'); + }) + ); + + it('notifies of error upon error response', + inject([PasswordResetInit], (service: PasswordResetInit) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 503, + data: 'something else' + })); + comp.resetAccount.email = 'user@domain.com'; + + comp.requestReset(); + + expect(service.save).toHaveBeenCalledWith('user@domain.com'); + expect(comp.success).toBeNull(); + expect(comp.errorEmailNotExists).toBeNull(); + expect(comp.error).toEqual('ERROR'); + }) + ); + + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts new file mode 100644 index 0000000000..9cdc55529c --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/password/password-strength-bar.component.spec.ts @@ -0,0 +1,53 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; + +import { PasswordStrengthBarComponent } from '../../../../../../main/webapp/app/account/password/password-strength-bar.component'; + +describe('Component Tests', () => { + + describe('PasswordStrengthBarComponent', () => { + + let comp: PasswordStrengthBarComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PasswordStrengthBarComponent] + }).overrideComponent(PasswordStrengthBarComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PasswordStrengthBarComponent); + comp = fixture.componentInstance; + }); + + describe('PasswordStrengthBarComponents', () => { + it('should initialize with default values', () => { + expect(comp.measureStrength('')).toBe(0); + expect(comp.colors).toEqual(['#F00', '#F90', '#FF0', '#9F0', '#0F0']); + expect(comp.getColor(0).idx).toBe(1); + expect(comp.getColor(0).col).toBe(comp.colors[0]); + }); + + it('should increase strength upon password value change', () => { + expect(comp.measureStrength('')).toBe(0); + expect(comp.measureStrength('aa')).toBeGreaterThanOrEqual(comp.measureStrength('')); + expect(comp.measureStrength('aa^6')).toBeGreaterThanOrEqual(comp.measureStrength('aa')); + expect(comp.measureStrength('Aa090(**)')).toBeGreaterThanOrEqual(comp.measureStrength('aa^6')); + expect(comp.measureStrength('Aa090(**)+-07365')).toBeGreaterThanOrEqual(comp.measureStrength('Aa090(**)')); + }); + + it('should change the color based on strength', () => { + expect(comp.getColor(0).col).toBe(comp.colors[0]); + expect(comp.getColor(11).col).toBe(comp.colors[1]); + expect(comp.getColor(22).col).toBe(comp.colors[2]); + expect(comp.getColor(33).col).toBe(comp.colors[3]); + expect(comp.getColor(44).col).toBe(comp.colors[4]); + }); + }); + }); +}); + diff --git a/jhipster/src/test/javascript/spec/app/account/password/password.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/password/password.component.spec.ts new file mode 100644 index 0000000000..e6f4983785 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/password/password.component.spec.ts @@ -0,0 +1,92 @@ +import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing'; +import { Observable } from 'rxjs/Rx'; +import { BaeldungTestModule } from '../../../test.module'; +import { PasswordComponent } from '../../../../../../main/webapp/app/account/password/password.component'; +import { Password } from '../../../../../../main/webapp/app/account/password/password.service'; +import { Principal } from '../../../../../../main/webapp/app/shared/auth/principal.service'; +import { AccountService } from '../../../../../../main/webapp/app/shared/auth/account.service'; + + +describe('Component Tests', () => { + + describe('PasswordComponent', () => { + + let comp: PasswordComponent; + let fixture: ComponentFixture; + let service: Password; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [PasswordComponent], + providers: [ + Principal, + AccountService, + Password + ] + }).overrideComponent(PasswordComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PasswordComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(Password); + }); + + it('should show error if passwords do not match', () => { + // GIVEN + comp.password = 'password1'; + comp.confirmPassword = 'password2'; + // WHEN + comp.changePassword(); + // THEN + expect(comp.doNotMatch).toBe('ERROR'); + expect(comp.error).toBeNull(); + expect(comp.success).toBeNull(); + }); + + it('should call Auth.changePassword when passwords match', () => { + // GIVEN + spyOn(service, 'save').and.returnValue(Observable.of(true)); + comp.password = comp.confirmPassword = 'myPassword'; + + // WHEN + comp.changePassword(); + + // THEN + expect(service.save).toHaveBeenCalledWith('myPassword'); + }); + + it('should set success to OK upon success', function() { + // GIVEN + spyOn(service, 'save').and.returnValue(Observable.of(true)); + comp.password = comp.confirmPassword = 'myPassword'; + + // WHEN + comp.changePassword(); + + // THEN + expect(comp.doNotMatch).toBeNull(); + expect(comp.error).toBeNull(); + expect(comp.success).toBe('OK'); + }); + + it('should notify of error if change password fails', function() { + // GIVEN + spyOn(service, 'save').and.returnValue(Observable.throw('ERROR')); + comp.password = comp.confirmPassword = 'myPassword'; + + // WHEN + comp.changePassword(); + + // THEN + expect(comp.doNotMatch).toBeNull(); + expect(comp.success).toBeNull(); + expect(comp.error).toBe('ERROR'); + }); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/register/register.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/register/register.component.spec.ts new file mode 100644 index 0000000000..c475c2f3d2 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/register/register.component.spec.ts @@ -0,0 +1,138 @@ +import { ComponentFixture, TestBed, async, inject, tick, fakeAsync } from '@angular/core/testing'; +import { Renderer, ElementRef } from '@angular/core'; +import { Observable } from 'rxjs/Rx'; +import { JhiLanguageService } from 'ng-jhipster'; +import { MockLanguageService } from '../../../helpers/mock-language.service'; +import { BaeldungTestModule } from '../../../test.module'; +import { LoginModalService } from '../../../../../../main/webapp/app/shared'; +import { Register } from '../../../../../../main/webapp/app/account/register/register.service'; +import { RegisterComponent } from '../../../../../../main/webapp/app/account/register/register.component'; + + +describe('Component Tests', () => { + + describe('RegisterComponent', () => { + let fixture: ComponentFixture; + let comp: RegisterComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [RegisterComponent], + providers: [ + Register, + { + provide: LoginModalService, + useValue: null + }, + { + provide: Renderer, + useValue: null + }, + { + provide: ElementRef, + useValue: null + } + ] + }).overrideComponent(RegisterComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RegisterComponent); + comp = fixture.componentInstance; + comp.ngOnInit(); + }); + + it('should ensure the two passwords entered match', function () { + comp.registerAccount.password = 'password'; + comp.confirmPassword = 'non-matching'; + + comp.register(); + + expect(comp.doNotMatch).toEqual('ERROR'); + }); + + it('should update success to OK after creating an account', + inject([Register, JhiLanguageService], + fakeAsync((service: Register, mockTranslate: MockLanguageService) => { + spyOn(service, 'save').and.returnValue(Observable.of({})); + comp.registerAccount.password = comp.confirmPassword = 'password'; + + comp.register(); + tick(); + + expect(service.save).toHaveBeenCalledWith({ + password: 'password', + langKey: 'en' + }); + expect(comp.success).toEqual(true); + expect(comp.registerAccount.langKey).toEqual('en'); + expect(mockTranslate.getCurrentSpy).toHaveBeenCalled(); + expect(comp.errorUserExists).toBeNull(); + expect(comp.errorEmailExists).toBeNull(); + expect(comp.error).toBeNull(); + }) + ) + ); + + it('should notify of user existence upon 400/login already in use', + inject([Register], + fakeAsync((service: Register) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 400, + _body: 'login already in use' + })); + comp.registerAccount.password = comp.confirmPassword = 'password'; + + comp.register(); + tick(); + + expect(comp.errorUserExists).toEqual('ERROR'); + expect(comp.errorEmailExists).toBeNull(); + expect(comp.error).toBeNull(); + }) + ) + ); + + it('should notify of email existence upon 400/e-mail address already in use', + inject([Register], + fakeAsync((service: Register) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 400, + _body: 'e-mail address already in use' + })); + comp.registerAccount.password = comp.confirmPassword = 'password'; + + comp.register(); + tick(); + + expect(comp.errorEmailExists).toEqual('ERROR'); + expect(comp.errorUserExists).toBeNull(); + expect(comp.error).toBeNull(); + }) + ) + ); + + it('should notify of generic error', + inject([Register], + fakeAsync((service: Register) => { + spyOn(service, 'save').and.returnValue(Observable.throw({ + status: 503 + })); + comp.registerAccount.password = comp.confirmPassword = 'password'; + + comp.register(); + tick(); + + expect(comp.errorUserExists).toBeNull(); + expect(comp.errorEmailExists).toBeNull(); + expect(comp.error).toEqual('ERROR'); + }) + ) + ); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/account/settings/settings.component.spec.ts b/jhipster/src/test/javascript/spec/app/account/settings/settings.component.spec.ts new file mode 100644 index 0000000000..266a33be79 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/account/settings/settings.component.spec.ts @@ -0,0 +1,103 @@ +import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing'; +import { Observable } from 'rxjs/Rx'; +import { JhiLanguageHelper } from '../../../../../../main/webapp/app/shared'; +import { BaeldungTestModule } from '../../../test.module'; +import { Principal, AccountService } from '../../../../../../main/webapp/app/shared'; +import { SettingsComponent } from '../../../../../../main/webapp/app/account/settings/settings.component'; +import { MockAccountService } from '../../../helpers/mock-account.service'; +import { MockPrincipal } from '../../../helpers/mock-principal.service'; + + +describe('Component Tests', () => { + + describe('SettingsComponent', () => { + + let comp: SettingsComponent; + let fixture: ComponentFixture; + let mockAuth: MockAccountService; + let mockPrincipal: MockPrincipal; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [SettingsComponent], + providers: [ + { + provide: Principal, + useClass: MockPrincipal + }, + { + provide: AccountService, + useClass: MockAccountService + }, + { + provide: JhiLanguageHelper, + useValue: null + }, + ] + }).overrideComponent(SettingsComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SettingsComponent); + comp = fixture.componentInstance; + mockAuth = fixture.debugElement.injector.get(AccountService); + mockPrincipal = fixture.debugElement.injector.get(Principal); + }); + + it('should send the current identity upon save', function () { + // GIVEN + let accountValues = { + firstName: 'John', + lastName: 'Doe', + + activated: true, + email: 'john.doe@mail.com', + langKey: 'en', + login: 'john' + }; + mockPrincipal.setResponse(accountValues); + + // WHEN + comp.settingsAccount = accountValues; + comp.save(); + + // THEN + expect(mockPrincipal.identitySpy).toHaveBeenCalled(); + expect(mockAuth.saveSpy).toHaveBeenCalledWith(accountValues); + expect(comp.settingsAccount).toEqual(accountValues); + }); + + it('should notify of success upon successful save', function () { + // GIVEN + let accountValues = { + firstName: 'John', + lastName: 'Doe' + }; + mockPrincipal.setResponse(accountValues); + + // WHEN + comp.save(); + + // THEN + expect(comp.error).toBeNull(); + expect(comp.success).toBe('OK'); + }); + + it('should notify of error upon failed save', function () { + // GIVEN + mockAuth.saveSpy.and.returnValue(Observable.throw('ERROR')); + + // WHEN + comp.save(); + + // THEN + expect(comp.error).toEqual('ERROR'); + expect(comp.success).toBeNull(); + }); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts b/jhipster/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts new file mode 100644 index 0000000000..d16673de03 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/admin/audits/audits.component.spec.ts @@ -0,0 +1,82 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { DatePipe } from '@angular/common'; +import { NgbPaginationConfig} from '@ng-bootstrap/ng-bootstrap'; +import { ParseLinks } from 'ng-jhipster'; +import { BaeldungTestModule } from '../../../test.module'; +import { PaginationConfig } from '../../../../../../main/webapp/app/blocks/config/uib-pagination.config' +import { AuditsComponent } from '../../../../../../main/webapp/app/admin/audits/audits.component'; +import { AuditsService } from '../../../../../../main/webapp/app/admin/audits/audits.service'; +import { ITEMS_PER_PAGE } from '../../../../../../main/webapp/app/shared'; + + +function getDate(isToday= true){ + let date: Date = new Date(); + if (isToday) { + // Today + 1 day - needed if the current day must be included + date.setDate(date.getDate() + 1); + return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`; + } + return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`; +} + +describe('Component Tests', () => { + + describe('AuditsComponent', () => { + + let comp: AuditsComponent; + let fixture: ComponentFixture; + let service: AuditsService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [AuditsComponent], + providers: [ + AuditsService, + NgbPaginationConfig, + ParseLinks, + PaginationConfig, + DatePipe + ] + }) + .overrideComponent(AuditsComponent, { + set: { + template: '' + } + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AuditsComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(AuditsService); + }); + + describe('today function ', () => { + it('should set toDate to current date', () => { + comp.today(); + expect(comp.toDate).toBe(getDate()); + }); + }); + + describe('previousMonth function ', () => { + it('should set toDate to current date', () => { + comp.previousMonth(); + expect(comp.fromDate).toBe(getDate(false)); + }); + }); + + describe('By default, on init', () => { + it('should set all default values correctly', () => { + fixture.detectChanges(); + expect(comp.toDate).toBe(getDate()); + expect(comp.fromDate).toBe(getDate(false)); + expect(comp.itemsPerPage).toBe(ITEMS_PER_PAGE); + expect(comp.page).toBe(1); + expect(comp.reverse).toBeFalsy(); + expect(comp.orderProp).toBe('timestamp'); + }); + }); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/admin/health/health.component.spec.ts b/jhipster/src/test/javascript/spec/app/admin/health/health.component.spec.ts new file mode 100644 index 0000000000..b80c96db66 --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/admin/health/health.component.spec.ts @@ -0,0 +1,295 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { BaeldungTestModule } from '../../../test.module'; +import { JhiHealthCheckComponent } from '../../../../../../main/webapp/app/admin/health/health.component'; +import { JhiHealthService } from '../../../../../../main/webapp/app/admin/health/health.service'; + + +describe('Component Tests', () => { + + describe('JhiHealthCheckComponent', () => { + + let comp: JhiHealthCheckComponent; + let fixture: ComponentFixture; + let service: JhiHealthService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [BaeldungTestModule], + declarations: [JhiHealthCheckComponent], + providers: [ + JhiHealthService, + { + provide: NgbModal, + useValue: null + } + ] + }) + .overrideComponent(JhiHealthCheckComponent, { + set: { + template: '' + } + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(JhiHealthCheckComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(JhiHealthService); + }); + + describe('baseName and subSystemName', () => { + it('should return the basename when it has no sub system', () => { + expect(comp.baseName('base')).toBe('base'); + }); + + it('should return the basename when it has sub systems', () => { + expect(comp.baseName('base.subsystem.system')).toBe('base'); + }); + + it('should return the sub system name', () => { + expect(comp.subSystemName('subsystem')).toBe(''); + }); + + it('should return the subsystem when it has multiple keys', () => { + expect(comp.subSystemName('subsystem.subsystem.system')).toBe(' - subsystem.system'); + }); + }); + + describe('transformHealthData', () => { + it('should flatten empty health data', () => { + const data = {}; + const expected = []; + expect(service.transformHealthData(data)).toEqual(expected); + }); + }); + + it('should flatten health data with no subsystems', () => { + const data = { + 'status': 'UP', + 'db': { + 'status': 'UP', + 'database': 'H2', + 'hello': '1' + }, + 'mail': { + 'status': 'UP', + 'error': 'mail.a.b.c' + } + }; + const expected = [ + { + 'name': 'db', + 'error': undefined, + 'status': 'UP', + 'details': { + 'database': 'H2', + 'hello': '1' + } + }, + { + 'name': 'mail', + 'error': 'mail.a.b.c', + 'status': 'UP' + } + ]; + expect(service.transformHealthData(data)).toEqual(expected); + }); + + it('should flatten health data with subsystems at level 1, main system has no additional information', () => { + const data = { + 'status': 'UP', + 'db': { + 'status': 'UP', + 'database': 'H2', + 'hello': '1' + }, + 'mail': { + 'status': 'UP', + 'error': 'mail.a.b.c' + }, + 'system': { + 'status': 'DOWN', + 'subsystem1': { + 'status': 'UP', + 'property1': 'system.subsystem1.property1' + }, + 'subsystem2': { + 'status': 'DOWN', + 'error': 'system.subsystem1.error', + 'property2': 'system.subsystem2.property2' + } + } + }; + const expected = [ + { + 'name': 'db', + 'error': undefined, + 'status': 'UP', + 'details': { + 'database': 'H2', + 'hello': '1' + } + }, + { + 'name': 'mail', + 'error': 'mail.a.b.c', + 'status': 'UP' + }, + { + 'name': 'system.subsystem1', + 'error': undefined, + 'status': 'UP', + 'details': { + 'property1': 'system.subsystem1.property1' + } + }, + { + 'name': 'system.subsystem2', + 'error': 'system.subsystem1.error', + 'status': 'DOWN', + 'details': { + 'property2': 'system.subsystem2.property2' + } + } + ]; + expect(service.transformHealthData(data)).toEqual(expected); + }); + + it('should flatten health data with subsystems at level 1, main system has additional information', () => { + const data = { + 'status': 'UP', + 'db': { + 'status': 'UP', + 'database': 'H2', + 'hello': '1' + }, + 'mail': { + 'status': 'UP', + 'error': 'mail.a.b.c' + }, + 'system': { + 'status': 'DOWN', + 'property1': 'system.property1', + 'subsystem1': { + 'status': 'UP', + 'property1': 'system.subsystem1.property1' + }, + 'subsystem2': { + 'status': 'DOWN', + 'error': 'system.subsystem1.error', + 'property2': 'system.subsystem2.property2' + } + } + }; + const expected = [ + { + 'name': 'db', + 'error': undefined, + 'status': 'UP', + 'details': { + 'database': 'H2', + 'hello': '1' + } + }, + { + 'name': 'mail', + 'error': 'mail.a.b.c', + 'status': 'UP' + }, + { + 'name': 'system', + 'error': undefined, + 'status': 'DOWN', + 'details': { + 'property1': 'system.property1' + } + }, + { + 'name': 'system.subsystem1', + 'error': undefined, + 'status': 'UP', + 'details': { + 'property1': 'system.subsystem1.property1' + } + }, + { + 'name': 'system.subsystem2', + 'error': 'system.subsystem1.error', + 'status': 'DOWN', + 'details': { + 'property2': 'system.subsystem2.property2' + } + } + ]; + expect(service.transformHealthData(data)).toEqual(expected); + }); + + it('should flatten health data with subsystems at level 1, main system has additional error', () => { + const data = { + 'status': 'UP', + 'db': { + 'status': 'UP', + 'database': 'H2', + 'hello': '1' + }, + 'mail': { + 'status': 'UP', + 'error': 'mail.a.b.c' + }, + 'system': { + 'status': 'DOWN', + 'error': 'show me', + 'subsystem1': { + 'status': 'UP', + 'property1': 'system.subsystem1.property1' + }, + 'subsystem2': { + 'status': 'DOWN', + 'error': 'system.subsystem1.error', + 'property2': 'system.subsystem2.property2' + } + } + }; + const expected = [ + { + 'name': 'db', + 'error': undefined, + 'status': 'UP', + 'details': { + 'database': 'H2', + 'hello': '1' + } + }, + { + 'name': 'mail', + 'error': 'mail.a.b.c', + 'status': 'UP' + }, + { + 'name': 'system', + 'error': 'show me', + 'status': 'DOWN' + }, + { + 'name': 'system.subsystem1', + 'error': undefined, + 'status': 'UP', + 'details': { + 'property1': 'system.subsystem1.property1' + } + }, + { + 'name': 'system.subsystem2', + 'error': 'system.subsystem1.error', + 'status': 'DOWN', + 'details': { + 'property2': 'system.subsystem2.property2' + } + } + ]; + expect(service.transformHealthData(data)).toEqual(expected); + }); + }); +}); diff --git a/jhipster/src/test/javascript/spec/app/entities/comment/comment-detail.component.spec.ts b/jhipster/src/test/javascript/spec/app/entities/comment/comment-detail.component.spec.ts new file mode 100644 index 0000000000..b7c6b77b8c --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/entities/comment/comment-detail.component.spec.ts @@ -0,0 +1,79 @@ +import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing'; +import { MockBackend } from '@angular/http/testing'; +import { Http, BaseRequestOptions } from '@angular/http'; +import { OnInit } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Rx'; +import { DateUtils, DataUtils } from 'ng-jhipster'; +import { JhiLanguageService } from 'ng-jhipster'; +import { MockLanguageService } from '../../../helpers/mock-language.service'; +import { MockActivatedRoute } from '../../../helpers/mock-route.service'; +import { CommentDetailComponent } from '../../../../../../main/webapp/app/entities/comment/comment-detail.component'; +import { CommentService } from '../../../../../../main/webapp/app/entities/comment/comment.service'; +import { Comment } from '../../../../../../main/webapp/app/entities/comment/comment.model'; + +describe('Component Tests', () => { + + describe('Comment Management Detail Component', () => { + let comp: CommentDetailComponent; + let fixture: ComponentFixture; + let service: CommentService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CommentDetailComponent], + providers: [ + MockBackend, + BaseRequestOptions, + DateUtils, + DataUtils, + DatePipe, + { + provide: ActivatedRoute, + useValue: new MockActivatedRoute({id: 123}) + }, + { + provide: Http, + useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => { + return new Http(backendInstance, defaultOptions); + }, + deps: [MockBackend, BaseRequestOptions] + }, + { + provide: JhiLanguageService, + useClass: MockLanguageService + }, + CommentService + ] + }).overrideComponent(CommentDetailComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CommentDetailComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(CommentService); + }); + + + describe('OnInit', () => { + it('Should call load all on init', () => { + // GIVEN + + spyOn(service, 'find').and.returnValue(Observable.of(new Comment(10))); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.find).toHaveBeenCalledWith(123); + expect(comp.comment).toEqual(jasmine.objectContaining({id:10})); + }); + }); + }); + +}); diff --git a/jhipster/src/test/javascript/spec/app/entities/post/post-detail.component.spec.ts b/jhipster/src/test/javascript/spec/app/entities/post/post-detail.component.spec.ts new file mode 100644 index 0000000000..3ccb9cf6ad --- /dev/null +++ b/jhipster/src/test/javascript/spec/app/entities/post/post-detail.component.spec.ts @@ -0,0 +1,79 @@ +import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing'; +import { MockBackend } from '@angular/http/testing'; +import { Http, BaseRequestOptions } from '@angular/http'; +import { OnInit } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Rx'; +import { DateUtils, DataUtils } from 'ng-jhipster'; +import { JhiLanguageService } from 'ng-jhipster'; +import { MockLanguageService } from '../../../helpers/mock-language.service'; +import { MockActivatedRoute } from '../../../helpers/mock-route.service'; +import { PostDetailComponent } from '../../../../../../main/webapp/app/entities/post/post-detail.component'; +import { PostService } from '../../../../../../main/webapp/app/entities/post/post.service'; +import { Post } from '../../../../../../main/webapp/app/entities/post/post.model'; + +describe('Component Tests', () => { + + describe('Post Management Detail Component', () => { + let comp: PostDetailComponent; + let fixture: ComponentFixture; + let service: PostService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PostDetailComponent], + providers: [ + MockBackend, + BaseRequestOptions, + DateUtils, + DataUtils, + DatePipe, + { + provide: ActivatedRoute, + useValue: new MockActivatedRoute({id: 123}) + }, + { + provide: Http, + useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => { + return new Http(backendInstance, defaultOptions); + }, + deps: [MockBackend, BaseRequestOptions] + }, + { + provide: JhiLanguageService, + useClass: MockLanguageService + }, + PostService + ] + }).overrideComponent(PostDetailComponent, { + set: { + template: '' + } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PostDetailComponent); + comp = fixture.componentInstance; + service = fixture.debugElement.injector.get(PostService); + }); + + + describe('OnInit', () => { + it('Should call load all on init', () => { + // GIVEN + + spyOn(service, 'find').and.returnValue(Observable.of(new Post(10))); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.find).toHaveBeenCalledWith(123); + expect(comp.post).toEqual(jasmine.objectContaining({id:10})); + }); + }); + }); + +}); diff --git a/jhipster/src/test/javascript/spec/entry.ts b/jhipster/src/test/javascript/spec/entry.ts new file mode 100644 index 0000000000..64edbafb93 --- /dev/null +++ b/jhipster/src/test/javascript/spec/entry.ts @@ -0,0 +1,19 @@ +/// +import 'core-js'; +import 'zone.js/dist/zone'; +import 'zone.js/dist/long-stack-trace-zone'; +import 'zone.js/dist/async-test'; +import 'zone.js/dist/fake-async-test'; +import 'zone.js/dist/sync-test'; +import 'zone.js/dist/proxy'; +import 'zone.js/dist/jasmine-patch'; +import 'rxjs'; +import 'intl/locale-data/jsonp/en-US.js'; +import { TestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); + +declare let require: any; +const testsContext: any = require.context('./', true, /\.spec/); +testsContext.keys().forEach(testsContext); diff --git a/jhipster/src/test/javascript/spec/helpers/mock-account.service.ts b/jhipster/src/test/javascript/spec/helpers/mock-account.service.ts new file mode 100644 index 0000000000..e21c10a370 --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/mock-account.service.ts @@ -0,0 +1,26 @@ +import { SpyObject } from './spyobject'; +import { AccountService } from '../../../../main/webapp/app/shared/auth/account.service'; +import Spy = jasmine.Spy; + +export class MockAccountService extends SpyObject { + + getSpy: Spy; + saveSpy: Spy; + fakeResponse: any; + + constructor() { + super(AccountService); + + this.fakeResponse = null; + this.getSpy = this.spy('get').andReturn(this); + this.saveSpy = this.spy('save').andReturn(this); + } + + subscribe(callback: any) { + callback(this.fakeResponse); + } + + setResponse(json: any): void { + this.fakeResponse = json; + } +} diff --git a/jhipster/src/test/javascript/spec/helpers/mock-language.service.ts b/jhipster/src/test/javascript/spec/helpers/mock-language.service.ts new file mode 100644 index 0000000000..0c7dec92f1 --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/mock-language.service.ts @@ -0,0 +1,26 @@ +import { SpyObject } from './spyobject'; +import { JhiLanguageService } from 'ng-jhipster'; +import Spy = jasmine.Spy; + +export class MockLanguageService extends SpyObject { + + getCurrentSpy: Spy; + fakeResponse: any; + + constructor() { + super(JhiLanguageService); + + this.fakeResponse = 'en'; + this.getCurrentSpy = this.spy('getCurrent').andReturn(Promise.resolve(this.fakeResponse)); + } + + init() {} + + changeLanguage(languageKey: string) {} + + setLocations(locations: string[]) {} + + addLocation(location: string) {} + + reload() {} +} diff --git a/jhipster/src/test/javascript/spec/helpers/mock-principal.service.ts b/jhipster/src/test/javascript/spec/helpers/mock-principal.service.ts new file mode 100644 index 0000000000..89b932b83c --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/mock-principal.service.ts @@ -0,0 +1,20 @@ +import { SpyObject } from './spyobject'; +import { Principal } from '../../../../main/webapp/app/shared/auth/principal.service'; +import Spy = jasmine.Spy; + +export class MockPrincipal extends SpyObject { + + identitySpy: Spy; + fakeResponse: any; + + constructor() { + super(Principal); + + this.fakeResponse = {}; + this.identitySpy = this.spy('identity').andReturn(Promise.resolve(this.fakeResponse)); + } + + setResponse(json: any): void { + this.fakeResponse = json; + } +} diff --git a/jhipster/src/test/javascript/spec/helpers/mock-route.service.ts b/jhipster/src/test/javascript/spec/helpers/mock-route.service.ts new file mode 100644 index 0000000000..3ddb291721 --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/mock-route.service.ts @@ -0,0 +1,15 @@ +import { ActivatedRoute, Params } from '@angular/router'; +import { Observable } from 'rxjs'; + +export class MockActivatedRoute extends ActivatedRoute { + + constructor(parameters?: any) { + super(); + this.queryParams = Observable.of(parameters); + this.params = Observable.of(parameters); + } +} + +export class MockRouter { + navigate = jasmine.createSpy('navigate'); +} diff --git a/jhipster/src/test/javascript/spec/helpers/spyobject.ts b/jhipster/src/test/javascript/spec/helpers/spyobject.ts new file mode 100644 index 0000000000..4db41fb8df --- /dev/null +++ b/jhipster/src/test/javascript/spec/helpers/spyobject.ts @@ -0,0 +1,69 @@ +export interface GuinessCompatibleSpy extends jasmine.Spy { + /** By chaining the spy with and.returnValue, all calls to the function will return a specific + * value. */ + andReturn(val: any): void; + /** By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied + * function. */ + andCallFake(fn: Function): GuinessCompatibleSpy; + /** removes all recorded calls */ + reset(); +} + +export class SpyObject { + static stub(object = null, config = null, overrides = null) { + if (!(object instanceof SpyObject)) { + overrides = config; + config = object; + object = new SpyObject(); + } + + let m = {}; + Object.keys(config).forEach((key) => m[key] = config[key]); + Object.keys(overrides).forEach((key) => m[key] = overrides[key]); + Object.keys(m).forEach((key) => { + object.spy(key).andReturn(m[key]); + }); + return object; + } + + constructor(type = null) { + if (type) { + Object.keys(type.prototype).forEach((prop) => { + let m = null; + try { + m = type.prototype[prop]; + } catch (e) { + // As we are creating spys for abstract classes, + // these classes might have getters that throw when they are accessed. + // As we are only auto creating spys for methods, this + // should not matter. + } + if (typeof m === 'function') { + this.spy(prop); + } + }); + } + } + + spy(name) { + if (!this[name]) { + this[name] = this._createGuinnessCompatibleSpy(name); + } + return this[name]; + } + + prop(name, value) { + this[name] = value; + } + + /** @internal */ + _createGuinnessCompatibleSpy(name): GuinessCompatibleSpy { + let newSpy: GuinessCompatibleSpy = < any > jasmine.createSpy(name); + newSpy.andCallFake = < any > newSpy.and.callFake; + newSpy.andReturn = < any > newSpy.and.returnValue; + newSpy.reset = < any > newSpy.calls.reset; + // revisit return null here (previously needed for rtts_assert). + newSpy.and.returnValue(null); + return newSpy; + } +} diff --git a/jhipster/src/test/javascript/spec/test.module.ts b/jhipster/src/test/javascript/spec/test.module.ts new file mode 100644 index 0000000000..65ce439cb0 --- /dev/null +++ b/jhipster/src/test/javascript/spec/test.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { MockBackend } from '@angular/http/testing'; +import { Http, BaseRequestOptions } from '@angular/http'; +import { JhiLanguageService } from 'ng-jhipster'; +import { MockLanguageService } from './helpers/mock-language.service'; + +@NgModule({ + providers: [ + MockBackend, + BaseRequestOptions, + { + provide: JhiLanguageService, + useClass: MockLanguageService + }, + { + provide: Http, + useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => { + return new Http(backendInstance, defaultOptions); + }, + deps: [MockBackend, BaseRequestOptions] + } + ] +}) +export class BaeldungTestModule {} diff --git a/jhipster/src/test/resources/config/application.yml b/jhipster/src/test/resources/config/application.yml new file mode 100644 index 0000000000..a7939c838c --- /dev/null +++ b/jhipster/src/test/resources/config/application.yml @@ -0,0 +1,96 @@ +# =================================================================== +# Spring Boot configuration. +# +# This configuration is used for unit/integration tests. +# +# More information on profiles: https://jhipster.github.io/profiles/ +# More information on configuration properties: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + + +spring: + application: + name: baeldung + jackson: + serialization.write_dates_as_timestamps: false + cache: + type: none + datasource: + type: com.zaxxer.hikari.HikariDataSource + url: jdbc:h2:mem:baeldung;DB_CLOSE_DELAY=-1 + name: + username: + password: + jpa: + database-platform: io.github.jhipster.domain.util.FixedH2Dialect + database: H2 + open-in-view: false + show-sql: true + hibernate: + ddl-auto: none + naming: + physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy + properties: + hibernate.id.new_generator_mappings: true + hibernate.cache.use_second_level_cache: false + hibernate.cache.use_query_cache: false + hibernate.generate_statistics: true + hibernate.hbm2ddl.auto: validate + mail: + host: localhost + messages: + basename: i18n/messages + mvc: + favicon: + enabled: false + thymeleaf: + mode: XHTML + +liquibase: + contexts: test + +security: + basic: + enabled: false + +server: + port: 10344 + address: localhost + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://jhipster.github.io/common-application-properties/ +# =================================================================== + +jhipster: + async: + core-pool-size: 2 + max-pool-size: 50 + queue-capacity: 10000 + security: + authentication: + jwt: + secret: e1d4b69d3f953e3fa622121e882e6f459ca20ca4 + # Token is valid 24 hours + token-validity-in-seconds: 86400 + metrics: # DropWizard Metrics configuration, used by MetricsConfiguration + jmx.enabled: true + +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://jhipster.github.io/common-application-properties/ +# =================================================================== + +application: diff --git a/jhipster/src/test/resources/logback-test.xml b/jhipster/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..c0acd00401 --- /dev/null +++ b/jhipster/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/jhipster/tsconfig.json b/jhipster/tsconfig.json new file mode 100644 index 0000000000..354ae048ad --- /dev/null +++ b/jhipster/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "removeComments": false, + "noImplicitAny": false, + "suppressImplicitAnyIndexErrors": true, + "outDir": "target/www/app", + "lib": ["es6", "dom"], + "typeRoots": [ + "node_modules/@types" + ] + }, + "include": [ + "src/main/webapp/app", + "src/test/javascript" + ] +} \ No newline at end of file diff --git a/jhipster/tslint.json b/jhipster/tslint.json new file mode 100644 index 0000000000..ee6491cf69 --- /dev/null +++ b/jhipster/tslint.json @@ -0,0 +1,107 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "indent": [ + true, + "spaces" + ], + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + "static-before-instance", + "variables-before-functions" + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-variable": true, + "no-empty": false, + "no-eval": true, + "no-inferrable-types": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + + "directive-selector": [true, "attribute", "jhi", "camelCase"], + "component-selector": [true, "element", "jhi", "kebab-case"], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": false, + "component-class-suffix": true, + "directive-class-suffix": true, + "no-access-missing-member": true, + "templates-use-public": true, + "invoke-injectable": true + } +} diff --git a/jhipster/webpack/webpack.common.js b/jhipster/webpack/webpack.common.js new file mode 100644 index 0000000000..4916bb2db5 --- /dev/null +++ b/jhipster/webpack/webpack.common.js @@ -0,0 +1,117 @@ +const webpack = require('webpack'); +const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const StringReplacePlugin = require('string-replace-webpack-plugin'); +const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin'); +const path = require('path'); + +module.exports = function (options) { + const DATAS = { + VERSION: JSON.stringify(require("../package.json").version), + DEBUG_INFO_ENABLED: options.env === 'dev' + }; + return { + entry: { + 'polyfills': './src/main/webapp/app/polyfills', + 'global': './src/main/webapp/content/scss/global.scss', + 'main': './src/main/webapp/app/app.main' + }, + resolve: { + extensions: ['.ts', '.js'], + modules: ['node_modules'] + }, + module: { + rules: [ + { test: /bootstrap\/dist\/js\/umd\//, loader: 'imports-loader?jQuery=jquery' }, + { + test: /\.ts$/, + loaders: [ + 'angular2-template-loader', + 'awesome-typescript-loader' + ], + exclude: ['node_modules/generator-jhipster'] + }, + { + test: /\.html$/, + loader: 'raw-loader', + exclude: ['./src/main/webapp/index.html'] + }, + { + test: /\.scss$/, + loaders: ['to-string-loader', 'css-loader', 'sass-loader'], + exclude: /(vendor\.scss|global\.scss)/ + }, + { + test: /(vendor\.scss|global\.scss)/, + loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] + }, + { + test: /\.css$/, + loaders: ['to-string-loader', 'css-loader'], + exclude: /(vendor\.css|global\.css)/ + }, + { + test: /(vendor\.css|global\.css)/, + loaders: ['style-loader', 'css-loader'] + }, + { + test: /\.(jpe?g|png|gif|svg|woff|woff2|ttf|eot)$/i, + loaders: [ + 'file-loader?hash=sha512&digest=hex&name=[hash].[ext]', { + loader: 'image-webpack-loader', + query: { + gifsicle: { + interlaced: false + }, + optipng: { + optimizationLevel: 7 + } + } + } + ] + }, + { + test: /app.constants.ts$/, + loader: StringReplacePlugin.replace({ + replacements: [{ + pattern: /\/\* @toreplace (\w*?) \*\//ig, + replacement: function (match, p1, offset, string) { + return `_${p1} = ${DATAS[p1]};`; + } + }] + }) + } + ] + }, + plugins: [ + new CommonsChunkPlugin({ + names: ['manifest', 'polyfills'].reverse() + }), + new webpack.DllReferencePlugin({ + context: './', + manifest: require(path.resolve('./target/www/vendor.json')), + }), + new CopyWebpackPlugin([ + { from: './node_modules/swagger-ui/dist', to: 'swagger-ui/dist' }, + { from: './src/main/webapp/swagger-ui/', to: 'swagger-ui' }, + { from: './src/main/webapp/favicon.ico', to: 'favicon.ico' }, + { from: './src/main/webapp/robots.txt', to: 'robots.txt' }, + { from: './src/main/webapp/i18n', to: 'i18n' } + ]), + new webpack.ProvidePlugin({ + $: "jquery", + jQuery: "jquery" + }), + new HtmlWebpackPlugin({ + template: './src/main/webapp/index.html', + chunksSortMode: 'dependency', + inject: 'body' + }), + new AddAssetHtmlPlugin([ + { filepath: path.resolve('./target/www/vendor.dll.js'), includeSourcemap: false } + ]), + new StringReplacePlugin() + ] + }; +}; diff --git a/jhipster/webpack/webpack.dev.js b/jhipster/webpack/webpack.dev.js new file mode 100644 index 0000000000..f612a44b09 --- /dev/null +++ b/jhipster/webpack/webpack.dev.js @@ -0,0 +1,65 @@ +const webpack = require('webpack'); +const path = require('path'); +const commonConfig = require('./webpack.common.js'); +const writeFilePlugin = require('write-file-webpack-plugin'); +const webpackMerge = require('webpack-merge'); +const BrowserSyncPlugin = require('browser-sync-webpack-plugin'); +const ExtractTextPlugin = require("extract-text-webpack-plugin"); +const ENV = 'dev'; +const execSync = require('child_process').execSync; +const fs = require('fs'); +const ddlPath = './target/www/vendor.json'; + +if (!fs.existsSync(ddlPath)) { + execSync('webpack --config webpack/webpack.vendor.js'); +} + +module.exports = webpackMerge(commonConfig({ env: ENV }), { + devtool: 'inline-source-map', + devServer: { + contentBase: './target/www', + proxy: [{ + context: [ + '/api', + '/management', + '/swagger-resources', + '/v2/api-docs', + '/h2-console' + ], + target: 'http://127.0.0.1:8080', + secure: false + }] + }, + output: { + path: path.resolve('target/www'), + filename: '[name].bundle.js', + chunkFilename: '[id].chunk.js' + }, + module: { + rules: [{ + test: /\.ts$/, + loaders: [ + 'tslint-loader' + ], + exclude: ['node_modules', new RegExp('reflect-metadata\\' + path.sep + 'Reflect\\.ts')] + }] + }, + plugins: [ + new BrowserSyncPlugin({ + host: 'localhost', + port: 9000, + proxy: { + target: 'http://localhost:9060' + } + }, { + reload: false + }), + new ExtractTextPlugin('styles.css'), + new webpack.NoEmitOnErrorsPlugin(), + new webpack.NamedModulesPlugin(), + new writeFilePlugin(), + new webpack.WatchIgnorePlugin([ + path.resolve('./src/test'), + ]) + ] +}); diff --git a/jhipster/webpack/webpack.prod.js b/jhipster/webpack/webpack.prod.js new file mode 100644 index 0000000000..28f0f2152b --- /dev/null +++ b/jhipster/webpack/webpack.prod.js @@ -0,0 +1,22 @@ +const commonConfig = require('./webpack.common.js'); +const webpackMerge = require('webpack-merge'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const ExtractTextPlugin = require("extract-text-webpack-plugin"); +const Visualizer = require('webpack-visualizer-plugin'); +const ENV = 'prod'; + +module.exports = webpackMerge(commonConfig({ env: ENV }), { + devtool: 'source-map', + output: { + path: './target/www', + filename: '[hash].[name].bundle.js', + chunkFilename: '[hash].[id].chunk.js' + }, + plugins: [ + new ExtractTextPlugin('[hash].styles.css'), + new Visualizer({ + // Webpack statistics in target folder + filename: '../stats.html' + }) + ] +}); diff --git a/jhipster/webpack/webpack.vendor.js b/jhipster/webpack/webpack.vendor.js new file mode 100644 index 0000000000..449024d102 --- /dev/null +++ b/jhipster/webpack/webpack.vendor.js @@ -0,0 +1,63 @@ +var webpack = require('webpack'); +module.exports = { + entry: { + 'vendor': [ + './src/main/webapp/app/vendor', + '@angular/common', + '@angular/compiler', + '@angular/core', + '@angular/forms', + '@angular/http', + '@angular/platform-browser', + '@angular/platform-browser-dynamic', + '@angular/router', + '@ng-bootstrap/ng-bootstrap', + 'angular2-cookie', + 'angular2-infinite-scroll', + 'jquery', + 'ng-jhipster', + 'ng2-webstorage', + 'rxjs' + ] + }, + resolve: { + extensions: ['.ts', '.js'], + modules: ['node_modules'] + }, + module: { + exprContextCritical: false, + rules: [ + { + test: /(vendor\.scss|global\.scss)/, + loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] + }, + { + test: /\.(jpe?g|png|gif|svg|woff|woff2|ttf|eot)$/i, + loaders: [ + 'file-loader?hash=sha512&digest=hex&name=[hash].[ext]', { + loader: 'image-webpack-loader', + query: { + gifsicle: { + interlaced: false + }, + optipng: { + optimizationLevel: 7 + } + } + } + ] + } + ] + }, + output: { + filename: '[name].dll.js', + path: './target/www', + library: '[name]' + }, + plugins: [ + new webpack.DllPlugin({ + name: '[name]', + path: './target/www/[name].json' + }) + ] +}; diff --git a/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithoutLambdaUnitTest.java b/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithoutLambdaUnitTest.java index 786062ee57..aaa8d03585 100644 --- a/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithoutLambdaUnitTest.java +++ b/mockito2/src/test/java/com/baeldung/mockito/java8/ArgumentMatcherWithoutLambdaUnitTest.java @@ -13,6 +13,16 @@ import static org.mockito.Mockito.when; public class ArgumentMatcherWithoutLambdaUnitTest { + private class PeterArgumentMatcher implements ArgumentMatcher { + + @Override + public boolean matches(Person p) { + return p + .getName() + .equals("Peter"); + } + } + @InjectMocks private UnemploymentServiceImpl unemploymentService; @@ -34,16 +44,6 @@ public class ArgumentMatcherWithoutLambdaUnitTest { assertFalse(unemploymentService.personIsEntitledToUnemploymentSupport(peter)); } - private class PeterArgumentMatcher implements ArgumentMatcher { - - @Override - public boolean matches(Person p) { - return p - .getName() - .equals("Peter"); - } - } - @Before public void init() { MockitoAnnotations.initMocks(this); diff --git a/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithoutLambdaUnitTest.java b/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithoutLambdaUnitTest.java index 9d1aa3a3c0..d5b9d6d1ce 100644 --- a/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithoutLambdaUnitTest.java +++ b/mockito2/src/test/java/com/baeldung/mockito/java8/CustomAnswerWithoutLambdaUnitTest.java @@ -17,7 +17,21 @@ import static org.mockito.Mockito.when; public class CustomAnswerWithoutLambdaUnitTest { + + private class PersonAnswer implements Answer> { + @Override + public Stream answer(InvocationOnMock invocation) throws Throwable { + Person person = invocation.getArgument(0); + + if(person.getName().equals("Peter")) { + return Stream.builder().add(new JobPosition("Teacher")).build(); + } + + return Stream.empty(); + } + } + @InjectMocks private UnemploymentServiceImpl unemploymentService; @@ -37,17 +51,6 @@ public class CustomAnswerWithoutLambdaUnitTest { assertFalse(unemploymentService.searchJob(linda, "").isPresent()); } - - private class PersonAnswer implements Answer> { - - @Override - public Stream answer(InvocationOnMock invocation) throws Throwable { - Person person = invocation.getArgument(0); - - return Stream.of(new JobPosition("Teacher")) - .filter(p -> person.getName().equals("Peter")); - } - } @Before public void init() { diff --git a/pom.xml b/pom.xml index e0556a7c50..bc89f839eb 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,7 @@ javaxval jaxb jee7 + jhipster jjwt jooq jpa-storedprocedure From faea5eb5109ae8ea3606588988d46c397a56629b Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Tue, 21 Mar 2017 16:49:29 +0100 Subject: [PATCH 023/102] Refactor Javaslang samples (#1469) --- javaslang/pom.xml | 2 +- .../baeldung/javaslang/PropertyBasedTest.java | 70 ++++++++++--------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/javaslang/pom.xml b/javaslang/pom.xml index 7bb23c0daf..941aac0802 100644 --- a/javaslang/pom.xml +++ b/javaslang/pom.xml @@ -38,7 +38,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.3 + 3.5.1 1.8 1.8 diff --git a/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java b/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java index 3acac34550..43f3d6e6a0 100644 --- a/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java +++ b/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java @@ -1,6 +1,5 @@ package com.baeldung.javaslang; - import javaslang.CheckedFunction1; import javaslang.collection.Stream; import javaslang.test.Arbitrary; @@ -8,42 +7,42 @@ import javaslang.test.CheckResult; import javaslang.test.Property; import org.junit.Test; +import java.util.function.Predicate; + +import static javaslang.API.*; + public class PropertyBasedTest { - public Stream stringsSupplier() { - return Stream.from(0).map(i -> { - boolean divByTwo = i % 2 == 0; - boolean divByFive = i % 5 == 0; + private static Predicate divisibleByTwo = i -> i % 2 == 0; + private static Predicate divisibleByFive = i -> i % 5 == 0; - if(divByFive && divByTwo){ - return "DividedByTwoAndFiveWithoutRemainder"; - }else if(divByFive){ - return "DividedByFiveWithoutRemainder"; - }else if(divByTwo){ - return "DividedByTwoWithoutRemainder"; - } - return ""; - }); + private Stream stringsSupplier() { + return Stream.from(0).map(i -> Match(i).of( + Case($(divisibleByFive.and(divisibleByTwo)), "DividedByTwoAndFiveWithoutRemainder"), + Case($(divisibleByFive), "DividedByFiveWithoutRemainder"), + Case($(divisibleByTwo), "DividedByTwoWithoutRemainder"), + Case($(), ""))); } @Test public void givenArbitrarySeq_whenCheckThatEverySecondElementIsEqualToString_thenTestPass() { //given - Arbitrary multiplesOf2 = Arbitrary.integer() - .filter(i -> i > 0) - .filter(i -> i % 2 == 0 && i % 5 != 0); + Arbitrary multiplesOf2 = Arbitrary + .integer() + .filter(i -> i > 0) + .filter(i -> i % 2 == 0 && i % 5 != 0); //when - CheckedFunction1 mustEquals = - i -> stringsSupplier().get(i).equals("DividedByTwoWithoutRemainder"); - + CheckedFunction1 mustEquals = i -> stringsSupplier() + .get(i) + .equals("DividedByTwoWithoutRemainder"); //then CheckResult result = Property - .def("Every second element must equal to DividedByTwoWithoutRemainder") - .forAll(multiplesOf2) - .suchThat(mustEquals) - .check(10_000, 100); + .def("Every second element must equal to DividedByTwoWithoutRemainder") + .forAll(multiplesOf2) + .suchThat(mustEquals) + .check(10_000, 100); result.assertIsSatisfied(); } @@ -51,19 +50,22 @@ public class PropertyBasedTest { @Test public void givenArbitrarySeq_whenCheckThatEveryFifthElementIsEqualToString_thenTestPass() { //given - Arbitrary multiplesOf5 = Arbitrary.integer() - .filter(i -> i > 0) - .filter(i -> i % 5 == 0 && i % 2 == 0); + Arbitrary multiplesOf5 = Arbitrary + .integer() + .filter(i -> i > 0) + .filter(i -> i % 5 == 0 && i % 2 == 0); //when - CheckedFunction1 mustEquals = i -> - stringsSupplier().get(i).endsWith("DividedByTwoAndFiveWithoutRemainder"); + CheckedFunction1 mustEquals = i -> stringsSupplier() + .get(i) + .endsWith("DividedByTwoAndFiveWithoutRemainder"); //then - Property.def("Every fifth element must equal to DividedByTwoAndFiveWithoutRemainder") - .forAll(multiplesOf5) - .suchThat(mustEquals) - .check(10_000, 1_000) - .assertIsSatisfied(); + Property + .def("Every fifth element must equal to DividedByTwoAndFiveWithoutRemainder") + .forAll(multiplesOf5) + .suchThat(mustEquals) + .check(10_000, 1_000) + .assertIsSatisfied(); } } From eab4a5f8ca47df80b1587069fccf76ac214e41b0 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Tue, 21 Mar 2017 17:10:30 +0100 Subject: [PATCH 024/102] Move custom filter examples (#1465) * Move custom filter examples * Remove unused README entry --- spring-security-basic-auth/README.md | 1 - .../src/main/resources/webSecurityConfig.xml | 5 ---- spring-security-rest-basic-auth/README.md | 1 + .../org/baeldung}/filter/CustomFilter.java | 2 +- .../CustomWebSecurityConfigurerAdapter.java | 24 +++++++++++++------ 5 files changed, 19 insertions(+), 14 deletions(-) rename {spring-security-basic-auth/src/main/java/org/baeldung/security => spring-security-rest-basic-auth/src/main/java/org/baeldung}/filter/CustomFilter.java (92%) rename {spring-security-basic-auth/src/main/java/org/baeldung/security/filter/configuration => spring-security-rest-basic-auth/src/main/java/org/baeldung/filter}/CustomWebSecurityConfigurerAdapter.java (63%) diff --git a/spring-security-basic-auth/README.md b/spring-security-basic-auth/README.md index 8aa299f8cc..ebb404063f 100644 --- a/spring-security-basic-auth/README.md +++ b/spring-security-basic-auth/README.md @@ -7,7 +7,6 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com ### Relevant Article: - [Spring Security Basic Authentication](http://www.baeldung.com/spring-security-basic-authentication) -- [Writing a Custom Filter in Spring Security](http://www.baeldung.com/spring-security-custom-filter) ### Notes diff --git a/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml b/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml index f6d15980ae..b0d483768b 100644 --- a/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml +++ b/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml @@ -11,8 +11,6 @@ - - @@ -22,7 +20,4 @@ - - - \ No newline at end of file diff --git a/spring-security-rest-basic-auth/README.md b/spring-security-rest-basic-auth/README.md index 3bd46bdd2a..328f46ed46 100644 --- a/spring-security-rest-basic-auth/README.md +++ b/spring-security-rest-basic-auth/README.md @@ -9,3 +9,4 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - [RestTemplate with Basic Authentication in Spring](http://www.baeldung.com/2012/04/16/how-to-use-resttemplate-with-basic-authentication-in-spring-3-1) - [HttpClient Timeout](http://www.baeldung.com/httpclient-timeout) - [HttpClient with SSL](http://www.baeldung.com/httpclient-ssl) +- [Writing a Custom Filter in Spring Security](http://www.baeldung.com/spring-security-custom-filter) \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/security/filter/CustomFilter.java b/spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomFilter.java similarity index 92% rename from spring-security-basic-auth/src/main/java/org/baeldung/security/filter/CustomFilter.java rename to spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomFilter.java index 8d2b919cb0..01e5b0b59d 100644 --- a/spring-security-basic-auth/src/main/java/org/baeldung/security/filter/CustomFilter.java +++ b/spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomFilter.java @@ -1,4 +1,4 @@ -package org.baeldung.security.filter; +package org.baeldung.filter; import org.springframework.web.filter.GenericFilterBean; diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/security/filter/configuration/CustomWebSecurityConfigurerAdapter.java b/spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomWebSecurityConfigurerAdapter.java similarity index 63% rename from spring-security-basic-auth/src/main/java/org/baeldung/security/filter/configuration/CustomWebSecurityConfigurerAdapter.java rename to spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomWebSecurityConfigurerAdapter.java index d03d9cc018..2ff0e30f94 100644 --- a/spring-security-basic-auth/src/main/java/org/baeldung/security/filter/configuration/CustomWebSecurityConfigurerAdapter.java +++ b/spring-security-rest-basic-auth/src/main/java/org/baeldung/filter/CustomWebSecurityConfigurerAdapter.java @@ -1,7 +1,6 @@ -package org.baeldung.security.filter.configuration; +package org.baeldung.filter; -import org.baeldung.security.basic.MyBasicAuthenticationEntryPoint; -import org.baeldung.security.filter.CustomFilter; +import org.baeldung.security.RestAuthenticationEntryPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -14,17 +13,28 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi @EnableWebSecurity public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { - @Autowired - private MyBasicAuthenticationEntryPoint authenticationEntryPoint; + @Autowired private RestAuthenticationEntryPoint authenticationEntryPoint; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser("user1").password("user1Pass").authorities("ROLE_USER"); + auth + .inMemoryAuthentication() + .withUser("user1") + .password("user1Pass") + .authorities("ROLE_USER"); } @Override protected void configure(HttpSecurity http) throws Exception { - http.authorizeRequests().antMatchers("/securityNone").permitAll().anyRequest().authenticated().and().httpBasic().authenticationEntryPoint(authenticationEntryPoint); + http + .authorizeRequests() + .antMatchers("/securityNone") + .permitAll() + .anyRequest() + .authenticated() + .and() + .httpBasic() + .authenticationEntryPoint(authenticationEntryPoint); http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class); } From bd237b2115e045ae003d0157e85b70a87ac50d9b Mon Sep 17 00:00:00 2001 From: Alexandre Lombard Date: Tue, 21 Mar 2017 17:33:31 +0100 Subject: [PATCH 025/102] master (#1455) * Example code to return image with @ResponseBody * Example code showing how to return images using @ResponseBody * Example code showing how to return images using @ResponseBody --- .../baeldung/produceimage/Application.java | 14 +++++++ .../controller/DataProducerController.java | 38 ++++++++++++++++++ .../com/baeldung/produceimage/data.txt | 1 + .../com/baeldung/produceimage/image.jpg | Bin 0 -> 6170 bytes 4 files changed, 53 insertions(+) create mode 100644 spring-rest/src/main/java/com/baeldung/produceimage/Application.java create mode 100644 spring-rest/src/main/java/com/baeldung/produceimage/controller/DataProducerController.java create mode 100644 spring-rest/src/main/resources/com/baeldung/produceimage/data.txt create mode 100644 spring-rest/src/main/resources/com/baeldung/produceimage/image.jpg diff --git a/spring-rest/src/main/java/com/baeldung/produceimage/Application.java b/spring-rest/src/main/java/com/baeldung/produceimage/Application.java new file mode 100644 index 0000000000..179671d094 --- /dev/null +++ b/spring-rest/src/main/java/com/baeldung/produceimage/Application.java @@ -0,0 +1,14 @@ +package com.baeldung.produceimage; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.PropertySource; + +@EnableAutoConfiguration +@ComponentScan("com.baeldung.produceimage") +public class Application { + public static void main(final String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-rest/src/main/java/com/baeldung/produceimage/controller/DataProducerController.java b/spring-rest/src/main/java/com/baeldung/produceimage/controller/DataProducerController.java new file mode 100644 index 0000000000..6f34bdb9ae --- /dev/null +++ b/spring-rest/src/main/java/com/baeldung/produceimage/controller/DataProducerController.java @@ -0,0 +1,38 @@ +package com.baeldung.produceimage.controller; + +import org.apache.commons.io.IOUtils; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.io.IOException; +import java.io.InputStream; + +@Controller +public class DataProducerController { + + @GetMapping("/get-text") + public @ResponseBody String getText() { + return "Hello world"; + } + + @GetMapping(value = "/get-image") + public @ResponseBody byte[] getImage() throws IOException { + final InputStream in = getClass().getResourceAsStream("/com/baeldung/produceimage/image.jpg"); + return IOUtils.toByteArray(in); + } + + @GetMapping(value = "/get-image-with-media-type", produces = MediaType.IMAGE_JPEG_VALUE) + public @ResponseBody byte[] getImageWithMediaType() throws IOException { + final InputStream in = getClass().getResourceAsStream("/com/baeldung/produceimage/image.jpg"); + return IOUtils.toByteArray(in); + } + + @GetMapping(value = "/get-file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public @ResponseBody byte[] getFile() throws IOException { + final InputStream in = getClass().getResourceAsStream("/com/baeldung/produceimage/data.txt"); + return IOUtils.toByteArray(in); + } + +} diff --git a/spring-rest/src/main/resources/com/baeldung/produceimage/data.txt b/spring-rest/src/main/resources/com/baeldung/produceimage/data.txt new file mode 100644 index 0000000000..3cd18170c0 --- /dev/null +++ b/spring-rest/src/main/resources/com/baeldung/produceimage/data.txt @@ -0,0 +1 @@ +This is a sample file containing text data \ No newline at end of file diff --git a/spring-rest/src/main/resources/com/baeldung/produceimage/image.jpg b/spring-rest/src/main/resources/com/baeldung/produceimage/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0262656a33a02978a6c4a4754856801a839fd597 GIT binary patch literal 6170 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 literal 0 HcmV?d00001 From d66703b5d9340c67a94ec59e5e66686cb0058691 Mon Sep 17 00:00:00 2001 From: KevinGilmore Date: Tue, 21 Mar 2017 17:46:06 -0500 Subject: [PATCH 026/102] BAEL-680: rename test methods (#1470) * Add files via upload * Update pom.xml * Update RunGuice.java * Update Communication.java * Update CommunicationMode.java * Update DefaultCommunicator.java * Update EmailCommunicationMode.java * Update IMCommunicationMode.java * Update SMSCommunicationMode.java * Update MessageLogger.java * Update MessageSentLoggable.java * Update AOPModule.java * Update BasicModule.java * Update CommunicationModel.java * Update Communicator.java * Update BasicModule.java * Update RunGuice.java * Update MessageLogger.java * Update Communicator.java * Update pom.xml * BAEL-278: Updated README.md * BAEL-554: Add and update README.md files * Update pom.xml * Update pom.xml * Update pom.xml * BAEL-345: fixed assertion * BAEL-109: Updated README.md * BAEL-345: Added README.md * Reinstating reactor-core module in root-level pom * BAEL-393: Adding guide-intro module to root pom * BAEL-9: Updated README.md * BAEL-157: README.md updated * Changed project name * Update RunGuice.java Removed references to message logging and output * Update Communication.java Removed message logging-related code * BAEL-566: Updated README.md * New project name * BAEL-393: removing guice-intro directory * BAEL-393: renamed module guice-intro to guice in root pom.xml * BAEL-393 and BAEL-541 README.md files * BAEL-731: Updated README.md * BAEL-680: renamed test methods --- .../java8/comparator/Java8ComparatorTest.java | 60 ++++--------------- 1 file changed, 12 insertions(+), 48 deletions(-) diff --git a/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java b/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java index 5c338101d8..57e3898274 100644 --- a/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java +++ b/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java @@ -60,7 +60,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingComparing_thenCheckingSort() { + public void whenComparing_thenSortedByName() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Arrays.sort(employees, employeeNameComparator); // System.out.println(Arrays.toString(employees)); @@ -68,7 +68,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingComparingWithComparator_thenCheckingSort() { + public void whenComparingWithComparator_thenSortedByNameDesc() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName, (s1, s2) -> { return s2.compareTo(s1); }); @@ -78,7 +78,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingComparingInt_thenCheckingSort() { + public void whenComparingInt_thenSortedByAge() { Comparator employeeAgeComparator = Comparator.comparingInt(Employee::getAge); Arrays.sort(employees, employeeAgeComparator); // System.out.println(Arrays.toString(employees)); @@ -86,7 +86,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingComparingLong_thenCheckingSort() { + public void whenComparingLong_thenSortedByMobile() { Comparator employeeMobileComparator = Comparator.comparingLong(Employee::getMobile); Arrays.sort(employees, employeeMobileComparator); // System.out.println(Arrays.toString(employees)); @@ -94,7 +94,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingComparingDouble_thenCheckingSort() { + public void whenComparingDouble_thenSortedBySalary() { Comparator employeeSalaryComparator = Comparator.comparingDouble(Employee::getSalary); Arrays.sort(employees, employeeSalaryComparator); // System.out.println(Arrays.toString(employees)); @@ -102,7 +102,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingNaturalOrder_thenCheckingSort() { + public void whenNaturalOrder_thenSortedByName() { Comparator employeeNameComparator = Comparator. naturalOrder(); Arrays.sort(employees, employeeNameComparator); // System.out.println(Arrays.toString(employees)); @@ -110,7 +110,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingReverseOrder_thenCheckingSort() { + public void whenReverseOrder_thenSortedByNameDesc() { Comparator employeeNameComparator = Comparator. reverseOrder(); Arrays.sort(employees, employeeNameComparator); // System.out.println(Arrays.toString(employees)); @@ -118,7 +118,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingNullFirst_thenCheckingSort() { + public void whenNullsFirst_thenSortedByNameWithNullsFirst() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Comparator employeeNameComparator_nullFirst = Comparator.nullsFirst(employeeNameComparator); Arrays.sort(employeesArrayWithNulls, employeeNameComparator_nullFirst); @@ -127,7 +127,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingNullLast_thenCheckingSort() { + public void whenNullsLast_thenSortedByNameWithNullsLast() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Comparator employeeNameComparator_nullLast = Comparator.nullsLast(employeeNameComparator); Arrays.sort(employeesArrayWithNulls, employeeNameComparator_nullLast); @@ -136,7 +136,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingThenComparing_thenCheckingSort() { + public void whenThenComparing_thenSortedByAgeName() { Comparator employee_Age_Name_Comparator = Comparator.comparing(Employee::getAge).thenComparing(Employee::getName); Arrays.sort(someMoreEmployees, employee_Age_Name_Comparator); @@ -145,7 +145,7 @@ public class Java8ComparatorTest { } @Test - public void givenEmployeeArray_whenUsingThenComparingInt_thenCheckingSort() { + public void whenThenComparing_thenSortedByNameAge() { Comparator employee_Name_Age_Comparator = Comparator.comparing(Employee::getName).thenComparingInt(Employee::getAge); Arrays.sort(someMoreEmployees, employee_Name_Age_Comparator); @@ -153,40 +153,4 @@ public class Java8ComparatorTest { assertTrue(Arrays.equals(someMoreEmployees, sortedEmployeesByNameAge)); } - @Before - public void printData() { -// System.out.println("employees"); -// System.out.println(Arrays.toString(employees)); - // -// System.out.println("employeesArrayWithNulls"); -// System.out.println(Arrays.toString(employeesArrayWithNulls)); - // - // System.out.println("sortedEmployeesByName"); - // System.out.println(Arrays.toString(sortedEmployeesByName)); - // - // System.out.println("sortedEmployeesByNameDesc"); - // System.out.println(Arrays.toString(sortedEmployeesByNameDesc)); - // - // System.out.println("sortedEmployeesByAge"); - // System.out.println(Arrays.toString(sortedEmployeesByAge)); - // - // System.out.println("sortedEmployeesByMobile"); - // System.out.println(Arrays.toString(sortedEmployeesByMobile)); - // - // System.out.println("sortedEmployeesBySalary"); - // System.out.println(Arrays.toString(sortedEmployeesBySalary)); - // - // System.out.println("sortedEmployeesArray_WithNullsFirst"); - // System.out.println(Arrays.toString(sortedEmployeesArray_WithNullsFirst)); - // - // System.out.println("sortedEmployeesArray_WithNullsLast"); - // System.out.println(Arrays.toString(sortedEmployeesArray_WithNullsLast)); - // - // System.out.println("sortedEmployeesByNameAge"); - // System.out.println(Arrays.toString(sortedEmployeesByNameAge)); - // -// System.out.println("someMoreEmployees"); -// System.out.println(Arrays.toString(someMoreEmployees)); - // - } -} +} \ No newline at end of file From e71358a9dec1baab03c32323940facc5645f73c6 Mon Sep 17 00:00:00 2001 From: Justin Wilson Date: Wed, 22 Mar 2017 12:47:23 +0000 Subject: [PATCH 027/102] BAEL-503: initial commit of a simple Spring AMQL example application (#1467) --- pom.xml | 1 + spring-amqp-simple/pom.xml | 46 ++++++++++++++++++ .../springamqpsimple/MessageConsumer.java | 15 ++++++ .../springamqpsimple/MessageController.java | 26 ++++++++++ .../springamqpsimple/MessageProducer.java | 20 ++++++++ .../SpringAmqpApplication.java | 12 +++++ .../springamqpsimple/SpringAmqpConfig.java | 48 +++++++++++++++++++ .../src/main/resources/application.yaml | 4 ++ .../MessageControllerTest.java | 45 +++++++++++++++++ 9 files changed, 217 insertions(+) create mode 100644 spring-amqp-simple/pom.xml create mode 100644 spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageConsumer.java create mode 100644 spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageController.java create mode 100644 spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageProducer.java create mode 100644 spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpApplication.java create mode 100644 spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpConfig.java create mode 100644 spring-amqp-simple/src/main/resources/application.yaml create mode 100644 spring-amqp-simple/src/test/java/com/baeldung/springamqpsimple/MessageControllerTest.java diff --git a/pom.xml b/pom.xml index 1a1108cb99..15e0e3322e 100644 --- a/pom.xml +++ b/pom.xml @@ -115,6 +115,7 @@ spring-akka spring-amqp spring-all + spring-amqp-simple spring-apache-camel spring-batch spring-boot diff --git a/spring-amqp-simple/pom.xml b/spring-amqp-simple/pom.xml new file mode 100644 index 0000000000..38738d875f --- /dev/null +++ b/spring-amqp-simple/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 1.5.2.RELEASE + + + com.baeldung + spring-amqp-simple + 1.0.0-SNAPSHOT + Spring AMQP Simple App + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-amqp + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageConsumer.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageConsumer.java new file mode 100644 index 0000000000..b757dfebe8 --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageConsumer.java @@ -0,0 +1,15 @@ +package com.baeldung.springamqpsimple; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +@Component +public class MessageConsumer { + + private static final Logger logger = LogManager.getLogger(MessageConsumer.class); + + public void receiveMessage(String message) { + logger.info("Received Message: " + message); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageController.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageController.java new file mode 100644 index 0000000000..deef22c4d6 --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageController.java @@ -0,0 +1,26 @@ +package com.baeldung.springamqpsimple; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseStatus; + +@Controller +public class MessageController { + + private final MessageProducer messageProducer; + + @Autowired + public MessageController(MessageProducer messageProducer) { + this.messageProducer = messageProducer; + } + + @RequestMapping(value="/messages", method= RequestMethod.POST) + @ResponseStatus(value= HttpStatus.CREATED) + public void sendMessage(@RequestBody String message) { + messageProducer.sendMessage(message); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageProducer.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageProducer.java new file mode 100644 index 0000000000..225f37bdd0 --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/MessageProducer.java @@ -0,0 +1,20 @@ +package com.baeldung.springamqpsimple; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class MessageProducer { + + private final RabbitTemplate rabbitTemplate; + + @Autowired + public MessageProducer(RabbitTemplate rabbitTemplate) { + this.rabbitTemplate = rabbitTemplate; + } + + public void sendMessage(String message) { + rabbitTemplate.convertAndSend(SpringAmqpConfig.queueName, message); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpApplication.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpApplication.java new file mode 100644 index 0000000000..b84a49a230 --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.springamqpsimple; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringAmqpApplication { + + public static void main(String[] args) throws InterruptedException { + SpringApplication.run(SpringAmqpApplication.class, args); + } +} diff --git a/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpConfig.java b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpConfig.java new file mode 100644 index 0000000000..78d79dd47a --- /dev/null +++ b/spring-amqp-simple/src/main/java/com/baeldung/springamqpsimple/SpringAmqpConfig.java @@ -0,0 +1,48 @@ +package com.baeldung.springamqpsimple; + +import org.springframework.amqp.core.*; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Configuration +@Profile("!test") +public class SpringAmqpConfig { + + public final static String queueName = "com.baeldung.spring-amqp-simple.queue"; + public final static String exchangeName = "com.baeldung.spring-amqp-simple.exchange"; + + @Bean + Queue queue() { + return new Queue(queueName, false); + } + + @Bean + Exchange exchange() { + return new DirectExchange(exchangeName); + } + + @Bean + Binding binding(Queue queue, DirectExchange exchange) { + return BindingBuilder.bind(queue).to(exchange).with(queueName); + } + + @Bean + SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, + MessageListenerAdapter listenerAdapter) { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + container.setQueueNames(queueName); + container.setMessageListener(listenerAdapter); + return container; + } + + @Bean + MessageListenerAdapter listenerAdapter(MessageConsumer messageReceiver) { + return new MessageListenerAdapter(messageReceiver, "receiveMessage"); + } + +} diff --git a/spring-amqp-simple/src/main/resources/application.yaml b/spring-amqp-simple/src/main/resources/application.yaml new file mode 100644 index 0000000000..4aca1bb783 --- /dev/null +++ b/spring-amqp-simple/src/main/resources/application.yaml @@ -0,0 +1,4 @@ +spring: + rabbitmq: + username: baeldung + password: baeldung \ No newline at end of file diff --git a/spring-amqp-simple/src/test/java/com/baeldung/springamqpsimple/MessageControllerTest.java b/spring-amqp-simple/src/test/java/com/baeldung/springamqpsimple/MessageControllerTest.java new file mode 100644 index 0000000000..c62c86290a --- /dev/null +++ b/spring-amqp-simple/src/test/java/com/baeldung/springamqpsimple/MessageControllerTest.java @@ -0,0 +1,45 @@ +package com.baeldung.springamqpsimple; + + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; + +@RunWith(SpringRunner.class) +@ActiveProfiles("test") +@SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.RANDOM_PORT) +public class MessageControllerTest { + + @Autowired + private TestRestTemplate restTemplate; + + @MockBean + private RabbitTemplate rabbitTemplate; + + @Test + public void whenPostingMessage_thenMessageIsCreated() { + final String message = "Hello World!"; + ResponseEntity responseEntity = restTemplate.postForEntity("/messages", message, Void.class); + + assertEquals(HttpStatus.CREATED, responseEntity.getStatusCode()); + } + + @Test + public void whenPostingMessage_thenMessageIsSentToBroker() { + final String message = "Hello World!"; + restTemplate.postForEntity("/messages", message, Void.class); + + verify(rabbitTemplate).convertAndSend(SpringAmqpConfig.queueName, message); + } +} \ No newline at end of file From a055ab5f81698fdf2330edd9d0a51371d4e18c4f Mon Sep 17 00:00:00 2001 From: KevinGilmore Date: Wed, 22 Mar 2017 19:29:59 -0500 Subject: [PATCH 028/102] BAEL-714: Updated README.md (#1475) * Add files via upload * Update pom.xml * Update RunGuice.java * Update Communication.java * Update CommunicationMode.java * Update DefaultCommunicator.java * Update EmailCommunicationMode.java * Update IMCommunicationMode.java * Update SMSCommunicationMode.java * Update MessageLogger.java * Update MessageSentLoggable.java * Update AOPModule.java * Update BasicModule.java * Update CommunicationModel.java * Update Communicator.java * Update BasicModule.java * Update RunGuice.java * Update MessageLogger.java * Update Communicator.java * Update pom.xml * BAEL-278: Updated README.md * BAEL-554: Add and update README.md files * Update pom.xml * Update pom.xml * Update pom.xml * BAEL-345: fixed assertion * BAEL-109: Updated README.md * BAEL-345: Added README.md * Reinstating reactor-core module in root-level pom * BAEL-393: Adding guide-intro module to root pom * BAEL-9: Updated README.md * BAEL-157: README.md updated * Changed project name * Update RunGuice.java Removed references to message logging and output * Update Communication.java Removed message logging-related code * BAEL-566: Updated README.md * New project name * BAEL-393: removing guice-intro directory * BAEL-393: renamed module guice-intro to guice in root pom.xml * BAEL-393 and BAEL-541 README.md files * BAEL-731: Updated README.md * BAEL-680: renamed test methods * BAEL-714: Updated README.md --- spring-mvc-forms/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-mvc-forms/README.md b/spring-mvc-forms/README.md index 745851a102..86abd7e4c1 100644 --- a/spring-mvc-forms/README.md +++ b/spring-mvc-forms/README.md @@ -3,3 +3,4 @@ ### Relevant Articles - [MaxUploadSizeExceededException in Spring](http://www.baeldung.com/spring-maxuploadsizeexceeded) - [Getting Started with Forms in Spring MVC](http://www.baeldung.com/spring-mvc-form-tutorial) +- [Form Validation with AngularJS and Spring MVC](http://www.baeldung.com/validation-angularjs-spring-mvc) From b01c2c5a949339419e54ebcd7635ccdc12f311ef Mon Sep 17 00:00:00 2001 From: Tomasz Lelek Date: Thu, 23 Mar 2017 13:46:32 +0100 Subject: [PATCH 029/102] Bael 361 jackson streaming (#1459) * BAEL-361 tests for Streaming API * BAEL-361 do not use deprecated API * BAEL-361 return to not read whole json document, only needed field --- .../streaming/JacksonStreamingAPITest.java | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 jackson/src/test/java/com/baeldung/jackson/streaming/JacksonStreamingAPITest.java diff --git a/jackson/src/test/java/com/baeldung/jackson/streaming/JacksonStreamingAPITest.java b/jackson/src/test/java/com/baeldung/jackson/streaming/JacksonStreamingAPITest.java new file mode 100644 index 0000000000..6f61793315 --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/streaming/JacksonStreamingAPITest.java @@ -0,0 +1,119 @@ +package com.baeldung.jackson.streaming; + + +import com.fasterxml.jackson.core.*; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.assertEquals; + +public class JacksonStreamingAPITest { + + @Test + public void givenJsonGenerator_whenAppendJsonToIt_thenGenerateJson() throws IOException { + //given + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + JsonFactory jfactory = new JsonFactory(); + JsonGenerator jGenerator = jfactory.createGenerator(stream, JsonEncoding.UTF8); + + //when + jGenerator.writeStartObject(); + jGenerator.writeStringField("name", "Tom"); + jGenerator.writeNumberField("age", 25); + jGenerator.writeFieldName("address"); + jGenerator.writeStartArray(); + jGenerator.writeString("Poland"); + jGenerator.writeString("5th avenue"); + jGenerator.writeEndArray(); + jGenerator.writeEndObject(); + jGenerator.close(); + + //then + String json = new String(stream.toByteArray(), "UTF-8"); + assertEquals(json, "{\"name\":\"Tom\",\"age\":25,\"address\":[\"Poland\",\"5th avenue\"]}"); + } + + @Test + public void givenJson_whenReadItUsingStreamAPI_thenShouldCreateProperJsonObject() throws IOException { + //given + String json = "{\"name\":\"Tom\",\"age\":25,\"address\":[\"Poland\",\"5th avenue\"]}"; + JsonFactory jfactory = new JsonFactory(); + JsonParser jParser = jfactory.createParser(json); + + String parsedName = null; + Integer parsedAge = null; + List addresses = new LinkedList<>(); + + //when + while (jParser.nextToken() != JsonToken.END_OBJECT) { + + String fieldname = jParser.getCurrentName(); + if ("name".equals(fieldname)) { + jParser.nextToken(); + parsedName = jParser.getText(); + + } + + if ("age".equals(fieldname)) { + jParser.nextToken(); + parsedAge = jParser.getIntValue(); + + } + + if ("address".equals(fieldname)) { + jParser.nextToken(); + + while (jParser.nextToken() != JsonToken.END_ARRAY) { + addresses.add(jParser.getText()); + } + } + + } + jParser.close(); + + //then + assertEquals(parsedName, "Tom"); + assertEquals(parsedAge, (Integer) 25); + assertEquals(addresses, Arrays.asList("Poland", "5th avenue")); + + } + + @Test + public void givenJson_whenWantToExtractPartOfIt_thenShouldExtractOnlyNeededFieldWithoutGoingThroughWholeJSON() throws IOException { + //given + String json = "{\"name\":\"Tom\",\"age\":25,\"address\":[\"Poland\",\"5th avenue\"]}"; + JsonFactory jfactory = new JsonFactory(); + JsonParser jParser = jfactory.createParser(json); + + String parsedName = null; + Integer parsedAge = null; + List addresses = new LinkedList<>(); + + //when + while (jParser.nextToken() != JsonToken.END_OBJECT) { + + String fieldname = jParser.getCurrentName(); + + if ("age".equals(fieldname)) { + jParser.nextToken(); + parsedAge = jParser.getIntValue(); + return; + } + + } + jParser.close(); + + //then + assertNull(parsedName); + assertEquals(parsedAge, (Integer) 25); + assertTrue(addresses.isEmpty()); + + } +} From e884e3f92455a9dd9f620d57952e8826d54f4639 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Fri, 24 Mar 2017 02:20:30 +0530 Subject: [PATCH 030/102] update spring-kafka project with support for multiple partitions and JSON serializer (#1472) --- spring-kafka/README.md | 26 ++++- spring-kafka/pom.xml | 12 +- .../com/baeldung/spring/kafka/Greeting.java | 37 ++++++ .../spring/kafka/KafkaApplication.java | 105 +++++++++++++++++- .../spring/kafka/KafkaConsumerConfig.java | 41 ++++++- .../spring/kafka/KafkaProducerConfig.java | 20 +++- .../src/main/resources/application.properties | 3 + 7 files changed, 228 insertions(+), 16 deletions(-) create mode 100644 spring-kafka/src/main/java/com/baeldung/spring/kafka/Greeting.java diff --git a/spring-kafka/README.md b/spring-kafka/README.md index 2731eca042..c8f01cc28b 100644 --- a/spring-kafka/README.md +++ b/spring-kafka/README.md @@ -2,8 +2,28 @@ This is a simple Spring Boot app to demonstrate sending and receiving of messages in Kafka using spring-kafka. -As Kafka topics are not created automatically by default, this application requires that a topic named 'baeldung' is created manually. +As Kafka topics are not created automatically by default, this application requires that you create the following topics manually. -`$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic baeldung` +`$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic baeldung`
+`$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 5 --topic partitioned`
+`$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic filtered`
+`$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic greeting`
-Two listeners with group Ids **foo** and **bar** are configured. When run successfully, the *Hello World!* message will be received by both the listeners and logged on console. +When the application runs successfully, following output is logged on to console (along with spring logs): + +#### Message received from the 'baeldung' topic by the basic listeners with groups foo and bar +>Received Messasge in group 'foo': Hello, World!
+Received Messasge in group 'bar': Hello, World! + +#### Message received from the 'baeldung' topic, with the partition info +>Received Messasge: Hello, World! from partition: 0 + +#### Message received from the 'partitioned' topic, only from specific partitions +>Received Message: Hello To Partioned Topic! from partition: 0
+Received Message: Hello To Partioned Topic! from partition: 3 + +#### Message received from the 'filtered' topic after filtering +>Recieved Message in filtered listener: Hello Baeldung! + +#### Message (Serialized Java Object) received from the 'greeting' topic +>Recieved greeting message: Greetings, World!! \ No newline at end of file diff --git a/spring-kafka/pom.xml b/spring-kafka/pom.xml index 73eaf3acff..11810a17dd 100644 --- a/spring-kafka/pom.xml +++ b/spring-kafka/pom.xml @@ -12,6 +12,7 @@ 1.8 1.1.3.RELEASE + 2.6.7 @@ -21,17 +22,22 @@ - + org.springframework.boot spring-boot-starter - + org.springframework.kafka spring-kafka - + + + com.fasterxml.jackson.core + jackson-databind + + diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/Greeting.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/Greeting.java new file mode 100644 index 0000000000..b4633e802a --- /dev/null +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/Greeting.java @@ -0,0 +1,37 @@ +package com.baeldung.spring.kafka; + +public class Greeting { + + private String msg; + private String name; + + public Greeting() { + + } + + public Greeting(String msg, String name) { + this.msg = msg; + this.name = name; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return msg + ", " + name + "!"; + } +} diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplication.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplication.java index 252054a9f1..50978d5ea9 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplication.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaApplication.java @@ -10,21 +10,61 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.annotation.TopicPartition; import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.messaging.handler.annotation.Payload; @SpringBootApplication public class KafkaApplication { public static void main(String[] args) throws Exception { + ConfigurableApplicationContext context = SpringApplication.run(KafkaApplication.class, args); + MessageProducer producer = context.getBean(MessageProducer.class); - producer.sendMessage("Hello, World!"); - MessageListener listener = context.getBean(MessageListener.class); - listener.latch.await(20, TimeUnit.SECONDS); - Thread.sleep(60000); - context.close(); + /* + * Sending a Hello World message to topic 'baeldung'. + * Must be recieved by both listeners with group foo + * and bar with containerFactory fooKafkaListenerContainerFactory + * and barKafkaListenerContainerFactory respectively. + * It will also be recieved by the listener with + * headersKafkaListenerContainerFactory as container factory + */ + producer.sendMessage("Hello, World!"); + listener.latch.await(10, TimeUnit.SECONDS); + /* + * Sending message to a topic with 5 partition, + * each message to a different partition. But as per + * listener configuration, only the messages from + * partition 0 and 3 will be consumed. + */ + for (int i = 0; i < 5; i++) { + producer.sendMessageToPartion("Hello To Partioned Topic!", i); + } + listener.partitionLatch.await(10, TimeUnit.SECONDS); + + /* + * Sending message to 'filtered' topic. As per listener + * configuration, all messages with char sequence + * 'World' will be discarded. + */ + producer.sendMessageToFiltered("Hello Baeldung!"); + producer.sendMessageToFiltered("Hello World!"); + listener.filterLatch.await(10, TimeUnit.SECONDS); + + /* + * Sending message to 'greeting' topic. This will send + * and recieved a java object with the help of + * greetingKafkaListenerContainerFactory. + */ + producer.sendGreetingMessage(new Greeting("Greetings", "World!")); + listener.greetingLatch.await(10, TimeUnit.SECONDS); + + context.close(); } @Bean @@ -42,18 +82,47 @@ public class KafkaApplication { @Autowired private KafkaTemplate kafkaTemplate; + @Autowired + private KafkaTemplate greetingKafkaTemplate; + @Value(value = "${message.topic.name}") private String topicName; + @Value(value = "${partitioned.topic.name}") + private String partionedTopicName; + + @Value(value = "${filtered.topic.name}") + private String filteredTopicName; + + @Value(value = "${greeting.topic.name}") + private String greetingTopicName; + public void sendMessage(String message) { kafkaTemplate.send(topicName, message); } + public void sendMessageToPartion(String message, int partition) { + kafkaTemplate.send(partionedTopicName, partition, message); + } + + public void sendMessageToFiltered(String message) { + kafkaTemplate.send(filteredTopicName, message); + } + + public void sendGreetingMessage(Greeting greeting) { + greetingKafkaTemplate.send(greetingTopicName, greeting); + } } public static class MessageListener { - private CountDownLatch latch = new CountDownLatch(2); + private CountDownLatch latch = new CountDownLatch(3); + + private CountDownLatch partitionLatch = new CountDownLatch(2); + + private CountDownLatch filterLatch = new CountDownLatch(2); + + private CountDownLatch greetingLatch = new CountDownLatch(1); @KafkaListener(topics = "${message.topic.name}", group = "foo", containerFactory = "fooKafkaListenerContainerFactory") public void listenGroupFoo(String message) { @@ -67,6 +136,30 @@ public class KafkaApplication { latch.countDown(); } + @KafkaListener(topics = "${message.topic.name}", containerFactory = "headersKafkaListenerContainerFactory") + public void listenWithHeaders(@Payload String message, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) { + System.out.println("Received Messasge: " + message + " from partition: " + partition); + latch.countDown(); + } + + @KafkaListener(topicPartitions = @TopicPartition(topic = "${partitioned.topic.name}", partitions = { "0", "3" })) + public void listenToParition(@Payload String message, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) { + System.out.println("Received Message: " + message + " from partition: " + partition); + this.partitionLatch.countDown(); + } + + @KafkaListener(topics = "${filtered.topic.name}", containerFactory = "filterKafkaListenerContainerFactory") + public void listenWithFilter(String message) { + System.out.println("Recieved Message in filtered listener: " + message); + this.filterLatch.countDown(); + } + + @KafkaListener(topics = "${greeting.topic.name}", containerFactory = "greetingKafkaListenerContainerFactory") + public void greetingListener(Greeting greeting) { + System.out.println("Recieved greeting message: " + greeting); + this.greetingLatch.countDown(); + } + } } diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java index f9edda2435..9353e63ff6 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaConsumerConfig.java @@ -12,6 +12,7 @@ import org.springframework.kafka.annotation.EnableKafka; import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; import org.springframework.kafka.core.ConsumerFactory; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.support.serializer.JsonDeserializer; @EnableKafka @Configuration @@ -35,11 +36,49 @@ public class KafkaConsumerConfig { factory.setConsumerFactory(consumerFactory("foo")); return factory; } - + @Bean public ConcurrentKafkaListenerContainerFactory barKafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(consumerFactory("bar")); return factory; } + + @Bean + public ConcurrentKafkaListenerContainerFactory headersKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory("headers")); + return factory; + } + + @Bean + public ConcurrentKafkaListenerContainerFactory partitionsKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory("partitions")); + return factory; + } + + @Bean + public ConcurrentKafkaListenerContainerFactory filterKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory("filter")); + factory.setRecordFilterStrategy(record -> record.value() + .contains("World")); + return factory; + } + + public ConsumerFactory greetingConsumerFactory() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); + props.put(ConsumerConfig.GROUP_ID_CONFIG, "greeting"); + return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(Greeting.class)); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory greetingKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(greetingConsumerFactory()); + return factory; + } + } diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java index 4f9f9719ee..84d57c9e92 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java @@ -11,6 +11,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.core.ProducerFactory; +import org.springframework.kafka.support.serializer.JsonSerializer; @Configuration public class KafkaProducerConfig { @@ -29,8 +30,21 @@ public class KafkaProducerConfig { @Bean public KafkaTemplate kafkaTemplate() { - KafkaTemplate template = - new KafkaTemplate(producerFactory()); - return template; + return new KafkaTemplate(producerFactory()); } + + @Bean + public ProducerFactory greetingProducerFactory() { + Map configProps = new HashMap(); + configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); + configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); + return new DefaultKafkaProducerFactory(configProps); + } + + @Bean + public KafkaTemplate greetingKafkaTemplate() { + return new KafkaTemplate(greetingProducerFactory()); + } + } diff --git a/spring-kafka/src/main/resources/application.properties b/spring-kafka/src/main/resources/application.properties index a1d73b204c..eaf113191e 100644 --- a/spring-kafka/src/main/resources/application.properties +++ b/spring-kafka/src/main/resources/application.properties @@ -1,2 +1,5 @@ kafka.bootstrapAddress=localhost:9092 message.topic.name=baeldung +greeting.topic.name=greeting +filtered.topic.name=filtered +partitioned.topic.name=partitioned \ No newline at end of file From 2d556cd763f0261efb1c2604815656a26a6e83bc Mon Sep 17 00:00:00 2001 From: Tomasz Lelek Date: Fri, 24 Mar 2017 15:29:14 +0100 Subject: [PATCH 031/102] Bael 738 (#1478) * BAEL-724 code for put/patch article * BAEL-724 fix typo * BAEL-728 more generic patch approach --- .../repository/HeavyResourceRepository.java | 10 ++++-- .../controller/HeavyResourceController.java | 18 +++++++---- .../web/dto/HeavyResourceAddressOnly.java | 31 +++++++++++++++++++ .../HeavyResourceControllerTest.java | 19 ++++++++++-- 4 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressOnly.java diff --git a/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java b/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java index cff78442d0..6de9e75450 100644 --- a/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java +++ b/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java @@ -1,14 +1,20 @@ package org.baeldung.repository; import org.baeldung.web.dto.HeavyResource; -import org.baeldung.web.dto.HeavyResourceAddressPartialUpdate; +import org.baeldung.web.dto.HeavyResourceAddressOnly; + +import java.util.Map; public class HeavyResourceRepository { public void save(HeavyResource heavyResource) { } - public void save(HeavyResourceAddressPartialUpdate partialUpdate) { + public void save(HeavyResourceAddressOnly partialUpdate) { + + } + + public void save(Map updates, String id) { } } diff --git a/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java b/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java index a2d5cfbd7b..f2c4ffaa51 100644 --- a/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java +++ b/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java @@ -3,13 +3,12 @@ package org.baeldung.web.controller; import org.baeldung.repository.HeavyResourceRepository; import org.baeldung.web.dto.HeavyResource; -import org.baeldung.web.dto.HeavyResourceAddressPartialUpdate; +import org.baeldung.web.dto.HeavyResourceAddressOnly; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; @RestController public class HeavyResourceController { @@ -23,9 +22,16 @@ public class HeavyResourceController { } @RequestMapping(value = "/heavy", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity partialUpdateName(@RequestBody HeavyResourceAddressPartialUpdate partialUpdate) { + public ResponseEntity partialUpdateName(@RequestBody HeavyResourceAddressOnly partialUpdate) { heavyResourceRepository.save(partialUpdate); return ResponseEntity.ok("resource address updated"); } + @RequestMapping(value = "/heavy/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity partialUpdateGeneric(@RequestBody Map updates, + @PathVariable("id") String id) { + heavyResourceRepository.save(updates, id); + return ResponseEntity.ok("resource updated"); + } + } diff --git a/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressOnly.java b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressOnly.java new file mode 100644 index 0000000000..f96347d60c --- /dev/null +++ b/spring-rest/src/main/java/org/baeldung/web/dto/HeavyResourceAddressOnly.java @@ -0,0 +1,31 @@ +package org.baeldung.web.dto; + + +public class HeavyResourceAddressOnly { + private Integer id; + private String address; + + public HeavyResourceAddressOnly() { + } + + public HeavyResourceAddressOnly(Integer id, String address) { + this.id = id; + this.address = address; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java b/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java index e68506701d..e283c5f5f6 100644 --- a/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java +++ b/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java @@ -3,7 +3,7 @@ package org.baeldung.web.controller; import com.fasterxml.jackson.databind.ObjectMapper; import org.baeldung.config.WebConfig; import org.baeldung.web.dto.HeavyResource; -import org.baeldung.web.dto.HeavyResourceAddressPartialUpdate; +import org.baeldung.web.dto.HeavyResourceAddressOnly; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -16,6 +16,8 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import java.util.HashMap; + import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -47,10 +49,21 @@ public class HeavyResourceControllerTest { } @Test - public void givenNewAddressOfResource_whenExecutePutRequest_thenUpdateResourcePartially() throws Exception { + public void givenNewAddressOfResource_whenExecutePatchRequest_thenUpdateResourcePartially() throws Exception { mockMvc.perform(patch("/heavy") .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(objectMapper.writeValueAsString(new HeavyResourceAddressPartialUpdate(1, "5th avenue"))) + .content(objectMapper.writeValueAsString(new HeavyResourceAddressOnly(1, "5th avenue"))) + ).andExpect(status().isOk()); + } + + @Test + public void givenNewAddressOfResource_whenExecutePatchGeneric_thenUpdateResourcePartially() throws Exception { + HashMap updates = new HashMap<>(); + updates.put("address", "5th avenue"); + + mockMvc.perform(patch("/heavy/1") + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(updates)) ).andExpect(status().isOk()); } From 94e1fc2da631a7d49837fec8aa548f4254dfd323 Mon Sep 17 00:00:00 2001 From: pivovarit Date: Fri, 24 Mar 2017 15:52:46 +0100 Subject: [PATCH 032/102] Remove unused tests --- .../org/baeldung/boot/FooComponentTests.java | 70 ------------------- .../org/baeldung/boot/FooIntegrationTest.java | 43 ------------ .../java/org/baeldung/boot/FooJPATest.java | 34 --------- .../java/org/baeldung/boot/FooJsonTest.java | 35 ---------- 4 files changed, 182 deletions(-) delete mode 100644 spring-boot/src/test/java/org/baeldung/boot/FooComponentTests.java delete mode 100644 spring-boot/src/test/java/org/baeldung/boot/FooIntegrationTest.java delete mode 100644 spring-boot/src/test/java/org/baeldung/boot/FooJPATest.java delete mode 100644 spring-boot/src/test/java/org/baeldung/boot/FooJsonTest.java diff --git a/spring-boot/src/test/java/org/baeldung/boot/FooComponentTests.java b/spring-boot/src/test/java/org/baeldung/boot/FooComponentTests.java deleted file mode 100644 index 72ccc0bfb8..0000000000 --- a/spring-boot/src/test/java/org/baeldung/boot/FooComponentTests.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.baeldung.boot; - -import org.baeldung.boot.components.FooService; -import org.baeldung.boot.model.Foo; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.doReturn; - -@RunWith(SpringRunner.class) -@SpringBootTest( - classes = DemoApplication.class, - webEnvironment = WebEnvironment.RANDOM_PORT) -public class FooComponentTests { - - @Autowired - private TestRestTemplate testRestTemplate; - - @SpyBean - private FooService fooService; - - @Before - public void init() throws Exception { - Foo foo = new Foo(); - foo.setId(5); - foo.setName("MOCKED_FOO"); - - doReturn(foo).when(fooService).getFooWithId(anyInt()); - - // doCallRealMethod().when(fooComponent).getFooWithName(anyString()); - } - - @Test - public void givenInquiryingFooWithId_whenFooComponentIsMocked_thenAssertMockedResult() { - Map pathVariables = new HashMap<>(); - pathVariables.put("id", "1"); - ResponseEntity fooResponse = testRestTemplate.getForEntity("/{id}", Foo.class, pathVariables); - - assertNotNull(fooResponse); - assertEquals(HttpStatus.OK, fooResponse.getStatusCode()); - assertEquals(5, fooResponse.getBody().getId().longValue()); - assertEquals("MOCKED_FOO", fooResponse.getBody().getName()); - } - - @Test - public void givenInquiryingFooWithName_whenFooComponentIsMocked_thenAssertMockedResult() { - Map pathVariables = new HashMap<>(); - pathVariables.put("name", "Foo_Name"); - ResponseEntity fooResponse = testRestTemplate.getForEntity("/?name={name}", Foo.class, pathVariables); - - assertNotNull(fooResponse); - assertEquals(HttpStatus.OK, fooResponse.getStatusCode()); - assertEquals(1, fooResponse.getBody().getId().longValue()); - } -} \ No newline at end of file diff --git a/spring-boot/src/test/java/org/baeldung/boot/FooIntegrationTest.java b/spring-boot/src/test/java/org/baeldung/boot/FooIntegrationTest.java deleted file mode 100644 index 932cce26d5..0000000000 --- a/spring-boot/src/test/java/org/baeldung/boot/FooIntegrationTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.baeldung.boot; -import java.util.HashMap; -import java.util.Map; - -import org.baeldung.boot.DemoApplication; -import org.baeldung.boot.model.Foo; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes=DemoApplication.class,webEnvironment = WebEnvironment.RANDOM_PORT) -public class FooIntegrationTest { - - @Autowired - private TestRestTemplate testRestTemplate; - - - @Test - public void givenInquiryingFooWithId_whenIdIsValid_thenHttpStatusOK(){ - Map pathVariables = new HashMap(); - pathVariables.put("id", "1"); - ResponseEntity fooResponse = testRestTemplate.getForEntity("/{id}", Foo.class, pathVariables); - Assert.assertNotNull(fooResponse); - Assert.assertEquals(HttpStatus.OK,fooResponse.getStatusCode()); - } - - @Test - public void givenInquiryingFooWithName_whenNameIsValid_thenHttpStatusOK(){ - Map pathVariables = new HashMap(); - pathVariables.put("name", "Foo_Name"); - ResponseEntity fooResponse = testRestTemplate.getForEntity("/?name={name}", Foo.class, pathVariables); - Assert.assertNotNull(fooResponse); - Assert.assertEquals(HttpStatus.OK,fooResponse.getStatusCode()); - } -} \ No newline at end of file diff --git a/spring-boot/src/test/java/org/baeldung/boot/FooJPATest.java b/spring-boot/src/test/java/org/baeldung/boot/FooJPATest.java deleted file mode 100644 index c29aa64e6c..0000000000 --- a/spring-boot/src/test/java/org/baeldung/boot/FooJPATest.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.baeldung.boot; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import org.baeldung.boot.model.Foo; -import org.baeldung.boot.repository.FooRepository; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@DataJpaTest -public class FooJPATest { - - @Autowired - private TestEntityManager entityManager; - - @Autowired - private FooRepository repository; - - @Test - public void findFooByName() { - this.entityManager.persist(new Foo("Foo_Name_2")); - Foo foo = this.repository.findByName("Foo_Name_2"); - assertNotNull(foo); - assertEquals("Foo_Name_2",foo.getName()); - // Due to having Insert query for Foo with Id 1, so TestEntityManager generates new Id of 2 - assertEquals(2l,foo.getId().longValue()); - } -} \ No newline at end of file diff --git a/spring-boot/src/test/java/org/baeldung/boot/FooJsonTest.java b/spring-boot/src/test/java/org/baeldung/boot/FooJsonTest.java deleted file mode 100644 index 2789ed0a8c..0000000000 --- a/spring-boot/src/test/java/org/baeldung/boot/FooJsonTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.baeldung.boot; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.baeldung.boot.model.Foo; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.json.JsonTest; -import org.springframework.boot.test.json.JacksonTester; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@JsonTest -public class FooJsonTest { - - @Autowired - private JacksonTester json; - - - @Test - public void testSerialize() throws Exception { - Foo foo = new Foo(3, "Foo_Name_3"); - assertThat(this.json.write(foo)).isEqualToJson("expected.json"); - assertThat(this.json.write(foo)).hasJsonPathStringValue("@.name"); - assertThat(this.json.write(foo)).extractingJsonPathStringValue("@.name").isEqualTo("Foo_Name_3"); - } - - @Test - public void testDeserialize() throws Exception { - String content = "{\"id\":4,\"name\":\"Foo_Name_4\"}"; - assertThat(this.json.parseObject(content).getName()).isEqualTo("Foo_Name_4"); - assertThat(this.json.parseObject(content).getId()==4); - } -} \ No newline at end of file From ae52b822554f52224129d8da35e3338f958865d8 Mon Sep 17 00:00:00 2001 From: pivovarit Date: Fri, 24 Mar 2017 16:00:05 +0100 Subject: [PATCH 033/102] mvn test --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 120d365569..7737fd4b08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: java -install: travis_wait 60 mvn -q clean install +install: travis_wait 60 mvn -q test before_script: - echo "MAVEN_OPTS='-Xmx2048M -Xss128M -XX:MaxPermSize=2048M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:-UseGCOverheadLimit'" > ~/.mavenrc From cd0873751c7663c93b9f86199ba9af4e09e7d61d Mon Sep 17 00:00:00 2001 From: pivovarit Date: Fri, 24 Mar 2017 16:03:20 +0100 Subject: [PATCH 034/102] add testing profile --- spring-security-mvc-login/pom.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spring-security-mvc-login/pom.xml b/spring-security-mvc-login/pom.xml index 3809dc9f26..f208c74dc4 100644 --- a/spring-security-mvc-login/pom.xml +++ b/spring-security-mvc-login/pom.xml @@ -200,11 +200,9 @@ ${maven-surefire-plugin.version} - + **/*IntegrationTest.java + **/*LiveTest.java - - -
From d0b0debd062018d8fa6106f063a9b89898460d5b Mon Sep 17 00:00:00 2001 From: pivovarit Date: Fri, 24 Mar 2017 16:17:34 +0100 Subject: [PATCH 035/102] Exclude integration tests --- apache-poi/temp.xlsx | Bin 3492 -> 3492 bytes core-java/pom.xml | 1 - ...CountdownLatchExampleIntegrationTest.java} | 2 +- ...va => JavaProcessUnitIntegrationTest.java} | 2 +- ...dPoolInParallelStreamIntegrationTest.java} | 76 +++++++++--------- ...XmlApplicationContextIntegrationTest.java} | 14 ++-- spring-data-rest/pom.xml | 13 ++- ...ringDataRelationshipsIntegrationTest.java} | 2 +- ...ringDataRestValidatorIntegrationTest.java} | 19 +++-- spring-security-cache-control/pom.xml | 16 ++++ spring-security-mvc-login/pom.xml | 2 - xmlunit2/pom.xml | 12 ++- 12 files changed, 96 insertions(+), 63 deletions(-) rename core-java/src/test/java/com/baeldung/concurrent/countdownlatch/{CountdownLatchExampleTest.java => CountdownLatchExampleIntegrationTest.java} (98%) rename core-java/src/test/java/org/baeldung/java/shell/{JavaProcessUnitTest.java => JavaProcessUnitIntegrationTest.java} (98%) rename core-java/src/test/java/org/baeldung/java/streams/{ThreadPoolInParallelStreamTest.java => ThreadPoolInParallelStreamIntegrationTest.java} (93%) rename spring-core/src/test/java/com/baeldung/applicationcontext/{ClasspathXmlApplicationContextTest.java => ClasspathXmlApplicationContextIntegrationTest.java} (97%) rename spring-data-rest/src/test/java/com/baeldung/relationships/{SpringDataRelationshipsTest.java => SpringDataRelationshipsIntegrationTest.java} (98%) rename spring-data-rest/src/test/java/com/baeldung/validator/{SpringDataRestValidatorTest.java => SpringDataRestValidatorIntegrationTest.java} (98%) diff --git a/apache-poi/temp.xlsx b/apache-poi/temp.xlsx index cbea3a410d193a848a5321af8e311bcf61f7160f..12a9b2656c24c1605df94f1a00c1f792a580f154 100644 GIT binary patch delta 460 zcmZ1?y+oQfz?+#xgn@&DgW*79#YSEkMrI(rS%DmQx=gyeR8`p2OU13g<4GuJ#h$ggec8*Dd~TU>w9kC)$`)mhp}fBCbUK_uR8*UW+g z%qL8wB7STzoc#V|a%JV!cLBiyRnsOKX`AWVCYBuLs9g79{okWMLM5)p9Py~~_+Eb9 zSbfK~$z1g>5}FO{q7cRm4rusp z=Uf5_-#BhFaQIH=wgb~QxUD&W;q3)7eX=x}_~@y5bkUalycw)P z0um05eDf~!Op-Ti2>y4!wQ|!0_xt)+Ec$LnoQc`fF=_ej*}s4O*mC{yd!~Y#pHI>b zG4d+}nr!%xn5=ex^6^KXR^5DDHT81tEOAZS{1J*)MTRAd(% zzWY_3MepqrmHJ8RCi@3?vvZiJ)SIe_8q1u}iIG> org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} **/*IntegrationTest.java diff --git a/core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleTest.java b/core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleIntegrationTest.java similarity index 98% rename from core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleTest.java rename to core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleIntegrationTest.java index 7bb2d4bb70..fc343e4cee 100644 --- a/core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleTest.java +++ b/core-java/src/test/java/com/baeldung/concurrent/countdownlatch/CountdownLatchExampleIntegrationTest.java @@ -12,7 +12,7 @@ import java.util.stream.Stream; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; -public class CountdownLatchExampleTest { +public class CountdownLatchExampleIntegrationTest { @Test public void whenParallelProcessing_thenMainThreadWillBlockUntilCompletion() throws InterruptedException { // Given diff --git a/core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitTest.java b/core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitIntegrationTest.java similarity index 98% rename from core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitTest.java rename to core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitIntegrationTest.java index 2c330c513d..2e38886271 100644 --- a/core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitTest.java +++ b/core-java/src/test/java/org/baeldung/java/shell/JavaProcessUnitIntegrationTest.java @@ -7,7 +7,7 @@ import java.io.*; import java.util.concurrent.Executors; import java.util.function.Consumer; -public class JavaProcessUnitTest { +public class JavaProcessUnitIntegrationTest { private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().startsWith("windows"); private static class StreamGobbler implements Runnable { diff --git a/core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamTest.java b/core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamIntegrationTest.java similarity index 93% rename from core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamTest.java rename to core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamIntegrationTest.java index c2eb1cff5d..42e85fc586 100644 --- a/core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamTest.java +++ b/core-java/src/test/java/org/baeldung/java/streams/ThreadPoolInParallelStreamIntegrationTest.java @@ -1,38 +1,38 @@ -package org.baeldung.java.streams; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinPool; -import java.util.stream.Collectors; -import java.util.stream.LongStream; -import java.util.stream.Stream; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class ThreadPoolInParallelStreamTest { - - @Test - public void giveRangeOfLongs_whenSummedInParallel_shouldBeEqualToExpectedTotal() throws InterruptedException, ExecutionException { - long firstNum = 1; - long lastNum = 1_000_000; - - List aList = LongStream.rangeClosed(firstNum, lastNum).boxed().collect(Collectors.toList()); - - ForkJoinPool customThreadPool = new ForkJoinPool(4); - long actualTotal = customThreadPool.submit(() -> aList.parallelStream().reduce(0L, Long::sum)).get(); - - assertEquals((lastNum + firstNum) * lastNum / 2, actualTotal); - } - - @Test - public void givenList_whenCallingParallelStream_shouldBeParallelStream() { - List aList = new ArrayList<>(); - Stream parallelStream = aList.parallelStream(); - - assertTrue(parallelStream.isParallel()); - } -} +package org.baeldung.java.streams; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.stream.Collectors; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ThreadPoolInParallelStreamIntegrationTest { + + @Test + public void giveRangeOfLongs_whenSummedInParallel_shouldBeEqualToExpectedTotal() throws InterruptedException, ExecutionException { + long firstNum = 1; + long lastNum = 1_000_000; + + List aList = LongStream.rangeClosed(firstNum, lastNum).boxed().collect(Collectors.toList()); + + ForkJoinPool customThreadPool = new ForkJoinPool(4); + long actualTotal = customThreadPool.submit(() -> aList.parallelStream().reduce(0L, Long::sum)).get(); + + assertEquals((lastNum + firstNum) * lastNum / 2, actualTotal); + } + + @Test + public void givenList_whenCallingParallelStream_shouldBeParallelStream() { + List aList = new ArrayList<>(); + Stream parallelStream = aList.parallelStream(); + + assertTrue(parallelStream.isParallel()); + } +} diff --git a/spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextTest.java b/spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextIntegrationTest.java similarity index 97% rename from spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextTest.java rename to spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextIntegrationTest.java index 8ee280a2e8..d49f63aea6 100644 --- a/spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextTest.java +++ b/spring-core/src/test/java/com/baeldung/applicationcontext/ClasspathXmlApplicationContextIntegrationTest.java @@ -1,18 +1,18 @@ package com.baeldung.applicationcontext; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; - -import java.util.List; -import java.util.Locale; - import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.MessageSource; import org.springframework.context.support.ClassPathXmlApplicationContext; -public class ClasspathXmlApplicationContextTest { +import java.util.List; +import java.util.Locale; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +public class ClasspathXmlApplicationContextIntegrationTest { @Test public void testBasicUsage() { ApplicationContext context = new ClassPathXmlApplicationContext("classpathxmlapplicationcontext-example.xml"); diff --git a/spring-data-rest/pom.xml b/spring-data-rest/pom.xml index 1845d60e94..1e1ec02e96 100644 --- a/spring-data-rest/pom.xml +++ b/spring-data-rest/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.baeldung - intro-spring-data-rest + spring-data-rest 1.0 jar @@ -56,6 +56,17 @@ org.springframework.boot spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + +
diff --git a/spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsTest.java b/spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsIntegrationTest.java similarity index 98% rename from spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsTest.java rename to spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsIntegrationTest.java index 43b5dd7da6..e3fe60d487 100644 --- a/spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsTest.java +++ b/spring-data-rest/src/test/java/com/baeldung/relationships/SpringDataRelationshipsIntegrationTest.java @@ -24,7 +24,7 @@ import static org.junit.Assert.assertEquals; @RunWith(SpringRunner.class) @SpringBootTest(classes = SpringDataRestApplication.class, webEnvironment = WebEnvironment.DEFINED_PORT) -public class SpringDataRelationshipsTest { +public class SpringDataRelationshipsIntegrationTest { @Autowired private TestRestTemplate template; diff --git a/spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorTest.java b/spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorIntegrationTest.java similarity index 98% rename from spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorTest.java rename to spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorIntegrationTest.java index 300fc081d3..bc321bc686 100644 --- a/spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorTest.java +++ b/spring-data-rest/src/test/java/com/baeldung/validator/SpringDataRestValidatorIntegrationTest.java @@ -1,11 +1,8 @@ package com.baeldung.validator; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; - +import com.baeldung.SpringDataRestApplication; +import com.baeldung.models.WebsiteUser; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -17,14 +14,16 @@ import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.context.WebApplicationContext; -import com.baeldung.SpringDataRestApplication; -import com.baeldung.models.WebsiteUser; -import com.fasterxml.jackson.databind.ObjectMapper; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = SpringDataRestApplication.class) @WebAppConfiguration -public class SpringDataRestValidatorTest { +public class SpringDataRestValidatorIntegrationTest { public static final String URL = "http://localhost"; private MockMvc mockMvc; diff --git a/spring-security-cache-control/pom.xml b/spring-security-cache-control/pom.xml index c30b0cd1aa..f25e85012e 100644 --- a/spring-security-cache-control/pom.xml +++ b/spring-security-cache-control/pom.xml @@ -85,4 +85,20 @@ 2.9.0 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + \ No newline at end of file diff --git a/spring-security-mvc-login/pom.xml b/spring-security-mvc-login/pom.xml index f208c74dc4..b3431da7dc 100644 --- a/spring-security-mvc-login/pom.xml +++ b/spring-security-mvc-login/pom.xml @@ -197,7 +197,6 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} **/*IntegrationTest.java @@ -266,7 +265,6 @@ 3.6.0 2.6 - 2.19.1 2.7 1.6.1 diff --git a/xmlunit2/pom.xml b/xmlunit2/pom.xml index d4364292d6..3b659c49c1 100644 --- a/xmlunit2/pom.xml +++ b/xmlunit2/pom.xml @@ -17,6 +17,16 @@ 1.8 + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + + @@ -55,6 +65,6 @@ 3.6.0 - + From f673acbb47748a7f7aecfd89ebe000d8a20649bd Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Fri, 24 Mar 2017 16:44:28 +0100 Subject: [PATCH 036/102] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7737fd4b08..bcff16f5f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,4 +17,5 @@ cache: directories: - .autoconf - $HOME/.m2 + From 319dd2653a073f310db05b6121696ebfa7049968 Mon Sep 17 00:00:00 2001 From: Danil Kornishev Date: Fri, 24 Mar 2017 11:55:27 -0400 Subject: [PATCH 037/102] Spring State Machine (#1424) * Neo4j cleanup * Neo4j cleanup * Neo4j cleanup x2 * State Machine Init * cleanup * White background, Java Util Logging * Change to Logging * Static import of asserts. rename test methods --- pom.xml | 5 +- spring-state-machine/bpmn/forkjoin.bpmn | 116 ++++++++++++++++++ spring-state-machine/bpmn/img/forkjoin.png | Bin 0 -> 50788 bytes spring-state-machine/bpmn/img/simple.png | Bin 0 -> 22706 bytes spring-state-machine/bpmn/simple.bpmn | 76 ++++++++++++ spring-state-machine/pom.xml | 31 +++++ .../ApplicationReviewEvents.java | 5 + .../ApplicationReviewStates.java | 5 + .../ForkJoinStateMachineConfiguration.java | 74 +++++++++++ ...HierarchicalStateMachineConfiguration.java | 47 +++++++ .../JunctionStateMachineConfiguration.java | 60 +++++++++ .../SimpleEnumStateMachineConfiguration.java | 53 ++++++++ .../SimpleStateMachineConfiguration.java | 105 ++++++++++++++++ .../config/StateMachineListener.java | 16 +++ .../ForkJoinStateMachineTest.java | 45 +++++++ .../HierarchicalStateMachineTest.java | 37 ++++++ .../JunctionStateMachineTest.java | 24 ++++ .../statemachine/StateEnumMachineTest.java | 33 +++++ .../statemachine/StateMachineBuilderTest.java | 35 ++++++ .../spring/statemachine/StateMachineTest.java | 47 +++++++ 20 files changed, 813 insertions(+), 1 deletion(-) create mode 100644 spring-state-machine/bpmn/forkjoin.bpmn create mode 100644 spring-state-machine/bpmn/img/forkjoin.png create mode 100644 spring-state-machine/bpmn/img/simple.png create mode 100644 spring-state-machine/bpmn/simple.bpmn create mode 100644 spring-state-machine/pom.xml create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java diff --git a/pom.xml b/pom.xml index 15e0e3322e..7c1cacbd33 100644 --- a/pom.xml +++ b/pom.xml @@ -188,6 +188,7 @@ spring-sleuth spring-social-login spring-spel + spring-state-machine spring-thymeleaf spring-userservice spring-zuul @@ -210,7 +211,9 @@ rabbitmq vertx - + + + diff --git a/spring-state-machine/bpmn/forkjoin.bpmn b/spring-state-machine/bpmn/forkjoin.bpmn new file mode 100644 index 0000000000..0cb060f74b --- /dev/null +++ b/spring-state-machine/bpmn/forkjoin.bpmn @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-state-machine/bpmn/img/forkjoin.png b/spring-state-machine/bpmn/img/forkjoin.png new file mode 100644 index 0000000000000000000000000000000000000000..642ab6949d18b49012529d7c5b9b14f909c9d701 GIT binary patch literal 50788 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�gf@SIUg-QO`umQ3S)QlWiV3@kS`RLu_ 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 D__yV@ literal 0 HcmV?d00001 diff --git a/spring-state-machine/bpmn/img/simple.png b/spring-state-machine/bpmn/img/simple.png new file mode 100644 index 0000000000000000000000000000000000000000..7a79bf1d895a401b2b59d4b093e770f3546bc3d3 GIT binary patch literal 22706 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 AC;$Ke literal 0 HcmV?d00001 diff --git a/spring-state-machine/bpmn/simple.bpmn b/spring-state-machine/bpmn/simple.bpmn new file mode 100644 index 0000000000..8ed463e9f9 --- /dev/null +++ b/spring-state-machine/bpmn/simple.bpmn @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-state-machine/pom.xml b/spring-state-machine/pom.xml new file mode 100644 index 0000000000..5393626083 --- /dev/null +++ b/spring-state-machine/pom.xml @@ -0,0 +1,31 @@ + + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + 4.0.0 + + baeldung-spring-state-machine + + 1.8 + 1.8 + + + + + org.springframework.statemachine + spring-statemachine-core + 1.2.3.RELEASE + + + junit + junit + 4.11 + test + + + \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java new file mode 100644 index 0000000000..971fc5dde7 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java @@ -0,0 +1,5 @@ +package com.baeldung.spring.stateMachine.applicationReview; + +public enum ApplicationReviewEvents { + APPROVE, REJECT +} diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java new file mode 100644 index 0000000000..1df2db1f86 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java @@ -0,0 +1,5 @@ +package com.baeldung.spring.stateMachine.applicationReview; + +public enum ApplicationReviewStates { + PEER_REVIEW, PRINCIPAL_REVIEW, APPROVED, REJECTED +} diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java new file mode 100644 index 0000000000..c55104a627 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java @@ -0,0 +1,74 @@ +package com.baeldung.spring.stateMachine.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +@Configuration +@EnableStateMachine +public class ForkJoinStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .fork("SFork") + .join("SJoin") + .end("SF") + .and() + .withStates() + .parent("SFork") + .initial("Sub1-1") + .end("Sub1-2") + .and() + .withStates() + .parent("SFork") + .initial("Sub2-1") + .end("Sub2-2"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("SFork").event("E1") + .and().withExternal() + .source("Sub1-1").target("Sub1-2").event("sub1") + .and().withExternal() + .source("Sub2-1").target("Sub2-2").event("sub2") + .and() + .withFork() + .source("SFork") + .target("Sub1-1") + .target("Sub2-1") + .and() + .withJoin() + .source("Sub1-2") + .source("Sub2-2") + .target("SJoin"); + } + + @Bean + public Guard mediumGuard() { + return (ctx) -> false; + } + + @Bean + public Guard highGuard() { + return (ctx) -> false; + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java new file mode 100644 index 0000000000..708dbd3077 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.stateMachine.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; + +@Configuration +@EnableStateMachine +public class HierarchicalStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .state("SI") + .end("SF") + .and() + .withStates() + .parent("SI") + .initial("SUB1") + .state("SUB2") + .end("SUBEND"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("SF").event("end") + .and().withExternal() + .source("SUB1").target("SUB2").event("se1") + .and().withExternal() + .source("SUB2").target("SUBEND").event("s-end"); + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java new file mode 100644 index 0000000000..e1bae10fb7 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java @@ -0,0 +1,60 @@ +package com.baeldung.spring.stateMachine.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +@Configuration +@EnableStateMachine +public class JunctionStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .junction("SJ") + .state("high") + .state("medium") + .state("low") + .end("SF"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("SJ").event("E1") + .and() + .withJunction() + .source("SJ") + .first("high", highGuard()) + .then("medium", mediumGuard()) + .last("low") + .and().withExternal() + .source("low").target("SF").event("end"); + } + + @Bean + public Guard mediumGuard() { + return (ctx) -> false; + } + + @Bean + public Guard highGuard() { + return (ctx) -> false; + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java new file mode 100644 index 0000000000..4e11851644 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java @@ -0,0 +1,53 @@ +package com.baeldung.spring.stateMachine.config; + +import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewEvents; +import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewStates; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +import java.util.Arrays; +import java.util.HashSet; + +@Configuration +@EnableStateMachine +public class SimpleEnumStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial(ApplicationReviewStates.PEER_REVIEW) + .state(ApplicationReviewStates.PRINCIPAL_REVIEW) + .end(ApplicationReviewStates.APPROVED) + .end(ApplicationReviewStates.REJECTED); + + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source(ApplicationReviewStates.PEER_REVIEW).target(ApplicationReviewStates.PRINCIPAL_REVIEW).event(ApplicationReviewEvents.APPROVE) + .and().withExternal() + .source(ApplicationReviewStates.PRINCIPAL_REVIEW).target(ApplicationReviewStates.APPROVED).event(ApplicationReviewEvents.APPROVE) + .and().withExternal() + .source(ApplicationReviewStates.PEER_REVIEW).target(ApplicationReviewStates.REJECTED).event(ApplicationReviewEvents.REJECT) + .and().withExternal() + .source(ApplicationReviewStates.PRINCIPAL_REVIEW).target(ApplicationReviewStates.REJECTED).event(ApplicationReviewEvents.REJECT); + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java new file mode 100644 index 0000000000..fe4e0f82ce --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java @@ -0,0 +1,105 @@ +package com.baeldung.spring.stateMachine.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.logging.Logger; + +@Configuration +@EnableStateMachine +public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapter { + + public static final Logger LOGGER = Logger.getLogger(SimpleStateMachineConfiguration.class.getName()); + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .end("SF") + .states(new HashSet<>(Arrays.asList("S1", "S2"))) + .state("S4", executeAction(), errorAction()) + .stateEntry("S3", entryAction()) + .stateDo("S3", executeAction()) + .stateExit("S3", exitAction()); + + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("S1").event("E1").action(initAction()) + .and().withExternal() + .source("S1").target("S2").event("E2") + .and().withExternal() + .source("SI").target("S3").event("E3") + .and().withExternal() + .source("S3").target("S4").event("E4").guard(simpleGuard()) + .and().withExternal() + .source("S2").target("SF").event("end"); + } + + @Bean + public Guard simpleGuard() { + return (ctx) -> { + int approvalCount = (int) ctx.getExtendedState().getVariables().getOrDefault("approvalCount", 0); + return approvalCount > 0; + }; + } + + @Bean + public Action entryAction() { + return (ctx) -> { + LOGGER.info("Entry " + ctx.getTarget().getId()); + }; + } + + @Bean + public Action executeAction() { + return (ctx) -> { + LOGGER.info("Do " + ctx.getTarget().getId()); + int approvals = (int) ctx.getExtendedState().getVariables().getOrDefault("approvalCount", 0); + approvals++; + ctx.getExtendedState().getVariables().put("approvalCount", approvals); + }; + } + + @Bean + public Action exitAction() { + return (ctx) -> { + LOGGER.info("Exit " + ctx.getSource().getId() + " -> " + ctx.getTarget().getId()); + }; + } + + @Bean + public Action errorAction() { + return (ctx) -> { + LOGGER.info("Error " + ctx.getSource().getId() + ctx.getException()); + }; + } + + @Bean + public Action initAction() { + return (ctx) -> { + LOGGER.info(ctx.getTarget().getId()); + }; + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java new file mode 100644 index 0000000000..bb7859c683 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.stateMachine.config; + +import org.springframework.statemachine.listener.StateMachineListenerAdapter; +import org.springframework.statemachine.state.State; + +import java.util.logging.Logger; + +public class StateMachineListener extends StateMachineListenerAdapter { + + public static final Logger LOGGER = Logger.getLogger(StateMachineListener.class.getName()); + + @Override + public void stateChanged(State from, State to) { + LOGGER.info(String.format("Transitioned from %s to %s%n", from == null ? "none" : from.getId(), to.getId())); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java new file mode 100644 index 0000000000..416da5f0fe --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java @@ -0,0 +1,45 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.config.ForkJoinStateMachineConfiguration; + +public class ForkJoinStateMachineTest { + + @Test + public void whenForkStateEntered_thenMultipleSubStatesEntered() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ForkJoinStateMachineConfiguration.class); + StateMachine stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + + boolean success = stateMachine.sendEvent("E1"); + + assertTrue(success); + + assertTrue(Arrays.asList("SFork", "Sub1-1", "Sub2-1").containsAll(stateMachine.getState().getIds())); + } + + @Test + public void whenAllConfiguredJoinEntryStatesAreEntered_thenTransitionToJoinState() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ForkJoinStateMachineConfiguration.class); + StateMachine stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + + boolean success = stateMachine.sendEvent("E1"); + + assertTrue(success); + + assertTrue(Arrays.asList("SFork", "Sub1-1", "Sub2-1").containsAll(stateMachine.getState().getIds())); + + assertTrue(stateMachine.sendEvent("sub1")); + assertTrue(stateMachine.sendEvent("sub2")); + assertEquals("SJoin", stateMachine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java new file mode 100644 index 0000000000..3557a63211 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java @@ -0,0 +1,37 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.config.HierarchicalStateMachineConfiguration; + +public class HierarchicalStateMachineTest { + + @Test + public void whenTransitionToSubMachine_thenSubStateIsEntered() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(HierarchicalStateMachineConfiguration.class); + StateMachine stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + + + assertEquals(Arrays.asList("SI", "SUB1"), stateMachine.getState().getIds()); + + stateMachine.sendEvent("se1"); + + assertEquals(Arrays.asList("SI", "SUB2"), stateMachine.getState().getIds()); + + stateMachine.sendEvent("s-end"); + + assertEquals(Arrays.asList("SI", "SUBEND"), stateMachine.getState().getIds()); + + stateMachine.sendEvent("end"); + + assertEquals(1, stateMachine.getState().getIds().size()); + assertEquals("SF", stateMachine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java new file mode 100644 index 0000000000..d0c1225c9b --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java @@ -0,0 +1,24 @@ +package com.baeldung.spring.stateMachine; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.config.JunctionStateMachineConfiguration; + +public class JunctionStateMachineTest { + + @Test + public void whenTransitioningToJunction_thenArriveAtSubJunctionNode() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JunctionStateMachineConfiguration.class); + StateMachine stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + + stateMachine.sendEvent("E1"); + Assert.assertEquals("low", stateMachine.getState().getId()); + + stateMachine.sendEvent("end"); + Assert.assertEquals("SF", stateMachine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java new file mode 100644 index 0000000000..1fd7bd85f0 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java @@ -0,0 +1,33 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewEvents; +import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewStates; +import com.baeldung.spring.stateMachine.config.SimpleEnumStateMachineConfiguration; + +public class StateEnumMachineTest { + + private StateMachine stateMachine; + + @Before + public void setUp() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SimpleEnumStateMachineConfiguration.class); + stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + } + + @Test + public void whenStateMachineConfiguredWithEnums_thenStateMachineAcceptsEnumEvents() { + assertTrue(stateMachine.sendEvent(ApplicationReviewEvents.APPROVE)); + assertEquals(ApplicationReviewStates.PRINCIPAL_REVIEW, stateMachine.getState().getId()); + assertTrue(stateMachine.sendEvent(ApplicationReviewEvents.REJECT)); + assertEquals(ApplicationReviewStates.REJECTED, stateMachine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java new file mode 100644 index 0000000000..cdd1e951e0 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java @@ -0,0 +1,35 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.springframework.statemachine.StateMachine; +import org.springframework.statemachine.config.StateMachineBuilder; + +public class StateMachineBuilderTest { + + @Test + public void whenUseStateMachineBuilder_thenBuildSuccessAndMachineWorks() throws Exception { + StateMachineBuilder.Builder builder = StateMachineBuilder.builder(); + builder.configureStates().withStates() + .initial("SI") + .state("S1") + .end("SF"); + + builder.configureTransitions() + .withExternal() + .source("SI").target("S1").event("E1") + .and().withExternal() + .source("S1").target("SF").event("E2"); + + StateMachine machine = builder.build(); + + machine.start(); + + machine.sendEvent("E1"); + assertEquals("S1", machine.getState().getId()); + + machine.sendEvent("E2"); + assertEquals("SF", machine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java new file mode 100644 index 0000000000..1b442bf994 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.config.SimpleStateMachineConfiguration; + +public class StateMachineTest { + + private StateMachine stateMachine; + + @Before + public void setUp() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SimpleStateMachineConfiguration.class); + stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + } + + @Test + public void whenSimpleStringStateMachineEvents_thenEndState() { + assertEquals("SI", stateMachine.getState().getId()); + + stateMachine.sendEvent("E1"); + assertEquals("S1", stateMachine.getState().getId()); + + stateMachine.sendEvent("E2"); + assertEquals("S2", stateMachine.getState().getId()); + + stateMachine.sendEvent("end"); + assertEquals("SF", stateMachine.getState().getId()); + + } + + @Test + public void whenSimpleStringMachineActionState_thenActionExecuted() { + stateMachine.sendEvent("E3"); + assertEquals("S3", stateMachine.getState().getId()); + + stateMachine.sendEvent("E4"); + assertEquals("S4", stateMachine.getState().getId()); + assertEquals(2, stateMachine.getExtendedState().getVariables().get("approvalCount")); + } +} From 3140ea166d05c59b605eb3ac0b5d55a116344c6e Mon Sep 17 00:00:00 2001 From: bs-santosh Date: Fri, 24 Mar 2017 21:49:32 +0530 Subject: [PATCH 038/102] Bs santosh spring mybatis (#1479) * Spring and MyBatis integration maven project Complete article is available in http://inprogress.baeldung.com/wp-admin/post.php * Spring-MyBatis integration example This code demonstrates how to use MyBatis in Sring environment. Full details could be found in article http://inprogress.baeldung.com/?p=31706&preview=true --- spring-mybatis/pom.xml | 62 +++++++++ .../application/SpringMyBatisApplication.java | 48 +++++++ .../mybatis/controller/StudentController.java | 60 ++++++++ .../spring/mybatis/mappers/StudentMapper.java | 19 +++ .../spring/mybatis/model/Student.java | 55 ++++++++ .../spring/mybatis/model/StudentLogin.java | 18 +++ .../mybatis/service/StudentService.java | 9 ++ .../mybatis/service/StudentServiceImpl.java | 40 ++++++ .../src/main/resources/mybatis-spring.xml | 54 ++++++++ .../webapp/WEB-INF/conf/mybatis-spring.xml | 51 +++++++ .../src/main/webapp/WEB-INF/jsp/failure.jsp | 36 +++++ .../src/main/webapp/WEB-INF/jsp/login.jsp | 81 +++++++++++ .../src/main/webapp/WEB-INF/jsp/signup.jsp | 130 ++++++++++++++++++ .../src/main/webapp/WEB-INF/jsp/success.jsp | 35 +++++ .../lib/mysql-connector-java-5.1.40-bin.jar | Bin 0 -> 990927 bytes .../src/main/webapp/WEB-INF/web.xml | 21 +++ spring-mybatis/src/main/webapp/index.jsp | 34 +++++ 17 files changed, 753 insertions(+) create mode 100644 spring-mybatis/pom.xml create mode 100644 spring-mybatis/src/main/java/com/baeldung/spring/mybatis/application/SpringMyBatisApplication.java create mode 100644 spring-mybatis/src/main/java/com/baeldung/spring/mybatis/controller/StudentController.java create mode 100644 spring-mybatis/src/main/java/com/baeldung/spring/mybatis/mappers/StudentMapper.java create mode 100644 spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/Student.java create mode 100644 spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/StudentLogin.java create mode 100644 spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentService.java create mode 100644 spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentServiceImpl.java create mode 100644 spring-mybatis/src/main/resources/mybatis-spring.xml create mode 100644 spring-mybatis/src/main/webapp/WEB-INF/conf/mybatis-spring.xml create mode 100644 spring-mybatis/src/main/webapp/WEB-INF/jsp/failure.jsp create mode 100644 spring-mybatis/src/main/webapp/WEB-INF/jsp/login.jsp create mode 100644 spring-mybatis/src/main/webapp/WEB-INF/jsp/signup.jsp create mode 100644 spring-mybatis/src/main/webapp/WEB-INF/jsp/success.jsp create mode 100644 spring-mybatis/src/main/webapp/WEB-INF/lib/mysql-connector-java-5.1.40-bin.jar create mode 100644 spring-mybatis/src/main/webapp/WEB-INF/web.xml create mode 100644 spring-mybatis/src/main/webapp/index.jsp diff --git a/spring-mybatis/pom.xml b/spring-mybatis/pom.xml new file mode 100644 index 0000000000..b0eaab5b3a --- /dev/null +++ b/spring-mybatis/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + com.baeldung + spring-mybatis + jar + 0.0.1-SNAPSHOT + spring-mybatis Maven Webapp + http://maven.apache.org + + + org.mybatis + mybatis + 3.1.1 + + + org.mybatis + mybatis-spring + 1.1.1 + + + org.springframework + spring-context-support + 3.1.1.RELEASE + + + org.springframework + spring-test + 3.1.1.RELEASE + test + + + mysql + mysql-connector-java + 5.1.40 + + + javax.servlet + jstl + 1.2 + + + org.springframework + spring-webmvc + 3.2.4.RELEASE + + + javax.servlet + servlet-api + 2.5 + + + junit + junit + 3.8.1 + test + + + + spring-mybatis + + diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/application/SpringMyBatisApplication.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/application/SpringMyBatisApplication.java new file mode 100644 index 0000000000..acfaff2669 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/application/SpringMyBatisApplication.java @@ -0,0 +1,48 @@ +package com.baeldung.spring.mybatis.application; + +import java.util.Date; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import com.baeldung.spring.mybatis.model.Student; +import com.baeldung.spring.mybatis.service.StudentService; + +public class SpringMyBatisApplication { + + public static void main(String[] args){ + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("mybatis-spring.xml"); + + StudentService studentService = (StudentService) ctx.getBean("studentService"); + Student student = new Student(); + student.setFirstName("Santosh"); + student.setLastName("B S"); + student.setEmailAddress("santosh.bse@gmail.com"); + student.setPassword("Test123"); + student.setDateOfBirth(new Date()); + student.setUserName("santoshbs1"); + + boolean result = studentService.insertStudent(student); + if(result){ + System.out.println("Student record saved successfully"); + } + else{ + System.out.println("Encountered an error while saving student data"); + } + + final String userName = "santosh"; + Student matchingStudent = studentService.getStudentByUserName(userName); + if(matchingStudent == null){ + System.out.println("No matching student found for User Name - " + userName); + } + else{ + System.out.println("Student Details are as follows : "); + System.out.println("First Name : " + matchingStudent.getFirstName()); + System.out.println("Last Name : " + matchingStudent.getLastName()); + System.out.println("EMail : " + matchingStudent.getEmailAddress()); + System.out.println("DOB : " + matchingStudent.getDateOfBirth()); + System.out.println("User Name : " + matchingStudent.getUserName()); + } + + } + +} diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/controller/StudentController.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/controller/StudentController.java new file mode 100644 index 0000000000..427613f23f --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/controller/StudentController.java @@ -0,0 +1,60 @@ +package com.baeldung.spring.mybatis.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.SessionAttributes; + +import com.baeldung.spring.mybatis.model.Student; +import com.baeldung.spring.mybatis.model.StudentLogin; +import com.baeldung.spring.mybatis.service.StudentService; + +@Controller +@SessionAttributes("student") +public class StudentController { + + @Autowired + private StudentService studentService; + + @RequestMapping(value = "/signup", method = RequestMethod.GET) + public String signup(Model model) { + Student student = new Student(); + model.addAttribute("student", student); + return "signup"; + } + + @RequestMapping(value = "/signup", method = RequestMethod.POST) + public String signup(@Validated @ModelAttribute("student") Student student, BindingResult result, ModelMap model) { + if (studentService.getStudentByUserName(student.getUserName())) { + model.addAttribute("message", "User Name exists. Try another user name"); + return "signup"; + } else { + studentService.insertStudent(student); + model.addAttribute("message", "Saved student details"); + return "redirect:login.html"; + } + } + + @RequestMapping(value = "/login", method = RequestMethod.GET) + public String login(Model model) { + StudentLogin studentLogin = new StudentLogin(); + model.addAttribute("studentLogin", studentLogin); + return "login"; + } + + @RequestMapping(value = "/login", method = RequestMethod.POST) + public String login(@ModelAttribute("studentLogin") StudentLogin studentLogin, BindingResult result, ModelMap model) { + boolean found = studentService.getStudentByLogin(studentLogin.getUserName(), studentLogin.getPassword()); + if (found) { + return "success"; + } else { + return "failure"; + } + } +} \ No newline at end of file diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/mappers/StudentMapper.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/mappers/StudentMapper.java new file mode 100644 index 0000000000..cf3584f7b1 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/mappers/StudentMapper.java @@ -0,0 +1,19 @@ +package com.baeldung.spring.mybatis.mappers; + +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Options; +import org.apache.ibatis.annotations.Select; + +import com.baeldung.spring.mybatis.model.Student; + +public interface StudentMapper { + @Insert("INSERT INTO student(userName, password, firstName,lastName, dateOfBirth, emailAddress) VALUES" + + "(#{userName},#{password}, #{firstName}, #{lastName}, #{dateOfBirth}, #{emailAddress})") + @Options(useGeneratedKeys = true, keyProperty = "id", flushCache = true, keyColumn = "id") + public void insertStudent(Student student); + + @Select("SELECT USERNAME as userName, PASSWORD as password, FIRSTNAME as firstName, LASTNAME as lastName, " + + "DATEOFBIRTH as dateOfBirth, EMAILADDRESS as emailAddress " + "FROM student WHERE userName = #{userName}") + public Student getStudentByUserName(String userName); + +} \ No newline at end of file diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/Student.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/Student.java new file mode 100644 index 0000000000..f33dd44f72 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/Student.java @@ -0,0 +1,55 @@ +package com.baeldung.spring.mybatis.model; + +import java.util.Date; + +public class Student { + private Long id; + private String userName; + private String firstName; + private String lastName; + private String password; + private String emailAddress; + private Date dateOfBirth; + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public String getUserName() { + return userName; + } + public void setUserName(String userName) { + this.userName = userName; + } + public String getFirstName() { + return firstName; + } + public void setFirstName(String firstName) { + this.firstName = firstName; + } + public String getLastName() { + return lastName; + } + public void setLastName(String lastName) { + this.lastName = lastName; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } + public String getEmailAddress() { + return emailAddress; + } + public void setEmailAddress(String emailAddress) { + this.emailAddress = emailAddress; + } + public Date getDateOfBirth() { + return dateOfBirth; + } + public void setDateOfBirth(Date dateOfBirth) { + this.dateOfBirth = dateOfBirth; + } +} diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/StudentLogin.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/StudentLogin.java new file mode 100644 index 0000000000..867857b510 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/model/StudentLogin.java @@ -0,0 +1,18 @@ +package com.baeldung.spring.mybatis.model; + +public class StudentLogin { + private String userName; + private String password; + public String getUserName() { + return userName; + } + public void setUserName(String userName) { + this.userName = userName; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } +} diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentService.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentService.java new file mode 100644 index 0000000000..d26115beee --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentService.java @@ -0,0 +1,9 @@ +package com.baeldung.spring.mybatis.service; + +import com.baeldung.spring.mybatis.model.Student; + +public interface StudentService { + public boolean insertStudent(Student student); + public Student getStudentByLogin(String userName, String password); + public Student getStudentByUserName(String userName); +} \ No newline at end of file diff --git a/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentServiceImpl.java b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentServiceImpl.java new file mode 100644 index 0000000000..538e650482 --- /dev/null +++ b/spring-mybatis/src/main/java/com/baeldung/spring/mybatis/service/StudentServiceImpl.java @@ -0,0 +1,40 @@ +package com.baeldung.spring.mybatis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.baeldung.spring.mybatis.mappers.StudentMapper; +import com.baeldung.spring.mybatis.model.Student; + +@Service("studentService") +public class StudentServiceImpl implements StudentService { + + @Autowired + private StudentMapper studentMapper; + + @Transactional + public boolean insertStudent(Student student) { + boolean result=false; + try{ + studentMapper.insertStudent(student); + result = true; + } + catch(Exception ex){ + ex.printStackTrace(); + result = false; + } + return result; + } + + public Student getStudentByLogin(String userName, String password) { + Student student = studentMapper.getStudentByUserName(userName); + return student; + } + + public Student getStudentByUserName(String userName) { + Student student = studentMapper.getStudentByUserName(userName); + return student; + } + +} diff --git a/spring-mybatis/src/main/resources/mybatis-spring.xml b/spring-mybatis/src/main/resources/mybatis-spring.xml new file mode 100644 index 0000000000..9f5bab3247 --- /dev/null +++ b/spring-mybatis/src/main/resources/mybatis-spring.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/conf/mybatis-spring.xml b/spring-mybatis/src/main/webapp/WEB-INF/conf/mybatis-spring.xml new file mode 100644 index 0000000000..c8b686358c --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/conf/mybatis-spring.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/jsp/failure.jsp b/spring-mybatis/src/main/webapp/WEB-INF/jsp/failure.jsp new file mode 100644 index 0000000000..66f16d4e09 --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/jsp/failure.jsp @@ -0,0 +1,36 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + + +Login Failure + + + + +
+ Login     Signup +
+
+
+

Student Enrollment Login failure

+
+
+
+ + Oh snap! Something is wrong. Change a few things up + and try submitting again. +
+
+
+
+
+ + ">Try + again? + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/jsp/login.jsp b/spring-mybatis/src/main/webapp/WEB-INF/jsp/login.jsp new file mode 100644 index 0000000000..5a895bb348 --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/jsp/login.jsp @@ -0,0 +1,81 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> + + + + + + + +Student Login + + +
+ Login     Signup +
+ +
+
+
+

Welcome to Online Student Enrollment Application

+

Login to explore the complete features!

+
+
+ +
+
+ +
+
+ Student Enrollment Login Form + + + + + + + + + + + + + + + +
+
+ +      + +
+ +
+
+
+ + + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/jsp/signup.jsp b/spring-mybatis/src/main/webapp/WEB-INF/jsp/signup.jsp new file mode 100644 index 0000000000..bc628862f3 --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/jsp/signup.jsp @@ -0,0 +1,130 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + + +Student Signup + + + + + +
+ Login     Signup +
+ +
+
+
+

Welcome to Online Student Enrollment Application

+

Login to explore the complete features!

+
+
+ +
+
+ +
+ +
${message}
+
+
+ +
+ +
+ Student Enrollment Signup Form + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +     + +
+
+
+
+ + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/jsp/success.jsp b/spring-mybatis/src/main/webapp/WEB-INF/jsp/success.jsp new file mode 100644 index 0000000000..7ae37bc241 --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/jsp/success.jsp @@ -0,0 +1,35 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + +Login Success + + + + +
+ Login     Signup +
+ +
+
+

Student Enrollment Login success

+
+
+
+ + Well done! You successfully logged-into the system. + Now you can explore the complete features! +
+
+
+
+
+ ">Login + as different user? + + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/WEB-INF/lib/mysql-connector-java-5.1.40-bin.jar b/spring-mybatis/src/main/webapp/WEB-INF/lib/mysql-connector-java-5.1.40-bin.jar new file mode 100644 index 0000000000000000000000000000000000000000..60bef5cfbfbecf98b7371ba19389fe792be305e0 GIT binary patch literal 990927 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

p?9?j-;qd_@Iq|IAN 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�|x-gHa$IbyvGKSEgJtrf{vp> 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

;w|$ja z`+gE|KM6y7AI|G+nd@0;+7bh*yPp?jm_I?+=nHz{Goix{vUr!Z;4ll_+sxSN0l~o> ze)y?@LCc>tvLR7hL+xL+RbX(AAN1<-RnV&ZDKY7Cjl#Z>%Ut^cx_j9-P@ zctb1pOb?5Z)sGR@i9og)*3b!(YJ*pH;49q~pB$Fc@b{w~Ce(7oN!@kpk-a*KGJ}!t z?`{Kn-ZkR&m-C2M=MM(vot&3&6u=)L@c^mak?M*nyZMPnRpXTz@(7^5A(rctVZ~!u z(CHGExuM1j{bxb7m5=6;6g#5b1Z}U3eLb7ukv2W@?t=9b&v1)mJ3QjT_buM@D-V%m zp5i5$f5bd8&~}_xDS4=vO0JkjMvAhkLXs;gG6lvm+6gL!Qe7#-2`*ziM&<8@6x#T{ zN=27!C#AhY${oI(0)J`5I0mZ}UA|Rww43_K0;;QfX65o6z7?OGauKVpZ5j5XMx`nb zRpH+ftTXV2#f1uazA&>&jCtBetIV3G1>8sZHDzD_CnbWS?xkZ3f=9VZ_3bj>r5OtW zxw6k@W~dGiys&UJ%3(KvMELQW$(XCl8R(6YoS!K2r|VOlNFROseOPdspI{k{H8 z^B~TxWM7c-2F)!Uw=n0G7(4fGq5jR%CEJgk?BTv#=DK9*jjTz$H!<}=V-G}#a0 z@kHCA<_ppEQQ{Qz6HBYMR}$yp--)P6n-{$V)p0v8z5oNLc_+MPb|2a!F!2kyx=cEY zW|%828|U__U|eGl_3Zw}I#+ke$BO2RN%}s_*xgmhWofmUbix zkpFFynQuHu>dFQJs_*~?BK!YuCjL+NFz5^AgRA=al9j`8-@~%HvDva7)r2dzkh}#v z&@RP(Id8(Tx!EKK6KvaHn?~MA)M-goNC`rmR_>3QqD)ChP1r&Ror|kiPO1073w&Ev zNJ%~Z`0clym6JoNtyL-T{p!PfzxBMi{oHN-G{v#?@&SmhFL}CbS+$|BykX0fSTL<; zOk0W0k_l;_G%Iai9GGx2{F^_JqS6{dII=Ry99OdF=wewiajWtTYM5)0MUg>)a)25b zY@^1wUhc_L!hM2VDnrq)2Ibs5WtM@o&by?eZ0MG;=@PLI)DaiEw(%9ny-r7O5dE>@@ipkZE^F5P>75$@4&;%TOE2c+^V}yg zuwluX5&DQG+HBu!rz@+5-76*Z#(J_0Q%&4a68WA zxuq`w*u639L>$aLIHUdI%%A%y4oea{kb5&m`vUM*_tWKy8M`>T^$=^Y*Sd7=e_j-)izwR3iwh)ev`!-;X_!$X+?u8fy5ROC){D3DJ z0$&-^{s>3qRt^z17<>Ri7@}VYlTa2B z1k}GyaD|Xs@I^=^upC4Yt`J<5E}R?jk+9Ss(mx&AC=+qP_9+o^FK`f+$ZXGMkT$EE z)+{JJ2!{^7NamOB(Ozn#XA7qeF7TdYAg(IWQIvT0UkwGK%t7G})?Xu7v>=YG;X{O} z{u(NL~7wFE|vvzvA~YiWU(kUZL*GCh!h49ua1ZmN}EZ+oU1<%BGj(*ZEz%z zIq*U}QHYe#kdvn7RUDzR6A){J(zB<^g{m=YKPt z2$UGAFOY%ksdUs9$fVJAh$YiAA@UI}LnE~|8;gQ49JN|@k%sGaB-J6FS4@F-JV4dY zEz1i*1EQ+iq{0_$Y5=@y;S07k%Gm)J#e8!N)76Mn^+1DWh`$KIg^Go2gcK=T0$0F^ zf9=ucbiW?a3@Uur4Y@Ia8fk=A*VyKNag={K|NH}v?BR-to`_(G#)NMED^Pp5h@Ish zI;4oV@EzF_tM2oMx=p&rMJ6cMr4!!4ZT@}NV2(uLQGzu8kE@LS|L7SYZpn;F0Lbb z*-feI>GDxqd`x`2Lfjy3q{{;0uB!-_H-Wer#4R9h1@Q?Gw}H5w=wc6uJ3!n?!`vmr zCvEL|aW`HF8$;q#1pYLL&w#iG#J!gOgt*Vf%V#NdKV3dYm%Vh^2jT$`4}$nSh=)LY z0mK(UJPhIy5MKiEWe{Hh@l_CCqd~t8;!zNffp{Fm6Cl0;;+r781p+bh9S~1~_%4X= zf%ra%r$GDw#1BC{4dO>2ehlI#AbtwsXCR&d@joDb4&oOeehK1NAbt(vHz1w`@f?Wf zLHriP3m|?6;(tN>9>gC&{1L>9ApQj6&mdj`@fQ$(1@SV7zk&EWh<|{11;jr={0qdZ zAYKFUI*2zwya~VnGyorf4#a&1fC(fB05J;|fKB%b0e)M1ji~80DG#9%@ds4^)c`dB zwE%Sh^#D@<0s^Mm*xxj&Hk~d(q$ij`shM<{1u&cLa{vwlXaJZCFps)B9N-9mBLR*A zm`^ntiLHM?mmdmPP~n>mO#lm#7N8km5#5glSWNdN084G(d{`!6xquavTnW%3;1~hN z+P-EuPC%=G;{i?pI1%6^fRh1E0SE!K0ki{j0E7WL0agL528aNh3a|#C3!oby3J?S6 z0ayzV2S@-U0eS(}0jvku0MG}p5nvO*X#l4KoB_}ea3(+sU^Bn~z#u>xU<<%jfNcO7 zfU^L$0}KHS17rbm03!fr1B?RX0SW+P06PG70-OV|3*cOUcL1CR@J@ht0h|x;Zh-dy zTmbN1fC~XG0=O99eE^pLydU6FfXe_b2e<;@N`MalTm|q!fDZwD7~pDvYXGhV_z1vt z0J{OM2lyz!#{fPKa09@N05<{L3~&p;tpJ|@xDDWTfIR?r0Ne?17r-Y0?gsc2z^4H| z18@((y#V(Cd=}t-fX@N!1=t7h0KkI)p9gpd;0pj>1b7(W5r8iNd>P;?0AB_88o<{9 z9tC&|;BkN_0KNh6O@MC!d>i0908avZ7vOsU-v@XK;0FLd1b7Y{2bsH0KWwI6~M0negp6_&0Bb5()4-YzRuHTiV9f+;7Fe^vngiBhU^Rd> z7p!?;9S+tJU>yn8QDDsn3mJ0(SWRFp1gjaWMPMBb)?%=hfVC8?Wne7_YXw*o~Al!8#tS6TmtVtdqbx8LU&l3W3!IRy$Z7V1>cz1Zx#otHFwZbt+hE!0H04 z8>}c;F|c~TS_@VjtOQs|uzJB-2iAJ9Hh|R!)<&>4fpr>Kr-O9{Sp8s~304ZM&0r0H zH3(K3tSw+|1#25v8L-X*Ydcs&U=4$n1uF;E2v}!>H40W9tO8hLVC?{FCs^lzwF|6s z!FmT+=YjQ3u-*mM`Cz>ptoML*0a)(^>q4+D0_$S1-UrqtV7(u#OToGftjoc=0<0^+ z`T$s0f%QSKJ_Oc>!MYl(Yrwh|tdD?o9ay`;x*n{Lg7q=5J`UCmVBHATO<>&&)-7P& z3f3pUx(%$`!P*1X9bnxF)?HwI60Ezy`V?572J16m-2>LWVBH7SXTiE3tj~e97p#3? zJpk5&V0|8}hrs#*SYHI|VXz(n>q}sL8LY2>^;NLG2G-ZXdK9e3z3+wwDvy!RJ zLo%ze+Vqk=~Bk56LJ*BZ4S*1H&NOhzNsWs_c!ukP1BiX_9IUcCSs;HZe zbbeqoGg8QnYHUg(lifO$P83G*9x0?pQxaSoPv^&m3W;=~1PgZM22w+z>|i{-B|VzX z;xyZHLu13)ys&<#vHD`o^_jx9_MvntJ2s-RKzC?Ef21QEO-4E+;dp;nIJzpiT4P!S z*V>-T<_oE8VO?ryEUlvtc?tqM!kyuGJk%EHiX{68SgEm!j&N5v8Sanwc7+LKYfN;m z>5qoGWwrvFb*^cTb@g^fU7)~bookYzwyto0dnidDj_59eiq%ZnR>2bOtHa#{G_e0Q z;lBPvcr9fCvpuEZ{IpFIhtqMmp7F!c?)FR=&L`VW7*4~siQb-`SUj2N?_8s?x~{WQ zJ5q~>QrWGG)z%zOdna3k68&v`$#6nrzD;e2;6z^{8Sd_%SXj~9(-HEZ%))`!MZ)Vn zRRX7Ww6*uI=W>E zQjN`f>)IzYorDeQy+cJ3&OlY&eTlVQ{fO!Hv3Lj0sD4w~g=3}TmU*hxAYj>&CCfda z%Hdu4vxktzcjiV1k&pO5E}PX@L)SoVc(E*7eAeLRfyLdsuq{q6i_^^Hve-KeB6Dm` z<#8!+_E@fv9*hhkU!>0&NRLpJNEYdu(!>KtPV6sSrW$ryu@yN-688Z1yRPv-)0n*M zjSD8W>2T&*WM~|Qx90}2|LU$xHXR)s-kctlf`AB!cy(QBG$Un)0$PNVWDc1c@l{;4 zldl`tmLAyNnH!B{x8xF-8BQUSZq%6Vt`MCv^caT=SR8X9s1Xg)NWZbyJxyxzC~S2Gp! z5*t*QNE5ND;;PMKnV~^f>S?U4anr;pbY{{+gU-Un3YnqBq0!OQuC7eJaQuSOdfYy6 zhUOM(g3FnJ`*|@)Ias?8Hk^QWcSk4?lhUnN#=y8URriKw~0tt^830Y$kDH1Ec zEjKna7#c+BCkC)!D7O_GjkxR-M>3sKewkCU>GWW}eJF$c*qIqZNrnS616nAtL#8&>*3WB32(hW>An z1}*ULB09&J{K;I8haWX|R^z6&O>JfDkBu7-IM?w#AHOj&$z~ZhGt}6Gb3%70-?pnj z{!SG^+EWD=VA1M_-7SfY6?zDO1(|##>!d3)`FI-5&te`5&P+76DB7%9PgR_I-5?TgL7F)ocppHs7Hy5j z2pK`VAg4cLFqh7Er_RBxNo3AN`8zN)HkfW3%5BE>!Sp$WQS1#Z!Zh0YXjo&)F+I8) z%PaCS9I;>7;uZfWSq6J@L=0nj5*(?4?a9o*b_BrY^wvyPmer3=@H)d;Tv2U?v_@=8 zyV6&QG`gHM4h(4{osEs8v!!Yh)IOAx#(ruGq91)(B!G#1x6!b;!Lmx{cA`YpfvjFN zkjf9F2Gg;kS!_9_^hT}@NLJu8xkRnRFD=c2$>L#cd0PT$d!d_Q3%|^DdTX$1TJ?v5{zpQR26a z;exF#nbCY9nW6qKE4%pzG@lNMHV$mgHcTc{6x{8l)oZNv?Wnv-Rwe=xg%tS~Rnk*P z=E7=3^kvQ-OOK*Hv>@dhj$Q)sFBJp|z)=#}Shnlnrgk75GA>HD-?f#5+(uH! zd@N1A?@_8SlUv-I&5wNX67yPpW_^GEx=AE4Qjn)5tD>= zS-G|NZOiuS{=cy5f#xMSd9JcpqygsejKZjgTpAlA2PJjUe5}i&8jDTf$C?4MQwF^t zTbe^o#*!q~zAZIM+Inmhs}-cBWE1`3rj1Y#WGdQ;C|m}~bdds~eTWQ8EGUEB2u-G% zCgW-0^^}w}nKt4sT~%?yu8vUg@_iL8!V549WMoiDo&;4_F1H8^JK4QRK!$}vw?HB%L2vpU&KimlRfkVv^1 z9FPplJRKp22-TD`K%Q!7Y~>;LRN;hd5+iB!f+B~~%YHcb^#9a)_tSqx3CXlmjr+3! zP~fkV&KRx~?cB@}u5Bfmw>Ti#NDeOY?YS`uFH}0FK`5_;wOJS&k%m`&X=rpEQqBfX zX)GH6sVY@YrbmaJ*3w4r8O`j-45ha!2ez0YbA<-N$x3^0TaIpGD>`a&9FB~LH=uY* zuS}xVT{8JAWiY$cv5&Yu;F+1%R;VqtEa?Y1(pqJu8Y4<2%qn5GXOz~Q9vo+u$~H=+ z*zWr1!K5<5+|FxUtG6^B&cV&pD;LCR-|57`gN+OJ8wpqLbuprfV#Z4e;~v@6Qq56R z;O>)1ja~2`vxEN)Te%ANemFI{%QbZ>9YFDkir>jQzDd;<<+qgTI{>%7lXvP*RY>jJ zPHaYTfFfQg0O7J=>a5)8I+V5wi{6q<)>AUoQ-VrzaBRSFA%l|(+(vV#EWIU#OZEgD zBKh2qqxLBB*nw0_mZ5hZ9{9LfRJw3Ac#WHPt5nPAin5bY)!=fw4so;hvu*Y60N8!J z=tgY%wBEwz^sCOaW zEi0#*4`s{b24#lFZ_u{?&L+^4qB)k$ zoIOTVnHoXs?;I3b6a%qK?Z!W{Oh|ZliY$@kWt8%Pt^FoupG) zPN4{MFx2CH#!@V+RAo-w+3i^AJa0v3(yo0{gx5@f=iREV%q&)&;(U@LMM7=@Eqrf6%CfmQIL_ISpQ!< z(>p*MgCyA{|CNV}2b@STD(yw~;f|q7l^E znfWS1U=`z@aaD|ZbCAg^b6N$X^7MF;tcnBE)_z|xC3H-9vX+-@j`hZY$c#>;&vx`- zV_93IY#8Dl|G#W(iB9shl>*EwZc-Vw>!jQg>D1`JHq|7YA4pLonf5%fwyX5kqu5@I z#8rFWhmW<%NB}396r9@ey~-ohL$Q=g|#VGrYcNOor$p#=e@+@ z(j-7VkCCl*!DS7}+rN|^3H zM0zOm3hRCZkKY?Ypg-o$INd=azqE}M*h{)kWauFk+E7D1qj*@`tyfFUdiJDB_v4%ng=OLR-uc4#{W7#2kEsDewikG~K zAGha5$n2))lRZOY`6wDFn2hA=xHU>7N{ zX}Q8STuX=h?#H@}e8}aNMBIfFV`D_Vxd!xFrZP9oLQW+--HZ~dR*j~Hhv}i{EKk8`8gY>x zOpI-&mv%CQxs}D@j&bkPp%9sf!0~U^HG13aubNT`E=oHxTclhnIPblx`8ZB%-ZND+ zn~_9G8F_;@GfKsUG;Z9WcbV?`iIievNH(YDGBnZY#RBxYR;9CK{1&69E{5HB(##2v zw)C!CcCZ*OnLeSkH|ro&V@DK=h+?;nqAwE8Zlj;QknHSC4LI(J+e24ods^AA#V66h zQW1*h^q_YQZdo^O&+yoA+5IgqF6K=tF2nHdvb)i8$czl7NhrJK;Eai-u7%;evK|BpfB;N+6nu3bo}!1N5w``2OeI{nm)*hK4q$2DW?FPIXFQ zvhf`q@m7^eeYrd5kfUes>)fJpWLJDaB0Z>#h_XdWka@7_q8OQ9z11+Vi=J+KbCWBN zbA>Cmd2@kDl^uVj2v4MTq(^Y>w5cv~9bc@RG@V>$vM@bzQl0X$^sqAZ#*5O)qfM5i zF0Cnk?aYl9(duH{-QS*EZb(X5PZ7O9?2XHmX64GuNLGb3GI=@TjB+)?=b)W1ZaSXA zNOpf6uN_M%hLqvZVvYSsN*5jFo<2$~i`rsH-KUhPE1zUL;NGm<&zf=h5Siz7W3C}v zasUGKLbf#dI&mX=Tzji(9Nc*rt9}bZo)01x4UR&l=)6d8D6%{x?52x$twFSNM&E{Li?8()-7rpIQp*fBe?ZtzY=L zMQs}R0>AZ3-s!h~#sBQLer^56Z#`>0=eM4>e(Sehpnhgej2@`pkI`;t25@}qcf$H# zKlfX|7uFyA)*lHmFIs={TYt7*@*7__9;M%vDW3`pj4Orp7r*satiZ#(Q)5ftc1+dJ zPvIfrl<^Jeha~bXYMdZV>t(w9O;~^TTmP_LLiU_U!_F_2{MIXcjD4_s4d!y~iNG$5NnXUQxM!v~!`)~=iZgXK9Sj{&5wm^*9U|W9Mrb~tX zBfss}f26UE(yY&>h8ohNqq)(Bo!gMc8puP&X*3KtKUPIY#bKa^VJCLp&^VUeo<$G4 zLA~%Sbz8#@`rUlP7JA8P0gAoE(j`ll2)oj6SJ~BmyM|^_YuBL_x@9Y2)f<2X%ZMVmwiDWz!i6;H_G<&+=4%#!Y={&zZlmEnT&$4Ix z?K!lG6`iqoII=3*uqNE+w-2*5tkvwd8z^DN>6iWCXnQ!}x91uc`R#f3;n;(H1XjZN zYmNASH1=lwNn^g_8&FbbbA^7Kdd7Lh8x}OkKCnnz?6;5PKfwfM>dU-7k0nQrW1FJ0 zYsg>9`;8A7AI3SAex2{Pk20~vlyZ+8G{#9d6Q<^4+WGm96Cm*0jmS6l0=vm?FSMIc z%F}P@8`|UHP%_*g2WSxXBENmKjgwwN2sKtLODtLDx0hl=KFHI=e{*qt$-W+WdH_v~ zlJbNAIst+*G)YW{lF4{6-4RNL`l%4Lkc3aFn22no!j5ozq&w8r-w|0ANhS~w?d|UG ziHFz;MCT&NY*JOO zNyK{N?P2dcrbsQ^fRxgZA4v~LJK|`E8IGenwa0!u$2F943Oki;y`7z&vneWj65-yC zSih=-ise9(X{n$8g8!1J1NU?}%3*s2pQEv3T&sVvQhN>bJ6B|Psp65{vvS&-(+z}< z29yWs;gP~FWPz2$e=R)Zw~w)2L&PS+UE%g*11-BPln6I2@Y}}{%^hdA`t9SX@(IR8 zxY*ug=b~eY=}$!S{$PG(;1`$i&!Z3NrP!W$tUcV(8z)kzQ9q?=aIHf+aKcl!C=an} zwNtfdUHI*j5DD5G3bi)SPtD|Fx1q7!kQx|3C%2)HE6KOf^x0z>v>gaINGKc5WYa2Q zGuYrhgllkLUT=`W@}nCfTN*|)TelVRUUbTL7}JBz4d^v&$!w*D1MZs)4LdW1Z4L9X zxsaTN=l76ph?{i&#KP!UdLGR~&UH|}i`G!IR!TF#`EN@VaN^{#ldO(JW4%e7GKrad zLk2C^t*Oz$AzEo}3$D2^hH`)60@P28`iJF9_ncxsBo<+&jwmT1Ok{mJdI2gSTg)`FeDnIY$ zNczW2QdsHi!RRZ$z1ohTnAvn%;k4{&=PYSEZOdt+r~BT`iEvU%u5rM4 z7%hcJI}TJ|0!CvEp>`$xseg4ejp|>e$M^5R#`o_4$M^35J^imZ5XqN2@Q6sh%Wrp+ z)IOjcI_YR6KYyP8mb!@2Sv9nkML~^*PK=~;4+-nFWS~#ATZJ9>+X?G;q}Pkb1^Mly z-7D;MetW&W!Eg838-vu)ggd0vS zSHB4KOx>yNHxDz>5Ie(f_uFTpHHLd;r)bBuQ-r)!;_6OC)MzX&2U#>w1TP|t zEn0c>(xX=_X=rSSr+3IlysZr@mMlAN`SSFZmCFWFgU6*($1ZDGwjzD(lGMQFbZSLQ zdU@)YB})g=Ee#6}V)iR&_9K2HZ_p1w4nM`ue~j)2e};UJvx(0}X*&#?O*XfOBVCc`s%EOy9!oZNgjdDG;Y4$I zLr-&OS1cB9Mx&elmo91U4sB>g#nOzbqq!&2+!I?*|KWHuxd>>P;62*W9LIh;nrXnq z+IX^=22IA%3X}r|6Z>%%GZ>%>qkeyvrb;{?dF6A9LS5<1;QK{Pw%-^ZoX_ZM0I} zLlSq<+j3BdD6?rElMq8iQCgb%9evb^sz*y7f2L2gZYj7 zjnDb*_v(N18(-rm`0We%2^u>iEbW(~>Eis{q=BA)qa9w1F*i44<>_K%$jS%94XNy4 zL+PY~21f-nWR#U(*oMXrE(Kkgi)g%~jQM`POaB}0hO6?FyQiJz=LQu_U`AB0ey(w! z-@e#>pPzF~ah>~weTmpF(O>&*1w*3B)YCS+?6B-iN%xsXo55&7k72wHQLc1iAu=o@P=kIxxz^Q5eken zccJ+bZteix*2`w=A!@HytX%n{lvpPXf4vME7VTeb>TQpAs$(^8llvETy~!zcE#x?dZ-%C zwY|Hi8NGEfqEIdzWU60^ux8m~uUuO?QzOF)##}z00iy5lp z0%azO`U^>ETx}{yBtysosIE77vT|U8$)-5jew>m_%fT^&FhnUTXGCWgh0Z!jxwwEt zZ>I!QDle|T2c=Fo1=@&PuyK^QcO4mb2}BLbAZh8<^&)&#G>jNYB6GF%k`hIgzX2gK zI_K!JDzX6kJtkl^iIXmGxUZ6Y!tUPgVv|Z#=ia=If{`LaSEJ0Ob37=@fl6{qYKWe} zYfI3eT1v-!w8diNE+!+EnK zZi}pvt$j<6!^x_gr`)#;hpKUU^>kI$6^pKFSQm<;BoD>W41%RC#~gFavZX5#aoypL zNN;yrSFDZvVyvi=T-}V8l`EI8q!=pBMuGHYD-mFMy3=t)IXjj07l$LA;Es?CZ%8^L zs3dnF)UyLo4MeoJ8>tuxV+yWiQ*+1Z6V}5QEnjjh4lA$13BqCuv4MVCoUD75^5907 zi1Uv}n5CwscH|f&95?T&q~Rctz!ADsy_FO>bh4gm1{!`Ond@-Q%^eeFtS?%Qz4+*2 zcEoxmmHL+Ah&3wL;4!%<)s#bKoRB00 z@_SIACyL=~QV5d3{l+aM&nq1PO{#zbpdTUNyaR@2m-y}L(Bd=hCL4S0L0UTlbP{5A zexZR*PiT-Q6_iY$qOCKO$rn&@#15&?H2&|eN3-~lqxavNuE#!8`l&d;y8l?e6K39X zNF7VHIH6zl-WYb7{CCj1|Ct_l$ZwRALf-ASuP1-`qvT7}+8^`VAE(p}L}SY%LqqAU zWFn7l9g}a=P%QbaUT;BaHtVPljhZ`n7RNNCe1V8eZa@3(KVev5-RZdY#ty;8sacl{sq zphnth>H(Kue}!P2PMG%VuTuI5?DJ@49Z{c0%M&0cJ>9l%wr@cVG2yAa#^${Bd8>Z= zR{Il(>w_6%!gF_2XK(QyfyQQ(O5fl)lSP8fP-I6bR%p2%5{`{D)4oPVi@Um{wtdRGL&WQ!LRt$dn^(+!c~|bC^t~x#5%e9rD!?kM+>4mvkc!huT-utxMvZZjnyx1C8}AoGOkau;WmV95cKD zyGu~*4H23R`k-AQ3e_8(A=%#O3@52OQ)=)P#RORxorEU?ukExV=P5jPMTV_>Z=tJ8 znJ$(aImA}0NKqRyQY`PWUYxqQIT=>Iu^bf0g6A05MLxg23S(!02`hfBMj?n!o(|hCH$|6NS5gehdLx8 z3{27FIC=PVLl=&qk#5>26YZ-onLh?4s69FVh-qn~D{8ND(+;U)9bz<2R;IiW(V#uL z-h=OoZuD<)EoiP{ce9;bBGJ{492e;%ijJ?N*q}t1Ap;2X=qI$nQ5RNsq@$xNEUm#h zwMpZ*uhId!tkE4Kl8lCuy>UWoT{PCe9w#F`Ae@E_*>Lqt#wR5~40Tbre2QLOOj58? z(y%gCkT~D&?_GjgX6JlDvOmqmrx>bOp|XYAKq|!SVZ-l!9}76U3EE)V$hQG z?AosIhW4<7OQ2ufB_-|Jm|P!ILUgNC#;aBjT`m6Lr#Z?fIdCNimbtFoAMSj&{u zPYTF!OdRD#eoCL3J9V=-(i-$|^ph#!7VTS0lS`3@M-Lh zwn}q%DAk>my4;DL)=Z3UQiQw|-y~g3YDQ}8P#0<%sqdw3SDQgkg{7`Rij7KIwVjlD zRj2ggSAt?2F;vfq9x?=^Sd`BgsIgMpqyj^Iq;12c<=e&YxpYb0DKlZIpB+<{P>YH* zgnEJ=X%kbUT6O1xUTC*ef_j*E3N>fPqVNXN;HJ`?QgIS{;L<#N!c`?fH@P=}>MtBu ze1=Iv8)?i)JR$GIBcv~-Y%^DN#oDN*ZxuaLUlk)F=gQOo^)(w3&{m|I{BXHW3x#x~ zUGX)hBgDrf?oiG3(CY}6R8|Qk4XN)`iLXlZry#*Avm|D9!0ApV$}m ztchZ}SL#rQ8VwS1WD!L}O3)I>@yVl7?VBXDNg?~kzn|rcA&keOG*uq!A!aa97RxC^ z47E#FsE6#%9@1z+%4R8_O0RRFkS2*n7{-)TNhGgHi{Vzf52Z;T^z^oM$(I&TK8KVz zgg8=TTS)3cC81Ed5=@FuB}YY~NRPsa2a{CvM21O{N*%-#F_H*|jCNCX^ciAg^pey> z!jqfhMzu-wQM{tU6LF@nebTt#G%N{-wgmYI$WeXL5$Hn_ zOv)IPiNx1U2|+Qua7gku5}}eD;fVAC!j72c?J`6r4Sh+IoS0im2NaY2J!Fp9DxFXU zOM7pdlpej^;eN7v(GPMBe8)?bE2cngzhg?e20iMv)t)SFF!t5px&Y(bQLNXAZyW7> z?OjTWpk3IH^$;Tlnhx}6Ty#(jTCNxst@CyZ;g003Gz3ib^i;*Xq{i)x)4LL8XRjh7 zkOL}=td4bcsKA@zQ(3`4AAuB+piPxFk*`a#Vz|3acHAA7(TIfVnn%tR+IwSU6NdNIU%tNYt}+t<(t=6ci)($pivf@Ha%gnUme=)Rsm zF>rA(;Z1ar797+$0P3CPC2#l4fP-i<`73kF58-f7Ix%v5tKLFp2oaUZ)7L;&sGrsJ z=H+Puqr>W~YbT&nzeD|izdN0Fe<_H#Zj7j3jn-~UWe11S(e%#JC%LwJf67wsBWK=Y z9Mmr^CLNzp_CpQ}Cb!er{0S{XdHVT0w(Lx0hSJ`!P__CBPc&CpO}}wK$aL>aOFvU_ z4-Hw{rM6c7>Zo`+Um{&79=n62oKF{uZ~m755zoYxc|UAKpGwJ(KbiCJ1AhHWeuE}2 z_baB3?k-J-H@YXDxIbX8Z=7(9itJ(1FHYPy@8k(LHXz z8I<`(+qfChff|z!r$7YI3CrrRk>ZaM0*#UVe01mRu@wCbaNM!S4qZvwE(zHnB9G(c zQQ7h&mX6HUj6A9>J2p(Gvcyw^nR9TDJU=l)u`+Rw(2%AlGVWn9-XE3Gx8Dxs(Dt{I zq!WL*cDy`yCF*|n0q7eP>f1pEauFRxG5_E``9YFxTPoj;JCt-zCAWw*WkxY~0HxF_Q+}~-=c^vZR=N-8*oONhtihfGbcu;-FQ`v>n34Nz+Y;)dW@2PYy zLRq;n**o|p7aN5~cx&4k~sjyqR;+%HtOrVEnx`GWVnjC}D|yoxsHwTv`He4?wmB8k=Rx3WE@Bxegb z_nfEF!GYd7W%=uY|FQ%=7|20N=@j+XtQJ-cYa2_lD2z*IDsjihlD}^Sn!A7k?Azlz2ujmHgl8_=DS~GRSn# zSt@fz()6)>nm!Cwrs$gguNKS3a>ruI<?)AZ%$0bceMtPn{qE4QII=Uu{ZcT4C zGa&uFcONS2zul;1`S1$ic>07hIX&N;S&XZQ;TlFWgXzRUo$)tyKpuMK*nb}LZ)RSd z(B>ShN9WB^&(4Q<#g?3N7VnV<2(rm{DT`m?D#hwGwtO6?s%qYbTuc|10KS z*8f(_vr}=sf7kzk=~?tjFWzV9|Mb-V7s6k4;IHYgBm4~sf0NCigQ~{mHSVK&^t(NI z=kk6k{oW7noX)9|caZrhbPyG_rK79x{(a`>eqM?BD$G5@45s7#;s=;^JzF|!H#@xP zLFUugZZ@L{^cwLX(>Z1;A7Z?(Y2iL*-187KWlG$GwO1BvH+yRzb{_-di_GfNn-YC| zVFD}J=moHfFER6;`fWpbX?c%8#=4cM2d8>~S$S(9$D6}q2A^(uCjUdL;&PJ@~` zua)biR2^Ylrt0|=>?uHaKu8^8V=AAA{lsnCPah!Q< zK3jxi&0|fh75iVrj*<6PcCx&ym7T=!s?TKH_g7}=vsjfro7L-caFq>go{oL!hvRA+ z*&@6z){n$hAH|Nre5*d6tA3pYe2%m4%~(g16m_>dGm{|tayf!um8yO{c+~h|2hqogZ`p^BPG!Q)r9L*|yl5Bg zh0TQd2Uvh;K=QX7wS{%x>272iTm_BAb|v$uQIPRm`uiW&!(@6(M4W_^4zLd1gu=tXG_nw;hzQ0cN|;O$N;H{jMsn~W z@<#b0tfza>G$zrcO+wSr$SjNb5``w;APQ_08(J%_<1l1&Z8>j&*}K_Ny#<>2gRH^F zTCE^|j8z4#$C!nMdsraIA7wQ`9<=tdxx1N(zyR!Ke{T+$P6O>rY+kD!6pymupm?~( zx{0N0tS_>|5oYTPA7et>!=P#5UUozxXmbrOMicc$S(CDYEIqZY_E_SVcF59EOgWa#6$G)PU&z{iV z!+xq?zm;Fw^ko{A?NCW+1txkWx)}mjo9j9L*>F8Xff>~@HUy7o^V%_X_e3?uo z*l+l9z5=PzuQlV}8UtiIi6sDIQ}l5yx=FWQ$xTGbS*Z zK}=>$V6x9H3J9k`iKCi}^xk}s9JR9`nqEm)g9+RS`qhpONC^#K*erm6SwWb zZJo??K~r%^^ILJqoBk4qY+*r6-i{oy2RYz_u&_!(4+_plT6dvUKm z$2#=AXb$XSy(mz(=nt~(`sdl0{t!D?{{lN-|026kf0$jPe~I0#f0=z<|0?^g{&n`U z{y6)G{)F6*3t1(ap+ydevfFt?rh3_p4hP)LR`XMlvMX?RUMTVcd&Q-USNIx)X)@I! zDT7kP6}n6jS8$mkt}tYZxWbev;tCHh$TF1flX#&E|GRlqDP7-9I0ur)SJGxz)YRe@ zf)!OF(;(I56FD7p^P9)F0st_na-j|KqTFMU(E#ZVy{5 z^W4oJCi8}yudWN%brL;2z*aqiWqvI|SUDP#m6DLFngW)))*3an)lRNvA@*=$sj9qI z%WI9i){bjjH?DF0!o)agE=iS3Q93aF+eo$FVL|;#Hb?(1J6!)B%J%Q0On(aH`43o1 z{~>#q{xrJ;Dd0AA0QTxXWk1lLVZTB*=UM%i>?L$(Ue=%0H2pb^>n~_E`X98Z`it63 z{m&iqFGEOFlES85>uv3_40LOI%8_RqZzK`8*mjCn}VbE5!Z|} zx;OGoj&!^VA;bZ5&}pzxq!`BCm@@d?loofRMPb0*9?5wW_R07WXtEuNeICWC&{nBI z6K)C`a8r2`x;+coEWXG!hp}guFXzZrXfj2s@X1tC=_pLCmuuv7(yueS#g6PYP-WDV zHdBgkzQQBE4QCLIpE21E<`V=zKluQ*bpL^EX&yO1A7}EE(?<(Jd}aGkrsGsq>Y~ZV zHSGW;DptO0gL+Y%v`xJtZwKbf9!H86!TH2y)(3cPepHv!T=#8dGcf zf<|4}KGxl8lBTbVqDF5OsMvz$UKZQ!%hJ7PFI$TSBnmsLF8%;Z>}ESTY9osjK8lWH zi$CB;DOruhy?fcZ7C-7Lgxtt_d@YrvuL7!Wz|YU5>Xmy?i(*|jAE?C2hX*YJ1SYg zCRD2r)L}I%P><`!nFQ)^0rfN$f1tMRblLxuz!Yq_c|yZLAb<@s*kno|KnZMjFB`>_>zyM%v$UBRzH3*xE{ATtBzg5%t zC$uU2HfHxiXt94%JB5Er>*Sx&*7AF_t^BiEp6^3#@St`ne@MHDKdjx( zAJOjRU)G-BU(=rCU)P@Dk83aSZ)*SG-|}hv+de;k%2&&O;G4yt_8rE5;yZ%>)VG*F z<2#1`!gn(NrEeAgl`qDB?OV@(DnpdDxpnZ;|)zd#Wf>G39vPm^xQ^N0@4n+ST-ZTWUzojzFBe zLt2DOeYf!Q_&ZTdp5Xfke;4``f}M=mJD;>`$u^Yzmh;u$jj;Fd3zW##D^Md7c6UC+ zhWhrg;VAl7TC36gDDy9BCaOgzxM^YC(z-1FBFpvZbt8$r>}-l>)llC@PR0 zTaZCpE6@q5c#ypdl?dO%=C}GuSe)NlS@&*|5_7L-wFG!iEkE~0T!sIUJ#2=%;+m$Q z8RQH1vI}svf4I=n_PlwiT=SyVDq3^ZBdygF)=Ykv9Yn45UdkGGl35uv_puA_!3~{> zu5=G>O%J*ebpI3k7keGG;#ahg)`45op+)HKxI1^Dv&jsbflP zY_Tzg9cKjCsm3(cZOmpp#&p(i1lbm27Taz#u!1p{oomcvmm7z(8;v8_y~a^&pD~|( z#b{#RG8VEQ8H?DTj3w+}Mk{;WSjyfsmTRW5Qk!YCXmgC?w7JF!+Ct+*?O3BpJIOdj z3mIKnhY`{eMw_ijE_Elgy`_Y$kHH_>^b2&A9=2nE`O zk{SNQBK#suRj{wKT{1Lf5kb1<=T0|#u#;abT_(_S{C&tYIx^&tWEzg6WcVd077bR= zbjeaCJD0t#!Ut?0`x3ttdsYlG)$QXl>_gR`?{%Y)MJ`9ts@$hIvIv>7H`D}(UX7qp zq}PjhO@ky(qe!n8yO1KezR!i24&)LSA{@y3p$Gv7a;aOVrr7K<;ev|oE-!(K4X-GH zEC+OD31mB<50tvEa6ng;Kz;}G!BWFY2lSy5sLBC-xCE+pKv$PQH4f;SQunnE=-Lvf z&H;U-1gdvH*Ofq19MEnDT}>2ClcGd&Qqkf(gH1yY>1D!L$AZRsw$RwXPBi*h%-G0M z#wM0GPGc7vr?cJ08SHMOpFLun$-Zx-*mK5a_KGo}RTzUv?P+bPu?4AntG3?Qrk!PE zw09b3X;&FTZb%g=!llt0_>~Tg-oQVg+CqTRX&>SrR+Ogw zQ90`D)kTk?qk4VaM~jYX4G`s_I4x4%#{eW!%p&1^yoe^VNOiv`L;~#S6*&y$4bE5POO}jc9n9{Y>7K;ES^#mVyT`bbJ!E`}eb;!6J%u~=wDCG_^Be3% z)5l&hx!lSvq>tIFu8Z@k8@qdzU+qL_Ugg)Qt;E!|PVD?u{t@ZzPGir?O*Pp)Y>gB= z0#|T~6h^>a!P#Gj0?KkVv#K?piE)oNcJu3%15_~`N#Jl?>1$f6&{~1w9%Og=7}~mbDP>s`tdUx9eXvHw7`x=w9@Sb)TU+}`YaKNXR;y+; zw97S83Eo*36c4gb($HwC>kqQKeazQVQ+F1PBQr6FEYx}SlMt3o8GLGx3j*rw%M`NyZ5lElfu4O?5wY~E?8IhnFrWC zt@a*P*;?NetV8P^DYSM{g$i$l3NkGAvHM!s$AVzJbP&i2Q7DDF&$UiL8>CciZ~1EbTBi`p$R{eFLEf4ojjq;!TG#^`yPnMo z1|CKHqCd9bdL)qtJ)thB`@E_@H8{2Ip$FI(5OF4N@$X?q>(qdMH-ow_V!H0()~UsL zJVNuB>R3$EiqOqoXk`(a@>I6fg6P-T!HT-2y3T`h0;ax? zeN#oE1JkAV9Hi($-M2jG4g{v7w;PmBy=0M7TMr`^IqZ?v2Ca223Bakeo5&h_nAJKj zIIndsal0V6kb=%LDRlqni+Ps>!jWm<3w zMNhPCbnj`M9-LnH)Lz!Q8=cgasj7@ss5sfySNAYQX>bxUbsDl2@yAh^?Uefc)fZN4NIR`F(fyd|Jj=k*e^MI#!a8lw8>ifhCh#tHC!;Xh-RxevYg@Gu zEzj=NF3~=yUCr+Gb^EsXGN?OF(7&ZWiAwTG{Ri^?EByui_vr2YlQXVkxzwJ%3)oGJ znKeu|Ynf@*vkG$xn`Q>s9CI3LGH0$R8_ct`J?3`pGv<)C&m7hsHgnpS%@OT!^K9(}b5#42S@2bxW4?Ox z9N%nnm#@)$hc9HF=j%2v^ldON_6?Zt^KCIN^JUEU`?i}`_;#6B`aWQOz;~N@mG2?* zL%zq&t9?H*ul2oQUZ-p3^?J4WF@36elYXLktG>$IqpvmZ)Hj)T>D$at>RIz%{XFwN z{ZjLO{S)Q``aS07^)HwY>ra?p(Z6Lrqd#eWRe#3(y8bKkG5rPe8~X3f$Mrv&Pv|e1 z-_l<;pVa?pevdQr2V6J5&kge_ZkbPWzxfkhYyOl^F`wa6&7bp`<}dgh^H;pt{0(1e zKF3cspXc4?3w(q5JHE;MBi~}a$n)l(`400XzRUazKhJ!bUugb~UuOP;Ut_+)uQUI} zZ!%xycbTvAy@K&C35|bM`1qrO^X~|Q|5ynAtg!iuqJqC9{QPB6#a|QEhAC=|IuS4e zVyZD+OgCnUnZ{vawlQDKF;VSGibG@cR18qbSX<9FhC<8S73#;c-@tS#Rh)`FD1PC9Kh+88=yALSoo_1d+? zly3v8;vYwAYpQQMnn&a;PSsCjTlo!Wcg@gOvD5jDn3}1tWeI*0re^8qu||F~wwSG7 z%4YCe_&PSn_XhhWzZFZT>l%BUe*#lMU!!(2zYXgJe7m$Be!Cpw1L&Ra!PGPs*LU(e zD0ai1^*zV$l(C>Ae7EttC<3Hy^=;;##1yipZyvv!ci^1A>;wKOgiX=^$he&8JZ=~M209>B{2pvE(|Cs2ypP|Dj`3yMCVn5nrW=Q`$K=zJpz#IP z#6L^x<=xsH{C+I0=Xve_6?YwQP88q&&FpTn>6^@!wEI;NuEg`u8?UIY- z#-PdPqiymkwz|bE%(%@%r|OH*s)wan(FsdZw9o~B+FJM=WwneXsO%J1@e zYU#Scr5u+x&{kEUrm{=k$XC4TC~xq+BQKn*?Bsh#KB%v3lQ+?K{cwS@S$>5t3p7?X z$*-~%wD|PgF7alzHpPE+OF29KTj{@T)I1iGr$)3iY_D}{x_n)IS!Vt?^I)tY3&U^( zO+AoI@y8FOA?!IuJ z`$l-keKS1l9>6CoY(1g}%`m6(dWCGWCVJ}~7hq8tQGF- zSR_aGcPvsOA9XBpMLzCWu8P+L=#Q-DALV zkA>Rq@i55!0E}=?gnQkS;6e9fSmvGr#qO!F**y)m)4Vs`vtW;VHoWJa2fw)=hg0q+ zk=y+gBJPFAr0ERzbI#W6GCIh2^Hs7@(3%f*6=Aly$x^JfoK=bH^4s>RM0NQc`Yi?v zh2=fG7u1H|<#+k89t6emUK!JVF#?{I-{XDhURW%@&z%jDd&URyhxX>@YUap7Y0Lb` zmvk=HB!kw|1^F5IVz=w^nvt)#rKCvD)r=ewNrfiA7D<^VzY$5-bFRebsK;DdsPXHj zQz5_mWjZ=GK(>1$v zfE${~`*{suc1KNFDZVXNitiFqe4miwhlCVAo>7XuREqbh6dzD2_E9PJQz<^8QhZFM z_=HMvfJ*TxmEtoh#i0^XeEgSEe8Q!W*)4~*S7M> zC+>cGlum{_h!6JcR^&C|c{D31KHKW=XZA8F^<8)!94lhZZNJE?;CQ=&Qf`UvuR(Ty z0|EE95OaSA=eU1>Lif+m+WiY$?fw;RaQ_asyH9X=TTyu`(|13UKc^Bka8j2#soqYC zO6SzZ5cvyU8@JQ?=HGJAU>o{^+d+rhL5JHxhZ{?YHxHKM?|F#!jKlJmw*Ko&$5?>Y zFhOlmJ7-CyT|wkmJ|}N0HO*fGzws)jiQf^h&wn6b>tdO!;Lk!i-`+Ez^MZV_U=sIW z`ijjIPRONJluv4Y`pPK;^x@MO;)`sYnJXr*Vf*WB$7BscjP3a>g%O|PsWRIz_ryF> zUQCU9Qso!LJV{%Si%xIPqf-yI^HAts%=S;06?)mU)Vs0JN4t-aZ_h}*Y)51@wQ<@O z(A)xhKI-JQTjFEh5by!)r`5I}KIQZX`~XL(qBxBdq{e3+!h=x_0#-vHs}TsQ1T58n zm}){L)q)yo6l$yKaDkcu&DBh}O3j8MH5aZ`E5UHJ3XD~&!b0^Ncvh_jE7j_-L#+X? ztNE~BtqDie+VF!~500vJ;Fww$epkClJjFVS-AOT3vuxFmwj&Dhl|bxlJP5v@yV8VeF6d=P@djUlFLHp>uA z+B`M3jJhDEEk~hZq!ns>!A6U8WeYZ8w)EJ37Tm`u_i~A_2CB@~d#D=laWz1gZy!-? zw?5*^V@tg>I+<&nN6l#L06nF_=Q~@C_Upmf>_dL!`4rT$$6F_&nSI zh4DqW6>fvVwnjMx7t+o<4BYB)szpXXzIqQdQAa|1^VzY@9IW$THS;r>MPh#H{+@<}>VZWm86HQ;zJbdAp(Y0BO$p(%T}gr-i3jW2watwA40 zYvt24&jnARRSGD~s9A(oaBZo=GW4v1seVn`E#W!UXik!1Viz{qvp)N*0gPq)B$dnV zlV}8y#70R>E>!Z0k!{4dHTor>X_Cuy?9PmD#BhPnF!M zlG$CF!hFU>^ewj0`HB{$pwKoy0SUI#Mt%|`NIWBf2=PZ;r8Y40olS8!-($>I6T6oh z&u}&-=eZ+8xQ}obk?$_Ghner}icfTM;+x_RIXT{V8KX{Cd{2C`UF!IbI6<*^PJBcB z5$C1&e)tq8C%zp%wNyFdd*aiaC*xb0}fr^0Fv*U#p;9J{w_aIC+Y*|BxyT4j(VITSPE3p{y}3>m zLPnwEl*B!XT$&}>=Mq~d6oh9T%A^V+CQ}U&^Tb>=V{%N%FNje!pH+;qZ8hQ8(=iWI z6CR-^e9YrVO;Qtu(3L6*=B?11Q=um6v-yVi7LdIIK=a-Prgt!8d+&hS-aD!7F$}tR zzoshUd#WP7p>6Rk4DtR1_j-SJ)&>fxAQYEU(94$)ns-B`mQAfJwHZM)^ z;)^JXl6=%4$VQIueDM3~L(q3Y5@)R(&N1h}mm}+HbZ+SyYhG4X`fgfz@19rs-8SXE zn_gZWpI^FOI;E)NjPkhDFU{rJ6kIYD`;-7`OtlHqPeaR9#By8_E2R|iMF~ahT8A3g zmKxK-$|}^b7+qlNUpLdg?u~T1;bQvNCG;^^zTr|P#fyF6b-eFWRvPnHdbY-MZ0pNOo2OX4eLtMuwA(+rj*dI*-DOmCVC{> zfT1*ymtVkPNnV8t6KCYS>?bMs`TZ#2z6JrOfB~m~0VUTiU_hy414Mu-_r@F`0!n2D z5J@M{*mqlKgO^%JQ>xG=uUcNmX;iA?_Tt(ziB>?qQvsdq3J^0C z)RbuyOQbJkX*JS`JKrpe+>|I9S2F(Ev}ko8$De$psB$?2tl*y=V9gvaJ{KEc7Zs!C z|JVTY-vF||FKGUo!1Uh?+5Q2v$_GNB|8{8aUk+FLi=mVMCAi+d0&elIfjj-{{-Xg_ zQ>kSSu-j>!vcBgR0}M&!jIZ6`^S_;Ry3FsC&+nAauhh27=Xb`JUyQG$QRP3wsH#KT zxvo;rZs+l|KG?5MqV3v(wOPxeRp{bUrmSPSUg9*U$B^07_R?b1imk?6hF}e9jo8|j z*Irj9bn@%vE6ZYq=LnnRa_iL>^A-Bp%CUc0VSsgnz{bL02}?IfrK=2sVF)eR5E#a( zl-L*vP(>B+Kq%maXut=R10g63^ng}@YvIbkb53|{3t+IDQ#@0?Mjsdx=fa(cid$7^$va=v}8c9K$`du_U4yge5V!APZn(vY@q z6o&Brq0;jA7iKhX6BbB<|Gsho{eAI#q4Rucv$v6B_Rg`*-YQz)|2;n@sEww%OB%gx z#X4)kw{@C$wH`T8j`EKzqrpw1*6Y8iCuOQD6|X3k-%Hf!kqV;0_oS z7y{D*cf!+wyI^(TZrBhQ3hxAl!I8iSI1#u9g#!1YN`d>5Oyeq!X`JPl#*H0=&pEj0 zE@fB+#{c!fMWsnu9;J^`1}3NA(bVCQgp*g5<}xdVfpww7#o0H|a!NCK@~`&|lx7tw z%JV6Ta#1-&X;zxasuV?OUJ)j*q+rsb941<6Caqz z`IuaK89XMt6YEjptdFcFR+hv=rS1C?mc3)icPVMf7ZsyxDrm{OV{T^2&xYl&l1jM} z)^aK(a5{JoxPv1hEqE^!1n+|jgZD#+;3()F91TN)V_|4;9E=S<0F#3c!tCH=SQ^|4 z%Y!>%WpEp832uisgRjH;!8aZ2p4Oye(d~0Ax_ypCx6iid+_1;C=_lJ~ZWLO|X7WpP)5=$ zT6apUJ0;eg{TN;8WZR&+y&vO(V3|W;vU0VvtD@Le;|La@C3&&5J5%)~&Bh*1ma|-X zt&^4LQt=$8OI>GgqC1-_6I9Jra&u)|{E@_3X-~K2FSSZ>&spa^f2+)0~|6&dQ?+)+GLOeVn{R zqhG@|JVQCO(JMhLR2d3FRp7!JW`*+LxllfA3>CmTp_*_g zR11C!)kgkM9h4QSi|T~xq2{4;(N&@IP~Xt`XjrIzl6gA9F;DNb%~M(FtaPytX@5$) zM3_Fc&zaewLJ#e#H_SpZi)tZn13e~325`?W`f)Yt%O-Q%S(=?|x~PE8+q^Mfi6b+s zQ9tI+!_SQ<3m`M*mz?7wzL;NjK6vx!Pu)nzgV%5w?qYOPp@+qC^4P9TEn=#O*c9^! zABmVpI589h&C=90s6PgI0duWcnx9>-(5LrjH#;0X3$sh7(*f)lN(;QDKP9 zE(!ZN`>c!>(`aUh9f%2SToR1eiI)~CQkOYn2rsEXEMU)O=dl#`lBz-Hs=;D(GZQPs zQnzeE0}7K*q1BXPp$Z&B^DP2DdyvQKFnseMEf$gtUomvH9qZb~LWTZC;ASMRAm)#; zuv^~z4O(~s)k`cK)+%)CqSHOtv2^bIAOV3x@D=R?U%_{r{y|GGJ8o1Ppf>G?jlmOY z3jRcSd3;qZVMJO}^$)RD$6&jAbp?gs%bRUX@ zMj<^k7G;LUp`6foR3-EPY7u%6wF^x|okNpR@6f~Oq0m$`Jv1FX7Mg*c4b4Q$Lyw{2 z&|I`DG!MNKTEP4IS71RSXrvS|X9VZ~Rk>5TpN@eylxvu)0z3$vm98wT19I7p^cwsE znMyaB3ZZszztWvMD0GIqxO=*WTEIl5hjJ|hQB7FLow%6Co&zH^Wk-EP`{Dt&&_1r7 zC7!xYtm)BjPl>%Rn%C0~UbIi?MN@9@ptqIYw4^Hd(Ob&(Z0`$Y!g~2-7HA0Nz*?mb z4-QcUij^Cfdj$s`=Fcf)+)UpSML96tO|cMN7(Iv;-90 zWt!K=_Jh03K7;eg?PaYqH`m7VsvNBL$xB+ zeq5&|R_HSA^*N@~d@*;-M|GK4t2;ndirYRApS0#0`Vs=6uV^(Kft=7cP&4!+)DInn zCZV6`l7+n@>`VqUliu2KeY-^td{ zM!A&^4rT|>b-LYLC)ny-C(hm6_^_j?PWDpzTt3(op6bPiU47^)H)6gi&f1&!eYU+V zeyb_{E%8)I-xQ?WR-S!-S1J2m8<4`jA5?bcR63xQcE?|bt5pUox7(2#*HP(M%%A>w zHH*>Rg>J(&3$&Pf1sYnw5l4QJeHg}Zn5BmE)O^-?MkJj| z_Y;iyBo@{VjT9f4Z=p}&&$|m4h%>>&SFSwf2+3bo^9=83FR}yrv5L5tBC${NJ_#CZ7BpArl6Lm`Vc)-Z5uBOt2X z12OGBsH5EvO|-FanKlkOX%nD}_8|1qCc=%{Loi&M1e3MN@Pzg-EYcohHun>CdM;47K{lHw*OfykYgW7(sSePHM^Aa$0gidHvzxQvD$u zOwk`6a16gKbYL+|?BOo6tQ21!N?KKlXC=AY=ag7gBIpr09x_KRFJulMe&Hy%!ZC=3 z(;z>b4o$)ta78#P$*An%pnH#axEq?X!`;x-!?sb`rHoODlm{Kzj@q(~F0*DfrnGR0 za7uESa7qc;rj%VWo9bja_TjX@u7OHy^(joPj_^4U3s<8xP@UF54O#(jYLXo)@AZ<>7d9)1>iTx?~Op9ARLJBnp=|3hj>ez z53!Gnh+`qHUk~kJZYcU)@AVR-%mvbs)GGEHcNjee|*#;q2kob|1CB~xD ztrnen%04?{u?MYZnOj!;M%l<*2d>? z+7w@mTcI#)!kusz`ne14&S_sf0N;kfco@DPkBOft5fM4VJwXol0xjGd%y1v59KH$i z!u_Ff_-42~d<$F`9sq;Gx5C))K$sQ24W0`Rh8M$kz>4q?SRcLhmMDrVkx`~2f{C65?+qa3BQDEgje8#@H$*Oyb?DJufi9H*Wgy+)wngKZNo3) z_Tdfq%J4?qDf~9>65fRShF`@4!dvid;mvq(cpDxP-ie2WU&r@{-^63WZ{Ts^U3g-6 zPkcECZG-E?$rIcf?DHY$X?U8SO9`VT@TBqxHOeAr8m!~zQZ(d7t@ybV4+^8E{9K9` zrK9t>^6{anD9=8jQXA#iCsf){Wg$+e+y(FRb15M_*#5N`9v4qB<>Z`8dDJt3!vn?SJiD-$RZ5?q> zp<*<*Gg}XyCk~PNVm`4$v=UYWJ5arzQ(+wTVu75)ztb`G2OU#?(lK=sDo3bE7m?un zhyu+bDzuDvp?$;$T_b+DJ`#eykq8Wr=rAr~z>J6mvm@!SJhB5;M&5wck)5zT@*3=p zyagXecEORz+fG#eX&@=n_<97beFxIMbr!$Abr!$Abxz29t2`=W+KL6RiF*Jka6YWE z&v!P1=lE`to62^j&ABBkR;II_ODA#z`O>8qj?mwo!Mu^236TOJm+}=#Jmlcte9fHxY2-t=BC-#9M)t#?$VV_b@-a-0 zd;&`%2jJz%r%8)%8=S?r)sC;u?D(V;T|w)gO$Ohx$4y&yhPp^flAN0CtUUb0wkpZo z>Tk;9F|#AmMbdKgL@|1@V{&NbBuQ~CUP3K6Pz?Xe#@>m6}ItSj35`D z=QW5a*9FRBESkNe%fwUFN(OUR=9FihE-q!A5`T)B%-ncu)#)Ov{mOnVQle+a?f7`V zc8+^FtH(X#-eOLtULu!rfP2QyaC+ynWgXz6ad$W8t>?5di^tEau2P-)1?M4W7jvmr zW*2jrR%#cMpckDYIJ=n3<3$L@b6$$)#P={0$H?M)m{ZCe4vQaIV;3Q{i-&7w)mph- zfs{wk3LY9?)_!NJV`yc^0)=K4^GsJB3kyLL?s!6tCp4EFe_nIb7gmW})#c=RXzpr} z>rEtmiKO58aV^%YxZ8$n%{wqlddh4GQ z_06*+>MmQk4`rCpQT~=(ccv6ADwG{be4qr_`htakI#}& zpZu3C^~qTh>OeU{efn>;)Pb`k)Mw=g_4&WmQlFhAp}r78smVee{I^=_i?bxup}#5A zKiX1<&XQ1vg-}|BHN}^IL#mYMdpIH0R|%<({J*98+Lo$BTYd9yN%eI?s(;j``1U`Q z>f5s<)p!4?RNtK?slKv4`dGdgUrPWnTItpAE%QAxPUCg z^~lq>FvPm)#m1+p5iA#3m! zvKH?m>+nAEGCoAs;~&We36af`n{1K7WUG`xc1Y)tol$0_E98(IB!}fF`BJV-zLsl|Z{!Qe_i{_}gWR6{C|^TgXY*k*+D1=wYRU9#OjKMCq&R${^iPM(LI^L60g^^_VhG&rlZYnaYcLmaPKkC($KlK_em!9Ve>-nw>y}(sXuj#6%*K#${Yr9(MbzDXI`L1jA z`mP)G2Cm!nLf1WdBiA^+vFl;IiEEbL)b*5pk!z{m+_hG}*tJ!^#I;+$)U{u49?sK zeUM7@!D^O1M6IFUt=7|rs*UtvYD;~%+Fl>2UZdZu_R&YF1NG7BFnx?VRv)WAqK{MO z=;PHz`UG{k{(!n(e^A|_PgFnDA5jnJQ`95+RP~rXT|K4G@VNDv9!;O+iRrUFRrI-@ z+WI_Cq5in%V*Lrv75bB&uKH7+8}x;q+w`YB!}Ud;ar$D<6n%+juKtYY1%0V!g}%(Q zL4VP+Q(xiPqp$RQq!)V*>8m{7>#IG#>+8I-{<1fuZ}g_=o4i%^SG;xfSG|q&&E8A& zE#40LR&O_bo42pN-8)F%;T@&F<(;7K@=n!vd*|tUyzBLMz1#G?-d*~8-VgQny$AJu z-XHY+-aqtDe2RX+r|F;i()G`L=jfmN>gr$k8tVsrm+FUnSL%m--Ssbh1N85Fx9i{g z?$LknP0)|}rs_ZY=IF*Z-4I$$!GA9B>;|0#TzzAj`-LR5$Vi^^979 z#zyVH#YUaLvWi$yaHJS$28W#q( z8qET`jf(>Njpl)GjY|VZjaGpZ#$`d5(KZ-1+6U8&D}t4c4#5KB%3uSdQ?R*lb+DaL z6zpPL6YOnt4Gu861@AVx2S*z{f|HGFgR_n6f(wnF!DU9T;44Po;C7>5@NMJ9;C|!g z;34Cd;P=LW;IGE5AutApe8%9AY1|&lHSP=*76*`&p_#@^E!UW(6&SO%2F7Drb7PLy&X}upG3IGE8c%As8c%6=8w<5j#$xRu zV~IB1ct)FVJgY4+p3_zsOSO&0GVL|vC2g;)AyUqmvEgOTdSp~$($ z;Yds4n@DTp+sIYMcaa{(kCB^13{%K^B{tC6L%sTodW?j95d7j?QJYVl?*4GD_4fGLaLw&q?fj-qN z)aRLv^d)9veTCUX-)c73UpHImd(D>m0rOJ*h}lX%YF?)QX|^^LvyGvdmmBG3d*dAQ zN~5mX(P(U5Wn5}@GOjeQHoBXgjeceqW3X9d++$v2Ofb6|v(4_t0<(wljCrlG!t7;i zFnb%@&FhWbW*=jpd4qAtywUj4yvg{}yv1~x1I(~_tC?XAG^?4nnf1&;W)pL;*~+}# z>}cL$_ArOgZ->#;aC4+N!kl2o#+) zHPoDEjW*|7lg%fr+2)hhLi1^BnYqYXXD+t3nM_713;SWweG_96i@u6K!Iyjb37|i?%mkjux5gqt~08qPLo_M2DJNqGQah(TB}# z(Z|f~(WlKF(HG5~(U;BFqT9{aqwkn+L_ap)jDBzKiXJm}M^Bk=$5iv(Sj5~LOE=$( zRWaX>)iOVbU108ywKPA9wKosMt}#E2^)Ww-4KzQG4Ku%pjWrL(9x)Ha=9q_Li_9-$ z%gwK1o6YZIJIxD8>9^mFcce>D#OZ>2F(w=^t5*(!aDC zrysSNq@T2!W~kPM8N_Oqk!4+!QNwDUah}y8<6^5-MjPw0j80bTjO(m+88=y%XAHL5 zXAHNl$QWyN$auuMDr1h-DPxhz>SCtdUtRYjl?18k1#OW3zIt30e8p z16k);4`wy7CT3k?J(ShKdN`|_^+;A(Q(c*7U6L){LyF*37JV)~u{0*6gem z)?-;4tvOk*S#z`YTTf(tW<8npjrCO4acfaFSc|hg){^Xq^-OlU^=$Sz)(hEnt)xL5S}$c^X|2faZmrDjXBB4;wpL}|W3A4fV6DlXX06ShZ>`H-W^KwYwqD8JWWAdG zy0tZXueB}v6Ki|+m)4H#AFZ9)e_C(kxU4sG!q)Db4D0QjYSueB^{hQPO{{lwT3LH@ zI$H1L^swH~xzYL{=XUGEoH5oXIS*O~avrrl%~@c5k@JjoFy|%fP|nNN;he43mpQww zuXFZW-{c&&zR&r|`XT3p^<%Ev`YAVJ9nH(|_CtP{D{ zStoOEvQFjR5k5`D%Kb8` zvnXst>+qO~jSiPov~NwjXIBhh-5jz!O{ ztVGXGoS>~7kB#iA>=I`@Q2X5V{9ZPUI_IwB=NF`Ki||f`-TUjrMz+7JM(0n-=MCm7 z3m|?AtjbT)&lhJKJZYqFHDc# z=-iq}#dD50&3BGC&3BGC%~u|`k2uX&9#z=k5I4-UFJPK(M@&eBzMgK!Nhom*o$|z) zZdUlc%*_hX!6|N5c+v@U(9jNaFf{4ZYu^&LE4Ixi=nsCqV!HxyHIC6XWVhGe+ zRw%OrYVyWH#lD!(zOK*}bMcD_?Mn|xDzN91AbK+4X$oe{X^K_3{H$o+ufh@?ePwVPO|Y$*nVFfHnVFfH zL1uOwnVFfH8DnNjY{$&Dj4{R#Qyjz7_wKJ-_5Qpn9rey`&rDCLW>s39?o-xxaDFD- z!t=`3kQfTlXLWs0Xdu5q+y{j=DKzZYs-{SN)5CqABn&p#CHFm~B}+i)C(PwaKrLyB zXJZJzy47*)jZ$=_3Bz-Tyje)zSWgz>4$)!s%J%P;O&VfJ(Tx~xCk3%VsW@!B|#0nzf%`SKLa1jVfi z-_ti>XDy$lU|%n`ke*j7aY0W`k>6PBA-YX*PoJz{dj`xc-M%vqUrR)O8_%_)~SC!hq>9&Z*`QSMUz0k5cSg0{eI~ z@E1DMOh0O7o6YlgVzMVXQfGr20p?xB~)333=?62TFShB`4QzbhLxVAUg}O zxx#kD*fA5d+`)9Kyd`*RZ^TDqXmIQGU5hR3^N{CSkoy zbeE*dA{2)`?1G1`7bPLJoN0xYL`PKFaEO0|Q|5fq7w|T;Rx{9+H)Y;~hs)&rvifIC zv#KrY^qVD;C-db?<vxYz%DET(UboOK*Fshxjbf`+Gw(HxfvpH}9&U=GCC*kNx9kWu#E@H__1Z6ySbz z?KY2SPMPX+jLMg+P;{(9E1<{g!}&hro=@cix>BF^YqhHS z&l~fyhYD5o%?F3eLHo zKez32u2{ENcs3#4(ifQw+#(#?lZOaZ^{s~tRYSw#j_)rO4Aj+WsLN7n22oHVI-(ng8Y z(<+9g1zdvP>Xjgm2B;H*Jm3^NF2htWj!2Sz*whn_NGNz@QAYna@N(d_ZeIqVM3O2% zck-}Xw5yL1U-U{y=(TSJQuE}igkh7@?>3=bs_N1V=R%euW#w7T&cUg)!VrUqGhr3l zat5!KV*ZSOJs=tGye(Z!^fhMHGD}M>le0lhxPG1`STvvDW$6U=q`vmYpR~1a!Sm=Z zCK`d9+mR$)_?s@!=a%Q&+?;Mj;{t!B{&3YFvjta~60thCf0v2SOTRYFzyH$mXnwQQ zQ-So5SO3ww4wj|9Jxc{fYeAB|Yuy5L!=_4bM0RjNoFcI3k0=l{naC2k)}KUSCPhd% z=cPEZ_gUsNk0`Qvxn&B2L$Iy6>QxrMK7+2z6h_!?pEnv6N7!DTH(bFcgNU8WuhKzn zZc)fu(xsFae|z~cCteBK*nS?Z%TBJ#$K07q?~{i0zUh5pBLmoekcb;mTJY$qZd3W}J{)8+=oWeFRN%R>_3yd=%YPr;HoTji;Ovu1Yl8A%gT&FNRibTH z0vDUG(d=Wp}Eo} zmdQ}*d`E9?)2r?u`c?kq33H{&TRMhXEq|Vm45G-t=>L?@(gTu=0R0Sw1ux3znTvD+ z!iuo|vzo|zTKrVPuMv!p{}`J_7k2cYMpGM-({x^%)gnJG9x}}fJ?`qg`bL|(ki@l; zkmQ?YanHny-)%oNO?6FlVNr6X9QjMsb4{tB;}>VM0ca!NL^NYTvm%<2=4oq*pt|}y z!P!8QMg^)KAy*}+ZeNy3UNb21kpTNwHB%Vgt%fmJ*?@Y$T;fS3Mo7#@dHGHLBY@dj zBbsyA$8mw8hwK^!rdWBq81%=X96MOd*%5fzeoET|>8uSTZquWAfm#E=;J2+&Ibbip zz+77?6_pgKv}5nY)OrDlxpCD1*h-@?{FO9Pw~-kbzHX zFn1!;wqWozwW2}J45c9h%(RbGX|BB#26AR}RJirY zFp&VLDWqCs~VI}*1rO0QUN5VY?Ip($y^h(!uBsxaa*ieG^Anx5Pv>5ShZLD7ta61UVD zJ4&}WN^n>rnQ7YyMRGF~K=Mi(j*(@?Z-pSjj2+2a6{RQ)k@z%W#3I!hQy6i%nO&HL zQX2+IVFp6EpTZd^^!{wL)CfiLGommH)iyLE$U=CL2WCKW`v);#vBg6uu!~zLt^_(E ziSEo*ZVKa#rra7aG;YxYhTXH)=G|?${(>j9U(Az zaRx?w>}(81{QDU-jQG?U4zSqXn`wldLnskcnINWg%OzJW|HQ!+4#FoaRJ`?+v&VeH zDsQiaoH?_GX}0i2ll0>h$_8a7%v1N|EdIr0+lB(_bG{0U)DwmPOKu24xRwf2s}an` z&ZL0rT(3MMmjtFVx8D=k==?>?l@a!a&$waSZ5|AP{G6{Q#}w2H*l$SX>=lLTKXRD$LOQ84+;M!kcx( zoufAi#7`I^cuz=ui5MEchzfIKLy#%9WBz3iD<7ObsY*^S}%AcQm7q(Vr)V@!)gE>Q7$mMmQC1V=J|TMPywn*B>fSi3gLJe4^D5A(4@lpX?)Fze3-OnGiJ|0oHebOnl^ zS5irKSceXDmQi+4&r7hsZ7~!N0dzA^^LbtZ={RWiY0ATf$Y5hUY^V%2vj-~o0D<&= z@R(3@G{q9_$}oARvx_SCUOeegP;*9zJbek}xi~luYepr6(2IdkG7GpAfkhCDe-MSh zeUZSzlsu63g#ra0{VX{Cn+hOXYpO`W0!2(nds(RYCQQIu=2)(bK!KuAH1`Y!U@dVh z*Tz%e=r*7>Y(Wo-gPl5U;y@+NQy|@nWWIt2&0Y)a#~41Ao52XWf&(}4z)j=f%|xhq zH}En}I0UeBwhT)g@F9z3WoB*t=`=c+Q<)GYh<|{UtNz0%7)%h?zK{-n2ol8LmQXZw zo=zedg^)zi3xhIsBX=sZ*#3NJN4Xj7w^wFg?VmkFj$Q;M!j2kcm@xeL)Igj5U+vL7 zMbe#+M1mARgwhj~sUhI#jG*SpFadnv_2}e}5yDR#$kdG;sMIlot~|hjibC2ieS?~> z_ybd*_zPJe{Q?Xk1TeM$T?)gw-e1GGYc+-m*Okx_2!eh_HOI<0(STHjH)yDM@c?wZ ztp`_xkoH>I;87y^3PjD>3N{7!!WHIViWKn?Mbh(-MbeEBz}WI%(+1^ww+?fkVs*QW=O9KHoexeJo#_OmD#W@G zl@FFbo$1`&54e^`nF|&O)e9;JRi?u*y0CLxDpTvD&1k5Fp9^5HLoHM^f#F87;1afF z3B965nJGlXjIJX}YztAwxx^j0-E>LR|95K8c1w$NLW;Q8H&f1)8vq&Ia# zUOR(B$r0;-jD<3?1xNGW5Ur5c{{@(WhH(x@{4)tjBK`G0Xi5Z9BzVG5DDrzs)J;L2 zcUSyf{Y#P(iXZj>4^MMW2dVG~OTl{_M)Qat<_&~S^Y&7b*cyk7RUePZi8+_)m4(ov zMpGauV&Nn&590}g#=01Wj1AZAg{Vh?)Wb<}I|-vfJq{b^WR|zkYZ`7)f@)_%{!td@ zoex1H3xO5hGd9{wvy`#Q~CJfkPj>m~~kSvs8(G=NRwe;c}iNd6K(cNjO4?`cd*Ot zBhK);GK9H1WH3&$oQKHaupe})9C!^c>*}EWL>VIRG)LxNzJB4CC-M|YoAeYpwD7No z82bwK;f8(7AER(2K<0tdU#M^-Nan#oLN$D=mIU-e#OXI4-q#QdBBA@~-Xk%D3HAOP z^Nc^OoIJW55h{rQg-gInU>XzyNf}NJ_Z26J>K_b|BKn5ILx}`6TsTgm0NVZLD$iH> zB^l!63j`4>FUcr*7q02Mnx;hg-I4ZHDT0{Z3~+e|N2qe*f&7ED{$+!R!uKuiLE$^Rof2UsS?zVQHgTgutc$f zG_vJ}d_;G>CejsCE&@)DV~Zq^@=)>X+Xnq3lb}StQ@6HXc{s_ZTmBuhljS$yX#co~ zeXIMcBMl@i!j4fgjC#`J(#+_Z0b20{r z1#Q9?nV}M3R)*ZA-{EBf^}+uzC>_5-^HQQg^w zk%Jv?<*DwfUSbu3x4vX3^fQLiVYvgXyI}9)T_s8Av5G%Gm1x8cKIgg^LhbtrMM3+Rua zE6M(OhA#;uIfF9w5UUOWu&TLFbY9~+lK~*qVQ|%f97Rx${&>frsS?g2WYb%z*VEv$ zDEy!Nn#>+nFBEG5|yE~zP*J7+=iGK7%YoTDo3S3IyP z`F%wZX4qBTe~(@2KL2H%F$m42$G8hkF`#^IW*9Z~H%^>ltSXn!)neI>Z(zL2)Fm)l z)qeXSqT~}^Lm3(d2?7EE0Ybk*KylZdB7g-N0)h)30zwG_0>awemEASK%fp4;$;Qf> zUB=Se(#q1yR@K(q^1owUFGn|fMK?QlZ7^}s)sjX@MoOAT&d1H#+tJ-k+TG2|+tSUO z&DzD%%d1hx*?n07M|8QTH9d;Ym65hm+1!!evGiadIm2z6=WEzyOV4jNkn{F=ztZ^&^tB*nb3Bm0h z_MhQG+CGVEHLf{Z%W8Edd`_Hoj~d<>m`fZ@)SB{xYCgx$3l(m8m!Qym3hCCZ*}m@D z#$z~FYfp84+YB{ZxE8M!{H7}0;_SP<>j|CzY3#bN&-_n4zv|Bq*Nt=S>^bBFlIDsZ zvIRVP4X$4ok9-|P{dQhkdR#?$)O>QX)Ck9lN#tzTTfBEgtfU=XksU0p)7<3Qk=@&J_}8pO>5p(_gQstFTev-$ z52IaR&>*R|QHK`I7@F%uZS>3#`7G;e;iQuLe7h3tp@ zg>yFOyvGIBfbkf}ooh$d7CO6EkI7q^}SU$8@# zggB6F@5mp%gpDGG$YXg%#D;U2lsXr_^AL^F2F%Hy!Pmqqr#EAU{EhU+~ddr!2*JYEsQtc9YGdX{~dfD^9C7*(NAr-h=-S*FOzG_iRs zxs#*wYjoJ7cAg%cN_%EQcqx@F1gKjX581x8KS9_|bZs@=P-lvguiF@}*`7zUOn|uw zX0qgPnM?M+t(;44roa;Ll#16tr{0c>AJWf&D^V9=$}$^F9B&D?|t=1n(`7UC784XTBA>yB`GF_kfwakBUw1`XPf#XP_%Fdm4xrvA9I8*VD{rK znuNWz@bBZgxmSP2QL=#9D};ZEuX>`-<2GEZ#_?6LLw0s6H~ZVq3(l7>J%nMb=MHwXUgQ~w0^7CeZj zu1Wj=J2Q8XU;j}|eK~r;n2HwsjTI&@AU1mkW_(ZFaY~Lh3yRI(AtntSywFTl3;aHP z;hC}*_z<457ZjVi>n17w^&*_a4tNut5(m6dPvHxG#2$SC1jinIC;4Qt`x1FnPhx)b zg3tMQN%A>+hk6HcC)vkY@0R()H{~ZV`29{a>Bj3V>?oQ<{P2ZyN`3r~y5L9Qk$u^} zVky%nz?%s|XfwtFKG4=Y3nYG9T=o}#s#zd(jt~%4z&Y-U+!qj7sbtQZygVEty<3I=C(=?&?@P)C*EztGUqm@%VbwRRUEi)e5)S0jH;45q=ESNiX z!Lx4{%pcn{ffrsHIThT{M6jLWT#Nu_$4v%tpWEJT%pmk&SqdC<$Z0PY8PH`dfu3OR_WVyBA_Oz% z3TKKOMTwW`>8eQgJG;D$EUf(I|gJtNg3sh z4#Y4EG$;7`wJGTpm{y49Qt%$BHXKC2LNl2PBxOn58gUs1Rv9^e#$}|x@B@XDOQJsf z%b2sMux8UkT6K7&Te8ebVQY(OQe+IM?RyMZGF2{#_7(pWh5X#OXEosOV8^|Qt>O#x zK|@=F5IRx2Hsg*i?dUWJHg(4{hbDO08i*24;QPJ(Gh4;h%2e5kICgaozULZ}k0aEy zK_4KtR#p?rBS}Knw`4NYIwPRyWn-&LX-!m)?wUY5JrePX&3Ui(ex7r#X-uA*bdtJ} zIyBW!T^;t|JHdxALJfls2xrzuYmp85;{G-sYSc@!wVtiyMiF2W!Qil|rz^ljDvcx2 z#=W*$jOEMUrashKBYtTVuOZn}Y9r3*!qV2tE=O`1hy1}ys~z9ap1`r-MDU{m>ZipL z`TXM=Z@BSPPIt^L?rLf16eO4DYDwmlV~(IlbW(x9`*XUp#xT(AzG5_8{vjyhLie9A zA|27jA0PQLw8HyDxvfJ}Vhp7@oVxpTZXgGi%GZ#zHk-iLek*R-0F9}7F_W3PeFnr#g1zexY!_#@CmQV{19fw| zBOR^l+l2GwWY0G1O)Pk4-!=$jAw%TFaU)Ci!||5p&0mTBRF9~0Vy^B*uPweejmbLo z+(ZkvGVBH?K2iJ9_aI#z(4sPJz8#=l_vEKzG-L1eAtMUY1=)}b8khapUoFNAg zZHZ>N8pQgvRk9r0SmJq%4uP24R!9zbC#+-HU@$^e?MWQ5oy(yoWk?xSv*-eCjMAvs z5^Vz)u(uzeTP+>FPZ^S232w`sfmTj~US|y7MVI zkE=nVAad3T@N8XbO~ADrSu7&NOE7T%q7Io6LovIJMN^@)OvWTK*{e%t&aLS#^6&yo z)J57qle0w|W&Uiw{@E)@8Nz;uwag~VFKn)B8i{bhw6bcaLXNnT<(v2>IEE@_a=L-A!W($Ek#A<6^kU88DsFb- z;mJ=w3ON5wZ~n2HD-5X+3O_fq1cgcShB`T6JT=pN2pdGFH;zn<8Zk`zAx1=XRnZYO zCQ&tVIiLhx=Hq5>Ds;eItM$G@d=lxAQyUruyE&B_T$JtltOS~=+H9P^JiMOv=w z!Wi?|Eh1KEni90`;`4D_Gto6xxyWuOB`Mb@u6CWeBEkfZ#P}0YMPnOLDTv4fZJdGY z3p*#}2uNZCib-ukp8O^?e5|mTMR}BcgY?vNMSZZ4$^x;B8P_D*(9$ZbITYtJ6C>BL z5e7O1NL->+r;m`+n00qWydM(O>4mg7ZkK!G6FqfxFqw4@ezQ$YiF^Ed5sE0bgp};? zrw0|FY&?D0emipMey{VYCnL4BsK?2;RjlYc9we--6l2Uc^9Jrc(qtc1*@m^G{nuJb^4t#gvqxUvS1~TdxXcGaC`2`Bmn2zZ6yj*Pj34!)B^tr|BE~ z85ZHseS>Id!sgp+q(Tu{0lEvoTq1zA;Fg@}g^PBe)`GSFJqIhpgZ}P6LhR2&Qjquz zqsaHjF77}%)pODu(tEexM~QR<+cGRft^^R>?;@ScIn+&*5LX#BZdm8e++v|-CF&q9 z&&sit*3w1m(UbXK5{DQKpV-j1z>JOqYb?!WQd1Zch!Iqgdr*FiVn+qb^nw$_^1&a; z#$oA8Y>zW&M(H7=oi+V)em5%?0(os%cvhMiAdmOstDlYJ_wB{c{?!lj5Ep6^&Ytwh zq`0pt9Y1Hv$e0nL^-c-O{{k?dczi9g)l$tMo0B*K_U&U8FpPCvX^!#IwTUdo!^+sM z>VWX2OxQHPYwsWxDYAsq#W&837h5|T2RFftp~yPkFfQ+`q80rhg;3HDi|Bg|i9epC z%6IUV(7Sx=mqZeFyNg(553EXA$y7^2gjHEu$x8rCt3<3il#EIWWUGLJBIq3OqVk)%t#<3;O!By9zL z{|}TrK_s5O^WLkraD`AfD?Fg|HvA*cR$&7^KD7#eXbh1{EfF@+f;6iu=9iy`Uec^K zX0D|s&CX%Gzb~m#$(<++>X*HxUZlQHTiD}-&9Ok_XBPHJB#!AwFSb4ytd&SKYo3&^ z>hg5L&cQFLblPy5HmlkAl@huCW*ci1V>j&c+pjB6ppm+BKxwKAmZHgY4i ztE}B>6Qx&f>%nGDp-LwRLmSohX*k^`)vit<;8z{PGZTWBXT;E?j))nxlHeF_jV6_r zMLxWy9sg?NkR;-(@ygUX!Q&CHH_+tq3yR0z4$>lXWW80TK5UW6s!#K`555Wa#wMoG zuKv0wqRMKAHtk0HOWUhvdxnL_yi1~V>z@V9gc+>N`K4L2o?qeRJPcoeqqbJI_d7&) zd2eMD=S5ol_%Bsj9!1MU)AIITwD|hHD{q%}zo9K3S2c3ylO6XAH6TA9i~Qt~j#!CD zfiG^inn!D38`SXWP#1Woo<5iE)T1KR!2#LOkDAdd$CB49vm9BxtNEtAWFFgeLEf!~ zV`dWCHAt;e_ee_MhS{lqbC5GLFMa#XJX+OHAJUqk_44~V!O_RvwJi=1Byfm)hRgt` zUAmuAb3aXayq1-MRaA?OV85{gRt5F4i6$7@B*X|=k37T;SG;cg=Hl$m+hh0D5Sk%P zT06SvMWdHaYp|c7Uh$LA(j0S0%7$-?yy&?;tfm#c;NawqDvYBJ@DR@t>haE%YLMqo z=hQf*b`XBm)38?j+@uxm0aN%A8lyyaB_3;Dac@{F1K z(0x+z)HWU#Y!BLjC8G~whguzmdj*16j6Ur{p`Cdo#!w;^O6wJYWsOgKgvDkCHa?R0 zGA{s73%?=QS_w%e(7~h8VS!2MeH*S!|BvjoU?a>dPWFl1L1>fifpDTZWGY|nDdg+r zruoLX%`XhZE0eDxfngiVGxOT(bAoBeg)<13LkEU|!_kaVt0fEC5qw@w>G`3kuMgkF zZ|3pw#8to0yZ&spFXXbAH#V?uWXdl_HhS>cSFFdzkChR%A@tSYQi%x6+|6<^ctkU_ z%ZJrGTi-kRry|jKHDjxcM$Gb{o#IHQ@_RxkZztYX04RNw`!{w2Yxma=k;nXjKQVxf zP&f)6vV?U8%-Mu)AI2+QF=vWx!UyCIgtV78sDqy&U;HKM|0rI>*L^i$NkqZ7Hc1G!~F9SJDWMqen) zQSe)Kj}4KC*iEJWQRz@Mdui~h)*xFJ;uAeq6}}_%B8rEgX>rN2Pscsk;H1Jyqw1o1 z6$VpGM!_aBC6;TsS1iS5g_aeXzCDv1jA3M_FQC~S-Od%e{f9G#s&%KU^FB3<)#6@9 zw|J)3%jBcFQ;cXxorJ>MzGy{*-+}#F6FD?JXdD?(%VZ&c#828P?D^t9B`$*g?%mh3D>?eK?ph z;tY3>S?2eDc?I@qy)H`D^iT5Gj-S1IHcY3w8Ko4<(`!?whPnPLS+Vpn4F?`~vGU5@ z0cEpNp6ndm#XAX3R9pEsbupMbnYz#j`nQsVi{8Kw$ZWA`4=tWWa_4hAV#Z-`U8E{B zUNNOb`7P|$J)hi1oNH$<1@)p*Iqn=M*+4+%{_z}S=9jHGUSB^82U()~kP`sEuHAVlgHm12Z7lFesVTC{8L@)r-`gUvXM6lJ7NfUusPJr|}(;7kSFP z$O(>77vQLj!1o8WW$sI`+VeFB*BD1ZxW1114u&gv_`rUGU>aDkY;Kz;d4U>)A$|)l!4myP-}wn_?jY@2 zUX@$N)b<5pQ);{;po|o9WJo^hg*TKUDTxY{Kt@TIw{^&q+Odd24eIS(`Gsj4rsU$u zyiTpa5|*=OVH}!tKfsW%5%gk5QJTO`G%Xo9BBOYr`hBBw562X?IBsH8A6CGvR4D;m zBs=*gHf6T1iEXmFhjVs5F(!|3bemFvi^y(3N!Q#k>ALOsev`XQ!N}ON=?)=IX!PkJ z?O4$Iz({cJ7F9^B-|>mj=>-Ur5V>XZ3|ppE?0nxEke6?94!aW?E`>zPR)R(fa}Glk zD$yb5)+E=iL(ikxgtee2kzDdz@mwO|-#0R*;i-1ecWvGN5YTAHW>WBri;YM2AR$vC z5y7F4si!N)NT}dbLkbd^IfKzUCd)x!tcl>MRg1!LfMx`%bz;m%OaSnB#x`4^_(qHb zvRf9yLwg|9qLDplOr+XB5S&3=vh$Q6m?6p3TIp%kY=L=M;#vYGQAJI@#q?Tx?LglP z#g0?_vyvhe<{M4#akoX3Cg&(u-bAk@Upw*^I}wD;>!!MEj&FLt-6}?Zj zQhp(+ zZNjm7Mb<5_wVSKPiA?DBLK|(`Rrcvz$0bItBqo-*3G~TVDw8?nHPQ(;O^UFkk*;Tv zR(rd;DU$Q(J*bvv*=lCjyLdNZ=r{N8^-Ly*@K+ zPO#6gkx}>gr(GQ~w#Dz-;4h@f2ccyR|G|jDFM$qK$9teMoc}IBvM0yiA0Is&B!Dg^ z!5$oO?BYRqHIjKN_%vcBf;%|w1!C^c3Ep9yzIDq0&~M#oE?rHvz!XnVz6g`VHCr(b zhEv~alf-_GYpRTL{)m*}OtRK)F=_2QDv42)DQ)3pW0CCU0PpU$9bmRFmKt~TPmRPXUd0X45MvbVh-EJlKI)%a+K%0%1y0U_D(N)nX_5PPB%TxTl2Llek%r^jQz-lb$Ber6Y}t)80Ks1z+eHN|L6s zB$FEJ)IJ5>DQsOI7Az-$71RFCE}0tZnI@y^d}N>FIw7IhZ^;KL1=0zH;hl$dwMRx+ z>?!vOJxEF(yb0rIw;-aN2vZJ1YYzNAOGq;GWGnHx7RaK>#9NXgsab!b{#g4os3O?n zgUt$;e1(-?R)j^#(I8j>SoX3^wn@3S(lVu84Hc*QI;~WU=7TP1gSv0m5R5$+UC?Js zJH8O|40SYt)VSTOfwn8{>5!%sJ)ZEnKmW#QdBkfajxN?|9USrC%W@eAF;p&f(mxKB zh&rkU(KKYz_tBP$wv~ytDF)d{2id3w;XLzot^MvYei${o#0WgtU`q1n%fF$TON}&} zt~0~I{PMeuz?I#@m0i{{2z9`L>KDxm-2rw5zIv+J72SY;-os%)52<-n->q&B`6ra{ zozDyJAmqZK@h#OLbn`*bEoG=g(iyckXe#asKRv>1*~T4Ti^Im;iDa{KGS#$>bhBH! zo60nn;Yz21IltojIP>`mHs_J1H!kNXM_Q4Ll(%hH*cH>lWW29!>;kS;0aKkE6!t!o z!?KCg#-U-i(43z)?-@S)HJX58%r6^{ai@3G6@iWqIS)AJckP;1=l8!=AGLK3-C@!e zr&`bI*gm^(K1WZ=yN+}hYW$jhQL(+?hutC15;f)86vaa0o@2vB%P1~a{9G;VXiZ-W zS@UjU?X)GK{|!3KW6pgm6TJDdv(lALgp6x%L-fPR$wf@T1u~<}+JeRyflLoQJqP`_ zHET;@qy>KbaI*oi$oH(V;teJLLQ?pfw$+yGM#;&wL%^D2&Q>3tHnDTgmciJ{BvBQg z9(U(I6zy%Z;guAU*V4QKc1>%mjjD*QU-3V)2o8EBV!N)IkUKAB4~^uUjHLL*e}>^Z zml{ZBYG9R0PT7Cc+(d^b&D3lQL^Y8Y?m=_YYN|uYk|gwrHS^_7uvi}!c+$=pZVFfO z6;-k49~JTC{$XznBJgEqvFz3y55RA*8VW<}!d0_)mJshFpSRdPAlX_yxsvQj5{2<= zjecZ6nxB!=Y?+(Z>^|ozEuzD|a3n;2L@SR`)CwfKn8~Q#D;?MN-I}O{EvvbU65Uf= zW6*jQh0QH=u4qyo%*4#tY7Dppya>^Ibe_;O2}Ln_JsNPWD+_khG>I=^z_~lG-eUDj zk@&-Y)S1k6Jt1vw)!R5XOA-b_?_9atIJ-;Z29eio%5&t0NZo(n5N+CH*c6X>KEclX zRrdcH0^6KoA=XlA=}ek2;M66zS;4k08fEfK;JI5q(}`5H=+c-+^$~0=ypwX!8DT1= zUR0`5o6|P=y(#Lhilt*(mk3Ak?8H#es!`^i%sTCvgO_X%>WOi)=4EPB*e#qn0{boY z6lM+=g%_t&xk`g)D9Wji@GL>#pSY($@Q2XkL)wWd3W}d~s9C}C>?N4{-sCHM$p6M=}P#5H}&Mt%mv<)&8&`9}VnC0_K>dBE>?_UQ%xcm( z5i4Hn$pm><&N)gaq5T=A9IX?s>J82T>rBIolhsAW1(9W{zUw<25(T~vgrO!xWeup; z$ZtzYbo6(xov|C(yw(;eY@4n-?fnzF4jk~^Z7_A$Xzq2}t+`lJmFjlGn_FnJg{DR2 z-|27J(7L}KbwH=U9J0M-`>X`!F2Di=W)AoO8*b4DU411xPhK@0@4&L!zS6ZW)C-2X zy6)?Uuxm&IyC8%u*9FP!YjgncT2^7(49M-uJ_g&Re<0t*4|3Yh2b5on_Ut}E?pXiB zFzX0`W8V~u=65w84(ejJY`!j@b}_d`WK_u=zr~n|(xxH&rGVy=(G#jdK!~+4lj0&5 zrDJ2GfSrJ%$n2tdETXx%$mTCEOlJnv+2f>)_3Q0)y7Y)pCB((4@9iS(I%a6((JFQA zinBFPZMEc`h+4(D#L-l*obIL`iSIk3qzaYR+sSCZyK^{&I^N;X#gW%5m{ zh_$F}Lu)X11DCsXz319HDF5}vH-Z!ped{HBpav7boj@VFt5_{80);lw<`nUK+*G`d z=NQ>)WmM7Gu%SgzKaDpz%WLg{j|lxY^7Lzb*Raa>@WxH}-=pXeh_EhhhUVp356Enu z&l2%F+&`%%p^+cpaLkZkGSt4Qv)RZAVl<$P5~rs#&R#N0b+gWo=O3%^oBrA z>0v7;2eCb~I(6PQGiI0T3IRVM@1&|+q6fAhpmVYZ@z-JMF*@7CP(6-D@b9SrB}<06 zs3T1SWw#s8Hv?uA)7Tqkd4EioN&XBY8_xOl{=~Q;3i`aJv2xyQUR5u{Hi6umA09@} zc?DT60*%%W97%J!(jF=(OgQ>UXYdo4st4JDGRha~aNh6y*FwqzPJA`*BDX1d$Kos~ zp4kp$oW=c40CItrm%aPGm2x8owv)-w}sSgr6*A>wVgr;7~-(VcD zrGb4Ov7?4mC#d!x6_~)5gDU!&20ZVP5NxFtfEHs(!C~PE%}DE{@aU#h*g;b#*`c`O z4@`NfAlg$MgfH5H>r-5i`SIKV8BB-#<_G=ag7;DY`R0lzun!?t4Ev!LPyZ=gre$;v z>qEPK6KVS<$@7IrY6br@Uus1h^`pDYcPH*S&ERH&^stWck`F0M;t4427IAP-#gL6J zU3nEr}oiTI=wkhOz||J@W2NfMra?A9Y~ z1}}X4cpF0!ZGU`p@a&1+&q9BIDhNyd-SH(l7;EcTyH!SvRDRn2NFV@PErosD_P%n1 z%{* zZtL`eCfBWh5PzOs)j#hN+ka=T{5Z2Mjr@Y=^c?44#+vaZe&wGB$W3vj^*Phen3=u# zMAz)YO2YfsTcFwXNi&>`U#I)6R^Np9r>Z<*a8j`p`U!LxHp6|f1p>7KCG@nrRO}mG zLe_qi^s!&5(g#A_iMmDa8zW1~Cz@CQI!o42Sj8J|#T7#JhAW`e6j;1*4H(${>eh!% zzY%??@{fbMdx(sWkR9d+KaT5Qsly*s+5BckBVM#+&wn%`jVIvRyKNF~LkRfJ7rKp+ zkO3FA?EDiDKN$A@Wj-1BKWw5zEoF5aNsN!UkOW~Aw(o5*F(Sd7 zePVP)oJ7z>oW#;VH;RJ5XmB0u+;fj!{ex5UC1{$w7wG;w{+@tGiw47l5<_fs(|sME zRsF}+hjsGeL5QB@J~omc`T$&HG(1xrj^cb;{O>)dAR)`8X_eR-YVLCHdnfBci3LP# zXCY2Knl9?HuPlW+L!?9hJ_gkp#5H2u&=FyWHYst|ABt#nwwY4d;XL&1*k$<-r*({@ zRhV-M-pxnKLK;y@SJ8Y(#^Fy@aUPy~EnAwT$562ejmWIHmumy)%607OO|X$OG3?%U zea-uEC27*kT5P#9AM~QJV;ZIk9x>Npo@YWCmJ3z{dPY97$ZSf3>+T)#J~p4s)J`m+ z@JV+b0LaWRpI_{sK;`EpCkK4LG4wv_{EDKce4c}uuO5ADt%?atP1Y(qh ztz`79jX%mmEaN8LHIo{WnlsBtz65A?j_#%B-x)52+=h7uS8iTdn*zOd5dz z0I>fzVPbFY{9iF)r64W4!H3|L!DeS5;F}o8_a+8qwSZUa`#iV;cnTT-!Y@`@64d|| zk;%*k=dU}MridVR)x}$Guxv+&VpnT=(#859((rNf^MDWV@6~`HqCQL=8Zwc3a<3rF z4H+7fN<++WOlU|biJKDG2AxCro(;}qtc z^*J>8uq~4l_o8)Rfy7EWXjQm1Cx6_tfL&%i>^R2I;tSrYq0q@$%?@BKEO19^u3|N9 zKXEY`+9@N1I6(kBVu|LL2!p~@_aD1C!F$MI;QJ6-Flbn}Et9P@fSJ&|f^47=d3CBR<8* zmI2w6q>`qI#;K(>KxOE6I)D%)|G9toKh2c(MK7DGW()0gjjzLxotJGRL%!~2{O{wf zhilH4O^0dj6Q7x`yR(}cpV&~6Fu}~mpb$=TvX4WFAPualu|C8qj?ytVdO)@{7(jP@3hK0 zL>Zp=7=<>egMuuk^pepLjHD6^oLGZmb1v;>My70Xb<;7s5i>0sPD-ZgMc3wGWvrPw z*Xm5q?BS(&)9&Gs@n(?&ji?JIoj)P8i?5vsLJYm`0P){m6^N01LHkhN$ zaGdPHa|-RH4qU-VTNlJf?kwKai17a%ojBRwx<|2!iIe{G47ZeBdSxR0Nt8`-_>{8G zB)~Tv2YkcIDY{>iaS!pSzGKJAskn2SCqMb;={JlnaZeaUJM9baO<=l3=0Rh3SLB&? z*`akn$<(cIP{FiacxMt-v;2w`wzZr$KQ+PLpwUcj` zGTdU}nK9fm|LPodGy5tNbu<5J9rgPxW_W|CTlL_BX}j94WtcN_bAYp`_oJv(o8m{~ z2s~9}CP!Pot<#==E52M9b^;rXszzJmry%_ZRuWx>x`tYJG*P>OqLqU!j}Z|{wiMP` z%5c0CzDdMCY&x~iaj~~%p=!mJZk{8L2N7CWqvUFX5t#vM()%D?=ontaKweh7M9>&|$~cnOI}c zzSMVmvDru9v(sHdKe7sQjb*wVstuGg2DITE2@zh*aG`wg8Ypb)G1_&-V|^c?#fo1G z16CM@beJ3yLsjWcDk!CD9=bwOQWmvTx7VPuV{rdeaOMgw<9a|X-F*9Ks7o-iow+!a zIA)h8Fs9{Gsum(qHeEWUK_3am^pX@|wjS1+jUUx^@M)L`apQJoRv$+*zNmI%cSBhf zg=HgloY2xbNx?v4lz0TGf8Ag|*`g#X;S@<;vyha!l=(%Z-ldqQMT`iNRl?rOZ^>ku zKGy56H3R_@a@fD623AX^AjmOumvP>mhKX5{F<%|XR`8WztI z5prjoz*yYVGH(nfY_53sO^HmIa`nrIezdiUtDTlEoU;QVFcVh+_NQ|6)p zal+s)ljhSGxXG9fE~0CwAv0-a({a!Ve!~m3qZ*}-taY^8CE*L-TfxbFZYwN*3=a~du&05Gfz#!Uz6&)ES(FIvvK6Kd!uveuj@=50oB+k2O#NTtXX zZfpH}>*m4o>q!!W&JKCZnC}l_pHuU&Z3DYgK#-|j`%X7#lTgN$DA%s z{zHZ8mkbPS1*f7l4i|i31?DorqrL5&EsWOWvU-imu;Ku&Vdk9zMZ7-+5G}vF0sTJlx*Iz zQhSzx6Y84DOuh+iZLtJ7xuXX-L@;&^3npbFZSy1Qtk(OxW*Z)z_;7W)R&mjJ$Oopi ziwuH^9+Z}0c~HRqj6w6#v91Jb9w~eXc`11fI)HwyWJ9MkvbhDkFWwVY^dNX@pJ~~7 zu<|snqM;f%UzylFK)j94%YzeP(sRKIg`Tu;q*$+07C-D|Zb`%tdwU<3MDMVqmZ!*W zTBvazOmdJozlnJ+xabB{PH6D4xVB5X9I~JbSd(F4){m3OvC&1;l)JxkEEPGPwk;qw zUx^}D5q+!tLGZKI^HRj}lJp=B3p|)bY+uyTI}v}u4W7690l{0~g!GM^ZOW0+g zCjeE*3=f9*{1MD*cy8^=g;koN0?grxC6^`r$Ooeny5Yqs)XAUwrVGI zD4*U3+Ph%_yBzFe3jVs0h*UO{O}_mcm)(Z2dI9}08wI9eCrN1?tZhil!*r^aOw`y- zI%@icp}YJ^nOQjr*86r$r)T`;;uFvZ0y0Qg!N3t!iG#mn1rqu})Ema@n~xm%?345x zVn@R1yFu$M=7<&U^b_(&_^9Xnlf0+w-ju^<+DGvXI;-cf#_?N&=X?4`?kz{k&>LVS zZ5Y;8h-}}cw7RO?hO9gk9uR$>xLkjZsR~~Q+bbIziIwdXvn_3u@p@iO3G-g zNH6W%&EyJko<}dt&tJjxTFA>C1Z`I%AkM=+;S%gq5$!|0CgJMaa)VNu;IKW8Edf>r zRAn$CP)Hx>XEYC;wv<=HVOIx3x)gb9N`>PPJfPV%4%rL~|cIU|j+qiN2`b8k@ z_lHb1z`q@X9#|cNCF;m?x3pIS29UguyqTUE1Y(#!FDKDzgzMFmTF3e_)_5XfK^pjG zt|=!iH65`XqMX(5P^V$C z8L1?*qD%v+v7;%AmuYYZuB1w^8$J7$v4-6$Q|LQYz%DsSN_R(k-z#f2Q;3`q%0?y+ z$IJ!k!tCsbtafa{tmao9;LZ{m4+XhhDu8I9si5ykrKqS`HE%0114~>^HtThwm4G;? zhG&GBWco53LJraR+CwWYqv-{%J{C9YRdVS||EisX!`Zem;*CYdF$p+v2`olATt4}e z=)FjFsS%~GX9KiZN*4v!<}4obP$e{pNX*YBfl?V)_sB8mcWU`9=NFK$ZL_VzG4FFZ zdY_;bQyt_OQLkD&r$b$;BR5Fklc3Wq2t-g-@J9a;fobY{>qdGelt4ZtTGOaG{R7dL z!%QqP;HOYjpgHEZr-}P((@4@j9IDP(2m?2mK$|3R6n88_b^!(M0c}^D1kU{6(+V&) z{=H)h7dduzQ6-eVn@JkM!;NyIJIPt=X=|jy)k>mnN1MEGxN4^P4;BSY%PVTA4p*vh5 z$13EJ5`4E&#XKN*i`qJC7~C{$QA`9~$+FLVY(;`mGt~lGaS9;dyQab@=ZgD7LNKvc z?aeFgEXhyh@5fOak|i)L7*?SwplRH)x6YPptST0;^tz93&2w3!#8Qi;ZUZB@Bk>m9 zt_T1&Ae?YkuvB9I*TChjCWuQ8&@m!H|Iq&X_Q!xXelac?{QE;%8U9TV@c@+K8u{D2 z=H>cp?Kc%evLe0>bEUpE9pTM~*0S7(m74xjy)k*nYZX;o${1T`ffAGm(%4e94Rq5e zv|L2d3l>OPyj01nZ|Vi(82}9)jtc`<0{f^^<~n*AGCgUU?6Ljd1PhtH`T*EIi;Ep^ zw4d{z61kScpN_liMH<0v1tl=fzf8%GE-r$aRP7@9$G#Y67fCI&bqKkNRpl9)irTz0 zo8f2$V0A6|pZzHE85GV8;Gai8s0hjSv&NBLAKCX7x+-E2_C*EA1;vFJPOg}w!UgkrZE^&rua1zj^ z9b{hQBr3q^3dwni>|PqI<{61)^-~b7p4Ha&)(TLbG({O&*RU&;Z9FNWH^q}vj-k|p z2}{yz@$?-Tn}0bWMYr(oxs2eXJHlk{^fTEzu9t?K7HLBGvkNlpo#m|mES%V1UE8C6BnQ_4i*R37n1IOGEqIi72=}k{T^N~wUJp*l{1lXHWgpc;I)?R zk4Ld7#EuY4Jw;VtY&YhTaCn8FW(gJevI(jt96~M~e=uI(nP#gS%nB{JDmxYRMM8fy z@ti33vB}fYXc@ZXT1i6MW^!4rPcL1~EN$1Dg~Q2zZ~Sub zASFffQ5e#G%BV)a)<{?(RK6xC>NxlBl*^v)L|(fQJj?iWGqP!sTQnnwcHmv=EU#=G zfS07_9T!4=37HOE^Qvi6fnJuLS4Q$~E(4T)ZQIi37%knGc6MF)__^J~?43m7hOlPk zil!Y-vfAB5t)F3fa(kocrH$s^@IEd1*h|Kxh_GklKyj)S*rs6SFR<*eERTv+vvfa+ z_MOT2kiY!4)Cd%Q{Fpgy_L)wO6zNQ5VN4NU*S{OM96Z~reEnUvVU){tnE`^nwyg0f zOAG?WW&`BC3`5R0JpdD+y@i(eT~C(QsNrzOI<3mWI<(ZS`c93k0752v8*zB&caXTa^LJVjBDsBAqYlx$hm`&8?bI_=q_b5ohJ+MXa=`5x?G?Fr2f=8IG4^Kjk%&i82UzBVP`Zs=FXqG$5n z(Rc?d=I}mzvbQIX#6FTUp^ooaqj{!75biQK0y7Z;?)*&iH$(RBpm=WbObe3xTJFkP z{54_JkL|5l+X!2a#jfewXtZ;j`_%5To$6?;J*n_*3j^|a!2_NxAcgVoJDja8D#x>kBCMq-g;EpV1RB4P->f@Y_?57o~ zPb`mEoGD-K5su`a$!zSR?`b@e;N4p9$v+Zn+yd`$W9%WO9oXC&i9){M@bO7rz`Mad z25B@zG|%wH16NLFS=v+TcRwWBW11CNREh%H&B4!Z?~D%5WX1zoncMLX81_aO-kG~i z`ZEsB0Oizg&(z%IqJvf}CK0V)tHbdGb`V@`Nw{IgfqC(yJNS=!GqAXUDA2pPHWM#{ zS7vfWi%=>@w4ZVC`LG?}UMXYn3b+qd;7W*2?H-K>R!r4AZ(rRtQ{?wS+<_DvEdq0L zK#mlw3kpuj$4WLO4^GV=jmcx?#oR@vi?yOLzM|NHJcz!~P|_A}c5zwCPEOqhyKEzU zQ?maC&EpK5zU(^4MH2<+1RgR`$aNQ|@D(kf909vyZY!4wxJBvY@%AoL9HG8}!x~L- z9SOU!%gP)H2)$t!d*>*ScZu8Z2ece5ras}K0Nu=cEKgTS1yaE;U)LjHpPDAC(cO$P z%5MWEu8bt9VGoG^b<=6n1)G-&lzmW=2$X#(l9W=d5^2C{3-d5Bycy73BB3B(-aI>( zNk7PyW&%4+1G#HM(mRB}Hq;v8gj~V{00D_U$S^-q5HYvMC~JSWRm*+V~`fKR=$n1-Ii~RjVX4|7Kp;hJ>rV$ z=gP|G3Ygxr*V6&D^1IY@W4Wz@Vq*b+Wk&z)=4Jz4cMXmhJafGJqR*0c#&+)tXAZ-1 zg=4?T@kEb2Fl0}TJScrbI$G{a==3S$1ZG#^fK7|G2GJhHs5(z=W@Ku9b=cwF})Lgg>CZf4V)6m3Fa>7KbpQ{5N(6F`v~~(qsSg8AcM~98v=fz z(pUE@@X~p4+Upa1oHEK`*1D5<(KrA$dz{)ZptBhq@N;|`rMkwF5baFzGmxigS}HJf zfPQ*(hXFw~vJVIWj`(uD6Q+rp^K?cD&22g%xDiiZP8Ar%vVP3Fm&y+h*-3Isw#?Pz zI&v{cH09T3$nZT*Fum2OEBU^TH6`xwmUtd)7S$i(n0h<1xtq0NW-5dsifyf2MQYdm zL^ffSEVWjOsE#wl4Xb~MA54A~*(E0pXOtld$sswZWT%Q-Cb??R{!LhEP*9K&pOQtp zM=P2TRbdc~HN#&w&$23N+K{*)P|@)4lvz6*U9T>tS}BzCoxN$vBvLa*w;M^3xycwtR+72$IEzYXlA6D7(hqmd^8?IY}@%Nr}3R zM!3h=RqP}>%zqz#^CS^7O8*nb3_-jJ4vl0HZ@1*fmme|yzI(`*$&h)76eOPrq=5n# z!Lg8^?IE?dCLzP78)_2o6@u)5*Bk#?>I0Ix!!*jH+Aj&`z8cL>a|0OlH^LD`=oW(Yym1eTXc6w)Mo;$bJXh>Lwibn_6Kv%XbaQb-uXe2GAE1P@Q3Eb4H zaJ0{nhX+1~k2Ek~ z(r7@}es zr0>*7d??HJr$uadqo7Z*L3wy}CVuTQ;JFcq=cTZiIzNz7`lv9cqJ&UMfnj%6OJd_q zsvP83Pih^H$wi9;)z{H%^CaNBL*MoMa#FxmJ>d zkhVm*(l9W=rqFYm>EtR0Og+;|x5FxdnRor{xd>B5zy0xZ6xUItihLC(;Ws3Z**6{@vK*uRt9Bf!Pd16&baZdtvL6x-L9?Ob4}-EJ;tz+s4r$*-0*Kuy}DH0tEJ*q-B|vPO?knnzCd;qx3q+KyU{vH6vR$(o~|1f(WgAA z&q@mXTm%*=?j(+NSHur40fW{gpjU>s8C{q+x_y)b{&TP;!;8wwg@AS-hD&a+{gWLN zaAlA#Kxsztd<;+}lIO`GEy~XoP(4WF1l)REXc+r=#CHk>@@R>JcJgQPSbwH;xSKiW zIbX}(pk+EwBXPxvEE7FHQfJYltiq1x#6Xnj8Sb=A{TKqud=LfxL-)UfMMsA?UmU-9 z;Pmf7@&5@HDHuCAS^iJ^m#A22$pJou;2o=XeZzPCChx~wf8j>(08?SPx{`c@LU5Zg zlQPZu$}MRSpJZ@Eu2Hq^9cswk;@=`b3y3yI68GzvD zn}VABvR%yPvloiTA~?`JxlGVSD-s6sf?V^&3`b7kF%^vf)mpy?e+7bR#v}=|I2LM) zgj&tOHU5qDDp1K&wcYwFoj0KWz5}1^xPUaE001D6|1WTzlFdJ3YjgMif@Zx`E^HEv zQGT+TF08gxXbwnd`$3kSBJGib=r`As=*>7+hlh>ZyRLMWqpNYd0NN)p=tYog`rxfC z%mRP8qbU#rzIO?8di#I9iRWm&=LdZNxlg=ytd9uQpFOg7+-4l6Z@6aIC%ij79y9?o z^V(}caAl}O#Po9bN_xHhX#9YIz(M5_-NsCPcs#1aSrmIiobYJ@9lqM5QPURX6``CnSBkFp(N-DS! zmKU1r{I!Z>qy7aB?a@C)p39b<$@|#wWJtv{-$EYzQ zst@NLb7bgAkvs8{6Cv8EUmBIemMLyB)8{HS{vZw*ogQ3LA}{!rS|E#0*RtTu{7#b2 znaPrQ8xb+E>mg{Sb&!xG)t#-^(sL4+5@t{z7#5}47NO}+Tb@30Le?*9xzlGAw4))R zrSa<*?;i*E1PU^;5So$4Ry-)NCudHfyLXs*JYQ+2B{xXfF0p73vNz-e+q})6gT+tZ zTqT~3oU(oeZHCyQ=&&knI4H#)>FrPXiGfGp_<$OTQ% zVQ8qqRimr$!;K%Q^%+UtAio-*IaDkoWp;)r@GzkBgve?V;h@^+KvL0C5hrTV#QI9+ z;tf6!=)~0}*;AE$KBgQ&y8PnS`&b`*qwt)M&%Z&P!LL65WK$iIHRE9Cn4;$#c*F zct7W)^h&j&k5~((1hoos2?PgNQX~GEe!NT3=3Pd*I@XH36e;U~MVm?07;H?VA4aK` z-7j#it^+K&5vWaN$!ZAwYyh9VuQ&li?&*%6dsC>iFS$OY6TFM|OyGvG!>r;8n;fIx zGTm>{#zEUx%;|Vauoc{ZX^%^4!3?DQ2@4$o^5qGwJYwC8+uUg1I&QwmLS3CLIOQwv zO%APnTqJOcWAQ?f-A9t|e=ZSF|X$q|7P zN4Bgl*jaCG~ZRdGElR3i(Z%C=}xqH+0%n1Dk4NMO_4oD z*c>)SlYhmthEk`6qDr&aZ`E)L%?Jb(Zdx8X9OQn5B+bha0Q-Wgz-1*|oi(r~F_f%5 zw*|Om-VJ?yis1(=-)bA`ksFdxo03tRvti*&y35wG1lg#M0R9@I4nkoFWA}|Y08!5t z28}Vqg!CFnokI{+6Wh1K7NtqH3!d30%NC()3)iEM)ME(cvoHJ^lrrj_QgcLLLMC2((>5-YI)RFI{68OtTA|-WTGCi9d&`l|N&b zR=ua#9!B{H`;%jft@mLRwnR$mnj> zUP6*ua;dIXY5_jZDR4^Nv-tGP(o7Xdm}wF5-wV!@lcOS%fFdS4Gkv37sA*cz_1)yO zgz?cokSTY8>Y2KQ;Pd07xsfIuEJcwpKzbhbNDuQ*UkUZT&5R6UdS+DOQKK@67;5}fU@U*e27BTDFAM&U1Kc{fzvQ1^x(_o5008O#Zo#%T_Kv^Y zFh_eEOG{%TV*3B0glXF#@uP49TcJa)zsvVnm3<*Z+Fb>}htFXY216VH&ZoXI!2rW( zo7g!w`}q{flJNYe#1C^qFNF^aFxZ|oOEV4-o|y*{{aIR4^AE>2q01F6>|V#!@!P-R_2BA6+xrjy)?60t9O*|1%#4j3E>s zb(Rhk^kEC*0sGEfK=U@{f{LOJSx={fFfujm5z5S`9kOGWQ`krjBckMlnSsW)0QT2l=*2qcOt=fNi*0NyH|JBLQ*ie zs~cGYl`)?whfo65kW?4@E5F8dYn7GeHrIs)ACqAYH!vsj0}tBw$Eu&AV-dh3OF4b_ zl#9Uy{~|!8_NOCMb~69a9~p3l<6NE6$E1YOJQ<#vdNebKc8WO;G?gQFa>lO_Z%pG2~R>>@AM|A~=&cc+yT0e2n%L2pftW3+Y0Gqm# z-A-8%rBL@=vg_84bT2mNzhE-o+#z^2J5?}VqI(=0qT9=(7$*?iy~U`8i(Wb{jU78A z+*;HfxU_al$zdKc1>Y?V_OLLrmRqrG%T7>m)vKJY*D7LE_LI=18EeT+KVq9T4ZE3X z?#aZMYLIj63O~~-Ipw<0nzU(sAk*)R{!$A3I1b^IGb#JOvg$uUmeeujpz}*T4F0tl zdNA?0>I{IpgRgYbFsh_yc` z)T+{?e+o0ghpCdQ z!Qj$gmE%1(x9q0wo8-Z-P+JPO1X-DWD`WbgG;i|!Asohp$yu32&{!3hAJ7%u7;l7%QmQHGA58Po}BD_)21Xbf$ zvzmW1UJB0G`i)(Pz8mN-dG{h7W2yoxAM)n0X}&13JY{Jl;;PJS7S@xI2Rk0^CC59B z@kP|q`$v51cWPsdMQie+19jOI@j-6i5RHb*f_dC0Z2Z?+jGnrRAQh*Ipz5<2tjMz& z>>aC7WM0lP6H;UM-xpP}ecT{7*n&AC-FhK4l5OiINC?HX*7PjB>T*{xAFE+mSIFiaO->4c8NwcFB zv|Lxw+yJ&kfb_pr<5a)=e46)tL*QZX)SX)Kqzd4rn_tJeJ) z^mk-fhz#p2ujGIDBH=z~gO4`mU!vhF6Q{85iiY!LiRy;JB@8M~&bDDPHT8;6(x&Js z7L^^XlS9D~q=|Oys$%lw!bu`66HKfa_k#@CgG(IBcSdI!`}<}0dx&@oTAsn4QZ9Ee zn3?25RB5|QZ&Eyx$2nDZmRS~V(Zdc!4>(!Jc2SXw5*0EBZuyE`D6k5XD{CXrj_q;k zY}APcaVRGeb!y>%6{wcpxOj9m&`%rVzkVr$ZtOL&`vO_2694}82p@=`v$nOWuU5~u z1*!h?jFYMA6j8ECTGlx0RvX=5vUa)Sh+4UDgftApF6KMA zS#*tvpmnk)VSOL=QrT zI~>oAljBNJx@e-!aa`oF8O7#@BG zNa(OqwUwPkhaGPB@>3#2y3iSB%45fq^mnsxpi5^-V`4<_flXUQ+1g}=PJzmV>rJzYWFz`U&&t+jir zUzx}g<3WkAf+Hj9C+(e_OAadD$#FrY^$8iUEDTx=p*RaoVSu$IiHGIx!3e**Pl71^ zH6xPOU$%S#h?L0S=Rgg6Und+xDef4EE1a#=Sirc7CM7J()}h;=SSXl#)nV);C;%(& zNI2{6b|p+&VP^+MQ293jSx7|A-%Z#(B1KHNSNVDmGqDFJO1xbkQ?{-`iS*J`ZL@~X z@Szar3e#@h#Ui#kG`9;Z&w!aUWty%omS~FkCo41N;k%ol(iDBS-jjG1-JqMN635Yn zCT5VZ44S&57mq`M8}Tt)y;N|5haW~HNn)_T7t-?hcM}9Uw2hrX7v>hQFXMBGSn5mv1PINSzTS!Ri{ZJjlfv6NitlU#cR&=kv1nx~79VxKN@}>lTNO zzX4oR{oTJe^AKl~FJ+u9EeCKigdTfe80a-|I=k*b7FC;>fwLa1ZOf^57ZD{>3F_ec z@GTvdlB?=SRUCTQ>Yw$fR;`|a zKDF2vuD(XqXAvr*K|1>}(O%|6Od~neFY#WZIlA>-flFdY`b_q4t=J~`rJtPXv$XZ9 zGK{jjj)j)>{A5;UyG_kQ=Em|C>Hm<9Z-yxHS{=%dM*7t=#1FbawISt}GHx#= zC5q+sXAQUn*Oyj975m$#rt8^^@h}cL$^dGfJ!!Wa^?_2;%kxlu_|nuWJG;OVB%0c) zQ=u|m_*Oj~;R~yKs}pw+7qcH|uusQeLjQe!DQfPWIvi-LGtpdN^TWg+2aPC0(O|R!5%*$(^^_xLwkvhp4o|(a;7Nj?el`-=S=wqFi5BNn7 zm`?e-$94rBM|U&MLWl7gUeUhkfV^2Km`RZOzL3)Sq(*$N$-Z}XV{WUk@L672zDxV? z4>n-G*#b^TVY&lu2E)i2jf7^Ut^By5PiV2MsRPj)HL!M!Z%N-2!F)8ry}LJdJzg2X ze3EaLULC-EvSTnlL5Df64>ak0JnF0QdcbyO;4wb`;DeUnRxCGO(!FB9_xj%XJ=^Wk zi;ckW@VA(K#`clEMS$^2?exFl+!sloRns8b(T$WqDa7>BqQ+OsmeezOhU265kHCB* z`K;~MKEwB;dFP2nKA|uKisLF(T9v zqPM9Qed@pVJe_ctv7ysT{%&2k;BRXD(BqGv!K+qBhGV-81FbYNSJ z>u74$b90st>mXi zz)!Cz!qkq%Eq)RG1(P{={7~gE0fyH2BOD^2VH-!F)J-!YLS%YI?~oCu$U&d2$r1#R zdKER35$$(kUGtnGMxdq*TeKI7cV9OTW9n`3{&tpH`$|0kZE0PPhyxu7@#;$|x*gxG zYuZocoi5CG1ZwVPCkR+8c$6+;#cN03&#n*FCq)m-KCzh8IJ2}kr_2G% zo`;yx&&00JgHLTl>|vCVN`g87T&l~db70aWo-9w0W%H{qerxx%joD{$UTptrbiNz@ zdmVOdr%>&R&|$5kglL@k9*2}7_F;+f7R60)baC^t$;}XTmP$$BrE*(O znT8%WK(nuD?P9G>5_H+oSU%E+n`cxs#MU9Y84U$xmb@0PYlbme85d5hk9;=&BB!2< zu!ucQBlh_8m_7hII-`ow#!j*G)pUoAO}a~$kocj_BBcrCy3wMJ_TuJKLq22v;Xf~n zdJ6Yot1{BpDA*qad2O^n$a?_^04-Zgiet`!Y1@vptBZ}z%@V;{

R;2v*H!yTle2 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

e9Yy+$?WBotD+=cy;Bf$CS|h@$$g-WwRw$pCp>R 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%0AuT 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%0AZz6}`@}C(k{P*>q z1kVmyUUF{&UmxUMfX4Q&=ys>MGvt%niiCSI+N;6N$HjN>{`188HdVLY=jb;loC}a) zL)mpN_$uevi`(0|MiT;{$38ql_iID|h-;T``}A-@Sr{QXwBv8z ztexLu1ok;}jFLUZz-i^eF5pjLKv&?36@;jYr{}{6hWUhh#aXB|G**p%yK-owHE?Z`M4yWl3XBmeRu?ihrawrR0UzvH{nQS-_dX3+? z^L_T7qw3V_e~Ms*5XW7HWO==xwFua`^wZH~d1#)5MqlNgZDR1ly85*D@VN1NXo?Br zjGYAgfQ_B_`;d;E{P7WvpFw$rpfp4U{gLTrJ&cI;Q60shc-@a>5d(?6>`=*eV;;&U z_~4DEP`x_EvM7VFNDbo-8N|BT$F%%?0P!_2ujiB`h_9&eHQ=x5l!gePJW3LnSI796 z-#(OM(qi4*V=MkXoMS70e1zg_P+nmucTho5GTm&48L>X8v7O&MWk7JC)I(D;WtnX8>^Q-Y&WuB$*ys386woiq9YPS3OgGh` zUR)>i>oO$?$}3d7a*z)}ymEk#Uc3YB>l0-T;wxtS4cMzjymFuq|Cps{H^rEHfDiSU zd$14on59HF!kGIXAD;Ld*2$D=B^bl6U4{i+Q z&pT^83Gyo%r3gBxK*o>ra3uC!do+gP^)5C?6of%05_U)<;Ripa{O6rBJ_q>~k5U8! z)F9)>eRvZ4u0J|M@%kETC<;O#>j^!?k?{LA#`5Q#J>C%c6^pV59aJIX$91?8`>s1$ zL-G0;yCVuBAnOS`Hm!i|yqd1s3kL4L)c1fqjVWIj0$XJX%VMsp}$?_+yJK{#Zl@`47zjo76YY>B;T>aCgzD8Y7N`Vb zA^nO!G!*;f8td_Y*NX?jzCKfafrCh7KQRtP65eq}fmE*!v0q9c5z?=OL!j%wr?#5h z+I)EO^??eVJm5~0c<(%eK*&#m{T}cyq`RHpU%@X9y+Da~p4}d@PlDSW)GwqLhtMyW z|6uFYNc0K_J4X-hL1C7t`HNz*q8%Lm%)z*nfol zPZT2k?!MbY@hNb-gZ4%AaufQ6{O&#og#8rQ?}7Xxy4&&pihjB21^x&7kC6X~LX_X1 zcYA0)y>E9gzJy+G!oG;#p9g^`pWgdDuwO!VJAq%>FE@R_|6uT18-zY@pJ!Th3`6+!Xg8oJE zLK60c_8v3%1@|ev4}|)nxDyHZihCjH`}z;|A0ht}g@nHt`hQ{F^+0?v>;s`cwFbZ7 z-)lm@uwD$&z8G$M$Un7qfs*evy&yr+poRY7PrX@^<>+< z9#|K(~F+s_i3y!19p`1{Ny`4o3L@W7U>b z{(qCu2D@N=GyqS$em51*U7Zh5a8P(&a9t8Al;#k9n4?grDg%^hloSbRtQ_F`>m41* zTT6hx>vIzvjr3Jl*DLd_rst-VB?IpR_i11AZ8PfM*Uw+C_hctqKcJ*A{46#qGWKi5a9lyqT-|!DyXG!xDZJG(WH?6WbuUk2&0%|$`C9p2x8m; zgivj8Ak;$&T2YlyBd`E}I2bZoaaPpDtx4Sy9Cp5YT=gPt#GE&- zx{Yg*Qkk?eq}zN&n_IKea#OseG~pwK0^LLCsm&nL#G3s^osq@)V+H#mhaUBEzBHx-3Q-8_(+ZkS1Iy_L>#p*tzKfN=Sc zrA$Oc`-H=GB|8>^=#%8{QftGVYnP>rL`B?SM0XrBagR)b2(dv;QAhsJg+s@5;k zkJ_EVbwi?V!|pRgwG(qBD()ceLrOiAD&7ntYQSMnaU)fSlyZu|B$@J_c9z|?(yH|sY6SsTWm2i`Ml4)vELY(eMSxdl6m0t zc`JC5^pKGLhJk?g4Vrpcou)J%q-j$`t z<|;Ym623OU<@o#tL33&R29Z0ASRuT5OONl6ki5-c_c3l zHgY9X$6;#!d18BYEr7Qw1M!8oN8eA( z0AS%&x2Knl>T8&_oU7t)MH)rXllQ27l&|M6=q+jj>=({7vG}UjtILTLt`^U(SOfb= zzB~T2@gY}=8$=KvcG{#A2QR@Xm^Kmg@dKjRxnLR$qBxphi?iFa%Lr22x?e=aY{FUq4K4(9bB zzPa!PFr<1>6?cc1X>mNx;Cdn*w_yuB;kej~{jLN^E|+ir-jyoe4r=>pan7XBg>|tR zuDBWu>61~tUES|tKawaQx?CmzaK&+RZ&8YY2v@n$cs_*y+>EQ-XvQ^^KbsgovpxD1 z3->OxfQ0NNwJgVI zvjk3fAQs5+=v?gAxA?{S*n@YTa;}AUzCvHm|ELGGL-s=4hobO-wl<<%{+C1jnLXIY zUp3ZBVK!})a@cGbb6BG^p-<$W5vvYy7;%^#Z^DF;0OLe7P={H`R?^(FeRdio=FtE# z3HW!3m_p(a1Wl7e1>z9|c!uKv_@oD5n~i|ZW8DpYi+ic3u^I&kf^d?u0OEHCq zwV6y^Q**QHU@D!znacb7=@g9nv^zpVLeGpOvo&YdVMP)}%WAPVhu1Ad68R6f(jW7q z9|{SC*I5iN=bnblzehG1oof!SyI>n#;Z+8@hST(NN^7Z@OJDRu+A*1uy}Pkt4?94K z@t9uR5|}dv@9>%my%@}_!erT-b-_g&U-IC3Fq)3Axe4+s8(n@6Rr5lNp&{eN3%S5l z`Mp}Unz~TSQ^MgEJUwk@N%q1Nuv2zvH#}{8ssE4#d!O?`eD?tR@QD_|OX3~Y`RC)C z?1A_&v&p5&FgIH>9wf6^rU5+s=608ljpI)kvC^Kp3IFCbEdCC)GsznOe@FTUo@ve8 zTGJ)V>3V*{#p+`unW-@kyBmfn4+qTOb%aaJEPL|}4Yv$tcDD|$JKVlm0n~pXG#q#| zA2)3G|E`n12{$&FP7C&qggPPqj)<+PRiVhOiB;dot(jFwdT$kTl(T)6)MFDiNKN(1 z&ELChNkR&;n&uKoEuONk;tuzV)*L0O@mk)Ez%HQED9Lr|WrTF#UbJVTnWj{KU8@M8 z;Wf%56zy24p{Cx3{zaAHYGWw6E(JL?se`%1R{XK#Hi+J#0E+>=8t-IrX_3d&u3+V4 zfEF=ion0MWKJhS_#a4Gw(DZ!qIQVE=O2Hp(AWt`EMej&X4-DGnl}GcTu?052dzWpN zy{tQLTV7Vo+&-$MyR5A_&yVb3`b2MTc2qu3r4vhQtG2Scysggo4+;g8RiS{otNp9E z9E`SD64K-2_IU9z7T0WjIs9ZV?U=k{D}+S)aE>5hTGWh5xHF3 z!$&_ZK@f1nW+{7B`pc(H-jJ`fcvg~2OcnO!)=ykXJf0W5H}bo)k^bm=2#;`sOPf?p z6WINu`@gB!S4Zg`Y;1y@q)Lj1*dCU(xb@foM~X*@k+lseD?BXpH8tgr+}FBK@YTSecE*C)qbk#cFd&Yx!mF>??8K9~?|i+cNI$anDXN!ZRkDxJM~pm|Mt7 z=Oo!yv!=CC8xT)6W*-m-;^NfhGHpXJYSv6y`P(_wJw6O-57*7T?{5bUR0a5ORdh}% zB8j?+wn&!k{hn9{;p5)Dr~gea#?PIk`pxfvp$tah_3*cviVWB(gnm_i`5a;K%TM%Ex+-~ z9@i+11i~1lR8ng(K?<8Tr)XWUyef>~^qYNvL89A5rIO|rE-&%bs^qf$hS5VUb=#>YlWJ$vj7ZGNkO zBy@NEFNx8tnIE?f9dDX zQOwgN#ts@{L`7lhRiU==@IEz9JTBwyVDSw7;r&>#duW0FPY-2GkMqUGFk~gX!tiXPQlxcG5-O=kEu=iaXDf-oY)MB{91u_g+!(CDCA=w{VeG67s%oi@|n@zE%T8Y|6`M7!x)D(eubbBdbDf(%ImT zUCuT^)Tp~|WSMg<^HGwBrMC{Si?!oeCioZd5*2K!{&V=;r)8N^`dR!-P{?_nXlYRe zLHg{OgWg_DF!&DJ==xdARH`n?D%e-bcM26x=AcEXexjr}6=khf>P=$*_<*`})>+g= zLVc1_L}iEo`k)n>uQ>ERpT?FjX&i&Us|qrz!+7EY^?w zF04;(;hE&2+6F$sO5uI@`eRD$osNEzf%4owp}OA4vhAolQQSHRNlP)AqCYaxC4z9j zuNVIo5G_we@%c}*Zy2#_NO|?+rY23I{Kf)XhgmB?ueER>=SP9#qGBcST5WH3u;O;% zCH6;i3}%6ShykIxkncJ|YsjYa7x;@xIuO=Tx=QEYL2-cG-}C}RSqR%Sjk3N@WdbNK zdyQ7HQi;BS-}E^e5*||>a+%0(i%)DU#AnW}8qGsXqo~L+kepI|mi8d1G~k-i%<6Am zTk~#XUcxW#wbBF-Kvsd1#&HYKb0B}m&^FnF2o^?FV%3;71!MC8d(HfT7U{0p^jifU zvX5I5Tk36r1SM|?K$z@J$ZGe@w>|7U88%1e@+NY;(dAbMUZu72__VG zK8e&U7Hqr54tb|_ys?|87Xq1chlS^*n$eMp)6`(fDhWCVG~-e`_V2b>Z*_2a$;d~&+w9DD)*K@t}BMT?M>Qlvs3NGl%ClR|z^6DsDtpHYY7jXsDEf{D!L>H)me_j!mdtf;F zqqX|%=SAAfB|edzhqc>15$BAnk{-uDKf{I7$hgH8o#ouu-o83 zPCdx1@lEFSwR z#DRI)rXexJex2tj?ufa#bEQqv?7yp#>#_m=0j`6`o1(a^4e4gAjQf|9GOK7nODonn znQ->|E{9S7XJqU55!P2xpq_4}SQecVkG)|mk!>zq#z!Aj$oB=sA1Ri>tH&*PsSXFV z{awL_@Kp$;rqI#mHcMw4snNemte`&-!J}yO@QlQzxs8rSv)c`%0N|Ddy%QUO_S(@R zn32!KynesuB4INMuw+)!G-%OSyCE;+r=`Z?4e6r(swUKelRMgJ!19Y#z<8qd4&hd_ zt&7Vzz)CJBMonN_fZvyD(BS@% zOBhDk_CRhH?P zV#Mr|?@iZw0j@@%F9V;q8a`afD0Y{N8k2*3$p>?pbOlu8H}xkb6s>t;=h}%c?pfGljf_Q%C9w!*-6UJXpB2jBgy6od)q?5CJ$u>1s zqGxq2k^IzFbA-{VwXb3sFca{BPKn2&;)30t;+*jK*w_wy`>E6RZI1G3UDTV=x%rwhp|ep_ON7L0z4C125_&vv#nhd2l0>Nni4G9K4Yu- z2tS2T9=Bx{?kH4yAC|eZP-eXS8(G4am`!m>t)Y_4s#opG1;pL73;}#zXYN*>Xodu2 zPa)W5%>Z%oNaaWRWNXuh5guA4k~=3am-Uk$#JGuV0irlj10<8!f(Gn1VWeIMAK}dp zj!k;?EBL`ma{uLlUxuum*nhloEcD)NRTDcl_l5>bpMrRo%+s1tn88w`*ge4D%oj2a zDh;*Lvl^$UesI~~QhQSAW-Jl^JN(GO%98=)D2{TIP3dOWjo)A9TXf1A(r8-#de8n% zUP<$v8cM=E_3D)@)2V75UD32;dQ){$VeJ&I=Z^g;4TXtGdv)ugs}EdEw_vg*!Hk6r zODC6oV}z#D7{XJPn$5Dh1iwB3gG9K!f>a?>*j`3OzQaAV#R8Eh>>rO)O-=qI@G_2~j& zE;4RDEZqc-(g>l8fUe4LvX!lrSN^d}SjXqNn>O$U;87y$0SNO~SIL!iaXgOBq3FSB-*k$NDM>)R}I=+qb|atNUF{#BrSRg-|;0ZOxL4q7Ar1C=QI^K=vp*A8+DMlm!>#Q!MxgSdy1AI1WKGhS~|26T>_L{%h?y_(Z zUK(U(5AMO~vmbS*)9csmm55fx4&z?C@!J|a4faVIhTg5hYfC5-&iFU~;^IF09WyjZ zFA;~`{o|T2unsHL`UCw{c(L5BnvYBJvVgR;e{p*n#lw1f8@_yC`V86ogB#_$lbn$o z`6&7mp2aVQd@)L4Ek3t~5b~yWlpkQ&goF~{fo~Vsx+t$zu>>zFpgAh#b7G~P0eMM( zR+*XX-=>R3cRa)=B%P30D@L&D`#wYABEB*`z{zhqHxt!MEQ!oiRWJkLjv4=l7Y zTpy;SGUmrn$aEMEsVl(Eeocy^kxlUCnOTgPq{6~hF7ZkC4${&iOFWy3vCEjx-9WY^|DW!dtn~)ct~Dx_6>YYez}m zG2H&w?ZjN?Kicv~;oetvp4l{R%&LDw z*^%{2BJ!?@rm@O`L+7Vly#QY+ov7=vZceo{6P7qf3sjB8;GSc*x?`t1 z#Rsbfc}=KQ+KT4{i5P*V-b6MbS1fQ57q`RibbvcAb^jlTw_E~ZWE~{@WE+@VfDQ42 zUH!+x^Di5E-=0@L;%N*LVl7wgfc5qHh3OV**oP#)r_1>RX7I|ph3ew!% z#0IEHI@5dfHH=`3x{QtD>UpH7roRKN#z?`IC9abNW1J11Zn4T$LaN=%%Ce?Cmj^3V z?=|}&mA9PGmYm1=pX9*G1wOm!8M>yxu>Jc3YDMOn^%eX__oiFCmFPD;!6VMiD2ZqX zRRITg)@^|{;r$EiD@GJounOY4mmdz4xzGVp8^Xn>pKKgfe%&@bdt%iAO4+`cUVCN<-KGO)HXeU0R zoq8eE3~7)yIVx*4xz^Hb*g>ssKY^2kBK^6fs64H{A6a(t!q9Ux@ThINR=XL|G0~wo z?k6v&`e>}}fxcR*Xd_h@$8Knf7T4i{!`)rtS5P3#P@Debj0?W6G3PL-=YjrzCOz^Hw7Wsj_xRxOfapKVR9i zTRb_RsJ^!5Vi=6wFUeB8L<+pht1c==#UYc5*>B3<+Ip7Bx|ncM&PC zdC>_r?=Iuh%H3~#*AzibX1_Pa-PCO}NZH*ZBfhTe*jS{b)46tPY{Vvtx!civ#-?^o z9305F%a1w6(7dcA+S`s?jK~GxzHq^qo;ZB*ODUVb2?Hf%4t7;s8clAf7-0QYtz8~p z>#HKWm~DiKIw_V~*lZe;XyJ3^e_K-G9s3uoQxQEV%0Lj#$A4QPm<5 zrbu`AP%GrC0lWNAD-^BZeNk{f#T^?wBFMJGjTi##wa4w@uD?6tP?tj2|rwbL-TOvJWz7tLsagNL)J-I)1_I>(8r##T5wjj9X zft0F8_#cdFf%Q0uPvqX;Y|L2*F0m$s%}AD!ioLN$Z4Fx<5TEe9i7>j2YkBHg9LPKf z=4y%N9AUwpqF>U-Kh101**r>Gv2P>_ZrN$F#u|2k4cGx zjJ=b5t!iUdN9f-MjdJ9gJd~)BEV(Hq)sSRU*b(n6bKUUpO-I8SaW6Ss022sEp5=m5 zMqk6pK;|XwS}fm7XLw^a--?c?1X ztIwCPfgiL?fHh^9x3nv+!y=~wvXgPoT!|+j;>K*K6dm5ub85ahwWO$od~Cx@A@weo z;o`I)EeeeX1VM*b;J3!TD@PQ=tc=~TAc-!)^+3MwTI$AsOW8RN(?E<83BT7l*Jd&A z6eZM+1-W2E@#&)2ITaU>m>>rbBd5M=B_~H69pUOSd-Nq)TD^)Q%<~q;RIpvLu%;DZ z6k;G%fe8TpeTvC)z(pAnnb`&#ek)r+f=RB6N)Qxc;jJ6lhmvh07qYMbU}h{Y$)4+l2Dw@e337J5BO8?9<%3~emdUZRZy zoiQhBir?jBDNdIf`m% zQ?#juHcgxE8nQVq_gqz*VQ4co7`)kRFHl0-%`a7(V`y_xU`-dt0$$2}l{GbnR;Hnh zbEqCPYC6Ku4#r*{qRlt7L)izE2onH<3$$`WtI#S9txEH%TD74q)E2=!+t2Y(L#xpi z8{!U-A5ht0hE|It5dxBspM@E%)gh?I`Wg@{L9i4-qvkWTCd~&VY&Q?IY3tUhp)EuH zn`rn<=+cxqQ>IMChAua>6P(oLfN2HcHS`Q zkUHTiofV3tqtye_-;PG=Ut?%~kSREy?pho6 zDqv{sSXzFXwP+Z&35M38b*fs>(2mw%;^!uk;aWnSzZ)h%pZ+DES~ag(HG~|)-)xhi zt<^$?)}@6FX`KX|Z}tI{Vw$MQ1yM5V0CHC1N%p=Or|mnVuhTzm=B&QWjv%&1B;!6E z(haR!TW8+cO?%Rol92~+RFsmbuB!Gi#x=3}NE`<(0K*#9VyXsnyayPXwjRL-9K6y# z6tj;jjjFcM(0a9FV6JMvF|=cm+>b*B<7+@TtInucQmrz52SN4W|6{?Fw}nv^ikR<^%~l#$oNmg zE}xF_;yCGeLpwtQv2Yev^IPo48QSj*?QHEFLpxVH&(MC4iT{9Lf%Zp3egj>HYUkt3 zzQB;*l;1L>3#308+Mmq47aH0{DCiDFQ?Dm%H7n+@rb!@4>-^!~v==h11p)KIpi5~@ z97)Cbb}>x6GGvR3kS%U9w9OhQ4MwwhF|;{sozaHp_Pur$D5;$V4A?9yAor%0QDtCVGA9WOR;^V*Cw;e6hSK zNrP%MvEn+H;mC#=(@{^YF{DeS%M9&C5I=IhTwutf#Gh2{CPTX!OfT&gY}~D=Ess6G z6Q7`mR{DE7I^&)VouGnv+JZ6r!Hsaoq)C%N8f#nKB5F3O2HV^rbKIV}F3+?nZqHP= z2e5e?w)J-H4prN3Xgg4`>8nI1Z9N&(Cz_s zPTD5jX2@Qcd(s@7fcIji`>^yu{hc*5mp2ys3DD$Y=qlWgF_BhViCk*XL9F3>Mb3nnVZ78qe)MZF} z#W#VXCzk?ilsu)AJ)l%;PaE1Z+OvpMkm!HM>Yv9Z|3mwyp}m0e{zYtj0&_DBX}5GI z2u4SpNZc|i@XVb#+7oH__(LJjI&-@a^yFQrwY;RgjNlbjd)3fh(_Y7W-v@GT&d}aK zE%}Xf1C+GYXJ~JVV^!@fLwj3$2kHA?s`jp-y{ElzXdfVJESDF4DBmus(g?<40K?a2J%XIl_9<@zJv7iwV{236X;u1Ous|$J(A522!6y4 z|AcCc?8BvRO#ic??FC}jNsG(;LG93bQbWgaUECN$1% zZUK9oW3!%k1cI3vJPC5CW}y%`G<~IJgLOmC1ruJnQm*QE-2zFMuR+K!8hRdfJs&{< z+A)bynBn#Sh6}+m(FdSWk_et<$hC5vJ7l)o;~wvs?(v=p<4q?~Qe|zqdSJF9B0w=l z8`oJHFim04NYBKn)88}eAW z#NA^zVrIIz<4nMkitizndBEn)9P6<+yulBGLmvpX?N}JrK}h{YxC(R{3awMbQ&F)T zXUIiJuS53nP!ldA?L}1pPop_=9?8sZIj6yy77a#Cb-K;d4r-ty-#q;3K2J?OKHlev z*s2B$;!&QaXs;(2#)Dn>z=EgCztPhf@ORtQKr&v|;^lRKRZm;MNog)>c-C9@ay?@q zYqQ0Wn$5P2wfG89%rF~ewJ??V8!mXM7bcVS+K{0SW-mZz^&y5{jGSkn%^s`yQ4g1O zY`D*;82V850?g)ppG^Ulo5XcyO)>O?uogPj9qJZuvt}6jFzp?y2kzN03)cb`tweUI zdy<&stT~1plA(zLs8RZGeFT_?DL1^pnY{aty&efBhd#>9*>LaCH)YDSY3@KMbu`u} zAO{wZ^A`CQHLUP>>lfD4gMwUN-%wTIF<;y>q^G5446THxN8Y8_8%#s&^6(|AaHOij zJ_*RLnhv&Olj8g^3NT1xu(TPvURqkptqhCcf$)05xNhR{$33VgBUQFpXJtH)Sl3m3 ztf80az@|$LeY`%w&?oAXl9qSU9`i8t$;hph5m0Q~A%^@UDryv%_!J!mXeP#K9-vXI z=nzAnh3zvuC9$3u?)?DcH}u(jTfkFdH!&FYgt<6{xHy~%2*Z?W>E+sC4KzcakIj%Y=&d214zpQ21Au-gNLPJ;0i% zo5y@J$YWh=0@jIb2d>ID&<#C|x~jaase0lZRR_xN*4I%|YG-z?odD;J1I2=N$vLyV zG+l-&PEEbf+t>t-WlQiWOOQ^D-X*o=mEcmUytQ5o8r=VWBnTR3JIS`0llGIKr&04{ zRhs9MfRqyBKuLbWicmOY;xF07eUZdXwUz*kQ^2+JoL`czpaQe?$-9GQ zFO%foKs$HJvzUt6PdxURvgaO(bWDc0dHm#hy6iP6mwD|h{17Z?(aXXu?rq3^FH`Un z4R{2?O*(joVqDs-aDGfPeGD(Dpu@U9ll-z{CSE;pJ=i$ZqK5un-77NDoA$N6fqi<8 zq03-U+H{4}GEC#VuA$_d8I9ad|7x6Pb{_~;D}t`6{rX7EX~ z>ck159QO-n=4(W&(sYqCo78(;{k$F4-x2xc=0tT>KcnBeYWgXJpR7EzBxN$Eofhdy zoFB|HPpVKd@qlP1@j6#=Nt!EFiRi&8>mC@7gpIU{Z|s!E7U5wQd;}-`5=(Y}Qj@-YwuS6iFrH@I8ZB`<;$>QO*9hxWxpNMv+_a@+n%rd>ut|PiKf`q_%pwC*jQ7HH1D?d6G)qgZtMD2J&t!84d5QZtq3hkMywp%!An?cP0~_(thcN#nKda=t<}uyf8uym}K3PCf z46jSa@bXsNJUrCapm>V4ll18LjK2&6NI`7m`xC zH?Rg-2lHa}5pS&3-wpcp(w+#P21-ed$p8%Nvaz+2?%*EFbU&OmJpEBCD}hPFgH1;3 zg!)4MSm#1tyccEGqSW@=Xiit-z6J#^)8k13nln}Pt3aU=aT=Fv>p&y)%=f2hRRKxhIaFe zYz^Mj=xK@BvIyS;OxNsO_X50i_MCO&!A;J&(>{0uWh-c1mB-DxD)}2eziu%4x#Elh zqo>_IJ)RLul1i3h{1H7(Pbcqd1TC8F^~M3_W_ zA!s>RKJC4xnpBAfO*^WT0h4!HdK%Bua$xNJe;eLBvk&DUL2-%JJW9@=@(OWt4#jP& zKU5QL=O<$e?3m;WcczRpWi+yDJQeSVJ>V+1<^Tl1gE#>gP6qB>_^Y~_?wO|PDPr4| zApPZWd}EN`G;o=Q2XzGY{U)>oP3lVjwAt!3(kvY{rhP%|e;Z8?HhbUiV$1xZ6(zF| z2<7Rn9b_ek0FUywwK;~M^XS*0BQ_sLaXB8t6WIh#P-U=-=5zJRz+|P-#)(BV2W7d|t^SnB~ z)!5TlpR*`(-!z(d+AN)FmM)6b6Ff|L;16& z=DDPlcm1HghQsV@eZx$$x0aF`!oY*k*tK!KVv=I{nV0Z*qx=+Duaj1#FWq9WuwI z{nentbO!%COQUcMoL_E7IZf|-60trKZVYtbVM5GP6%Cs2C^s&vH-9F{TVK`C2<#&f z&}jbZ5x%C#mEGJVc5LeEnMncew`ZN-YRcnETlBCnC-yKY9$)eD?u| z!)idKxog26PCEeUpTTbf+2foaOgvMRU>5v=32V^#$~qj+$c$mmW8|u+)U?Ma(|O>-)qv9`-;xRW#^{k zCkcn7y7cGO=;fzG}M zpsl5PYvsSFJs57QG=D*bEBoc)Hj}+jGH(BRvV(@6k~A;pr-m%9sY%*~`XypItwuTF zmmrA$;#D}-(-q*M>D12j`FT}^{91F;Zw8n@bk@qxnp(WI4mA3W{HMuc&KXq5>-JXk zI(#^vzv$}3SX!*!9mTEfR{khS^nZG{a9_l1N#L|vYOAb;N)4`tQGI?NLqZPxvJ*SkrHO_wz# zFzx@R9`Coqna&e`<|9Re`7#H8vd@?Drgh)_OXo2bG@%vvJDJx_eNtUY4=5X|WPYzW)SH-i%Ia9XeXIzJ(u5e;-j z)(5KbJ@O=7tj^EhLg~U)#msob*VE0h#>YtG{??IQTLpg#({A=qZVzG}UJ)#Pk?4>gym@r7Go%5R&*%onw+?YDk(*;V{K zxYX^DRjaJs-xCgOJLSN1#(m0y{?;){6IwMM`fO{lpEt9gJ9GcIT}fXR9G_22pL9&3 z7p}vdb&k`~k+Pk)R?N4Nw{u$!-8n15xM4S_Y@FI!5uN)}u2(uWQXxGlDUneUO!} zJl2HGd#`2#e6GjbJSBY=j8dO-8?ujR(^z!<)Ji5_Z$s{7`g5#?aXy*ovlE0`XI238 zEZl&$8HDD?WscTJcW?S7ftoZyd`ci6Ukv1ndt#xWA51paTWKEaM5(Vcvcdk5V3@E= zYZJd1yw3%GXT#H)30AgrC$X%9xqLM$@oXAkuOl!uUEnm}@W8`?i=*kLZHzb!Rp-HY z$_Dw9xvPo`ywOOP>q0atB#=4nF$w$GG+gYnoctlQU;Bg@ z^|TiHcfK2H{j zRs}nPaqAcADouT8RUjI{^U0|4R|Go!>w|a>r0I zbG6c?DdXgc<7-fO3Br0zDdO|h+GkT!pC+}>xM9|P#DW+rh$o(z;xjYA-#yB41y=+* zdcwGOAMQbx*@Tc|$Y61#IEqklH24HMo)n8?#Id$tECK&Gm%kMJ<6Zs< z;GgL7PXhmBmwyT=5~sTS(}<2wW`KLJIKx$M2>df$>1Kg{w#z>U{B!%%Q|9u|gZP78 z{zJe&-{n6P{0m(Ea`0EU{FP8%mCNsi_-dDbA;d3o`D?(xIN>LEK>EX6{u{wx>q=h- z{(4vZZQyTk`A36)Ny1ON!N1hy?*xCN%U=k7pDX`O;BV@Kzhz1I8ze68gRd1X|KZ?Y z*$02C`ru==%YOv;k4%i8@CM-Hs6O~?PU>H=xF#uH#}{Y7T`aaF$`M<^-{$fMpq}=G zpF9Wt4zZKdPcW%G0pAV-w}j83@USj1%)b%q+s(fcz9+`NF)_}=d&KqJzd_u{{k`Hb zQ0{NI`&jrpPCOpm6rUU8=}r<)=5eQpr#k6QgTK@J_|Hi4OZZ3`&wm#G3iw_b|NhSU zo-LlkzvuFP2>5mx|Nh?k{=xeG(fXcmeJ`-Sf3m(8THlMT@5R=4ll9$feJ`=Tms;P; z#LM~j3hR5N^}Wi9zuNNund4W&chC6uTI>543-4R3?{(Jqdh2_G^}W&h-el#wS-gdR zZ?(Q#`B%UP()jl_>wCNPy~Fx$x4t{9^gAv8F3Z2$!t0$@_#Ug?yR7fs;yupz+zWsA ziT88&16KTl;zLgSU*YdzSNccb?{8N5k6PcytncGi{wH|7U6AKV@hR{>O^7_6s6-<~ z%qZPSXsNiJ2&Hs85fMlTWCRKV6#+w_A&2wv!wzEEho@f_wx8 z2nrDlD3!L8f#@29pa{WW1Va!MBN&R{AOyn@cn}OP6}FQR=o*P&RB7Jm?PN@8-q`J= zq%_y~Xge8);iU-1Bbb0-B7#W>CYNg4$rJ=r5lq9d=?G?EduF0*7J}K>kvRzFA}B*J z55d70dq`=Kyq(N1y@Sl(O%A1G6LfqJS+H^^DM#d(;h@)H*`}uwJyoTJ>UQF_f~qlS zrsY|Po&iwEqLsN4*-2_}2;4rYv>5v~9uZw6A(COd79;M>0uI9hYSClBUncyGo4A|Q zNf4S*sBD6PxC0$KNc|Qfmoc7jH(7$YGRzW|Vm~HArHv~w?GED0T|k_!n2sh2tyu}I9~&}w7irl9R0arYEzN7&MFQJN`xZb+UWe&+ zunqYEft_1OAs_<+HLZx1?jT3+B5Uv62IA}u`T_lzG|-2HuY_+&5GKnrz;XXb+5qMe z3G!>?3X(;xB>ChjGKgG5hLXRK5o8M)ORggm$n|6@h`?FoW-^c5MHY~Ih?m?;7L)tP zQgT08P97ku$%AAKd6)#qBjjlEHxeOFkvMsp^pa=D@#I-@3W%07$v??L1PiF{2p@*T~R!!%!x(LwSCI#m7*9U-4U$7)~E2{fNhqlI)n z9YAa7AiA6mrmeJ?2I)|`o(`j@(BbqCbOgPEj-uDl(e!#cmflKB=uSG0J_Na6rjr1Z zQvjFK>Bn>`{glq4-_tpQLgxxo=se+Ida$sV&KH_#xe%w7!b!ABIG1{bOXxyjH?0vK zr;CMWXsz%ntry;vy z0W`0|I$LzcC5e!l;q&t?D8AbXd zWV+|fyGd7>A=NDwdXB=NG0(xQx=q$fG~SthGk zkF4T2%(fBo$06&6O{5Dx8*{~vuOzMG?<2isnbNE*GdHtx7l3zS&Vu7@HZ?1&Fl!T; zxrZFHva~3($k<7KQ@6Fspm|06c%@48K5f@1vS>=iY&@Z)RU^I_Xl|D`l`UEhOr-70DoerV@pdR{9dN6$v z*vd=v2>L4RrEhS0d7cykJIxdS1^RIo$s;4hcVYTv0xQk4T*nbbd=FwX$d_c4_&(^v z*;FOD;s@e~Ff;BUtHh7Qk0IUtWQF(%Xgyg-V>T-Q*QerVoIZ~g{|&mCz+In1$r5*c z0WO)l{$nx#aQzobQMv0&U=)nIzJf4~yS^5`Av)l#SNs-S1|35D;&;%hOxjCC-m0vm zx%a*JgEf8TLrpk43ignbxO_w|0DsC(a`M(bv*}&eY$~vNQ-IH#T4im7o`{^2>>#J? zAg5kOV8DmeUI$v*Vd##K1wdSM&qIyzv9z+W!^nzbFod-^F+>1V)L{{y}FHz}f@ z!(9A=jHdr}wPK9Zil3nsd`?4J74b-ZD7CthBdR;eX$KEcuZDTCgPgvH1Xt#r0Rnj^ zIrCl~KRuW1Jsw>Wcjd5+DAjXh)0eYx%^~2OzKK-rA!mV>_1pT1dB21FJIL89CKm4? z=d75RcdqTnjOs3O9(o+DW#SI<``zRZ0_rh2%5>za!e-Dq?;tZaff{-T8Mz4*)}cGe zAGbi0xfVPW6;13x@o78B`G;>I6Vvl!9x95`_}%1BGRS|aZaioZI1EpMBoFfMd*lL; z!e5eaNP~sqC@BV1?Iqa)B?W>&1_>e=EhwZ`V8kbAWVMh%0)j!-3Ynxw$RZ~TIphK% zpIjmolB=Fi(yM>|T0bv+Rw7eApU+bXSwP;$9SWAV%(yC>pJ#mYgIBTq<5drb!|gRwC#iwgCColC1*tB43gu zlVpEL5h?}vp0G&iJ3o64Nqjp~;?IfGa6tnrWtSn_}k(zgj z)!-tvNGaY$E=_80k?Lyj3~ZYyirDrd;2M{i$u391rI+p^SAdyQq-;HK;^peZY4miY zUHD!~E})mu8|cjr?cP92fm6%|+MNruTL!c{4`_Ej(Cz}#B$Sg@p^6+Wcu7=PNRAa2 z0qxe1vxUWElTb@;6dK5F!cwwZXe5saKJv7%oIEeABrgd^kk^GH$tOZH`H!%M4isAH z2q8en3GH;6&`A#wf}jtsrAG*1+A2h7w-BYr3Nd<$u%4bSY@lZd8|kIOG4wLwSbBqS zJiS>sj@}}iz-j&%U{+C^=IJz2%3Ugmj14yR(=vZb*5cpbdaj&jI?2H_t7b{1LkodAuAaY-z*#a6RZ3BwLooMaxXQyB*qen6!&NI>sW~f0oQ1|&!6uSv zX3E_{oLKNV3G;+m`NFJ%Vz3#ls`Gi(ApN>f`eDbmKp(k+0*%LpVxtCR73LJ?7UmV^ z7Zwy|?j+Zh6&4k4{iUe6o+IM=S&Vm2oTVkTSxaiO=Cm2@&D>2yIaR0bR${@}Qmd6a z$qi*0MHvZ^{T`VM&OZ=UxPW8{7m`BZA~Hm{n2Z!IA*I5lWQuSZnIl|576@08YT+uf zM7V|=E?i5F68=Iuh3iSTa06K{+(b?kZY5_5TgkZ~urC&FCzlD^$u+`GvPIZUZV~Pz zJB7Q*J;J@@Z^HfLap6JoobV8NRd^UAz@y{~;c<`xPk&hp*PFD+WP`~gdT`Rmz*9q^?4Z^=*hP_8m5I%qz_96Yf@G-qa_>5jF zd`@oyOYBzROZv3%4bCuH0g7WG$&w71B_bI@n3TztsZv@XW$~~nG)v0nVGGF5QVv(J zs>!!fE?2M)C+|slXkn3~$eU6=xESdsPe}#f(#U%9H>nU@I@rTIqygZ{Am@^;(m-$- zV0mwr21!MrzHA{ENrOS1%K|%iy)*=3bI9W)CKW?$F8PAgNkbtl59IJ-=^$|BgFId+ z4FgvJod*KTBMpaMz^rr>#pn2J$NBu6Gy;@8fqKa~y!9eIjGVz+FVWRx18=EJ{bU_) zsY2HgKePpUL^qJtye*8LKzzI{8mP__d0TY)v{i40i&F^r9J{5`oo1?H$4T&Wg8bA#a# zRn8ziW)*g~$z13ngJzHHSUraf*-QqP zG7(I+TTn{vAh&L|joacOSJ+M|(b~bLxlQs#gNzWf$P_V;93mEyMdBc*r6b4~X)F!~ zw!vxhVyOheD718(W0udgOw(*^mgF?6kAW^0yA;}N+enp4rSX=LIuuZ-0}Twr#YYc< z;W;_8B(MjO5pL!7ojehI6K$UY%t2aHsH{;sbJR8Jnw;Ullc5pY@@_jG7YoNvmw2)>h&T=`{!*fg6TtSFNQ%Wt zKyOn(+n5HUIfMAbS>#A@HaS|HL)M99}r`a-n!A*(8>eYs3n2qv$2u#D(Ne zaS{2uxR|^t){*bT2J*AGgi7Lank}xN1>#CtB(9;~p=G?^exfQie1IUK{M zFeBfQ(b7cAUK{32@L|#IGlv3c;_xG{n#Gs4hl+?8uX_jNtT>vdXO1Gxj131mfO!tG7 zJWM9tMjomi{|M3Y{<@1ijIIpSPzP=SC40P+ZU=b;!V)7if-jDBlR@G-7?~LHh;iTu z8(@Ssl4;^Gq*6T2Sw|`+W2M<9mPnN|J?7Z<9{Gc{el^=#zgl_l<$V2v4LNNG`I~mZ zU1(x{2eszOfp6c|fkePaPI0VV={~FQ8^*eJ4Q=w1>&Dt{s4GOM9;! zZ)^UKg3gZVc96$V|9>33pthqTKR)jXOP`0xC+jBcCQo4zL&k&aX-e)R&+H)2+Nrke zecMs|$J=^8HeTe7AH#QV&IgWp0U07*2=nVA;uSZM<>F>gRxcr8P-agQuXGSsM}|uW zONT(0hme3Y9~V5ZLuO~CL#^(csTM%D-KZ>=Dl8lScnAXuB3mcqJ-3Ve9gQWHqbQtL zT|NPn=w0L=pxc~J>p`=|=I8x$H+eyTuDw`4QG#x~1ia+sG6`{IU_(j3kc*_<?tQh@1i6v&6f|+2B4;yqjDu-b1bx?=lk}<(G_{n=0+13v7UYUYqoA*9nzIY8y_9Df!*YiHWZ=r0` z>W3(6KPqDukr%ODH^HETQESx zm4tuqBA=s$qiiRo*j8Le$SRa2Tr|l7`3)(`U^2KfuPag#SyjFtIUch@>oFtt!OSMk zQVV&Pwq8v}K_oQwKRHvbB7 z^Ztu_w9*2*dje1$GKtM5j$RrOm z1-FxL>^u`oLG}8!bmbjpnb+jxY&;Q5vUR+CcdprhD6?5b5^q6{zGesc&e8q+;F^6o z8EZEfy*aE6`eTwA`gfA=%hWAoND2++6!54b)Gv42OGaG0aQfQLwZ3gR9;j!CNs~XI zu*t@@PbnZjo`f6Xz##{lDqp^t=4VP0LvXjL7}{C(@+>SzHTC*Ro`X`%G23(t=;qpo zv>S|0zI!b0rjn4@N6x1*%pK0cIj$KwezU8nO?FWVx`&%?l@eSy%%?Dm0{K+qGluH- zTE&|cZBviO{TJR;n!%eYZRZLJl#q!Hv-&}^cq9+caiN6fTH%F>pnNN6TRts>;zd4m zHyxiyFu+d0M?Vuad|ceSeVa{DIXt0`$+kGT2AzPpR7p&xzSB8I79jQgCY3fD6STR| zDrd0W&3r4MmgI7*U*qb6B6V19skQ^?#F zjz-J$1TD}ihrVm9Dn_4VcDGN5iW42mEv1DT;7&p_hdyij(8{3?aw|teD+jB=CZC{P zu!$U*TH=gEiQ|fR;vIA>ca)sQUC1o9!)M@KYzHmbLMEh1wS^2YL(kkn%~DEs(9$A( zd_Lu*y@O7$>l<05r`T7CR9nbED;~_Oq|7j>a*FhcuCOA#NH?vkE#x_*BIq!k=wNc3 zV-Y4{ifl>pZA!)z80kg7q<=|#B1Bh`{lnxqVJ zgk+F5DT~CV9CC$}Pi~P0kloTCa<4R)JS7bw&r1i97o=kHlH?(uO2f(5(n#{7G@5GC z7@94Oqj}O;S|E+5qoj#+tTdTUm8Q@dX)3LiX3$1y4sDj^(tva@?Uc&s(b59iBbC$N zNEP%HsfwN@dFi>*BDz_sp|?tl=^m+$J|s2JC#9wIb;(EHmzLA7rIqvt=?H;IM+&*p z8ey{3CLAoa3tp*1SS%eatd>GTKne>z(mLTcQd~GwS}*)jI!4$eog~~Woh;lfohm#g zohJN4IzxCvI#YOGI!pLmI!9EbbHzOA58^cGd~vRHf#{Vk7CWU)VpQ5J9xGiU{$9F5 z+#+2m-Y#7w-X&cv-Y5N8d{){bz9QWq{!6+^{9L+O{7Tv;QRy}*Q`(MVR2nVqmL^Dd zN^_*UL8X2G)Y}K8mC~b9tMr%@k{*}3q^G6z(lgR2(sR;z(u>l~(n}Efvh;%Viu9`V zy7Yncmh`3co=l|oPERiy-;4FULr47 z_sEB<56dgn$K_S(>++H6yYf-$M{-;F*=r1N=NWD-%rVL5+2=Egqy)sIEuUOB%;)ewZTjqV}<<#OeY?u{zwLJ zn?D29d0W+I`O+G5nF3taVh*^>#he@_N`5g6M$>L}mDGZLlg9}k@dX}5o-Mq}7kE_J zD?G&)c$mCWxRWpNXz~%lPQHkwC#})8+N%P>JAfzL>AcxIv__{RyTLV@vWH22K1E?v zwu8>xK_1yeiu36#>25lEC8`i*zFl;Vsl@!h-rfUFieh;HuI`zhv|(mvZ|~R(id{7f~2gcJ>Rb))}jHc>*rnN4jFFTi*hg z_~B{YUa9V3siH&aG!xZO06M@a_|l_x;1e68F8Byu}0lF{;Y zxU5FQdGZ*{Q^;`KXvShWZnd)2T@XFUX!&}vAXby0e1kj=Td9Dbrm8-r(F%&m%Zn4^o&sd>YG+o1Fh%<{3h9wKR6_Z?T?OYg@ z@ykOc7*tu$CroAA$sKYl6%BE2=fXY(%1(g6PJ+!ogF^N>T*AJD0qiRn!M=v+>^r!R zeGkjn5AZPi5mvFEJpBzHYf3M(MnIJy#8$~SixUY0mdTR@!LebXJXx3&7Q)T)E%=tf zB``>yf+?j6G^fh9y6VM~ScdVL=i|ZA45Pr2y5I<0jw}93F1z$85MUlI^3PplWe_XE zMdJX=DSAa3p!k9-Op>cm4n0_~1HYdGZJ|9G;ld`LA9E-M1QkEzC_!kfn9x>MwuLPtNd%?1X^o0oQp6e}<8vK* zG7=~yZy^CBTp>Y5l|Y>^vTkCiQRAGkx+2u*YQmhEF3dRs>KISbCu7sa-RnIOwGo!6 z7bfGJLgU5pKxMhVRW>ytO~Rd6DPwZ^FileTdvXd%gghDe;(SfB48iXU<*=hHsLzwk zk=D}H9bDHD;$lUbdLxt{4V9Cd69zY4wD{)QPW&u`UeVCwWRh6pog6f|(CN=)0!N|1 zN#DW;U$?RvPeemvs2(A50nP3DqeeNI!UZQG){v>(GRId>>nEqr&RET^VpKOM9oe zzo@`%n+_(hbpg1#a`9q*Xo$&rB3X|QCPy@SL^RsoY1HYXiwObVuUiR}Ny~Z8Z6z>X zs;YhrxjlCW)Xc4r4zX;H5JW>&Fv;t9T7zEyVIZG3OH5Wnr%}Ii*GV#)tLnIo@e_`CZbyTKUh9?bunh$C9IvE<@)68VO-rSa&zF=?R76BI{-T`R zUD;63mG^)D+@0(00;Fl(Qd8socO9?N)C%6zT)vZ@Ub#Sc|ds(xj4bq z(gZBT-K;aHN*C}c7vrAZ9c-ls)Kz*yGo=@FQTo8eN?+)y6hVKb9}HA3g+aDMROwK4PX+T*;EoBXDs;sBYl#R5rvW@mow$on94tlw=lNKwx=(S3c-l6QJ zbCmsbzVakpq#UHnl|!^tIZVrxBQ&WTrB5o)(Pxzx=nKk=^b_S6{X+RW{Zn~eBFdYR zUwKO^RNj%!SKgH_Q$CSKDW6IcmCvL}$_eQf<)n14@}=~E@(*db@|Cnw`9|8Td@Jo# zevqD2PD!sSKTDq|zsOWMEo;gjvZddt;3qC4Oug_F^c+4SQoV^>!aqg zfhg(^SDUl3Y6~_=ZN-+VarUU%hCQPu*o$gA_MY02eXMq7U#VT#A8L0+S9>TCwU<&? z?X5IW`zuY=%aph}Sh+$Ss*F^JDWlaZm0Q)Tlo@KVGDjVyJfMzI9#pSYmaC(ct*T?2 z@CQUmCwN70Zy{QSoU2Glw?KwG0QtR;v=WZXGkKo;JxU203dpwn9`qIxNr*LrADy?# za#vUnaqcR`jVaqw!aAm0VI5PBu+CGngs>h|*2s4W-q}_jc7%1r;DwH`j(9x75!Ml# z$2r0};`CU-=NX9EqvhGcN1(qlT%IG}4SvM(q4Hd8FMzl{NS-JB3lQVwdoa&LyuVbw zmz#}w=ipAw`@mm?Yn(4HaOI!g*fSm9tlUTD6gSA@yKB3XG*n&6HQ>Uhx98^NzmfDCma z6sk8t8}(-BsZN3s_|thb5sW|mdA(*%YaMe2k1PDIXaiedt9$zTpRg!Et_ifqPq&QZMachG$>L|YNh;*6C|Ng8ZCp4RzuY_R| zOd*fBKyJp>QtI6ZX=D|@|;U*eN9HYMqlg1j@I#fU_3TYYp;8T9X)pE?#Sf#&&&dz>96Wl5MH zbI!0+rK?n|mA-Ekq}%-X5o@wNn{pv~stIS%2eFOnm z24mDMo+(QX^mV-wuUg$Mp`i zB1$QWH?dsL0y#-G?jQ-Bmue*VdYu<($l2LCQ5|sro4I%cgypzWhFrUr(DS3Xoo~Vx zOQSk3flM(!>P^DLQt)90*<1=!90xV=b4w|ViO3=8`_&NOeTef9w8(vuJeDw|xG&<% zT?ZLaBZ7qyq+18f_8}?w=RUG+NYvoqEGOG{a1XJta2FPRa@>daksbImCOL(6?#PcK zn>Ly3sM?hQRlCwHy(>2FN+3TL)g!pf9{;zWkyD?kGHUme9{sr+>ftOui8Ffu^3|u2 z_B;bE)o0;C^(b^vpMxUxc^Ig^0)y38krurMSF3Nq-_&M4??{zT4GesDc>Q=Av)JNr<^QHm4)Jl9QTn!q{8*n z%v~pOhjZlb_Xvae;B4b;#8*OQMS}Qy4UBSx1V_Fwqb73Ur=zCRdCtn4PVc=WbCR<7 z8wql<>v-9iNP0CC|B(3485A{DfdF+tioaqeE;s(6Ki3d^Xa8#*Ags*Q%?3pj8!}q02YfF!cUFt5VQkl#wW!QdYc7Ngg^2B@9wiMaI-sI^SwA%0UtNp(4 z)|vBoCw?shH4;P^m~;*Jx%V&`PzJ*zW+@0?;>XE;{N0x)Bj$c>fP-WBtECh%`MR?r zR>WK_%)-1ajJtNZ3lQd|wzG&4u@IfO^YI`5I%h@%t@fvAwG(c@ljOXh1H$BQ3Q~r# zFclsa&y}qIWnx|1GS4^pIELWXI*$QZ2;nV|J03$-G$OzTHVwEm=A zyNvAB29l?=%gOWFQ1XQ~jC`dHCqHN-$j{nH@~3tU&DBQHhT3S_L>o(E+70x4Z5(Z{ zO`sjMn`t*~GVP&FrM>(s?DJXwYl_ZZ61AAyHDz- zEs}a@_e;IBB~p>LOd6;ymxgN(Nmpwn(s*r!bc?o9ny0On7HMmw9ohz|T+5f9)HX^_ zYn!B3w8y0PwXM>J+BUH#970y<0~jbjid5Q^=0S70On8wTqQA&IcubJ?BIAYKu?brx z%XLP!q?d#rnFg0i^MxOo4r`@$@^Im^vO&5~-h?}W4>n6J#UA6AK6FJQZiIcxi%@VX zwcB9I-EA=C?lzcmcN^(cYPZ3ZyW3!DGv95h>?~V6mt2M|aQxziX1*kOvQ$`JVCp~_ z$bHHnvrm4dUDl!49`cj~g3VTtWcR6bdx&wGoF9uhnh@b$Z1vp;tl(e~-<>u|+`iVp zO)P^P#Lq>bmT-W*6b9hm0We5B9af+Vf)%tU!O#vuNP8MGv_lZljzBH#8Hj4nLQH!O zF3_Gw?)?Smq`e5;w3nc-_A;XRt1v)&1&TGtOf?_B7~w8>@?$vcDA=ws_ytfa<-7wU z&oK)Jd8@3tMx}j39zZt2fE|TUmAEVXtcq1KFyfp+dlyvgJ?!lVX%?s1p2g`ldArM3 zAH*r-%;^+t#5f8uk86I~F>j6~$>A`0#a|a>+n+*n?K5bjeGU_~uizH#>$IbiDIO0!McyGzufjv4Kp;QvV$%&c zWG<1dB9%gZZdz0!|Y?+)H}AD{5^xbMv~->gjN_uO8;h5EAl?>`Wo?1 zmV%yJiek=VWoM_zkEJ>KT)}Zf`xde7d+=*NKv4S;vF;Sk;ZIOc`xzQ)r=f}VI}~bv zKr8J}Xp7_PqRTKz*WfnYfIDfaFwM$GAaXXFPwd|pG*kW}^Cp~`RD(LmJZ(`Ab;RQa4$ zc6L9PGSvJedDm^-o39Zl9O7JpU#|@f^m@=lZvgG|M$kvkhb#2vaJ7CujMZDgc>MyH zpkD}6^aR|lw}V-Fdzhzp#JTAVkLq1vvwkt`)4Rg~y(b*jFM*?aA9ztO67!Y>8)iXU zF>fJQ4Y^{DYz|d#7G6n3ion$O4oSw;55lF9r(BYZsed^n8B?c(xeTV{eb}-p@;DLK zMBaXBn^-)rh?o&RXaHN}C-J*}l={mAM({WnOXLG0YDFKIB_9;JsVOi>V2)J`TyZqqIMc_4cUvmHRL)=VI5^86cwBBOE|=}ZiaJ5Sc0*o?IAr7= zMUI{F!6Z%GI71cSvSx!E!xhdxz|CW!t&G-%`{CrZIh`ChVxeQdP z8%WjNV2ytjj-iHsC z;b?8>hNw9S4E;99(x*YLeh1Xi?}Uc>OeoZ6L0rEZ3G!V?0A@pf{XV!{UkJnXMMwnh zhwJpkFiu|r6ZNHVi~b-?(@T*EtbqIUm2khl3RdXrkr-@-_4*b_>RaJSeH*->?}S(N z$Kefq7rd+Qh7a{8;4{4(zSZ{-UEfE7`hF7C50LiyQ>2UjH0h}yCjIpzq*y;nuF;<( zWAx|Ac>M)(i~bt9Rey<0(_bOe^;gLZ{TNxMzd=g$x5z5}ZL(Q^hiup1CA;yvZ>Xw&>k+7SkSPz44`O zHLAKT2l!G?5jfkh&lV1>%lH?@! z!9N0ls4wB)s2svAMQ`KN+XS2y`WbQ!FO%dS&Z~rpl;=;4lOAOZCp;Fxy$~BJjGDq=yE@WN#Qz>&=I5&_~UW%b&@pYDSlAi)XDlMrV5V;lc6kbG{Z?#O`lIu zri%;0uth%n+Qo{NpY^0 zuH}Rh%q)SUqQe0x&O^S`D#7IykCwq;=VkxQK&J27HrY~~Z%5rqAuluA0nSeZ<}CG` zq;tz0tDDU1J>+KzL}~Hk7YQP{PNQOnz5V7Lwhtn_=>Yz(I1Rg8W%Q&JGyDx2(|vhA;sL%;d%-0_54E zD+1&d6cRbnoPCs_(8vusbuk=D2{?xeRKmp+lD>#W4^eqHWfibmv^S3{henfqWW zywkW@MY9vpXf%<%5ynNccW^fnUM`5`h=igJwFlv+yT^9Z@Q{T6X?$jszNZgQLI zh!_-&l{;`(Jx{rfKb4ir%L;Ct$}#0l#bNOqE64H4-{VucL-|n<7@bMT=t624T}i^|PP!RA$YsVQWRg)t zZa4aqS;jyz$GDu#GX{}G#$d9{7)Dka!^wK1m~1vilC8!yWT!ETB#mpyd&YI-W8-@A zPh&heWBiTk#*H*&OrTL?B5h(!rmc-Bw2d*9CXCx?2jg~nkujYX8F$iw#!NcMxQmW3 zX46r|9D0LsH@(T2M<*Ni(%X&u=rUsgU2iO++l&Y3E@KHz8cXRO;~{#$D5Xys57TFj zRrGaZHT~FFLr)m%>37B>^px?a6f`zVwy{-;89Ssl#!jiHu}hk5B&GRAh4i4YSNg&@ zDE-SgB>iSQB?pYda(m-BxvTMl+{buH9%{TSKWV%!zi7O{ni=o0_Qr9x+W3?`YMfw? z8K1Eyj4u>oe692`zE=hqKPZEYQ_2YAC*^wM7v)CdSLJ5o59JQyjB=;(r*gM(R$1($ z%7Z>hS?QCN4L+un`BY__PgC~#4CSydpd9s?$_u`b@{TV<8L-^`|yp_}|Ra)7z? zDd;P|gQ;x#EL>6)L9kDL5A&i@PdFtXm$_2xK@@7=mp_0Uxepm2QrYYYSni~h z>v31)eaVIbrN8_ke!~aNl_L2gOa&mJbd^8GzL;c={Hy#4_Rk{od(89Wo2ew>*vXR;5l}{1$73Cn6A`wD z=2AFB^MtVjt&Q(dE-n#aZaT=T!Jo$s|FqI7;alp&5=K;Yy!Mb-){((sl~rVw<=Kps zon0N3D-|#;c|UwPAo=p3majH6_0@$IzIssPYXAd$4dDu33`YA3;RatbxDm@t_OWKVXJ)wXm#iV%>`%;1kZEtF%L6`GvMp!LV%&q{Tb2wUar2SHzd$o5?Z4SfS4-*-8*^bLZJzM-Dh zx*=rCf8vVg1L)*MY&q*iC+?gU0_qyv&ai z0Vo310#NwI)D(b%5k}q0qj?}LKDHcWH5#pftXzCHUIS(xKAWtmStRz|<{fijO>f$2 zGsz9nRh!AKVzM@4j$)E2-Zxjd-;DE5=Bz~=<_Xn!%8*>n zf3Ghh%#81D(0p^j^36kqghptRDrARk{~9v3XXh*({ILDhH;Cl=^zZDT`8#MQAhc><)(8c$7S{$?tnZdOl zU^5@{yQe#!A&T;~G%O-N48DCdhHPEIL5WA&h{)@pTdW{2!iu?4d{mMaavQ*hq0v6t zY)F_kXO1_MTtUxA`6|Nr6Q!zh+TtLm^u=*jc6MKcRl;A`NBNS#eC0TVy*LN^Ajyb3}YtlsZ^vaSEW5noNmg< z6Z94yo0SWGEDk=zYy_mn4T^Sy!UuW6zW`m62`ava^RW<*n7m{sJhz7a`<(1u}fcAjkK2B%H6~ z*x!J@zPFKRz5|1N?|E)Xjd02WNEi6nhk9cl>WzIUj*|y|&VcSX&TVPC2OnpcWw_(? zg5AYj`+^R+!lI%ha1&X_o+29@dy1C%u?!L{hb8mm;>R#eTUDmvtedVL?9TTg=)R8; z9Y4i&^cl4GeF;5%U#FpCT8KNh_d0YIr_nU6&l!kwsl%a(xII|-W^PnX(hJ)7GyVF~ zQY5&ss9H|rJ2L(JWTzAc@W|?wLB-tkVFHOD5o)rm5~v#u9)S?@4MEpWqzp9PYMX>v z28Nr$hRt$%;g0-RrayHG6w$a?)V6ATabbSyw?OsV5cG!udjfU*5vcFaf@c0&(AJ*= z?frSs)n6Nm{dM6we*?JD-w0;=n+kwD1SYQKzAO`WtRS?9RxFFNhqyCJtQL>4%?Is) zYp!>8GuJ!2nd_b1%=ON0=6Yv0bG@^hx!&2$T<`2=u6K4b*FC#YVTOnwUd1|?MZMsS zy=37^)OR6!G1RxN>8R7=u!$N+oqWbv*HLRCud;30{Uv^_qQ zB<&E@sxdGAmLU0CLtTFyn)@$=*8a9Qb)BHMzcUp1yTO(I9&nAnCtUCEG#D8^a!#DO^ey&yEhWW{N%&pwtoW=7DwhDMe zA8v%@ur?P7S8SKR?(ArMxvQ4SoRa{o=R z+CK@_`KQ2k|E;jwe;XX|-{H;Ta_|X>NylYZ+c|0AF!pBl8E{4UpZeI*ScG)^Z}&iwU&PDfJEz z!nC`?c<@5pNQ3rpZ8K=kRN|6zjWn?E9@>k5w1@Vlz|S0lr1PVb1*qHsA2q>oVqt7(wCCo`uZZ4!owcNZU?d#+ei5#R9{o-;&&f5T{(wi1fdFxA~oWLI2CrFT14hiXZT@fJrTDQsSf|mB+%-wZkat%&#AAm6_OTKIP&HQI$ZzZ?N2C?&yPvTg<)5G$eo^jDmmhWyZ zJ6TL@E)r~G1!8kiU<)f0^h<}0tQoeVAgVpWnhP=PZiun-`T1PhgxRbGYw1o{Q#bVI z8iyvW5K*fgqt>fpNV*1#Ki+|QtQy6NolCKt7AmP#w^(&*HK+`0)KK#;zk=ki5^0C> zsOJ7x5owR%yu60X>| zMy!9+jspvp&^;XvJTM*;qyTG}UrzxE0(Pn4y z{)%uojpIK9=lTCcygUno0~AICWN*tKj6yu8rMUP}UgETrw^UriGQNPtT@QxE!o&{9 z9r;ddQQ5fwi(bwTk$fVPUNHz)H`1q)yy3`f_#k zzG!KEX zC)t4(q*0(HX%=Wj+5}pYPJuY-5okkh4zwe;2ikjzw-&IMwGpJHBWx8?tOg_DUUs2S zKK=r?J8u!kZWehwXw4m-v%?*%t+S6{D&fS}##B3pU&d5>aTx-y@wu>ct4oHn4qOXb z1yMV)PA>Vn48O^hnYX1PAs|h!prhh?epIP5j|<6CM3+dClnYOnDDJL7eo;}9UOPy3 zez&fnDCVF2JddshJ>tt-clLOyig1j1c^@4;B&wCu>$q-|ZyZjHmyReUK;R3pVS{dbs4iox5GC`7v5flGU_xff;E#i0VzE%6eAYL3opwxOdoWa8x|x*BJKo zdZ}YBT?3a_^Ugtb$MeofcMkY3y>mL20n6pu<5ne*+J3m-XqeuRx&lV#G-(je8s}x@ z*L05!H@`@~$R^H<(Ndw5Ze1dL@fvWu4}MWbBF=Vm zH%}q@N|IdYUWJ1>g~BU?9460*yKoMZ_h2bGPCg=^LO*hn{6J25JHEK>BXBpc0J7|X zd%z#K7jgshp?+Wi5}Jk3IxX0lxBRqS(ejWa0Zz5@I5Wjjr*>H-S&!_eqqE>;Bx`w<#9rtW0zU%{{DPSL8!|P&Lr&lis1rB?g+V0ZK>|a96s`#} zxG|`}%|R7z2^ugh=z}>yKP(AaP!bHo>R={38q9)C!6=jmbKpQQm#|=c5)3vVnZZUR zC)k+O4L0#~3K3k%M94{x!WdR02-X~ruz42E zSP%DvcuvW`sXC>`s`%m6FhDf08hUfnvHEiHYUuoz%}jOV5iBL<`PLd+t)@T-HU~3! zK6DJWhF-x7;MQPUSR72itHF+GyWuf!H#{aFS;gsq4e(SUW8I4$>g6ZtZGBMo`7mbX zaG!W7NzTi+a!AZ|`>y7A66_2t*cJ3(H|)&CkP++-b%Q<9I#AE;0E1k|pS?;42L9&` z$p4`Oy|4p)umgRu14Y<@e%OIa|H}^8>~eO6OH;U`JooM0j`PrX6=eVQb#(daS~xFP z9mQAMIuW2T4>w>pLm9P}2hGYYr_;(vt+X&vu_AuK%jv>g9$FxupDWjOBGzE#o6}-Q z<>Jm>C}I8Ei@4RedplxLYMW(I7N&Qkwpmt~xG%cnu5+ zUW){7G>iyd2iFG2!1&#F! zViP_EL!3<*h7gSn=H}X@B(=G6g!RbgcJE57?Lu8etDyQ2Hq`Y3*@*31i1PCt>>bvonN^RE)bfUg|3>0E=s9+3b`>g zzbffKMBT3?G?H*E26@!?Qmz)dpR0vvmsEL@Fk(kQk0t41A#U>Bd(e=mQBEI7=w;ky zgzHPFA(58Rz72 z!I&Qt(9$7sA2ylG2U8f;q==8lx$>Mw&c4495Hd#+28T?Tu5d4=@D&_^rcQ?+<{i$B z`oeUjSd+d2=i&Vh(^c;BPOFg3^H-;qJxb*jbd8e~Kwg^yTEfq*7X1ekfvA22@}qi^ zt}BDwL@-R(JK%NB5G%+G9s>Y@jN^?sQ&X6BpVX$nIe7_wgR#MnE=3%a#jq6F@b+XN z8AMCqO|p?}!*%^8*+QG}LmyhepEOR}(T*whoEXpr*ZX!*gFCfKSo8uv5VxGbc@;O)YsIVO-ZQ!ja%h@KW#%`!FG#58E!^wV~xO*@(adXX8V1I-W}WMZ3)}q&&Q99AAO(&am z=ybCloozM{bmRrdfMqD}bM{&z-66<^u`9txhCql7XCuG|Kfx|`l{6I$G838z_RB{O zB6C)ZRK`bkz!X7S3_8$tJTd7-kR=#*?mIT$b10dgIu4@q#c3^trgRh=DI7}r(GKit zLB9sjw(J_g0FI+|*(h<)H<@M$c20%Y$X<4>P{F@R%Gqdk9q2rei#T4Y8kG40Hpb-@ z_PDnYw<3KVSF?QQw(q`(ltqOHYu+e$_~1AfF#I*2pfw4;)5?jH(MS{pX71Pd|W08c8*eCa1}pV4c0(k9=X&~ zHb9F)*;4pTFRT!sWN{Ja4K|2~nr$Ppv$?u=*RV2Zd~Q~_dKQ;^D&>4u0bh&5R_w?l z4&hOcKo)mRO=)8v_4ELRl{80`wRl-yRB^7-$>&j;%4u1Y9fA71n;VmKQwfq@x_OAu z7f8spN?ZxB<)HW-<@!Q5uxQ5$`q;s90V4bd!*na}0=XaQ>iOP=vMud~p2E~#vW-@0 z(4n#0T^ft*R-waEIUV+z>XLrh5A!bj;fKIgnyX}2R{_#4^AccYA27@!FwIM$mf0U_ zn**Spc{#K)2SEpOD0DZ6K|k|KxWXI(Bh9N|l6f`EHLr!`<``IEUJo10aj?bw8rpM>eI{FH$Up&{w(ko2A8w6Pm=lJtq9!aX#}o69W- z({f=&hA2|8hwh=^971GzFT!-Mi@h6pRM_qbIqlWHlo47gD=NF2rnsam$}cO!6>>14 z6e62ZD6S~mq&R)qj~yV7W6dXd`3ib~_v#=IdsfJ~=|g+yQxYUtN~Mm?a7FilxF*be zBicHs6ICO6ONVg@;CeHhknpp0Qg-%Z?pWVHJxUr$MM*nEpH656R(h~+$ zB;vp-XoZ=tI^WQxmPvZd?aAb{yuZ77uZgE~Qyy>Q^&@aWWk=p{JMy~V4BvEry@lg& zyXDpt7QOiGb&CRge+=6R*JM1+dgXhexxTYV6_sv~!(tHBfbQ0He1)MSW5XIa_ z>YDpWBlAhp9Mcz=PmzJ3!xmo-6AjXyhpN5Jv08Eq4;B3nr`2a2H1oEhgX3ZbY%d zgvKI-phj$HAVLW0bObbH6WB!X(SFdD-6XEgeG@s9o7p7LkrQmqCNu8n+L5Fp=Y2`m zvs>5{q&(k~6>KWI6~B9iEM&LgHyBaK95zilz=ipAUPaI}!K(-gMN=m*PI-2Fm9s9J z-H}oZNd57PKBQuAqd@d-rEK(WrEHYP<<#Buy~>yGIWKW#ALYJ%B93&^bHtr@ow}Fh za$Q5yadNRbrkIC7V5@`xXAA3QAv6{ zR~;qd@HdSss<;)qEFnk;TLFvG6cUcxff`|P#nDVelr+Uegsp-QKQ89dyBbj{==)A@ z>kDySXf`7{kC2XUoJ29dW;@%H_<2^P&@Y0i$5nB6Uei(B2KFYWy|EFUTaf6E@`QUs ztbohYB+|5Q=HoE+2=WWJC!C9ixN=0{sVe#fUWP|TV@{LeV~_m@1;Uf$kf%U+3ogKg zd>XmF-=HgHstq4mb>K6rE_`FvL(Z)}3et^;YBeU=Ruj_LI*-Jy zeA2-xB;BlLkL)2rEuTTW!b<)`jF2D?#qC+L4)7N3y``M3z_=k+oK5 zveD{Fwp-oEUh87=q}7WYvU-rGte)g8tJ2ZnI_S)%3vK-ZC}ekXMjJ9onWI{O*5n>G zL-@dKC6=%lX0VGGY^Gq`lSE~+1mnJ+5O$Yf+z-N^Y&KuY5f+ z1Z8t-JVs>BF(Rw+7?D*wMzo3`Z1sbBR{t7~5kXqy`KhOy_}kU2HLZa)n)u)CX(&gz z-D~M^9)<4RZY3hz1a9viaQ=c{f7N`I@GcQU@Rk-B5zMzolBAzIUO zN|Zr{$m6$#Imd4#KbCfN8DH%?O0ZCvew2RqStu4JttuLynQJ*A!;(IFt^ZYyzeblfrD(I(}z;;AZn5soNX$?dJJT_+2^kDwcfK^LG z>NwyS$p}8-WXjO*vr1E;N}Uq#=I1<+sM8gLcg3jM6`zQT?|eV%tDs--I$tKdsT5uD zr$Mcz9u@r8J=%#9=Qm^wxq+S_OUZBK3_al@;HPjQBH#@mTjL;L-3Ycd0ixDKXkgt0 zG3#ceceg;ox)m*Ua?lfo7QSLX{|;2wgFCAW$?ST5$W3&Vp&^B)Y?v(SUX9fwVPaMm6NX4 z9#UlOC6`ja(oVAYA#?~idQM~~XI0ci~JxHN+_#MWxdog8@DD-3ZVai7e zp&gr#DL*NKY_R<_DDo_rxL zL^-+)7a)Hg^CC$<3g_WM{GL0rgMk}cIHW?YkOp-^I-D0Wphd_BiBJGKhb-t5vSDB- z48@^L7#qrhiJ@GW9Lj?kp}H_HR1X%08p4K949Y@{U{k0u91Ast*FxvP=}4~BT3!d27I0g`uOd8bnLo-RI*%Y)FHeL>w(<*N5k1Pv>DQc}PyP5O zBE!@nagCb_l=7e-+H92w;d{FX1jE_xZE?JncSzbDzJv z&p+Jf8Ta|8`#j5^WpFYo=gIrEArjZb4n&N29W!DqN$$b)QJz+v^gf*OM9^{ z{l$Z^B)wo>T;X|KyyAH?Ysed4L*5YnL&OCygT5Xi{MHT1!6!hRjD6>1IJT|dJ3k5z zh+jX{%p0VT^CrkenJKXfL@wbd=m2 zdY;@8dXXFo9ZUHyk_+HIw$AbSAy0Xv<0*l@MC5UlrXp{>Yo_{?KwlZ6^QMJNF)fXI8m9{#l zikyN#RZ-p$vF9CBJ?5aQC>LC2^JKtEqhX8-9YobJJ>7zI`>KhUuy4agAqd zUUmgmdSo8vrd6tZTN=^kWYC}PY@=g-rd~mRaq?#&F8_*|m6g(7Udz*`ksp@xxZ>?M zC$}F~`MrYvaUsvoLjL%Sli!9nRYCu3>&N_B{t9~5$*o5LEXfW_Qq?!1uq3A)-^%f@9M6#B5zD}VXGSxnNH`>Y znd-3kF(IB6&6Fcq)+jF<(-Y#gqM0mGD{XX{3jgU1E>pD%@vj!iNH2pB$w)6`Ii)Nu z5~)&5=fBedl{uh{cKJ&{N%1I$RbdUV3aT3f469H3e+NVIN=-0iHNr3)&yGYZBn2@s zkrT<5qd5nPi!m=7VVfJx{R`OU)(G3&s<6$i3fo)(wcM((&8-I8TnDzf)nJ=j4Ys+K zVb$diUlpX!wft4EKGzCxl+Lw+Rq#I7GOHkdu4OsJLRBz7*Rrc%ey$a^nhmrv;(5^= z34diEthn%w6P^42!v;r(0+P+p>$t(a32%hnhPOk<;e*fz@LA|XI2HOB&V)WCdgwFa z4}C?#p?{K`&^IJM^c`s#`hj!|og$q>r%BJy@8r_ZALOdgpJa>;WRfkBDYi=Puyr!W zHpo)jPgdALQf7z9W;;T5*;(YE9VJiOdE`aA4td+IN8Ym=kx%R<b=#pZe^U)V5pEEV~WOv)j@FJ3-so?P+JbBfZq_M6a?t(`)Un zbh6!z&au1G`|O@{q1~H4X!oTL*+q1P-H)!a`_r}dWpsl*fR@>p)5q*9=;QVvT44{S z2kc?=W&29{kv*KAw6CIH*v0g;JyKHbYbC=TEd}iBB+I^D%CyHxjqSfl&Fl$M2m2|SyU{98=wx>vA>|3QN_HEK!`*vxeJzZL5-zjagXG**6yQFe^u5`e@M|#S>S9-?2 zPdaKZkY2DCN-x`sq+|AC>2-UF^p3q$`p|w*I%zMHzOo;ZzPC%HpY2lV4||17?e%iV z-XJ%yACa5dWpd2kD0i?o$-V8Za*@4F?r(3G2ilLzgYDh&)pk-IXIIEK*?Z+#_I~+Z z`+!_xAClMDPs`iw!}0<9S@{|JsQkM9oP5H5LH^2qN&d}#nHl!0EM)(k)w5q?E$lbg zh4x#lr~Nh?Y`@E{vX8SH?f2Q8_J{0l`(t*m{V7{+pJXfT&)FLLUu={86MM}5mHlj= z5h0hlKwaFN{p?XWiY!e_ObJs~G6qu{xj#8sfvHVwGqMIBAS1E`B^rf<ya-{x^v%hQ+s zl49X7&t>5-&t>5-<+5<>$8%UX%AR6woMKX+ON01kVTGiYLdHHxOI@hff|N_TxRTG_ zopyt1BXNVNNJ|D!ivtwdxk*Z>Kb)Yx;toURsXe&JMrQF_44J3;2sj0~o`ahwFjt(f zko=Xbv+y|z`#=f%AuDV`y|4v^;V`rfN1$6c3wnoZ!N717Muf9rOgIN7h4Wx~xGvli zu8(xfvD4}3jL?(f7N%}Z!<2h#8m8P^(=gRqNO9eum*bT<23bi>ZWy>JtOz2L0eu}y>fmH+H#pa%Z`8~W-#KIw7|sJQLy-tb_CHd z*ryEi*dECgp+}jcPGMHAqoQGT`IO)@!o=iQxqP=~vSatl{rmZW*8!QI?ne~iGBzZk z9sx5JRa1APe-x{93=?s#!smk=Zh_l*E1ak})DB+=O~P%Vc{qWS)gF3=JK|(r1lNQ+ z!;Rr?X;(o{^d@E^J1QLz1D(j86Ol!D>Ul(NS)^WYqZmwdZ*1rdjfK0H>N$CdHrR{l zw#U*m4p8fIh(LuJbT&R%oRWR|-jklN(PmA3^TBe1Z>6h9+ejRsEvmg zZP@F=y2}qS_J(k(4B^bb$s?ClA!~1WCtMxfBP5GcDt^*hAW!EP|5OX*9=;CDaC*ck z%Y)n7>>YRL!(GR~a6T?5o1Z<2OL${XkP|z-CEzr71K8nlX(sVuZwO)ONsiiSh{|&g zVs{<%d>23Y9y?ycRQ0Jo&o|aM&+mIvb)S1r2|q3K<%j^}*?q%OxRS{(DlFhW*M-iJ zEK(0&DvY>0J(EBSPXk|gGEUPikR6^1b;Gwo{qXJH^klnE*AjNb34zVW+rS%d1NH%* zvh*<4>_cyQdbx{$Uu@6m3V$S}41VE;S}o2*_)bv6GawkA3E}WvX+zKOcEXRm^)XPG z06OhDR!V)4Dym^A(kHf~TD$jx5xx)GosV<2Fm29K?HXVR9VxcRK4zb|z}?~kx2x2# z;0X9dppptHvm>qm#j4jaF9c^dK>ZYtE%Au11u4ealW}5sJXU_?Kt)`z@H(L3^|;Y( zK%9IO2U`Z2;Z2Yi-VE)-TcJyMTiV*_>H)f|7ek?|dwC&+&WuNQl&Wl8C(<@9XKnCU z$+)3srrl>zoht0CVdK)+NpBuM!KQe3hKK2?U2uZzf@jnx$(kWjSgJK(h#Z!p1BS4$ zls#aG5|(lX?1DE)?|oA4kbP3#kaDSZxm2fjep0H7)xXwB5#%x4j(gyTdLYf`&my=2hIn=af=Pf&g?q38GvGY93v1j930w=EVLsMfinSJC zt%YzIEW)}=u=ZjYiq9*dG7QL2T+Pm@dJI~y&)DZ;X?`L8erdE4NI{Gi>I$RfMbZBU zP)h>@3IG5I2mn{4I7uy>&bz?|000OO001ih003ieZ7*$kb8&1hYGh(#FHL!Kacodw za$#*{bY*f)WprU=VRT_GV{Bn_bCp)_a~oF`JuAtQtgP)=b>dPvt{Owe8TbY>{80?& zKCRd5mD&o=`0ac5-FMEpckjE}|NQsw{{V0Vzt5n6tKE1Uh75}`m?=~;=z=ASr3{B< zS*~<*u+vyg<5~t)tjUfeJ2e^BWq3sfH;uYHy)+u~+(_Yi27|bo#tnJil;QhnyeiLI zY5X9K*V1@BgCF7z8Ge+)n`!)5rnge~NeVwr;kJeW*YX;!Q?}REtJa+585P4bG-wMN z(rdwTf61(^jj!FTzfv7vtt^`3=dF69>Xj`|j+~^=q=w#g!!>xua%XA{$0Nur@vR%C zweHz9XV&44%xkWO15vKhD$V<^9r){fRnVjti>EHzoiJ4l(($H73 z9qVjkZP9Y)jm0W0*;36ksuv8`mbGt8cq?{YLm>)3C)0CVk#1#-QoEN#&#bdl(=hls zVhKAkD_XkDW{UckE$>_;ZSj$VOk5_2qn^TBT-)`tjcU~wM-=)b40m~gi8og)^V;-= zVf))eAV=D+m+fW8sxNdW;_Zx}WEqYwoqzo(ogY#Ba-4 zQ8ouII;0ittQdUWj1ik6;Z?_-{omFL2leLfqVGjmCPUO-4vA#JX#otu^salP(PIFY zoOZ1?JG4-`8T#s9rIM?y)IY+CrdcJnV7Dt(aLKdt^3b#;W2Xr1hH|XY%ia;4O=FU6 z;5FW@bo!^7Y8IKfYZ|0;7M+m9@xvUoMyQ6_4toA2PSmxyMVp9>M1$asG{$;dM+1Oh zh3^_0qrB{l4z}uM{^dtUUFftuwhmef-Iv(8P3F>F9w~8?a&pDjtoVW>9X!)}WwEMU zRr|a@eoFuh41bB9_20i_ur3_Cb%t4JYom;%n;KVYhw}JSKlWL=`lDeO zw5=(a-P3KHMZ2dni$oh;a;<|;xJQalxbJ}j=q@8EyMd4_nW#9-BBx|c)swUvRg`Hp zW#cQ9b9N5isRBaQnI&#URn;7$01HCXdG^MJ zRn0P+^oq@f@f%4>kw2O_zCQVgT25P&fz-K7dL$V zD*0kkOpuA3H%Ok}naeS>_wSsBrvw!Xi73Q;^!+DfTI(m|H0{D?>~;qHNJoeJDvR{pfQB6J+`$3UZAtsN`j!YROYC7X^y`J6BiHno z&{wU$!1RWEqJP1`ISE@lk4rS)9)P#-|<{Z$~Tdo4Gwnp30S@)4BRy}j%gk^@84Z`IlE|DDCZQl z-&PA-g0bvT!L! zk@Ty{`Q-ftcuKeMOS+`iF{L|%*3+bYN=(*Szkb^NPF^On3vHAwZj$z?GFe{>c>JTt zyI8zcV(D>iv~8Q*BC+ju+upeVpZfBEbfwhGy9jvd@D(-a&GI|35BJ!y4|!6*-6pxR zIq(baba0>4c}3agH`?w}-rhLygT1|(5Azs%(;w+{5zfuqlX*9xZ+Q(y`b8%c)zlXB zzIrovc`Xz1b*Q_CfZz-0lSRCCe5B3t40&}~BUa$3W9030!!9X9`Q4v@F(b*Hz@Kqk z1Qdul*cFW>tn2y=v^$HVyXPf7OanViyU~>;n7-i{PPt9Y`#LDtqaaAq9TaVQc7bBX zCZcf4<q_8cR%a`m_#oN{efd1Yx`H_OwDy^9%-)tLHTOL+^A{J2oa^RWwK|2inIyk_qB zu10b>e}BB<22h`%2qg76>ZRkd*c(gqS4JZVV45pxx-t}){2t@J(XEVXF0G#-P zhM+D-pHU1R5?L81x1cV?^QE;CYw3(a3;r&7cy4jfJ80K90n(eRu1+%k*sHUM&oEbw z!wpz{cJ-a-TIqOA?8jr#6_UjyjtE#RxZ3p%m{=i>2G@OuFq2k21IL>cMX?A1Hz zA=j4jZZSXy)=-OEaLt=0Lwm0)tunUpMEO4TsmYh6YFf;`8^5uHS|}5HM#|8-_M)vQ z-R7;>f*GH04Zr-;6qLm`@CwTUJ%j!rUAVTqQe$2~f^IU4FG2oA=cIOxUrF0voQ$5x z!M`-sxPygBq@rq#yF-bDA=rT#xb8ty8my+#8fXs+TaCY#>f`jR+|e-_cGL$q#b#)U zP)`W8K1sD+%l)-?cKfHayt(N1*Vcv^6gsee4Hu3MJ1TAT}GKY74LD-(qsO z1)+lWy=RPN>z|+dSkcGgu39xKZQ}gAm@_%=C_I4q)%hH+8aD=yRSH2iN#1thB;n1{ z3a1$ZNa!o%VAFP3^P%G4fsuO5b$m(RHs*od_L0BxrHghmh@es@k5QXmk_T{ltBle1U--&tD=gifj(mtK@By?>sastYoeai_*=Fj zzYr&5Vm?F4542DdPIr_X3A<+ID7SCpP81O##Ajhgf9?u`uSWI!EzLUdIc=ygrJR#= z5BfI}PNEQr(i)Do##Z0x1B<;`;?pM7?WSAA7f?~mC|ePQMOlT~Dl$V34pk&XMbbdU zrb|Ic+UX@CkA!<-A~mOTz@<3XzmT;VmbfnZHDf1S-%|(G-tSk>S|yZt2fb4xHisn* zmcW`w8PM>v7ybvrtPfvXm}H3 z{y)`1;oC5qDp@(2sXCb0n7K+AnYcPQdy)Mg$NyVJHdGdr(DZ;Ujm*j8dcC}9&1yen z#I2}BK*cA_#4`F?;)hXeQEgRHe*PYGxE8hu;o&KA5O`LOeG5^Mp$?Y=a#Q-$G&~+w z{`>p8`vZh2G6m!pQIzEn)ZduM))?L&!|&lytx-O*$Y5eP}l zE}=i=QB6|V)ZEo)Amv9pvqG(XPm5Bu7cl|F%YBfK!=s<9=rc@jzmxLks3I$Oapc=I zG861uTYasL_v(L$5Hr|%Kh5{x@|#|jZ25S5Ea1c;Tm*?vIErs|IF9%WM_=^affig{ zSGPQOi%<=GvA*I5K4?X04TtJU<>9|hAQZ&qvdFIzXI7JNI~1VBQjneU)O?OVO5-6vfBa`*?&(mX*8KqjLI?o@ zBKiNBYAQzdrVe((CMISsE)rI@X43YKZmz1X&Spk-|4qA+L>)y`VYHF2g;e_%Ju3)C zp*AXvQ+b%OPEYB4k@?m9IshhtYex=X=)!(&3&-Y97cI~YbniARqKXX+?d9k5rABE`R(vw^i>!fvrfKFZ zTdKh=ehYQ{5Kq_?a1%G)%E|=z4)Lr$t)z+CImE851NreqD#+Ef3$`jkS{N-6nl3!m zeLgj5-MZ(QmgSJ}nrw2$!lHQ45QuD(QBx%5LycsmW)O(j&0ig0a0@+oP9aa?J&16u zMY-8VW^~l8;)J>-284yeL!jl!RB7(YI7w1L#Nx{?<4_BklbF+ew+6`Lm@~+qbaDDA z~91Wy8%Hr_Ex#MRG9CQvlp@K9d^J;&W$dE;S*_XT@Ey z9*pwkt$XBYy2kAy4MG@x&f>g~G*Xbc`WL{OVZA9E-*q(#a3C9oy8XoVvid=58v3&u zX1BVyYOnntVG+)X6a075*MIa_V&dlFwY~wo3=RS!_5T~dif*p|CxVk^{%-{LEZDDo zYFR>vg^=Wnk7H=hors%=2qz9hMzeeFG;S&E8+Yhz?~Ri)J+Be3<5;>g z7(zLWGd7w4cS35+t`&2h(^enU(bnF)_30`M#hlQ)Gv3bptRtVPV z^_{9L_*OZpdLIym5;~eqem-F}--_mP>-3Hj1lOM~OPROZ^7;a|DxI(Dw9)%VCaTRi z9_3~*&Y8-f`ry`t7wvg1n}TI_oEpaRQ}DukvfvkXBnfS)7dj_aTPXN5xKUHFn#RaEtRbuuQQ|95GqJJKhkoSrPkdy*2dFv5 zTT8$ZbK6HIk;CZkD3`{}#8tLoCxZBGw3Kk9M25IZkiVtqq_`_R#Os01B68ykS5iAZ z(hKcg#1|y+p(LzqC+H&UklJgJ`RF|1@OuJCJKwp>5xK44M~)GOA3HBd?;*x8Ysugi z$2?frV!uh=vTpHC;|lk6TOv{W9C=iPNxd$f9w$cAC~5SIi3IIY&3LSHpI|Amz7B* z)z=r>TBKo1FnQy#=3(PQMD{2a4S5w*P^Wqh<}F)_QE1EfkBH}tarPn z8+n?vPBL{~i(*ZW50iHYpXZb1a;4EX~frh6m}T-j?a+~v`a z+lPmj)zU~%b`llEi*l;8)Edfic4m#OOm?tRx@nHg1_&Rhqp4MvQ~lA>tm|WH>$~`) zYQO@w>s3)ozMSb_bn5V0z2gSSZ?n-qrL}n%0yIwAbzLR4A*>fv-f>f=&xEf##d->v zVbawaui0E>Fpr|$7>a4yYtCjRbMAOI$!%`clTB){TEW1AWQo0a*?g4Uw1yFB7_g+& zv2VU!phx$5pwDbRHLy65E|r0k87`Okn{5>((x8ugNv~fQ0(XkFN#nGVeAldU;~^Kf zt1|6)cKVw8l#sGrr4j8~7OQUm9NQr|7kk&y?dX64y^YW#2_-tFCZ@7JP6@Dlra&*= z!zfWj_Q2P~yH$k`Yh^Dh8#?G28*NsT|Jyh(zG4Nn!Wr6vgf6q@#3wR`6MNnQV{WJo z*oJ&jwkw)b67vY#CVBD*Ct84|s()muS`B_eqeMSjJrzIYTHRvN7U%LsLb+ zQp(LzSYQva#Xl>x%(XBZ=E)u6#<#eMj!ca8aLQU`OtYAPXJI^k;#hH^X?ET0^MTtZ z*5|orjSd~tv(MB}uV?!VIdP#G=h+^y!zVO+8*@){^9?7Off-n;<~i||qR)W`!0K_c z!hNbv{4(P_$sLQg88xAC=4SNOKNPUgx@5Tc>ZVBjBpgksFrlKFa-Cmm63ya7u_R}* zdm{dDR8wTdtAOs|2d*4ldAl1`ul=w!iAl&CxvEA-OAVA(O$S&{(^E}JIEA`{HkDxqH4$`>YxC} ze_u2pxvxJzr(JyI!qy>|D1C7cx|$Yv>h)?(7Wh-?8VS>~xFy;VIZcX%EP(_TI{p&G zH*^&fKcHLag{$9-M=NGd^pWq>-~{0$4ESTpuo?b7oX%T1)f|QYP3b5F`jFQ$V%zJG zbyA+{?c@z@;ice%zdk>syPIR612e?*q<`SIeMI=qr#`Gg|O(;6W(HOA3DD@nd5DPpHC}W)0psV^8dw+4ezIscxCf7ona2r$ivt85R2!Rt%+7 zE(y2PE!#Ebf9l4k`~^4Cch#%@)(+PHq3W3$%Q+aCiWu1%*_)V&dzzRzx>`Bdi&~m} zUznW#TR&1%<`ljy8vjD-y=1S0f-iBAZ!lZKE5C@i-bSC z+Xh{k|0g-7JDzDcW{yRP z4MV@Q(!H8w{_o-Zc!OmS0dLb)No1{$WE_KlyDVQODFI3H(t-h%Goe7~V-Q`8=m)aa zHQwTNdcnQSTpd(g%F*j2`Y+m7>bD||f(_rgeZz%(jxSjb01)+AqrWKNd}qDz<@dN) zq^sDi#31ukJjtPq8x>`OYq;h_nW)UP!ciHWLaV}_2g~XA8s94#7vbjcAhaO%bf9H7 zUHbRf68-wp80)R-Hu)+i3`;t3mSPfZI{7&qXu4sl>8b70G*Trb!wro#nj$o@XcuOdG;Scto0E-x;ey8~cSIPIifc$>|n3=1GgR_l_nTvy)^Y<;_zabT) zDyxX9jP_~Idtr(FYdfgs4^=WrG#@xMlr@+X-H;Z!07t5Oa-Vytdub%E2L&MgL_veX z`d2DhPz3B&+qES2OjnDV7|K?*%cYm^-?ro2xBcU^8W2cF?f^(vd@-mkU9r{^Xrjn% zkmD1Qc5^kX$Ra3(5JQC&Efxr)8cNMwN0XT}{DW()nqXltn*GaH4*yyASjS*!Q> zG#|_6_g93`%mx_pv;uXC^kxP;>K4o5yJR)WL=2hgQR@mbX+`=eL8D)#ykku0FHu$o z2Oojn7}&|JIqWA0&DFu>`iLyF~4?3x;G-&?{ZA>v? zB$%gwoL!mBFq=ehLybVquk_{qOGhH|Rdc!RU+3EduU-iNQ79Wru_SIg*Tpzg8DG0y*SXmJF2ZZANKa9?~ZhERLk~gL?j%TUd z(n(gXa0c)>`7&fYVh6S5eX3Z{{=H5)t?V^Yy6FpYKRlzcNe<}>v^T5|@GB4S)^$M5 zkE-ZCF^KMMhr5G2-x2ES=3N8Hj>bhd@DjItIP$h#;tZlN5~F0hMRg=P6S7jZohzm8 zAzzg82EZaQy&XQW)l%AS)C;GtI#paqp-{XJ$LV^0R8#mwBP_oNte5oje!4J7Lu-tW zYkSz6h~NlpXYn(g{x18_-yQtzq*T(T!RzTcC`ij}(GY%xvVo9srx7SGv^41vX6X~u z5N8N!o~?ein@Q9GIA&e)SerqXF!uQx$DH)U!Q_6OO|tHd!PaoZR?=09T}rvx#O&gc zZ@QxKoMJEk5fD_rd=^-w^DaEwXZZ!%jbh;s`k!R$a!TDR`6iq5w+p2EAIRpQ>Sio$ z|NTb%UpnsKtm$mz==i^7yhP2&1yvl&zggN_-aB0diY)*KO(s#qq>c(jh*{)9j!woB zo7RBAgZGdpZOgMI@G^**J`|?d-=1$=1^7HQY zNf5Mh=lqAQulepUTqcCvt~o(ND|?hlYgO3H!*wXZ<;kurEIq7&cI$)^4pdxNUpJ%s za_C;c#G4ENE^WQ~AG`P4T%54U%6WTCtTWn`Q-O++j%F6z0;k_@#RlBN8M2GD4%}Mm zJ(p?vd;%iq*$R9o(Xnx3#I?T5iQED0zMQ;S*oKtY%s|Gco`@mtSg zk+zxma_eqX-?$F2A;NM0EWyJ*KjcB5jgV?sLoNJ32WjHg3Sa8X@66oth40X8OJ7`> zJtcv-JsI6dW3D&CF}OAXa%BkM@IDjT<>e^~6;HimoKZ6)sW>u29My#v`!p+ zvFb>yLDHSz+7hV@A#Q^xdk42VSBen_T25O3?{+NpgZXiXJ4uZ;FALv`tZ;h?=^nPi zYt8y_ml)4L#+Vs1URpuVKB}n5*fobUjJ*m`@W&)Hgq77;Acgt4X?y&+EB+$~>E#>@ zeoSG!jf!{q(#uIo{vZlq0kxB4+MEp)sTD@Ia1D{rYWEGum<&&}IoP0t1fq;k zq^x;jUSZqfUM*ardBNR=?1H_{8DZ7Gi!~jb_TV+>Qd`Kj&AX1#%-|8_jgUT0;3jj9 z{?P&4I%TITrH);Ti)s&(tI4}&OxEC9nb9TWS7zu@z`B?>M?)+-qacZj7%fal^@_R0dBO3hXZ~lZph|!; zB(E#W&YZ?}ql)VGVep@HC;9`TQ~7P)aK1-F#QrC`+pCyaSh;-j-OAoV%-PD_%$ZEo z!QS4?*|)p9pz7X%ZQ zTPl_Y1s!;E#~$BvZ~14hEXS6KHS{dnD~U2R_|xyM@4xIEkxV%~G-Aio^kk0948MoD z|L5x~e6RYZvT|H{q>-+Y6LK0(K2f!ynD`HAh&1$Q<`B6sBPJWJP&K0kofZ|F32st$ z+IL{=o2l@3h0dvgyX0zGtn006lLf*7ywAmIi-Qc!<&FiVgB2r-c>AmH%9Par( z_OXhhnF;KnKWfUy6+AIUNLI98@P-a_!ux290LrGU_f&|Mi%H z-$nQ$jIZawmb5kxUq%WY*RnXNsfHIlRtDvW`YqO~<92Ks^G_9gtJdR8GQFVo1A#BX1$RvS6F~d4WY@_4zYq3ZPOZy zi-VKT;;P0c1!HgB8d?^#0jCP&C0INoRZh{}h!C@X5Ez?LLvRGj2IdvgU%Eb6rCai8jg(B$h?0~ z1+#Iz{R(2mn4KZt7C_W=sbtsmx+t*m+Soj^{G^|!XO~yCv{ZG(GL;Ce3lr##&O@hQ)Qrn?Y$jWf3SkRb2#+x>~)rXCq|7+ zFl^1rH6*n|@{Ig#o3=oUrDp}M!@vf6=Rl0dUdv`Hm`D3H17~N5hmB_(;0b2rjjhwb z(@zJdgUwfH{L1n<9~WacrT>)p^fYy5;MPk_Agi(zxWe`JeE-cx}1P@fZR zM*#jw)+7(NbPx2qnZBE(%~vx5&w3rd=)L8LglO}eTOL%V${X-{reKFmquz zpE3gmHqQxwuhQKfn`c7%clb{rfN}b zHGi$D{A|Gfo`owo(9?14LfzwtY#D;1jG-?JlN3+GhiG#U4@g26VL-G>CMV>CRUp7f zca5pvN)~Oq#ojGUAZSg6!;G0f0gt)ebmYHunX}~Fq&vjo+#ILLM;)t}(~23kV$)yE zGp)Dym>t8BpQSQrm0z?>lmOOZv8{;%t=Ytj%6)~@i1Zft)T9_mo7J@waj@(~0$) z;!&H|m9qk}=(pvr$|+G89VfOSb?9?aWQ&ebrIx+`i+;fBwg;2b|?Hoy|xN@60kxa#c%L^xccDbts}!%;C= zvA2}euWG8P9R5^S=q+sS7^wbLYHEGe1^4hK1s-j>yvD>nuB2!(&^)EBXsA8lj@nES zG0SQiLNBC1wWP5~+h|66Bv4gJsi%SOo!k7#p)=Mn@zf)WKZ&gKj6-l)|MAdzD;X zVP`r&$`n_J$8ga`O#=IfP;AUvQjf98#2L526~}I#+^jAW!CKhVYAP+Pc6WMM1Jm3~ z00^tFFS0MGFTVVgR^M(k6*5?ES1PQo02z5DVtZJV%riY0|@|FVIrW zFUj%Y<7?^WkGG5(=FyU{UjpY4Gno*rYt42JfoDXqlvIK#Ro~d(l>*!(!;)7EUAL%}OITuE>zO@E% z9IkX8J1Ucy(mgP>& z=k)PX%m?E9yHc5p(-6B-Tuy-U-a}u6hzBG6`^DX7?mOq7ze1g|MK_QNtBj}hm<2%6 z@NZUutg|IUHLqHYtZ1?!qBk$i<>6?4xl;kR$S;1wTNw7M}u=tXRiLB08rE^tjk|g3(B!oq5Qq}x@J6uuG}3tU|VM% ztxYZ)L7qvxUs;0#1Ib$eBuA=@Y?KH#=gKlp2rVj^O7PSK+R`NBTpn*Ew1%uF&4ZaY z)>XLq$y3z{;8aEq6{76GrKXs$WC5_ps%CTT+ST=RmP)IIe02Lg6T|iF8z8Xw%?((_ zy*b=MjW^CpXc>en92hagRb5S}P`YQM%L%v}Z>4ItT|rb=g}N5zFoCC3!l87DWN-j;c}Oyq~)MJ>jHYdZ^%R7@pJvlR8HuLKn| z3u$&>E4(x}l*eEo>?J@s6jWCwEY<2`nP-4f%q=Zb&`Q_9gA98h2w=;4m8RzkWc)mU zmX5+A`SBauN^b(P87^s&SIyNVpl+w4{R||)LO4sgpeiR&N{Qrhezuv6hh{Pa58&)+ zwr?IQ#4x8(vYER!Mugc?FY8xzJjg$?Eh>3X&Zm3Ou%Hg#(h%}h~* z*TD6mU$D?t-)!<4J$$;L*Qf$iZ(J8YK6UMeV%n8eDvq_kml{vB3dHXB*uFnyb*=0l zszgvW&M2R$cLz|?WAIVa(l4F<*akl;n#-eE4ucDUfej_1O(8sO1T3F%!NcQ6ImX5g zVXZiEtWXKj|9imhBiPV~Odk1#L$V21(u3{dW?ho6Ks@(>#CGRuO)5~&`pIlK^0eTD zs*ArJ)bhe)*@aLufXfqV1$HG!QP51hjuKnkC!E8f;-dmxs+lrk9B7v+$|Y7uUeAp1 z2Vsm*Ru^rKTE<3iv3!9dvW<$tOa#cVKAD>`#)}-Oy^W%}jgrtpMmkChJ%S6-F5QlN zZamhEG}7~!S?x(8XfIM@_Yx>3@P_44yMLG^#Axu&BZxfJa#N(nEpen%R(hJyT#-Rah(`#OiV%8kdPMf_2-B))6z7 zh~d!_v+*izYE-~f?NCS?l>W9&EBRmBBgOQq-dVlTk;z}q4F>0Pdn*2fs; zT&K;Whfq_?)L<^3N9uGCSwMAs`Y0zTwuMSZ&;5 zoAxdgD>3-V19sAQ;cmOtt7DEtn)lDM(aj_rHBh5F;2C84A8Sgi5Lqq5B61{|OwbX^ z1*kyfLxEvL=;DV4>+)(IST42G_k+&RzN%|G4y{}JFkBL>+bg7B(nUv`Rh>g_RQ1v+ z&)nw0u8>4Qm2|F-dF!a`s6M6q(y4d5Fh)m0N2FVOg#Gdf}%3$;M= zBcVLvon!d^rRtGJ<6SC*YcNt~&>^Va?V!jno;)yN!_7OLz-g|i^Omi-vOK9(g=qJPImcbrwojVpBEhqeJa3=OI)#(oX>Ol~U)aZO zUGmM1N9c{46K5NjnRITytL^Ms@;M3N3$Etm8ne8MG+~rfRfZ!Bi)zUAIm7kTJuTzp z?7I3s#b4aAWI@>^j0iqO}5!-D^4V@ji(&cbGUf#^SifV72Ud6X0%5~fF+8eRiMxvW_eC1z3p z6T6iCRTXaGUoiPTI?QR64&l01PJLC)kEWewsFi_!WgW4B#8Q*bQxs4H`-c@V8k@)q zzJodJto5NRORN;E9Na@2d9Jol<~PwYOQjxs$SyVHQBn0GH?)EeG2V4OCky{Gxybn> z+mx?YSt>9bmBlKj38UWS)jux7J(A2D1GcQWh`1jB$6G#~-3oV36!(^S3g_xxA58oV zF!S)UYDf*IP^6Ka&y5sH``5&k1OKMu8YNcapZ)sHh|K-3b?5iL&Ie1XyLk9U`jAWJ zN{>4X8p3po$mly<R(H&9Wwu^gHg%P%kVq7HYCoaXZUpt(zxKUscqNYldsd6l&LePaZpaFYTb+!1EJ?$#0PCjQ$JB3{If91cch;p~%UN950Q3 z3ZxpNstlzqUv*eJ`E+=MjYo*o>Xo3NGb8Fk9_UGu0x>I>;2xGiBK^H?R?Fq*S)7V- zT$0kFt52 z7pMDHGb6)0`jAMz98JD>x5gG240pmhNaB|ulhY>Icv>|{K>x86Qw1c&$dKmyp;6^> zIMNUJf`jLtIyir1JoesI2(1jkf5jFsE@35C(e0+cTR8dt5G|BSFbj)P*$aR^cF8N^ z{vr?lTS0Ch#8pSM-})SVCCktmh?(VLVyRO!Sf20^`?vo3pFe)Ep%D#Rg_AJZrFai$~ynTFyKf5 z?S4D_*&n1{h!loxYedos#R%H3jD={jYvvhxE(ih=B-{Ei(D*35>Cax(mAFw;(!?2r z>7TqzFo9F41*!rs0`;=XO_Sy6o ztom@V{~f<lUDxNI1yxD}H%<0%t1mAi+mqH#ey*`FA$t*?)#-glbO!2&COj{)F}$H!61Jd?35S;;c?vYaSccMVGq|i zDH?<2PD=2^Ou1rY9sb~$hvArOSY!IIO?I=5uULCtuH9{_5B$jC&Kb_e=!JUDj+02a z=*28`;xVKjH$WtIsS+|4B|g%Ng^8n~N~VdEhiG}1FshiDA$KZAf9!|u_!zfWwWGNk zZz!1oeJLtNY&+JhTr|i+i9TYcL~2)JJ!PL{As2d4@6&js6LOhU_JoqSlBFK7V|en> z-;1N5-69deZ}9TZix1~cw`uq_MKW3mEj~iDf<-zmFsJ5jg!~xrMAf=zuavS?E-sfb z2TF700zQ$RoT04+o6&@9y#A4j7XfG@v)}vR@j6Hdl*Pa{@iE#&_+Gq=P8f9K6BV7L zazVi5#kib_el@;s(pk|3Sg?>g34A9=Z(Yb`wQ?w^`KgdFW18oyNPiYeZT6joad;<} z;SF9|#Rnb=`4smr(70wDOisf#S?hv;swsrp#S@=}%P(~01`c)`{9?8bH-QBbqM60o z2+hF~gLa=yV_5fkk8gBj!3Za{&s7(S^+rxB+@dc-3zS_8TD}#WV~2Y-P?k8d32A^w z%8-9EO8y#&fiq>TSD-a8#u_cRcRb3O9#PWeLjO9Q{U@0rM{FOeJeVI@$-N|9NftX} ztQoXLKL*}NlPlPg6JM?=t|jZ!Sf5v1Z*)rNO#kEu(>YT1(O<-ds7PD}h2UyzS71QUJ2RAl0NvMOWnc ziZO|s)ZMcbW&Lq`et6LU*+Q5XupF$7+>NMZ_2=H+3ve{>`T=!hj8D`|bv*#Mho&*1 zwqaJ4`{VMImm`tz{`^3us`Gqd&mY73t=D z@fD$<{&KoFU+zrtbXY$~c)wzWm@EhzHNI#X8XTl-r1mJJo>{2f7OR(!8+BnrQ|_Tz zU$|y{|7Up3YW?siF_~qezEitPiGQSO^5($nHoN1xm=BRK^-}!}KQWgt#EbS-0s{fh z366V&c@fXxi2B8Bt$R}ScEb5~lh;tDfb$J0d%&Yh^Uhp{V-CKev$|VCKG%X$?xA!s zK4)G?p`F}qz_jLE*0TEE@aec`M>XBhib$QINw%j+w&S99BU95X5U)#V!J@F`8Un$y z=>3bxwy>o(VjH3827zNP=k~Wvuazfb@~)rn*3}O|0g$O@BHi#UM{a?BiYKbg=+PG<-n@1HkVD8gRnFV<*J!-Xccl z8QJ$MWnc8T-Hqq^Q^>!@qMaEImQG;!l6`JkgA{o&0^RhHZK>a#7O@5P!6AX~-2p4M zD?6DNIM1pZ>=OHkTGe&72dJJ8DAnAazkTF9oNh1eM^xM~F|TfTvZvW&JlYW~n0n~8 zuYP5ZNv+uNB!T-ybrXpj^b(Qtt4$fZ8f`J@c-aMVh5Bx3Tm_8jE-s_ty74RZfJxv^ zOj?T~vONkdAUKwFECc{HT?1-v8T3u~-=Ru(&hdD*+1!$6bltGxfEjP5pXDIRme#Vh z>$ce@vdno_A>-<%Yzoh>w@i@I#fwhsO!DQpBXTGf8W` zOX?c6Dh0ogvNg|D^Fr?Yh9ThWb3@pV%6e9>ap79P_!!cqj9?zKQrQxZj8zpydO>H% z9I>j#<_znQ8zpn}fpj}=I0cQTc0&?b@;kTfazE1Qrb(!dIt~3Yj+U_YeAxS*^$LQ(xx8;8h&yPl^S1~A%n|!Ra!ZLfHm8Ww=r*ey@dqWsFonot;g&-{V z5BPJBcL$Oh)hu&wDY{yJm5&m3VpHpAY{FO|X)W`%J|v8nQC9DKX|j@Kq(WNuRwi!6 z1ufO{g=4H6bNaSDuHUz2!*=r4YfC?_t^~6=(sPTcT#mEDdsn2wCnPCml;5=0Jxb;?-E-ubvkEJE~xc;#PcK9v&sL(WN#CwOu{F2TX!-cKp zj;izA&AHGP<^l5R(|LD%?HxS`7Gc0Wgr#N0FI`mdUjV;K2h6UvV~SVxqEhjjcgE#X zN`|CSJDRix@V;k>{rs}|JNJ*$0uPh4^4IQ?y19qU*M{JbF#aI1pYD%ns)}k{1+`;K zfqmO3;Ea|rJhS)szLH~;y1`jPU;^8_04|2~K29HYWl&MA>h&C__fFPKe^VjsU!&I8 z{M4&`77vvWTyREHXy;aC>&oD)f7#O_<|9R&W~<394@Lh%H#~%5W?DrJjf4bzlj!fU zJt7aJ8Ek`W-HJL1G7LI{x<~TP_M^)5C#-D5^Vc)OCvUk**CO2Y;gG<9ki=CpRlqM$=Vpj zoqzY_c)KxfT(oo;DQMd#J_e#)coYxsBbHJm$6Uw86DSl&@-2Lb;|8CEA&7ELWb9AX z9cI(9rzQ~#X;&+j-Fe|K9^e~k&+_Vbr9RY^aj~zFcCb^v6`L!XpJUQ-IZi?2aRHnx-FuT{YE(D(Xz7jOexu6vtpQ?BVRu`q6aPSPTdu9+664u&p z8zBf52jA0l$<{3R+ynNCThL;*HA)v|l85fARb;QIX-|&Fz3u~1iVdhP?h6w04QSKm z9Fi~Y>zvs?G};nA^EV}at6i1~(*7;&p^BRWjtjqL-Lie?aZBXUI?$B)7E+BHpsV9o zv@S--RP$)hEmF&*o@u%^NslROQjspIM-~8*Q5VY+f%?VK8ZERYi{~k*OIgWvXI10i z4+`W4TP1SFbY79`6>=JRnhNR$%B5uvG!SQTDI{$_Z-|E@M$2w(b^8s1zbb}Zp4~gc z<}Ovbuh~xk^d}as*${CeKWtiO;C&2Hmz}2uIKDGHzT^8id#kkKCa@R;AUFpwDOS21 zF*%^{El_^eA!lLn{)JO?p6NrqsLlfZK*Gc{+NA5oTZ6y1 z4t2;3`2L(8D9JtgGR`xM(0FJ;E%3w0Gg{6VxZ;H6PFnUiFhM^o^WW8xsTk3!GL;E<&&WZl z-4cPt$w<@NRQHX>#SOk8(>7*VY84~|NQTS6uu30?Ja6CvV+Y6F^+pr(FseKK#;VQo z0eN2=;p0Q^CyNDV5~M1)9fxqUykmfJ(F!%JbNttPLt*zpH>kpnHNB+_KkqOL7Lcu1 ztppl_dTK{ARWzv{q< zd8T)fb>NCqMJIzWnm!Z$AoR-Z5?MhXMAVK)>(u*0KDb`#9W7JJ!V9by`W-?99lBopZ)mbB#5}nASXZ9y<-)?pPc6 z$(naaPis}S4H~qZz!2**Iov>vvHb9>aJc>&-E&?*aIXVO5PLuGw{`PC?wam-8of`x zb5API($X3ydgeF`L;J`Lh2Ri=n>n##rS_vFxAkrLtx4g}DVwn}$o>5hjg6nfT3X^q z?2k9ruB9mnI_BO~rw)={V}dDv^nDETKd}GZGhp&vIKtx_5Ksf4i-zU@uV+Bf#NOJ% z$lxEnGyiI)`FHrAs{pEkH(z`f!vkvNL&QP|{L)YgjN9U>wxX+p7K~r2 zp7TRN_T+?)NAAl2S=?awU?zG~7UYDC8B4nHc6n{Ea(`z%(hdX!Wf z2C#{u+Rz#l_OJQhbV-u9R}NAxk%V53W=?I0L)g$Tto0x~vDy+gRddq&mC3vxAY+!F37Zcm0j}DOHJK7AVakp-N^4 z&m+p(^AYok5e|OghZGux{5V$VS&~$OIDQO|e6T_Aj>1uA8FI$}k(HgT4WGeYKE&kE z@GwSGR*|#72}|fbyR8v$qVha^ZUp8c{(pz^pp64f4-iTMKq!g-E|ks^HulygHYR}n zN8^7*&PSnBaeyC%N3BnP2tDG>7lle4!+f}2AbUQ13NUdO$<>f$UB7`rbN%y&Dkqw+ zf3)9`y8w1_eS56aEQgod>-FI=DnF)`#gX(BQSL}qxWN>*Izyd!T9nBVWG`Xi8uaq; z{kH(w??%h=!fwySAf#xBcC z{9@c{gw^g2dgl@XieXaf1;v*qmlY|4U{oO||Kx~_JzrJrwE3Fi?{GF$zY~6c*XvNi z934pR`u&sIMMo^h5i%33v-UN_v6{Y4@e3NU1snlF99vy1;tvGDR6l>l<99b9hIXQ! z1&mP#QP=oAs4;7^b4;Q2A>@QupA%5AMCQ5x1#82G(T@Xwz^UieIhW%R^1tH)Y{CE{ z4~UHwKwn4rxA9T2|A%dnq4mEz(V~?$WdTj#I$R<8291x$`{(x;yAK4)+mz^VuTv|Q z4+fEhs=TXu6qPzGn-o<>7ot>S2$NP}rYRXf55lp8>{qQFL+cw%?*Kjkrz-)PtL^UA zY-Qh*CG$H^VL0hoG7Va`7&gmaus@4)IBi$3%OlQlTr(<&8^n!>d{s4mhS1b(l=KrE zV|S>9YO{8#2Jy{-`aRhdp!VnML#3i>_u~vE~j~PW8v}H(?E($j8$w@FzeVH+_V6=iDR`F7&>i%#aogd;Bv@9Kr zCL4Un8_#y|@KI7z;%)b_-L#Bcj3J`Ed1}N&n;DjLPB8lI>qN=RVB`R_oM&Q`^P4~E z_Ygt};-CmZp;u;J|GeZm4go%G%oWTw8^y@?;v=s*yinP#t4d=N!TZ*MF~byDNj~^h z`)qmpDGwZ-1C(~H@!9X(w}frZ5&PeG)a&%W#<_Lfb z1wcyvjqpVd&=>trq?fgGR&ueow{rxb#{Z*#`48}K(t#+Tg#IZOD{Q(@J|%LHOnXUy zLYEW-H#qxs<6PPzZMHg9GnnWOr#2Jyk6I@=H zH=Gb;5JQ5`j_n`?H@~B z!~^UBWXnVZ4ebhqg3X2zNDC?Vue#+j$3ICbIx-C@&dxN8`<}mp6rc%W4?&pq2N zHP*+_wX$h?O@h6XT^;KI#l^Wl>+T}&(5=8chMvNJgK?0=FE*-6=`cEibnDetdAF;H z7YHXcJcZTR9{kUI%A_wB=TOrG;7#KpeQ@3Q8Vp|e%W%Gc{u@yo6p3+D07x~I1&0#_&pAjaSQ^qk&H6f6LJXrkbz;AjYu z0jT0kjCJ4s@^0cVr)Z7Pj^Hcrrcat~xhhc@Xot>bC*{8g_**DHHdXf{^X>uaX27-p>OJqd%=Ea; z@bcY{(i5p`8A&(D;9VG%XT98hgys)@waD*XotX?Da{4FCHM0;bM|PQ5yeAN8103Wd z*oaX3P=xXjC*#JUE-@s^2kgk*eq9{HF>!B^hM*;Dh(XbgNGB*O9qXv4uAOJxEGDKf zj9*BtxkQzT5Gi*+XfwzeLV~b;EObDO8R6)PXGHm+`%(_iSAVIE4$*=<5CPc)0uW)5 z{arTw+d5Ft!qynjD*kU3mQB)n43Yo<->@+C?N#m3v`2kLOg40oYjEN~g+#%+=PL^+ z^CjHSU4Mw`PDA7Og)!w6c-phukQbHT@=f~5QvLdzoz(-vJ5>2L3++xQk5hK!f-+vt zf1Kw6upqr%2=9HLfms2SwDWDV%yQ?9f_A5@vqdsoee94ZGA5||A@|s{2sN5GM3vMj zT0gpzm#hmy1X>rV8K^9iZzMBkqv=N1Wpt91NXsd&HzpiT zq*aTQm=EfjCWB;|sg+9@r=*Otjl{(fCU#ZP@cA8nHvw}O;KxfGv&Qsk>N3OAAx5-s_hhG`GkjN0ng5rwlK8{^qB z16UC;If918i#e&;PZSW~*o^DXF3ScC8`HTKX9%5Ymr4lovIm+P={1_kF*+GRo-|AYBMXv5d*G!4675a01-!)v?eS9P=N@2}6*U7)N! zH{K`1h1g$d2@A0FCBN+VOSZNWa-;CQ>^q^|7>H5by3>*`dEVQ1Lm=J?p!E)@-`_W& zb?|22&r_3gv%WBNr@ZR-LMRc&F4n<_AK7!+pi4*z6oGAB$QaUQQlRb!ERP*{d(tu| zM=Dd&V@vI}F;hPnzp1q-(?C77Zbm<@jcJ;eixbB->#K$$0q?suS`=>88LvU|SS3$X zE}RBxuvAL{tF>M~!NHU--NE$7q&jvj-85;ilvIRp6nbmrPCgcK=}cU_V|H6lbo-no zlz|0apMy4`v>xAg>6T+OvA4?j3tgXfneG{spGW1R-Y0%Yj(7{g{0{7K+*Yr)@opBR zT<5T>Q|__o)@D>34kHbi+*c~sdUXkoVYZ4?({7R%+Yt*zKz5D3QshRFTp+5=@!S+cvC@n`*)0?H?B^gt0mo2GZoaI!FqO&iZti7y1px z(ml8f9Sl|^i3LY&B9b&<>}B2(W7r7Rh4=cvbZ#QL@;xl?y)`(A|ISO?~ zvCXJ6_;)04K;!1+lI%Q>Nuj)^sTQbasDys1MJ#jJ3vJyCxR_2aeh83R*Nfk~|Gk2jBn5O?Qi&7U?VA%v`y#!8H9P5q=VKUPQ>o876yA)8^ zw~c{#t%xJRjm351FUXL$f|X-?>=(Upc_xq{l5I^yp*)IMf$SWk%&GjMrhROaFuh(# zf1lI0G$wV0_6GS2B@o!I?zBq^_|i$jSnkg|b;+6IDBH&tTh}5C>~Fe?<#pvj3ro8L z4BL9PR*9XcKn%hsqdRJSx+{~!*!qry9%EeBgn2r|r_m%`iMr4eko7$&*5@x)5;G0kd5)0U% zw#tWL@22hd=a?Vd4^wI&sa_D;5>9yRN+04#qpHiW8?K)$UuWy1)1g+el{XG*5Eo5D z44X(B_d?S?sSla;Q;$uG&uO{#n}@!WHCCj*RL31YdTLT~bd&_dC}>60sfN_syhw6b zJ;b0z3f2epi||5^p_R)Kb&k_-qsNtQwS~SSUN0VylV@>4oikr`BNls%`f-u6Q%CFG zP8%rSJ!VmHk?}Msn5LFYCQmymgG0ics~k|FM)_bcc>jc{UXOSf`l*J)sTF9fYDWSN zTTbdm6w`aojDrtc4%03o6{4u%zz^}imtG2}wHwN1v}?al0^MMw!(ZA29QLs0RQX1;bZ|xscNHS z;s_8k0_qD>3o{`Dz}D_R)dl0f%L!E*HEaZ_HEol%5$R;9+q>K^6 zMQ}|SXwBx1b?L<6X*ydroCo+X=-3jijrLRTmdkW|keH>Dx;$!MCwpq!h62Ou|y4%L`j2 zLQhb&5L(zT7%f$iab7Y7Kal;|`((@58*0;KC=Ur9%Y8AmyuuXee#z;+>Q0^o%Llqm z)%|Q&nAbLR<`w60lyuH^ERiq~e;%TCP~!}V!VDeHK?h6a!a~iY5G(VH_H`ICQ~vUm z0&<9VU~&Qe(VtHr0CB8S^SK3w_K`X2H)_wi7g@-F)+zm=3%-vB+6QNi(2nnjY7^_s z*8}q=q2PNlt z0+xxXDys@H$kS$N-^DBU6}5~cW`%KZH_#cX*fgm1WT-A#pxh#w)^~4$>j3gvPl{K% z1o$xajBFK0dXa}&7JZPTqp{#g4pvw$v1wvK+6`Rv|BDx*l@1;?I@ zwtt&W8S9vow|C?T!t^~Kf@CYAZ@`!x2!aG8<8k{tg5fjWPT}jV)UAc4!s2_L9AXI{ z0U1BDP1MzQO@naEM0TenAuN>U1v{-6YH3p#_d}kzt6cUZDG_`Tj?VHNDN91tN z@TeQ-$j1 zqf~7|IGZ5|C7t{UpDiW#VQA(Y;_jMHgdgzF8B$72Hf8D5aVHcCYgPjVb>^ly2pJ8WH|3q1EY7ZvXBspiF1j zJ_c?_Pb$+Czb22OM0!7c?n)um(rDr;RYw1t2ss`CN#g6J&qX_|{WC!yG>;5K0;UXY%Fx;$nQ3h-1mU?AfAQyFN8M2K*fgAqT{cS-fgJl_n0Z2Jq^sYEK+3 z0K+(?bEP6~1Z1)BX>!EdIRKO(8{k_s(Yf{7Op-#E7-&%D)wZ<%zG#90?Z8>`)U$?$ zU2>K$gGhh8UQo_p+{7{dZffFGc`gUvGMn>QE9dCfd7IARPjZ(3@xlDZW z_iBqN`RVmJQ}?w3KCx(C%S1iM`@S{EgWQ;ScEMf91Y=CGpSYpMtc1)knFT(f6T;z4 zY4ERLk_E&CqW-^o|CD1+8#tPo{M!-xe+W=K*MD%J zbfVxbNnid`UCsbb680awCf#bp(NbW-)K{0GE4VEIAA4^T1;;WiGkJ1+0Dch@Nj(Km zvoJk%JxzSP022qF8eeNe8-o}hH7TVAe!hRa08o)IA2}nv&MqG%H7h0s2^1P|IUL+m zz~<1%Sl?LR5D;(_4SyIz9ngCfKi*GM5JOjj%JClDhSc3oHdsQu*EC4Pi{~;Iv6@dzZ?~AE{P(#a@6oolz zb2DLp=m!~^@ORW`nS53-sQH%6D^d+{*Trqv(Kq_1%-+7*7o6hd7974$p)bsftl4(5WmKttOU4ihweDgLY_lHp%1w*=>pF@mdYl2O8WRw774~Da_j{4MYTbPqBGxoi_FCFJMQ>29@H*>@c}R zOU|{LrFR-y+tXXQ{WJyS`YYB#cb(e<<6{Jvhp%YBTxZo=j%TTUl8maNmO;lzP?l20 z(6e5W-t4eSvOYW=XE!Klv1&_g36)-`(Hb(c@0#0hHeE@s+)Xa4Og4Its^pzoXLdrf z4nDGn$Q)0~@+jGP#9F4)HY(3-xq1xRw`;7~U2BUG^e@EGe}8ZfnH)ECy*jQVpLdPY zRAMk3riq?tn5>CXVo3LPX~IjyKXO}c*UInLCe^JStg>2k0gcgQn%G>b4rYGbs(DiZ zlZX8yEjOj=)5}8jjbze`R_lz3zT5=1-00UVnL=m2@qfYOW zDkd&IDGA(fQClxjVQwcV)#NQrhsTLmis2UJ=W);`s2=GNpK<;PKa5VzF4H=NPK%go zo;f2DUuFbqr?4!GwG!VK7o$>j-WKKpi_IDFCOX>MR(tpjj|$@>*Vh??!h+(Gq2I+i z;wmD>s_fzoD5Z{frLK5y<1$=T;MU;ycma)mQIlL(0^TsPB>e~X1djBH+<;7E?` z5O|h;PbQgz6h03S{Fw!M-*px|LTN$`%$*Y=#vj=<8h?(Z?JOZvl$+^a$|1bNUKnBe zq~sj*0oJ|(X~)bhUk z={~+KoOj=Mhc%kziq%%0kJVvG-13C5$)>mql$0@5;n}$dmjzbmWmXf-tT2q-7$j92 zMcIrsr~#h^eJf%lgdnJjG-jOOi-qAW02{W*PdrjO|3#_|ckY85!-dF#=@XFS&Hlco z`4KlxDG1alM}w#Dk0~~i`@M4X{1mEYB7eS=Z0JwCAq8@34xwzt+@nDD58LQJF-lOr z1M(SFi%`FXXO*6A4yK!P|NP3Vcn#k`)ZilQFwSJTg7VZr9h@2ft(1T({N_1ED0!JV zK(5@Gdb7$Xvurl-44NZn(%aZjJ9~$h?PL7Bh$Bkr6*uFwJ^$w~2$e#fJCzNnE2;n} z^>=l}|3avL0@FW7_57P;PgVld6$%KyW!*N5O{h^3s?~(&8lHf6O&zi z(y~&J*GxOBJGG3uFN1pd{eFfE8`a&fc)xyU? z&7e@hiX9?4>UV*1p|z}Ou&uNtVUrAkSr+ODa)GPWq{F3-8TsmfOR z1N15RBn1hBYQ09&>PN+d7fmIcN2t_vhBI4{88ff_MOS6tUIUCJ*QGZpVdGiHgqB0m zEp^~~kCzM{RcRtj?`XpQYn#kVnx2a>hJ$Ao>`m`n0H1eZI+=89z67qwnW9S3jF?$aqE#O}X|R)sML@OEKcAW}aiMFh((9$y4Ac z#2}U_#0&13GepcHZSp{{=83idvxv)joH)tfbv+aI1(77j8nWQg*_DD<5#Eq3-5)t?d$ z>53Es8N8h}hPowyA?sW}2hTA#Y-8Nrh_41B-a&e24ODo0NSf!XBrJ?^^e@DU3xe%J*@gJWUP15zm=(sSOyn=FRk_mN@ z6gPf<*?Rn%K-?_?P#Nslf9os#zyCE68y9OpL1X;CKKbv{xngxMZ5OlVkvFDHdD#>^P>t}6J{cxpg^FX!|YT3XlGQ8_e9j>$t}pcLbEkqPTQU9u}` zR`Lx;sF!H}kx6y{-I!(oU89-a;c`;CXW*z(x@Y9*Qd-yK0hVmn_<@#e*Yp9GY}e$0 zhiupE0fdb2D8!EhnQ8is(RZMF)%XjA?+`8g3#WGvZTuHf?*ZEMFIL|{+Q1K5-%*NQ zvj+&WTE=(cL_S83?D1UEFOdN~(k~hQ^GMRddSgFNn~oo}47KEjBpXOV=aq*xn}t5{ zExJ6};h6^8Fr?sz*TBr+%}FV9GCZh~c2h%#z@(dWXcRvn49`U-LDAU1u@yUK6o?I$ zm|j5gSRjeNU#K&g=1|%j-n|{jphG3OU@2sy-G-AsN2d$t=4@0w4~HY4F%oHh3DTk- z`le_U+#;o|H|lKWH#6G5edV}x5`E-hGAA^3#JXRK8^+Kgt|t7l=}s@glsbDilr-j- zDjvTxPP!Ks&+}Y(gY766VO2sdbJCo{T|89Cpf@b~(J7+;yF-{v_C+|d>T=98HwBtB z(X)cNrg)Llr8DL-O=J{|lypLIL{_k}L1sFw-d#(MX4;EjUOd%A;Jn2Kx2cDNrR;Pn zqkVe1rb94wv8rlw6DP9DHM&^W$m}y6lvM5`)A{N3@?=9<5*tZAD8FRuMMF9vCkL9x zORD5C%9#WWI+IdI>ibki7CQ5W;CO$OMeN*>1<5Aj(C|8?P>C%wr}bx=K*`6?L0))-%ESWc z$T$ULtMYfzrwdJGx#ms#Y$fwK3#-1trpykfG!GW-{z1`^4KmfzN!3y=Tl13X^Ae@? z+5VjCFzYEi;&nUbl|<8_HT9y}-2=^vd#2N8n^j^^MXvHNmp*IP&Th$CUsBu>V3F`; zTcZ$&C}q{~I#>znVRcxDqUjl|-gMd8Mfa0?ff=|`8Cpj1cpVZWSYwsWyjF6(<|5k` z#7GFEb#L;pXlbefx6!q97m={q%4zA|j2>JZ2_a>t<}|044n}a+Ketji^$PA>mJhUO{9j zT+JUeh{Ow=f=>Z}5ZNi?eT{@NwISNZq-d3$VEYQr9Ng7A6ydmDnFRBbTO^65JGXKY&G*fcJ)t(C z{;+#QFgJtRz)pwQKre^fz|VTQyu&}UZ%OCbVzPPj;av4b?Ha`bJtmQ$*^@`7d$PoX zQC-giLKaAc#Fh6iqEP}9N7MZWI0zYms0YuLpDfHwa~32>noJqf_b$j9jcWMP=N5{B zjl>>1OCZ~#6+sGTec(c_DbwdusVW(A7$!eB4Cg|y>-({?)s{mEIe%m)B02v)?QRa{ zC#7tn!(=Q_t*L;NO|LlNl;^z0G74g2Hn2aW-8M)Ida;u*FfUfwym6TjOo;~X_~dwsM~SHsZK z@bwD7a+N3sUp5axN`OEw=Me7+(+iAka`y) zUVDv1$K_u^MW-PX(30;lu5`zv54pZQJp}IW^v{YTO2f##V<0DJlF1*1ZtEt%pB{H2<6p=fS z%u#mndqyj_gzk1UggwHy%TEU7jqu3vLoNn%`SDdtunHASSW8k(WiaX`3-!Voe(~yo zcS}|}C6%FNPK*nht*RBHR!#``vPgUR?69yUSYT2M#Etwc*ci?QAUsL!0VNN^E1qIdjsV{o?$Q(B z`>NQ=;;N9}V35nqDRBF#YJjjN3QDg+Z!SLm~L**Tvaf!kppx;!4)IS8Jh zomjvvc2-^(chF647sjd?yjb3u{gJD*DWpr9;azRx z1pJ~uf2o9>NA&V+0ec54z=oOr|Ax*Ju{D1)XkOkC5k&2`Z@x+HYX;)$hG!Dsri|u-9Wdu-Bbh947#BL^7@3D*=i&x48EP97+lJmD-G&%KJ{$#^|H`4RYI`t&6gG$!~U8ib}Tpf z`{%9`t_RK8oWa=`K_zjRHFzgXwI3G~N^-)v69)_Zb-ex0g^UZVC=Virv;2jpU@WkA zq3W!O;ew1P*(kwLqGM9QRdE7|_N|+#z70F^rb=e>>kXK3Kq*c!ryF5~CvhI$ky9Ut zjuOsjn9GzxiBPEWAURYfC{EmkR#~M4Zw~z7B~Q%b{TCN^{$=rPLlnT+ML?e|!~ad_ z02GH4V49Pu-M`q^X>||pEtTd!IlFc(Q(p;W7QPmx*v%*z)iOUsGn}OsRzm44GU?bv zHY7|Fq%)_Am=jZoC>|yn0}lcZtOT&B)5vRRWV9lNVTC<-@pyd-0(tTGfM=7*si@v~ zdeg@jijdwen{&6kd{6(iW4WGb)%DoJ@dtaL<5S(+{bk;N=n?!|D2&|sEe7sS`JkK2 zTMqg)=bu!#-PL^wT;IJx_+M{`IS>2iK)zAAZ}+=zsIrp&mH!_KZG2u?Ql-)@d6ZG<=NEc@=Zl8zF*eepiMo2x45MLti zk5$-nN^3{>}}`MHLsil*4xGoUgsutJyU%=0uP!26CH(H?!UX_A8yP%g~ZW!M}CrMWzrb1Cd_I@u+V#9m`3dcxSnKVAk(tO5 zVwr2KOqkkkV7JYYvt_8(`I74HYhWRek)JOVLvPIE{0%GSVo@nw@yhY}$el$HA?-s4xV)CJz zxgtt5-P%sz+dW>*6{L)w65I;-wwtL3f~ObCT5V$Q+bm-jm0*dXQ4+-PJ1{?-$dRfg z6*wkJqVL6@E=qQsmAtx7s+R0)23B^|)kreEm=v@)7o^~ykGNUcR~S=oEVX4h`u80# zSd|OoWimv{I%CU8a2y(n_z>4uGokhE$eBYEm&hI_682l3UJ%>QAL_krP)fY3U`$~% zGAYO|(~JUNw3#R;NcvCA+1FRjiwq7LSgcr9m0X@9iALK+Emal}W#CK7S;LE*)CF8L zjNUIN?Rwnp+)~zIUUBlUm!F^Pg>w1?>|%=z&q14eznYI@#>aV}vZ^>}NYUlJGQ7p@ zw4U(>1Jko=q;<=Ys%X4;!myPJs8KsZUmleN!AB!0a6ZPC$Mz_8JY8tXQBgT`*|zR< z(Ezr7BspR@-2lQ^Rf?eBE3kv8vuU z5iZjtl61NuVMK3Nxz+NdqNCbHcinQFZQa&@-))W1uGzS>NxeZTMZ>YU;X0}Hyl*f$ zmJ9XKcSQPX>J9xn1b8g6J#*o>F%d^&wqMFs3QtOKIb?0&vLS4gJE^5ar?Wac9A2lA zAsTNu7wu5pYC?BgYS0z3aui<|t#i<_as|zmSZqjku#UQ{f_SA==?Z462gXX+jjWr2e~=*kx5r+05|K zT8c4Rc+At(io^1$!?liYe~al_H!Hs6{9YBXO}9kK>C=k3o3l_=<)lUKK=m&f0H|(p2iIFCc{daax4`)BSHmSz*yGApdik2)nemOO^YDGe}OgtZo<@r@@ znnrI{*@$Acn2Vc?LW{5-5&Xc@ZT^{2tWK{q!1ijri2YYYOV@hwW74>Xf)U*#hq_J3 zn!d%x-LSgVBHS63)tnB<`4F3^P6J^TYz$HP1!>c2g>aVKj`?O!{Z2x(gD$EbK@s-) zE3Bm`7g#%If``vbzyvm?&~-`*+VU2RnEFXcR72>3wGo-xIAybtr5Mfj`Wm7fNnwSL z4tE1_F&$scOuX0+u7Yy~8Tl7p3#yunMPk;LhN}3F7K$bR7&3TcT?k54^{qmFM;&el zhGtqkD<{0*LyywmL8mkBF-vP}Og-*O-F6syUomEh3UWOT)ccTJ?>QE(+U53T=ugGS z?PWNNf1Yj1_z+l(H(o0CS+uwk!-GD`FzW{qQt0niB|omBLw%dt{plC^SUS$|ge=TG z*Xy)@j3n~VFK=c{;uCSG@Oy`0&K|8+A} z)9B!Q;-lf6`e0N=9(S2br^@gcXkTn zjfd9b9~7Ko_S2!6STbK|OB&}4t7|)dX0*8tt^}kzsFDuCle{Jj`w^QHvh{bx1-J@<81ikZ=&Vm?p2S z?gB0nxJcn!Ms&s*9*$b7Ajsh~+lRK~J{?DY<^#GIv~#fVO`0<_T;LXn%nv6ZZxx51 z;i$6g=*JJ9NJ=g_15244%!kZ;f6Rs{dER0T@_?F_hXM+WV9>vPr&0!PNyu{p91C0$!z`qpPwSQt@68drE!t@?}439EK zE^hI%ROpZLs^@G9-$OHLg+*Kecbsav4K7pj}1pSGPUQ&m;SU!W+O-Xf54@O)T=8s2#8 z@sY(coUz+DZ-CAKx4(x|VN8-64=AK?Y^l2` zLSJNjd{+1KTDt%F?31Rd!TrfX$01pJLR77BHpI^t?t(|B`Rp952KoqYf1b(^_RYYw zd(Q6$6c6^~9CM;4>LFu5Ki^8Xj6&UfrqCqf>OkC_Xk1&&fVx?SpKA?SuCSmT{|A`P zZl9soK~PTy60`B+?htYs4o$rcX~VK6{+8!jiHvW~K5R;ymDSv@?^KNgfj4=PJKE!T z{)Uh$96Q&-N7_iUODbhZ-&pHKk1e!)Q7q%XjDqTq4#|y76v-}}IWnJR4aRw-C8V_9 zYXp^2C+(52 z9l*8Td3Qn}(g$Z5t^Cp<@2T4m^+rGMzS|D|px9T~3b0G~)M5{?dwl=Gy?N46^od=& zJ(TzY;u}_S$CHbM=0n7u6-UiUwgeae-Ah^YJvM;RkHLLu)(qJh^Z8QW_TL_Lzjb%v z`^9bECwK$f5$&B~x=D$Qo|Q_!k4wVdjfXmv zC};8|bc)v7?;MG2n0u?FVcKq3n@Wwdm_%}lpkb{vN!OsJo@#Db*rIYVjam{F_$%pj(B?$IyYIfR&=d>*AjWsg;HFefCmbjy)4@B99h!W~=cz#z zZEEp^%~O)P`^L60TGtJgtnDQJMemG@SB>#x55s4!(u7$p2GV4bG2fblEQxkBxZyZW zz66Kb8G_X7Y<%w~VfpcJ$*D{k`$I~lL|H10nzx2qdtbQ@D?mw2F8_rj#dJYCAT*!V z-z18o{9ZizN9J#;>8XJSEIU5#x)YQl~PEjt86w^Rob{!wE zu+Y3yL>g$%tPh{{5ccL7dewqQ6Bd|f4rmW|aU{|N!S*L6eB2?Tb}Sc1Sk-YC_sTAi z)k!Xg!Y*k11nfH(j;kjjEv6BSt1vvge_tg=#vl1%5TE&Bjp*{SkA8vd@D=XWogN_KL_bJDVd@<9> zi#mwa=C+%F=dlJE*K{;|aG8rahZJeLXY0t0n8ukp<#+ZAh6_o!V)}6>%b6~Et={zw zbu>a-WkVd=??5%*J*?$UxSVcfMNP9`l{aiR3t8RX(IH^*N0sqzwi%ATdpjaO9GmJ6 zcsW2eu5rI3piP7+P~-QTw!?BA6}BnHPMFy% z^G0qSL%)&y9{`3xdA}RkPs_dd4Db40X2J*L%;2TfqTY#g_XV%-E3MHi7^$|vkUiad&SVy;9zoBAk)hTRHN9E0J z)Y`ALcv#u;=;fY5)5f_&HY!#b?0bFk6%@$VXp={rb5vwx4{E;{pQSE*h|`%q$A;aY zo(8_iRdv35SYI+d|3=P=u_~qxVAF;Qsc&7$KjDDoLy#SXa{$(vb%Wr|32c3 z!L#4*>~b>euqDK)!}gfVDp~V4&OH}mgGgVoU2L+Z&9&VN&+S=u*pS0+R{Iw;aHpNZ zmKwLI4rIHuDV-H`&$T@ZX%luR&5Yn3L3i<1oez7+^dns+;lk6{mRV$k51pijxfAjv zed`DC$`28cr=cl}8paR|oD1bo<>}_Qv}?8VoxK8f!HB)I z34E>}rCAXHJD;jL1UvdMHG|b|(rJ1JmxglsO$<)3;QQo{u;kAumX|R@{=&5KG8WUv zml}fAhJkg43%d*t_82~N7x8w`t8 zVx@x%;6=>Hmi)(sN; z6k*3vCFmqVApC}3>J`(JX}U7mE?u$Rx@3El+4d@TCAo&KaU(k_7?tDi!i_2{H|C<{iWl;V7Y!@h;lU^lJHM@n1L?0ah6CyEGNuFRFD@U1top>Q@A zgq`1MyyW>eN9S+Th`LCIA2cV{dIQZFzHXY%gkLVq-5*a%FH~a%E&wbYXO5ZDnqBE@NzAb92PK z34B$>*)aahIcH|hxjEUn2^WHdMG0Bh2^vsHLXaRyfUwA-a7k{+mE_)-o3OZ7ao?A! z;D%^LtqUpy5ET>!m%4AQ)-JZ%+PYL*YinJ;=b3ZvlH>+>`@R3~ufNR9nSGvl_L;f7 z_`~B*0l--Kn#ZU%W_gU0jM>tt@qljBS{T$xV@`@T$Cyhc^C-KXvgcE30i_mt;BKet zA_`8XU@-+tC|F9tG73(i;8Y5hOYKaGS5V-mU?l|&6f{y0prDC@RTMN+5TpT5qtt4e zP74LC^cgaZu*YaKPB)Eqs)G}a7#*h3>4CY%8mh6D>aLToXH%1N zsKU7voaZrqVw_LG1ys7hG%lpuX>9fw zcN(|QBDPSayQFcqG=63p_n5|3YI?5+mKyibQ1^R`pBoQQ@Suk+Hy)zkVVdVQ8u1a+ zc+@l=qZw?cUj8c$47p7UjQ9P2Zv5U26YJ zO8tt0_b7Ouf?rd;4?M&-Zt)&e4q#3CB%K3=TVJ8)Z!=#2GjWYlo~?8Pzr{5;5DlJxc?HrjucZ9xCa)sc&!FH$%9%<1RMY1y z4;#QwGWl$i*GOJ#@;VPoL(;+AIUd%Z&n3jU7W4Q#O1(wFO;TSdc|8@+mwW-$T}Zty zqSVQ>!AN@Ld@%*%Xpki|E82NZERQdv%BPt8RElX^6h`qCDcsLjO5PxOBP}*SL6e6S z@l{x2d}s1zns(6Sr%}9`x*}G`TO|*9*d#RyxI zheC_rPf-84{GQ3*H~Ft=ULR25ZzTUM<^PU?-%I|XhixaU{ey>f@jp_Hk0k$?=J1B( zf1+u;CHbFeChtg29FqS<^1o92spOwg{5b_*Nd7m9zm)tdiitz=e<0`JUwhbo{!hd& z5<)sxnEfvr{NE=3*5v=8@9!l4-oxHf44MDn;Xew5=3Z=tDdu5MV+uR@+7y};x+x4q zRO4P#a8n2m`#?zKwfqVSuEY+6DTU=>9}5o!DHPZk7~dg&P-?mHofN4a_L)dC1@T0Y zfmkUr#ndl1(tn#gB1>d@L_guBR1O8X9?@U;C~pAO9Z0S6aEW3NrH-P2xS`1Rh#_Jq z&R8v03_~;$!zu4*`XnAGMo>^dc_S%RNCicvD3+qcquIzdHCv3r<%-di8bcMwQo+5{ z*EmxU8x#`|8^uHlN-3CRieo+EIA_o@3MQNS<;V=h6cTK$rXYSOPM}~awV#I6ZG5Mn zi}C?eqMU*XQxJC)(@jxDQdf00g92iaVkQOE6wHc>mLKWsBvZ^bMU9x2;gEqTYH5Tz zDw{*WTq)*xv?-z<`L96qT`CqJdk_mvv4~)KvMCl@7%!oZrKVU$@hPS_)fB`E#R`-j z!f%R|rf9%@7mZQ`q-dh#Dk++!2ug7pgCSL|oh|;3K;xYDK%2ik&{*GrU#)>qM@2By z7!0k7FtDpbp+I|Oi$4+xM5I{Fz+2wd7Er|%{*H#`g~3R$Bit@U3sz3dVPMViw?_ii zp{B4DtqjbnP~)6%Fx0^yMODONe=ClWJ8ODHaiFm`PQBlR8a0YAbsE7aM#GJvhC)&&|mJA$}U?4uf?8*UG-3WoeG^`~RA ztOTfFQ`|QkJgtpjALziX4R_LxWX3Y0Q)Dn=c0;&zbnALrz-f&u8%Ebqa!z8!MrSP< zvDVJya3VQNopFQV(bb{0&JL%5)*Y+6(BIM-z^eLE0%?3o^Vhdgwzd@4Vzotah(U4B z`Silu;W3QEfwJ2o2-bOlNM}n&d{KGvO>v6q104ywk`ckaf(-~c2UZc4MN>yiS`%pR zsOW5Bkm_X34un>9G-DN`BhuIu>$@Ycu48n4u(hovfV~E0gxg#F9oV>25Dh%?O)ZjDJK$mhZ+Oxa7!bBb_79vXIopiy(2QC-QQ3jXu#!XwFlO=2Rk|fAw}2q!834J z3nA1v12GqIIHw?n4D&D-M_5`}JrcLqs8owhpB)SZY85)q_pfXT5Y~np{4ERp?Lqo> zGkJr9tfF0Yf?LzGTLkY)+8}Ky(voOV`-6N5DMhVto&uVuYF$I1%_W$WRe=ryr$5wy zBP=ZFjq~+$XXD<7kegtO$-Sv*rUrgq|oSq&b zR&m4s%7Fh9Dm|jfr8oGA66VA?ftV5u1v^e)F!8_HuZ2Xg&A|vsj3XHSs77Q;4IPB; z)FZOh0B&Yu#H9lSUIBG^;{VQWVNa3&_uGJ@n*PQ{MZPFRf+*$-l24BNBTB6FD;Swf zuy_lWB+b{?__0xHRC*~Pq8NuiE>K)iWGdEE-oC24?5bON8m^wY>g+^HJtju_3qlcR zCjyO%f8l^J0>|pUUel@Q47NDTBKP#pa7VzES>>U|1)?g|gWNu9BXUL* z15H7ttHb51434J$qf4$>-w`NpZ}+dS>+FcjRyB^f63Zf(AJd-ttj}No(pAH1;)->l zmi6`ira%lk6k4OgAp=Ug!1-jh5KG2t4dJ#xW2NF}8Udph5yu2;bqK+Q6oUe`Iouc_ z#lxZHsP4d^7p_;T3dv-%CxUwC92b0^a7$xllv8H5y8X>)54Wlg5WY)$_B`BQ-e7ppwxj zL{L2_??CF99as~<=0i}lkQgIGtZPaxoDs+<28X-jM`={e!|gm_M|eTCnvh1)I5)1y zAah8>_-scpL<)+`4_8IEFAZ}m9G!wn5NMd;Xcuz=QB2QpwT^^A6BMRo+%eN_p{T=E z6b3pIhz*_yrYJZSv6BqX>Qtam-?2xN445sKVZ}Td6bBj)Q?nRftpr6zVA* z&i>5zN6<*|RFXVzaXxzCXKen}sG|*!S0!e@DuDJ57I%j|DpxLqytoI+7+iWNur^j* zaY#=L;J5;BB8CMT9b_SW9qlj^7whsqM(DPto647)0E&5?p%4x?Kiom+nU46?T|LYo zbuc=LqlOs;O3_Ngl9i}rHAaxVD)u?NV@wIxQtIUYXtO|7m+XGRoz`ErvE#b9+ z82KIJj#T4cm)M?qL_V4@eu`4-XGb-$M8z3bt_l^aA9Z+nm0&a?z*8K%$em(Lvbf(_ zktA#DM&|X1KYpd1($+2LhHtuGNuP$8#I0-zM^L;V_UWOv0O@gY6rfosyEf3CNQEh( zmF=$n?5de$3=kJV=p_@b$*@Yc`8$Xkk%mTQL%O?yIFCA|pt=u1vSdjg9B|41gkkdr z7Y%!He+M$eeEsX@h1VjlnHRu$BkyC-zXA6mDp_$Uu>r99>15YdN9qG)pp)FeWd+J?gNGL^SuzrdYIt0p2O3XwX}l2`F^T;}{`Qb# z>kTVFTQs@lx^&6X>ZML+xrsc_wR)ZQ$x0l1INdF&j@j@1V!XDSEtwjt=$QS8*(jh9 z@-BqXrf!rTdd+<|%s|BGG5HL%MLXLA z&ae&xP!_c!V4?;kQivH*sysr92{9w3haiiI>sSg`1|}*!i4#+*PI_B+`1iaeVV#f~Bn(CTdbjiUw3d-{WxJcBXI{XgVDrEu{(i9Xo zwc)v>2$R1r(L0mqlyM<-RPyfP@=+92JNQqXr-mk4n`D@!_s+y)-WoQK{7G45UxfvDR&Ze+Ra0gvbex(-YvVjD?$-w!54v)|<@fz? zI&*q^BD>A2S2^}$JME(Wfl>zy2XrRh< zZGlj1Yfa=+3X#a_ua0ko%2Q4Owl3z2ix(jHs9~fb7>sh`0nz5k*m)|}t_ZH09%u-* z67PvsMs#l;9WPNM<|Hpd5ukbr`P%cwY@J^Lep^SAmC!+Y~QYViR&PEw4l^vg#fCy*-kfD>P zXeOwsQU3?_LdJ*Fv))FCfJ$zlF@lOwd$_T)K^b03=pyG%%*=2c8<<;Dp@WcmyO|ZuaTHOB>mf0N3=fA$-abRsZbj3o z!4Wcu0^P=PMUOMZv;;K5R^9!5&T#^C25SF77)Qt6l4T(FRfoO*; zE|IasbsU@x@|_N}_5x(HJFDZWg|h+CMdTz~1T7&q>FN|6qebX?JhZm9m>*Id4k-~a zCmad7lgvpd>vm+6p0IWY3o{b1#YH%6-TZC>mJ||Hx?9^OcNZevyrQ(YP*maVZYGZ_ zIF!pL86c{oro3t|lwOP0YE~j8V2*-VTK6oRB35^d@!%OG;-WCO!YYx-0+F8UqM3=> zyi`ulYQ&49diy-PEB-`2_07s}k0@TR$fw@f?5GuCE3a^4^fOIy&Y4k7fX;H%8Iw1D zj7EGc?!H~wvzVq5D<&5R_8aA3nF%`_9Wh0Xs2F7P_nQ}y9B;-+@CE-wD@)EbhNQ8VWZcT)^Ly_?e zt>cXDd@|Mp0;@gT(tu+x)=sL4g_|KrY=hPg!=nDq@lUJvreu*8&*Is_)1M~?1 z9`c96BrKDhRJcf!OWwt#6Ud5}^vayx%@s1JFQJ z$I4L|@0m`fBe^YbzkIGjLzhNyO68$Y*s&-hF|MfK5pOfuVK58k%}!u2p+LuI%t>T0 z(X7~M{rs@2s*LMp)4Om^ss%1_lsBt6&H%KPXtiZHiuUY4Q%6+U)`G}^M8K_;mt0S$ z{>VONWHfe!qqLlxX!&$gRDFG@rwS{3>r0fKM2%9yJe$OGFQtpGal{lUt+mnln5wE$ zmAV;a@vA|@lNeb&YmpH^t)o(e92At1;BF;*Id-5zB$&kbuU1?w-SvbkXZIYUn4Dlz zFBpn}mcyL8WX8NwDB0RORBu_X4{nE%qlsIAR z0N1fTQPd7eRBFO#Wi~tB)xiSrp09=W5y78l@J#0GI8f7t{1E(RN62sVy!Om)qhBaiy(YrCptLu48R+6}HeW!)b`C#Wl9LR$OO` z>%|RNJ&F5m=ePt;3Mru|P2N@$`#mEuNQ+$3(cwMVqaY;C*Nh5V+EQ*m4CM8F%P zwBOodqu3jGysGk={ORTM%P~)iJ85cLY;hNLIs8Zt^U+f8+Ym-J)f>#?$0g4mk1Nr3 z5Ks{u`?QCoxZ4ZhC+tGL$|_fhITalb8oPMh>Hkx`Ov#*Imyjhp)b?dyYtVSu}_MpZSf4%+Ap59#dAivEe>e^vBg2{4HN`P&xn&xdt&nJ zQrf5IZS8r&h!)DhA?Z@ro3$+Tt~u`RfP=;{+n- z%QZUVoNS9Xs0~3_`%wp5yeZz2;%!^JgPm&s(7v|CyW*F~<9cBFqIQ2<8+j=P^lhKm z;#cB5YX811eoY%U;s_6aQD7gcCQbJPTl_}+R*K)*;`ic1TYHtj{s-|#TYMxwwza2; zME|INWQ#u$aQ-YlvBh7sKiJ~0RPR%Q-)G`;TYN#Qyp29@6MwU{9}p4r$@&!Am`0oT zrTEI$zC*mwIYIL88V>jmTYN44Y3swpH@5hf__r;-MJ9$-5gUf-$JpXO#1lvNn13L4 z&CNNqaBnaqph4MTTErmWfJwOu-5R#|PJA!L54QMG0t(zipNB@bEa}xqlBwNnT3cTw z8S&_cY^fpMNSy*ha$5>*k1Zu4lr$-@q{o&iRL_>FNUkz1Il)gzo;}gl+xsBy@sm)D zj2T-$W<1gB7~41wH%F!;Wf{lQ$Qek(GLuHm!rhhGxQVi#^x86qQn{4ZU;1o0K-@3o zKwIXauoFL%gKT*eri|gn(MgA$Fb1(g4yLgujTti*>%>lX^3{P#ez>VD-^8jQWK3Tjb&K9VVM%6^aV|eJaRKP+g*`r7cSkOZBz-I$MsCBW*bvd7K=I zv@XXG)t{&R#Fk^l{S3g?ztRuda-2RyN`&=k`s&^~ORu8{oOrUMZCg$tz)w`%MWU?K zMhvy(BzY{V<@Hswt19Q)@;F&$YiqQ%wwz2`fAkUEai;F3*jfO$P9BfCi#&mFV5-&< zU4D1n96=_hb%RXIfuD5OCzjf>Tvph!QrqEbFJo*uT~<*?Gi-SxjW&~LJQ>Q2w`H}Q z)q5#z?cpZW72A@*i14I9-dpM%Iy%cAuOh3Nll8W~LBG(J z^W_3te^Gzd*00mAvgJa#$d)JTUR!@oKbTap8J9G7e9~Mb%4x`0Ms(z(t&v|{n;&z9 ze7u(u1+KaYa zBA3$aZ$}(Q>S8Fu^${H-C~QJnDMb3wZ`1Fv6xaDCo5H1NyVcQSG?o=$l*-nm9Ijc5W-4Fe;j z+|3F6#`XHzaxE>iP_Cm3yzMmQ^|UdC@(c>jq~NS5OY3nm)J`}C)6d7k`1mTNa+bWIC(Q|d5OH#*1ypI z)~&MR%TK86o*X0qBYMV=+U?AOaB^2-CMgfL{)55LPVVjSvJJ~fvGtAmro=XM7-s7? z=r<*1R;tYHI!ejQXge=Q_~Rn4u=T&_zq0jT>ZshltK>>;807#xx*(rYep7q6HJ`!2 z{El$GdLc5ZqONXsRXMVaa6~zeM%nty`YTb>rnjTr*6Va+a97&$DtR>p*Ca*6v8CNO z_o21sv6GT#ADcY;I9pyz>fXW~kCU2J#8Qx}jCt7@Ri!JYxciV<$=(hYBvKS*% z|IwEB$gQ@#SGG!dpDpi~Ker9rNVO%|Ir2gIkZoibsEIvH8@i3E-Y*}q<)ip%WE&}H zha@ty{J8p&licG;wDFiNw<{f4BBpf7owjxfu|<2th-mW>$gp=Q6q36UHRauNDezY=Aiew{Je)*sLx#K0Is!C2cEXCRB?3+B%#!TmNmJDM=!%PF?yKKZn5 zOhDBJYaxHEIIPuv*w=Wp&Cl3!zkHTb&!I|6WFo3->#OD|#pVEtv7sYY*z%x!-j*-O z7g5#AUoZzv?y7wHeunNJ* zD0&z-VO+0yIT}Z=ib+he%jC=5xR7#jq0w;Yt5vpqMZSs@e+c&)O5npd*HD=}oNJ9i z>0vh~>7z#mB}e9qiPax@cr1uLl1%IF!9@gXS6*gWu(YyT-Sx=s(c1zvpwwl`S>&R0 ze9Ey^s)GPZ(>zA>_T46{NcX#&{h`K|Ky6@c{FT(|L?3OkcbQ2p8y(SbBYU?~*Qw%{ zO-InVQ0&cHGT(0W4sW(AIBEicjz~=~6l|pvX1boHoXNR8anHFEL-)Z}#g77e)*>%T zdwW>jo^Wosc5|z&Oyayg{L7p}fg;KEeourv))Bg9(Z@5LCG;F6p@Sr^Pi<0GC=7TNT^oS>&iSai0ptDr09?CR_FqJz+{ZyTf;Oh}qCF#{hL-^7PJNd%!t5 zB`;$1^&{PQi!Z!??2xv{Q%IyE)Y}{n|ERvNYbR+*TNDbcjSV=gpzpfz2bcXjBLRB- z>74pjHzF?xHU-sVBj@gha|NBj(B7om`w?N5B9mxFO=kz)JnQ~sD@I=_@r#;~?)MAa zdx+C}otO2#6>ykq)9PHVd&hLko}3ZgFgSXl05Mo!MVrv$xkYT!1A6)DXjg~<;u;EK z*M<*!!Cv?kV@FPYL|5|TPseg_jHEqwdkgKHl=bBLeWNd3C`meZx*yjOj$WoysOwF7 zh=~(WCu#1jEBERScIsTYv0{aEJ5gOYd*=@jLZ&zoWA)_7ouYj%wIpuU4xQ&Xq~-?Bgue{Q&p*R~l*s?aE8(W(XvH zKVbqLX~uGWsttD5&7I^tL(7boHO5^`Q<9>@5kFTu=E!Q&m0t2)ku%N#z56yT_O!{X z?$h+nm+r$0I?bdjH0tcVCYm=iek;|z$yw9;kzCUeaPGcVc*KuLXd8Qa;rptLlBr$2 zc5o^IWG<|LDVY(5oHq%k7%R?^z9ex;a;CN)A@CN7V zx!DnRmQFYH4%v|8n!j^ZbH}3QU`xQIJ4G&{!R>*!b+*&(^#P6wt`_NVfdo~2Q9&Q; zLO}fA>EGFn!ANf$&qI!M2z7x>v1G5$NZ==o6vISyTZi6e^m=)j;N7e7lL?@s7@*6H zvu3HcY02)-s8?&$U2jgR7tlk8qmn-=s}4n#jeb-i80vJOT^Dk#p!{ZkWIBo_dd^VS z?o!LVFmcKXobUW~@iz{=5pPNNs|4#KxOUV~{N&wg2)E(n3X|xDecmP>o=6+c{p@7= zS!#P!^f=dj`^CO1qinTdz5DK6JptQ%ILhzM$ol#rfa)c^EB|ji(#&dz@fC!>tM1!t z{gIg5Lb6KytXLmY!&0Kp0H(LLB)?LsMC~9tv~?bdC`$yfvfCq(T(tc|gf_?`Ty=JS zSY1dr3KT1#CvCXnDV>Q|zX&Jmn-vpIc&*5l2I)LDThQZ;VReYuHoYy1xXpV#FO(h8 zn|{ewqb}cNtWtN5ah5T2FH8N>Q2R+C)SlcMi^#WD#U5x}egvx0A?(s4(Lk0Q8D~!m)FlPpSQezPI+b3^78uS6^rLr)f3xr zUY!hf3Y$XxLW6z}Hhb#%1EbS05QOOn6j^u?W8)vH2b z!ff(~G%h-F^&NL`C@w!LUY%Pey-xBOOzR6Cz1Vi2;ILx95{95@B4={+N89L|WGpc# zLK~ODsvTD1`n|KeIbs|I7SWGh^D2LnMW$P&^X|Slaq;nIfFqOU#w`76^-FP4mr*+Q zw1B*u<&A5C>VcybpsN7Lu2v`c<%Q(;24*VlQoUe^J7meuQU(LYnXXMjk|kwYE*6o)u5upY#aL_HCF`DVEL5aZNvJ&7$vK4dao+E)j6aN- zmHby)yz1@XDu2Uz;*VtPJ3l~|k>HyQJI}wg1@*B9X$b|B55(K(T;<@q1EabF^ zJ^)Ghg^OPAS|W+na(fznsuqor88zc;{h@MtSda3H$D^~e4 z`pBP^G~Zt7_#a#$m<&wGMQ4WNu8TANxH8{YW4hQdxrZkpNi}udVe;XBaK*UMRE6GI|LIZ;4)B;m1?TorvaMk@Ql=(Us{uM)}rCL(5Ve4liX zp353$FI)jaAa3fvd#Q3nX!nn-*DsJwy7)fIg{nDktMm`YPiXQVlL#iRS{ zJ^B^A7WH&Qy+n!bSFb02@wV}&eLWj=d-SKbb#^4GmA!g2+gw1YH$92nL~9H_%o!4S zy+aO1KB*w-C`sqGBw?&r$JC#rc20KG3GZQ+G_)AdotmKN7&ghJ2CoLZhn%pV=8mb-s!hiL#tvh>lj>n zgumt3=TpHRdC8tw#>7Fgyd@)hG*ssap~hZ6XNj~GHQ|$#6&7^w#(fx>ER#XLqao03 z5IUb`pT zIWW2J(5;x_{NjsSH@Bd=4-;yr`Zb{Fq$*=K=g74aGX}E?l5p7U-kvjLl(xecoM)7% zXDIIKj21mtBrUGIvm>kwjX7lV)JK2s%4qes({Jgh;}eIFdLMR9J(3f)LtZ^N`UuXZ zqt16U*W!+eX@_sv?RRRE0K9)6rrE+HbxSxGQR=J8=T**JUQ;){YWeb-c_%HetgBhR zs6O^0TkVSTb2*u=ZzNGuTAJ*)9R6=|V&jqOU2tT*y9TGacQo$sYFX6eO>?d(MDGnT z7+XDNT8oNXRlY&58dZ6liVb?(sN#s$q2f;WdyTeM)mx{nSMeFznJWJ*?Q9jFqn)er z&(nUQ;`3FxMz0}Nyg}8|=uM=GFLvWgRBX^QNflq_#+R${HG0vg;w#fCK$IGQ;%~2IXymqY8@#FW5o$u zfBeIs2UPDjVmK5`OrDEhKLM$o4_Vp<$ki@Hm|g;dwM$_r&g~|wHv#+3f>iBh9NC~z zw2j&(^$#Bm*KW~n#hs(nZCI+||3kIgF{NvF&}@ORbvV<_+MVu9o3Sqq=?Z!c4o)2u~%Bx*$PCdh58OiD`Yz$ol+ST zWYT9A1=;l3k5XO=athf#$lU?`>BHxK44{vJ6y#Abh?*Ql!Qet|2jo*~NMRQYEiBv) z!*;`P23Yh+Y>}rzE-tb^0{#X_!JG`lJugmu0Akl*T;y=v<&oOGFiLwE#%qtjWNjx* z)1HKx+Fq#9o`!kaepsaa0+wlSfnR$Mn#?X(tq*{(J`m2*^Aa}lZ0+aT0}5zoEATOu z>ba2yJ#Th5vJUZzY7J3yKLEKDnHfUvhNBJG2-4UJ2Cnaz_;Nh71Nvad*YnlTw$p(# zbdMc**xiwLkYsUJnJ6qO`G2(|#RnlPYs4-n$Qp^8Q@9I?_Nq-PrjECRH>ecYIK8taEcAWm0h@ujvTvWo;Jr4b0ijb-&{cUr1 z`ze>LfBOG=wYXtWi2D#@3!lc=!l%)_#FW}gl_IWc zblcTOVyxnAuoP!L9pQSyZkWno@$6?1Hq#E`?(Tu|#XF&52TaqC&%@0APi?_%qeR{~ z40H0v=qGc;IKv>$1*wrGFJ-bZknHcH^t zIQVJ^sn`MaF*qgA&jVZk31sW%!%_N$FjBt=$}pa$UkXk7WzeQy9+S8Ekgq+esRCCu7OPb zI>^zlN1k~@g76;_+m|7ZjO#X;?TFjXWX51^+A~q8cfriK^c0k<-;}_GMUq8r*Seg3 z^x-%o57Os*iHq6;)wpxBY7zJ+VgBqDr3NeG_?3^nWYJzxdN-mblySXOc!k&4!iE#? zI{>~Cj0boH?J=q*wOvrRXe(qzb9ccU%3)>F#}7b%AAcI=ZiI{yAKwRaow{$Ukv1!4 z+8>*g$*qt<<!~0I%dhPP?t~>^vp3kAxZ7{CcPVyp<|!DBm~abXt#+d6B4Hamr0qf@!$jU)pvYUmV0NyyTdDm+*;(4-+8*HSF66pTAfGi5 zLVM8wc#3!)doL#E-qZGCFWqTyUyS>mhBQb#GQS)5Q<7~btWeCVrf7e$%MWs40M;D< zgH)Wz557Ri{|))USK!tEj?n){0`=x6q28xsGk6TgCzfKenNxhbmMNi*c1)&T6vCq@RsAQQ5TY=nas;gAD`#sC;&41`i652hMN z!BQh1RvJSfWE>40#xbxC+ni$*!v>=St~N$1_{_xhW#&<-j%UZXuvRw@2}rS<|LbPtHWh$!;kL9{_ZqftS$@xO$q+ehCJZRiQn zaR|`~2+@fM(Mbr=;}D`{2+X(JF*!H9~Y2LUaN`bPhsvE<&{a zzk=vXs8S_`D8lq*?G=~E+FUXp)kDe2$f&>J=Roz({O45{gxv zVBi|daK)#0Qvi3B1lX=2O;gh-iR0 z9i;xIQ?`C6)Dk*d(6&f7vQd3 zz%HiP-Q)X_TY}MwdmM&L<8g+HOASnz&H;U8|TBR#s+9G zE`p$O3A7lO!CA)Th}l=bRmOF2lW{%VW!wn&7&pVujg9cAaVzXFZigp~JK$MkGrVNn z10NdqGR?T3N#o}%*LaYPG#+9TjYruO<1sef*v@K=ovhy2#g-a-*mC0u7Brq>A>(Ow zf$@yOzj-JsGg-U#8kzzcoWQEI*R?kgPs>?__9hw~1_apv?QP^h99mee_71A10`7qi zw0E^%B3|x*SF~R#fs6Rr3yTfyJ?(v$&+o=O;!0ol#9~E68wraqh-o<+Tui<&0h4#a zMSDpFJ)mfB+HSa*XwTB0NGaOC4RbCzp3wUe?EX^xyG-Hrc1YUtByN{O%L>RFbwUh?5@ZSM)bUvrz7vK#QzXR{9*oOJ=ImQd%8Wmp$2UYw6 zyrtrI;42k7h*k^P2<3x_XfGg=y@*Km3e*{|B4WJ>ON_T*x$zDn)~{fd@jk3JK0ucF zTe#5p9b9Mp0TJjUc)|Dt-ZB0Pe>OgY&y6o)f_n+vp#9nv++QbQOCQA8<2ZWejxkaA zgza#B+$pPU5##R(49-k~8o$wgo4ltRdiM0qVS4(V_Isx%-i2uDL(M9`o2j-PetHl$ z(nT)f4TM`aI*7FceoFgai==)NGAL#6IH==nd#P|ZNQSa&y<#WaOnlNSx?p3eL<>l6 z0gOr|D>IE!OE2~4r73!;?G;a?l%=w=G_SD1){h{&?`mSy|Q1CUBp9z@hBGw;jx z%G;6T9A4iIxCGA3L~xdwWPqmnxEgN0hw^jwmgUko**$wEHrNgG9#i}%x2!+*+J7&ku<2|ztAh*Jjmk#6 z0Quva>>cvYviB+0=4y+zWmx{DQY}Gy5mge5;dt#i?L+nbCyK$=4(dMo)(27SP%dkN z#Ym4FY%U?2n^3}2U>r|{GM)y<^Gqn`*)W@Xp`Pc!3hskuJ`ln@4joJs%2Z z@nLW-9}eg9qu~;M3|!7f!Zo}QHu4hK%tygKJ{Eq>C&Jgflo@;y%j9LOKcCD7^W)hu zd>Sj^6>Ji(WYc*So6TpkIzEHV9- z3)y3QF?)tDWiRkk*lYY$_9ky&@9^d9Bff%t%Khw1-o*aNo7wj~sHOAO+8`d%hVrmh z%-glmyhAJHo!VT!UR%u1(w6Zvv{U(+T8N*coz2hFHt-9yOZf)vT7IE+6Td{ejbEm1 zuwx?%`YXZ2mLd$G0lZ zxDT?~Tu9SC)c%0Nd<-hWr2b~0m7AgRGFS=RrTr1}(zR3FymXkY-Kc$}EaYbG4DDk~ zNm!$`;I94|#rb+IsC|NY7M!IuX@9|#2bHDc5i-EDaRDannXKWqu*Y1<-)6&Coo>r z(?{j7@a@Kmfz3N%M{SAA(iiD;RyfdT+ycImjLBWlwJ27!!9WJ|QPe@gkqEe>sn@|A z9AXX%-#OiViTvk?ybr)&{ty)KZ7`lcg2?+QRARcC?|?bHE9N3Ef}<434Hyh#wSPM9 z1XvWaJr-%-^jM}rzgFoi)A$a7G608O(@kh~!A|nS4J$KB4x+X`5Os}WrRTU}iFXrA zJSvtri6wzHnIN&mO9g7=MyY8{vK+C*9GS#XIHec*3=(6zNN0Ct$|#g6m>m}?gB+nE zNus1uH4-VQj!031cyN#u5-IrAQkp`Xw7E1xFU|C3dNa1bQg7x)I2O|aMaq~Qi(F{C z{gW)>qgjPs8~Ld4rTem6K8mG}Q?Li;j9*?;abtUf9p}d8hD_YbySsOl8Sg4Hp(~t} z-B>D=^vEmACXSiCx2&H}qUui-oH1Xkl2B5O7)EEE+Jwm+sBV$(XYt0AWC$ zzYACU#a5Ic>3WVe$CqA~p`^nxh{74$%Dl8D@7}T;T9c{PbYD(cF6HL#E$dHf=|^H| z+ah=KqQc?6{$)Pw!nYS`;6wNnzCc!A!X~jY#LhOhj4ekdzMM6xco%zyJ%{D%v?6Uh zV(NJ9Sc;)d8>LmN?^=qnr=jX*!Z2+N#h4#)Z!FxX{1UZDRr`^uo`p=5S-Jdq7|36M zVf+^`ioXIA`KwUD-+)>CEtt#Sh6VhWa58@nmh$)ERQ_x5^ADhr{{~j^-@#cg{7059bT)#2%}Ru1lY|EiUz1G{7Mm?>HczCn#Uh<86aCn7k-^X4^J19o5cSRxlK$Nf##VGc<7|Z@4 z#%YEauce6zniu1NqEtIdOwxvnMvVKiMKkeU`m!%nyt9`5e2Qna2JC%Ok6}eQd zcCupC28`0ml{nxqOFLSL0|6&%xyZ^NK*6w7>!-wl38!kA+V|Lt1%53}35FDC)KZjS zuwj*EDmI@Ar)k_32W{*}B_h&^$3=M}rh2k|gMNlKxk<3M$3cfU=`be2k95C5_4FCG z@L;^S8+S!^y05lGA3ez^F-A`kj)nL%IVf_3B6E^2jThcP4!t%ZGk4ABNvr9DU`wP*1}}59%hI$pjMm(3&q*6LYxat;yj3m^WhwE0bC?5giFOmaErJU?!vnFiR)pT zxB;FKH^S>;BfKMSf!~N*;X~~E&tfxtBkoi?eg@9z958i3`WdW%V|0nBG{l6}if~g= z#n`2rx`jJF79P<($Wkph7Vg(mFy%qa+p61$87a^Y-cl?|sTtk$ey-5;GT(Gm-ow?5hNgL}?X9C<5B+oOTm7l3tpL{{HWcUFN(ILwPXA$+WS81X?SfD45aw=O*F981#fIZJ;N#!Ta~jZ zAA#rb8M@D-)7LI|(`WfSQEj1cG3FU7aNS#fG|6?D?h`nQTY4|d+zPA8oW0o!V|vWs z&|Uk?y=5t+Fr@4)v(+sM(j~G>8$xpxC9r-@qiKzz2~6A42NehIaoW zNP*j75b9*Z#ZDM4cELpPI7}6L;6#jT#8WUw?1Or-A6mq-uvR<=8^rT)xp)C?7QcXd z#LKWlyarE-*WnHE7W_-R13!v)*>Lebn;3wE>k6b1Tc>^|`Ydq4{IFvgFFFIkuPmhBc_u|4ANDCECpFN<&3d*a{hWAPpP zMEt=1C4qe}nTABHrAn@4OG_IjJ&MwAM*2P$yzaT=k1$bDwZ+=Or>7ym_psw&o)X&@ z?0|RlbZrlq?B!VL9_0KPRLUNKWA#iu3pv7Q_D4NiiS0RTzY@3tA-+!uVTpo1%eBM? zX+jBMgXlUYgbRBKVW#)fz0N@rEOPJ8dH2BEi;H%`J2l07;N8VL;g?@y45 zKsqy^gZf{S$M{&pjb zWJpTe;ddM9LgZ@*vfr24TfyD~uVeZ{EW`Er?7hB}{V4g+uu35!QfA7yvNWQ_w7q5N zG{KapJDH}@uh%s0OGipZK)9q&q@01@B+nLHJ~lq=vLawXGb1LLxVnX-}P$^aWIo7r#~WQFoHHc5uq@iNS& z$~HD#M%an6GbU4Chhh2vqz4;v*)jS+qyiHu@Mr3P!$MdHMI#?(xT~*gwJi@4XN4NS~oo zsV?{ft5v>dbrPq}Sx86NzQ%7yo z_nC%I(COL^q$#e>#CY^f%&R&oafGHiZ5tRJBg;8tixs(&)jdGNbP-~uEZXmrj@tEC zv@rqy{-)vtwM(84w%mXybrB4bm%w0o84QzGAX;4oCGr{=FRw$ix&g}MjWA2z1oPz0 zuuR?#0eJ_s$t@6(cf(rwGdNq`3m41#5zQWeE%HIQUv5KGdj$5$$KVCI9Z_o+d?X)- zPvxGNqMZrvyC+|R;bnE+rNemGtv1|%GI&&-cX5codbQ2ujQV@;LlJ#w!cm6i+AUqM z8wc!hlp$X{wB15Kfa~^z>a0@Qro<7Z_8C-~>^O!LCb=`0hdN2g{s+s9lKnozf&2uR zi2Y22<5cX-W&o{4zKM{33o_)}&`-XD3fHe-qI@rbhfj&|@G1JybRkBi2+g$~vp&Y< z<}FJ31U9ofH&>k8XChnvSaZ($oO8ZEx#xU;PI6)$JplG$#wUnxS%0A$oqtWZ(TPN1 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%0ASlEEB^J_* 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 literal 0 HcmV?d00001 diff --git a/spring-mybatis/src/main/webapp/WEB-INF/web.xml b/spring-mybatis/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..569b5ef9b1 --- /dev/null +++ b/spring-mybatis/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + + + myBatisSpringServlet + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + /WEB-INF/conf/mybatis-spring.xml + + + + + myBatisSpringServlet + *.html + + + MyBatis-Spring Integration + \ No newline at end of file diff --git a/spring-mybatis/src/main/webapp/index.jsp b/spring-mybatis/src/main/webapp/index.jsp new file mode 100644 index 0000000000..b60222e7de --- /dev/null +++ b/spring-mybatis/src/main/webapp/index.jsp @@ -0,0 +1,34 @@ + + + + + + + +

+ Login     Signup +
+ +
+
+
+

Welcome to Online Student Enrollment Application

+

Login to explore the complete features!

+
+
+ +
+
+ + + \ No newline at end of file From 44f5742f16c7020de4487670f33a1e6527137308 Mon Sep 17 00:00:00 2001 From: azrairshad Date: Fri, 24 Mar 2017 09:55:02 -0700 Subject: [PATCH 039/102] (BAEL-746) How to Copy an Array in Java (#1474) --- .../com/baeldung/arraycopy/model/Address.java | 61 +++++++ .../baeldung/arraycopy/model/Employee.java | 25 +++ .../baeldung/arraycopy/ArrayCopyUtilTest.java | 163 ++++++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/arraycopy/model/Address.java create mode 100644 core-java/src/main/java/com/baeldung/arraycopy/model/Employee.java create mode 100644 core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java diff --git a/core-java/src/main/java/com/baeldung/arraycopy/model/Address.java b/core-java/src/main/java/com/baeldung/arraycopy/model/Address.java new file mode 100644 index 0000000000..43c6d77fe6 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/arraycopy/model/Address.java @@ -0,0 +1,61 @@ +package com.baeldung.arraycopy.model; + +public class Address implements Cloneable { + private String country; + private String state; + private String city; + private String street; + private String zipcode; + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getZipcode() { + return zipcode; + } + + public void setZipcode(String zipcode) { + this.zipcode = zipcode; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + super.clone(); + Address address = new Address(); + address.setCity(this.city); + address.setCountry(this.country); + address.setState(this.state); + address.setStreet(this.street); + address.setZipcode(this.zipcode); + return address; + } +} diff --git a/core-java/src/main/java/com/baeldung/arraycopy/model/Employee.java b/core-java/src/main/java/com/baeldung/arraycopy/model/Employee.java new file mode 100644 index 0000000000..757a8f8ec1 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/arraycopy/model/Employee.java @@ -0,0 +1,25 @@ +package com.baeldung.arraycopy.model; + +import java.io.Serializable; + +public class Employee implements Serializable { + private static final long serialVersionUID = -2454619097207585825L; + private int id; + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java b/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java new file mode 100644 index 0000000000..2c9a97c496 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java @@ -0,0 +1,163 @@ +package com.baeldung.arraycopy; + +import com.baeldung.arraycopy.model.Address; +import com.baeldung.arraycopy.model.Employee; +import org.apache.commons.lang3.SerializationUtils; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Arrays; + +public class ArrayCopyUtilTest { + private static Employee[] employees; + private static final int MAX = 2; + + @BeforeClass + public static void setup(){ + employees = new Employee[MAX]; + Employee employee; + for(int i = 0; i < MAX; i++) { + employee = new Employee(); + employee.setName("Emp"+i); + employee.setId(i); + employees[i] = employee; + } + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedViaSystemsArrayCopy_thenSuccessful(){ + int[] array = {23, 43, 55}; + int[] copiedArray = new int[3]; + + System.arraycopy(array, 0, copiedArray, 0, 3); + + Assert.assertTrue(array.length == copiedArray.length); + Assert.assertTrue(copiedArray[0] == array[0]); + Assert.assertTrue(copiedArray[1] == array[1]); + Assert.assertTrue(copiedArray[2] == array[2]); + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedSubSequenceViaSystemsArrayCopy_thenSuccessful(){ + int[] array = {23, 43, 55, 12, 65, 88, 92}; + int[] copiedArray = new int[3]; + + System.arraycopy(array, 2, copiedArray, 0, 3); + + Assert.assertTrue(3 == copiedArray.length); + Assert.assertTrue(copiedArray[0] == array[2]); + Assert.assertTrue(copiedArray[1] == array[3]); + Assert.assertTrue(copiedArray[2] == array[4]); + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedSubSequenceViaArraysCopyOfRange_thenSuccessful(){ + int[] array = {23, 43, 55, 12, 65, 88, 92}; + + int[] copiedArray = Arrays.copyOfRange(array, 1, 4); + + Assert.assertTrue(3 == copiedArray.length); + Assert.assertTrue(copiedArray[0] == array[1]); + Assert.assertTrue(copiedArray[1] == array[2]); + Assert.assertTrue(copiedArray[2] == array[3]); + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedViaArraysCopyOf_thenValueChangeIsSuccessful(){ + int[] array = {23, 43, 55, 12}; + int newLength = array.length; + + int[] copiedArray = Arrays.copyOf(array, newLength); + + Assert.assertNotNull(copiedArray); + Assert.assertTrue(copiedArray.length == array.length); + Assert.assertTrue(copiedArray[0] == array[0]); + Assert.assertTrue(copiedArray[1] == array[1]); + Assert.assertTrue(copiedArray[2] == array[2]); + Assert.assertTrue(copiedArray[3] == array[3]); + array[0] = 9; + Assert.assertTrue(copiedArray[0] != array[0]); + copiedArray[1] = 12; + Assert.assertTrue(copiedArray[1] != array[1]); + } + + @Test + public void givenArrayOfNonPrimitiveType_whenCopiedViaArraysCopyOf_thenDoShallowCopy(){ + Employee[] copiedArray = Arrays.copyOf(employees, employees.length); + + Assert.assertNotNull(copiedArray); + Assert.assertTrue(copiedArray.length == employees.length); + employees[0].setName(employees[0].getName()+"_Changed"); + //change in employees' element caused change in the copied array + Assert.assertTrue(copiedArray[0].getName().equals(employees[0].getName())); + } + + @Test + public void givenArrayOfPrimitiveType_whenCopiedViaArrayClone_thenValueChangeIsSuccessful(){ + int[] array = {23, 43, 55, 12}; + + int[] copiedArray = array.clone(); + + Assert.assertNotNull(copiedArray); + Assert.assertTrue(copiedArray.length == array.length); + Assert.assertTrue(copiedArray[0] == array[0]); + Assert.assertTrue(copiedArray[1] == array[1]); + Assert.assertTrue(copiedArray[2] == array[2]); + Assert.assertTrue(copiedArray[3] == array[3]); + array[0] = 9; + Assert.assertTrue(copiedArray[0] != array[0]); + copiedArray[1] = 12; + Assert.assertTrue(copiedArray[1] != array[1]); + } + + @Test + public void givenArraysOfNonPrimitiveType_whenCopiedViaArrayClone_thenDoShallowCopy(){ + Employee[] copiedArray = employees.clone(); + + Assert.assertNotNull(copiedArray); + Assert.assertTrue(copiedArray.length == employees.length); + employees[0].setName(employees[0].getName()+"_Changed"); + //change in employees' element changed the copied array + Assert.assertTrue(copiedArray[0].getName().equals(employees[0].getName())); + } + + @Test + public void givenArraysOfCloneableNonPrimitiveType_whenCopiedViaArrayClone_thenDoShallowCopy(){ + Address[] addresses = createAddressArray(); + + Address[] copiedArray = addresses.clone(); + + Assert.assertNotNull(copiedArray); + Assert.assertTrue(copiedArray.length == addresses.length); + addresses[0].setCity(addresses[0].getCity()+"_Changed"); + Assert.assertTrue(copiedArray[0].getCity().equals(addresses[0].getCity())); + } + + @Test + public void givenArraysOfSerializableNonPrimitiveType_whenCopiedViaSerializationUtils_thenDoDeepCopy(){ + Employee[] copiedArray = SerializationUtils.clone(employees); + + Assert.assertNotNull(copiedArray); + Assert.assertTrue(copiedArray.length == employees.length); + employees[0].setName(employees[0].getName()+"_Changed"); + //change in employees' element didn't change in the copied array + Assert.assertFalse(copiedArray[0].getName().equals(employees[0].getName())); + } + + private Address[] createAddressArray(){ + Address[] addresses = new Address[1]; + addresses[0] = createAddress(); + return addresses; + } + + private Address createAddress() { + Address address = new Address(); + address.setCountry("USA"); + address.setState("CA"); + address.setCity("San Francisco"); + address.setStreet("Street 1"); + address.setZipcode("59999"); + return address; + } +} From 11cdf67fad28587f3be0c1c4ca498ff541de81e2 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Fri, 24 Mar 2017 20:06:32 +0100 Subject: [PATCH 040/102] Merge modules (#1471) * Merge modules * Update pom.xml --- pom.xml | 1 - spring-security-basic-auth/.gitignore | 13 - spring-security-basic-auth/README.md | 13 - spring-security-basic-auth/pom.xml | 267 ------------------ .../persistence/service/FooService.java | 23 -- .../java/org/baeldung/spring/MvcConfig.java | 39 --- .../baeldung/spring/PersistenceConfig.java | 14 - .../baeldung/spring/SecSecurityConfig.java | 15 - .../java/org/baeldung/spring/WebConfig.java | 17 -- .../web/controller/FooController.java | 74 ----- .../org/baeldung/web/controller/LinkUtil.java | 30 -- .../web/controller/ResourceCreated.java | 35 --- ...esourceCreatedDiscoverabilityListener.java | 35 --- .../controller/SingleResourceRetrieved.java | 29 -- ...ourceRetrievedDiscoverabilityListener.java | 32 --- .../web/controller/TestController.java | 28 -- .../main/java/org/baeldung/web/dto/Foo.java | 11 - .../src/main/resources/logback.xml | 20 -- .../src/main/resources/webSecurityConfig.xml | 23 -- .../src/main/webapp/WEB-INF/mvc-servlet.xml | 5 - .../src/main/webapp/WEB-INF/view/homepage.jsp | 7 - .../src/main/webapp/WEB-INF/web.xml | 43 --- .../src/test/resources/.gitignore | 13 - .../MyBasicAuthenticationEntryPoint.java | 2 +- spring-security-rest-basic-auth/README.md | 3 +- .../MyBasicAuthenticationEntryPoint.java | 15 +- 26 files changed, 10 insertions(+), 797 deletions(-) delete mode 100644 spring-security-basic-auth/.gitignore delete mode 100644 spring-security-basic-auth/README.md delete mode 100644 spring-security-basic-auth/pom.xml delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/persistence/service/FooService.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/spring/MvcConfig.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/spring/PersistenceConfig.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/spring/SecSecurityConfig.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/spring/WebConfig.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/web/controller/FooController.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/web/controller/LinkUtil.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreated.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreatedDiscoverabilityListener.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrieved.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrievedDiscoverabilityListener.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/web/controller/TestController.java delete mode 100644 spring-security-basic-auth/src/main/java/org/baeldung/web/dto/Foo.java delete mode 100644 spring-security-basic-auth/src/main/resources/logback.xml delete mode 100644 spring-security-basic-auth/src/main/resources/webSecurityConfig.xml delete mode 100644 spring-security-basic-auth/src/main/webapp/WEB-INF/mvc-servlet.xml delete mode 100644 spring-security-basic-auth/src/main/webapp/WEB-INF/view/homepage.jsp delete mode 100644 spring-security-basic-auth/src/main/webapp/WEB-INF/web.xml delete mode 100644 spring-security-basic-auth/src/test/resources/.gitignore rename {spring-security-basic-auth/src/main/java/org/baeldung/security => spring-security-mvc-digest-auth/src/main/java/org/baeldung}/basic/MyBasicAuthenticationEntryPoint.java (96%) rename {spring-security-mvc-digest-auth/src/main/java/org/baeldung/security => spring-security-rest-basic-auth/src/main/java/org/baeldung}/basic/MyBasicAuthenticationEntryPoint.java (96%) diff --git a/pom.xml b/pom.xml index 7c1cacbd33..929f98a674 100644 --- a/pom.xml +++ b/pom.xml @@ -161,7 +161,6 @@ spring-rest-angular spring-rest-docs spring-rest - spring-security-basic-auth spring-security-cache-control spring-security-client/spring-security-jsp-authentication spring-security-client/spring-security-jsp-authorize diff --git a/spring-security-basic-auth/.gitignore b/spring-security-basic-auth/.gitignore deleted file mode 100644 index 83c05e60c8..0000000000 --- a/spring-security-basic-auth/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -*.class - -#folders# -/target -/neoDb* -/data -/src/main/webapp/WEB-INF/classes -*/META-INF/* - -# Packaged files # -*.jar -*.war -*.ear \ No newline at end of file diff --git a/spring-security-basic-auth/README.md b/spring-security-basic-auth/README.md deleted file mode 100644 index ebb404063f..0000000000 --- a/spring-security-basic-auth/README.md +++ /dev/null @@ -1,13 +0,0 @@ -========= - -## Spring Security with Basic Authentication Example Project - -###The Course -The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - -### Relevant Article: -- [Spring Security Basic Authentication](http://www.baeldung.com/spring-security-basic-authentication) - - -### Notes -- the project includes both views as well as a REST layer diff --git a/spring-security-basic-auth/pom.xml b/spring-security-basic-auth/pom.xml deleted file mode 100644 index 4cb0efb9e2..0000000000 --- a/spring-security-basic-auth/pom.xml +++ /dev/null @@ -1,267 +0,0 @@ - - 4.0.0 - com.baeldung - spring-security-mvc-basic-auth - 0.1-SNAPSHOT - - spring-security-mvc-basic-auth - war - - - - - - - org.springframework.security - spring-security-web - ${org.springframework.security.version} - - - org.springframework.security - spring-security-config - ${org.springframework.security.version} - - - - - - org.springframework - spring-core - ${org.springframework.version} - - - commons-logging - commons-logging - - - - - org.springframework - spring-context - ${org.springframework.version} - - - org.springframework - spring-jdbc - ${org.springframework.version} - - - org.springframework - spring-beans - ${org.springframework.version} - - - org.springframework - spring-aop - ${org.springframework.version} - - - org.springframework - spring-tx - ${org.springframework.version} - - - org.springframework - spring-expression - ${org.springframework.version} - - - - org.springframework - spring-web - ${org.springframework.version} - - - org.springframework - spring-webmvc - ${org.springframework.version} - - - - - - javax.servlet - javax.servlet-api - ${javax.servlet.version} - provided - - - - javax.servlet - jstl - ${jstl.version} - runtime - - - - - - com.google.guava - guava - ${guava.version} - - - - - - org.slf4j - slf4j-api - ${org.slf4j.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - - org.slf4j - jcl-over-slf4j - ${org.slf4j.version} - - - - org.slf4j - log4j-over-slf4j - ${org.slf4j.version} - - - - - - junit - junit - ${junit.version} - test - - - - org.hamcrest - hamcrest-core - ${org.hamcrest.version} - test - - - org.hamcrest - hamcrest-library - ${org.hamcrest.version} - test - - - - org.mockito - mockito-core - ${mockito.version} - test - - - - - - spring-security-mvc-basic-auth - - - src/main/resources - true - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 1.8 - 1.8 - - - - - org.apache.maven.plugins - maven-war-plugin - ${maven-war-plugin.version} - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - - - - - - - - - - org.codehaus.cargo - cargo-maven2-plugin - ${cargo-maven2-plugin.version} - - true - - jetty8x - embedded - - - - - - - 8082 - - - - - - - - - - - - 4.3.4.RELEASE - 4.2.0.RELEASE - - - 5.2.5.Final - 5.1.40 - - - 1.7.21 - 1.1.7 - - - 5.3.3.Final - 1.2 - 3.1.0 - - - 19.0 - 3.5 - - - 1.3 - 4.12 - 1.10.19 - - 4.4.5 - 4.5.2 - - 2.9.0 - - - 3.6.0 - 2.6 - 2.19.1 - 2.7 - 1.6.1 - - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/persistence/service/FooService.java b/spring-security-basic-auth/src/main/java/org/baeldung/persistence/service/FooService.java deleted file mode 100644 index 02db7a733a..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/persistence/service/FooService.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.baeldung.persistence.service; - -import org.baeldung.web.dto.Foo; -import org.springframework.stereotype.Service; - -@Service -public class FooService { - - public FooService() { - super(); - } - - // API - - public Foo getById(final Long id) { - return null; - } - - public Long create(final Foo resource) { - return null; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/spring/MvcConfig.java b/spring-security-basic-auth/src/main/java/org/baeldung/spring/MvcConfig.java deleted file mode 100644 index 74c11478ee..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/spring/MvcConfig.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.baeldung.spring; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; -import org.springframework.web.servlet.view.InternalResourceViewResolver; -import org.springframework.web.servlet.view.JstlView; - -@Configuration -@EnableWebMvc -public class MvcConfig extends WebMvcConfigurerAdapter { - - public MvcConfig() { - super(); - } - - // API - - @Override - public void addViewControllers(final ViewControllerRegistry registry) { - super.addViewControllers(registry); - - registry.addViewController("/homepage.html"); - } - - @Bean - public ViewResolver viewResolver() { - final InternalResourceViewResolver bean = new InternalResourceViewResolver(); - - bean.setViewClass(JstlView.class); - bean.setPrefix("/WEB-INF/view/"); - bean.setSuffix(".jsp"); - - return bean; - } -} \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/spring/PersistenceConfig.java b/spring-security-basic-auth/src/main/java/org/baeldung/spring/PersistenceConfig.java deleted file mode 100644 index 4ea0053f48..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/spring/PersistenceConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.baeldung.spring; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ComponentScan("org.baeldung.persistence") -public class PersistenceConfig { - - public PersistenceConfig() { - super(); - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/spring/SecSecurityConfig.java b/spring-security-basic-auth/src/main/java/org/baeldung/spring/SecSecurityConfig.java deleted file mode 100644 index f40c599834..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/spring/SecSecurityConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.baeldung.spring; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ComponentScan("org.baeldung.security") -// @ImportResource({ "classpath:webSecurityConfig.xml" }) -public class SecSecurityConfig { - - public SecSecurityConfig() { - super(); - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/spring/WebConfig.java b/spring-security-basic-auth/src/main/java/org/baeldung/spring/WebConfig.java deleted file mode 100644 index fa6f5f6d56..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/spring/WebConfig.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.baeldung.spring; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; - -@Configuration -@ComponentScan("org.baeldung.web") -public class WebConfig extends WebMvcConfigurerAdapter { - - public WebConfig() { - super(); - } - - // API - -} \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/FooController.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/FooController.java deleted file mode 100644 index daa797ee36..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/FooController.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.baeldung.web.controller; - -import java.net.URI; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.baeldung.persistence.service.FooService; -import org.baeldung.web.dto.Foo; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.web.util.UriTemplate; - -import com.google.common.base.Preconditions; - -@Controller -@RequestMapping(value = "/foo") -public class FooController { - - @Autowired - private ApplicationEventPublisher eventPublisher; - - @Autowired - private FooService service; - - public FooController() { - super(); - } - - // API - - @RequestMapping(value = "/{id}", method = RequestMethod.GET) - @ResponseBody - public Foo findOne(@PathVariable("id") final Long id, final UriComponentsBuilder uriBuilder, final HttpServletResponse response) { - return new Foo(); - } - - @RequestMapping(value = "admin/foo/{id}", method = RequestMethod.GET) - @ResponseBody - public Foo get(@PathVariable("id") final Long id, final HttpServletRequest request, final HttpServletResponse response) { - final Foo resourceById = Preconditions.checkNotNull(service.getById(id)); - - eventPublisher.publishEvent(new SingleResourceRetrieved(this, request, response)); - return resourceById; - } - - @RequestMapping(value = "admin/foo", method = RequestMethod.POST) - @ResponseStatus(HttpStatus.CREATED) - public void create(@RequestBody final Foo resource, final HttpServletRequest request, final HttpServletResponse response) { - Preconditions.checkNotNull(resource); - final Long idOfCreatedResource = service.create(resource); - - eventPublisher.publishEvent(new ResourceCreated(this, request, response, idOfCreatedResource)); - } - - @RequestMapping(value = "admin", method = RequestMethod.GET) - @ResponseStatus(value = HttpStatus.NO_CONTENT) - public void adminRoot(final HttpServletRequest request, final HttpServletResponse response) { - final String rootUri = request.getRequestURL().toString(); - - final URI fooUri = new UriTemplate("{rootUri}/{resource}").expand(rootUri, "foo"); - final String linkToFoo = LinkUtil.createLinkHeader(fooUri.toASCIIString(), "collection"); - response.addHeader("Link", linkToFoo); - } -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/LinkUtil.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/LinkUtil.java deleted file mode 100644 index a41ebb5a5c..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/LinkUtil.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.baeldung.web.controller; - -import javax.servlet.http.HttpServletResponse; - -/** - * Provides some constants and utility methods to build a Link Header to be stored in the {@link HttpServletResponse} object - */ -public final class LinkUtil { - - private LinkUtil() { - throw new AssertionError(); - } - - // - - /** - * Creates a Link Header to be stored in the {@link HttpServletResponse} to provide Discoverability features to the user - * - * @param uri - * the base uri - * @param rel - * the relative path - * - * @return the complete url - */ - public static String createLinkHeader(final String uri, final String rel) { - return "<" + uri + ">; rel=\"" + rel + "\""; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreated.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreated.java deleted file mode 100644 index a677888101..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreated.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.baeldung.web.controller; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.ApplicationEvent; - -public class ResourceCreated extends ApplicationEvent { - private final HttpServletResponse response; - private final HttpServletRequest request; - private final long idOfNewResource; - - public ResourceCreated(final Object source, final HttpServletRequest request, final HttpServletResponse response, final long idOfNewResource) { - super(source); - - this.request = request; - this.response = response; - this.idOfNewResource = idOfNewResource; - } - - // API - - public HttpServletResponse getResponse() { - return response; - } - - public HttpServletRequest getRequest() { - return request; - } - - public long getIdOfNewResource() { - return idOfNewResource; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreatedDiscoverabilityListener.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreatedDiscoverabilityListener.java deleted file mode 100644 index 8d19ef82fc..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/ResourceCreatedDiscoverabilityListener.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.baeldung.web.controller; - -import java.net.URI; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; -import org.springframework.web.util.UriTemplate; - -import com.google.common.base.Preconditions; -import com.google.common.net.HttpHeaders; - -@Component -class ResourceCreatedDiscoverabilityListener implements ApplicationListener { - - @Override - public void onApplicationEvent(final ResourceCreated resourceCreatedEvent) { - Preconditions.checkNotNull(resourceCreatedEvent); - - final HttpServletRequest request = resourceCreatedEvent.getRequest(); - final HttpServletResponse response = resourceCreatedEvent.getResponse(); - final long idOfNewResource = resourceCreatedEvent.getIdOfNewResource(); - - addLinkHeaderOnResourceCreation(request, response, idOfNewResource); - } - - void addLinkHeaderOnResourceCreation(final HttpServletRequest request, final HttpServletResponse response, final long idOfNewResource) { - final String requestUrl = request.getRequestURL().toString(); - final URI uri = new UriTemplate("{requestUrl}/{idOfNewResource}").expand(requestUrl, idOfNewResource); - response.setHeader(HttpHeaders.LOCATION, uri.toASCIIString()); - } - -} \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrieved.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrieved.java deleted file mode 100644 index 3de7918105..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrieved.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.baeldung.web.controller; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.ApplicationEvent; - -public class SingleResourceRetrieved extends ApplicationEvent { - private final HttpServletResponse response; - private final HttpServletRequest request; - - public SingleResourceRetrieved(final Object source, final HttpServletRequest request, final HttpServletResponse response) { - super(source); - - this.request = request; - this.response = response; - } - - // API - - public HttpServletResponse getResponse() { - return response; - } - - public HttpServletRequest getRequest() { - return request; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrievedDiscoverabilityListener.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrievedDiscoverabilityListener.java deleted file mode 100644 index 45cd7c4d13..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/SingleResourceRetrievedDiscoverabilityListener.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.baeldung.web.controller; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; - -import com.google.common.base.Preconditions; - -@Component -class SingleResourceRetrievedDiscoverabilityListener implements ApplicationListener { - - @Override - public void onApplicationEvent(final SingleResourceRetrieved resourceRetrievedEvent) { - Preconditions.checkNotNull(resourceRetrievedEvent); - - final HttpServletRequest request = resourceRetrievedEvent.getRequest(); - final HttpServletResponse response = resourceRetrievedEvent.getResponse(); - addLinkHeaderOnSingleResourceRetrieval(request, response); - } - - void addLinkHeaderOnSingleResourceRetrieval(final HttpServletRequest request, final HttpServletResponse response) { - final StringBuffer requestURL = request.getRequestURL(); - final int positionOfLastSlash = requestURL.lastIndexOf("/"); - final String uriForResourceCreation = requestURL.substring(0, positionOfLastSlash); - - final String linkHeaderValue = LinkUtil.createLinkHeader(uriForResourceCreation, "collection"); - response.addHeader("Link", linkHeaderValue); - } - -} \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/TestController.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/TestController.java deleted file mode 100644 index f68cfb2eb7..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/controller/TestController.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.baeldung.web.controller; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -@Controller -public class TestController { - - public TestController() { - super(); - } - - // API - - @RequestMapping("/permitAll") - @ResponseBody - public String permitAll() { - return "Permit All"; - } - - @RequestMapping("/securityNone") - @ResponseBody - public String securityNone() { - return "Security None"; - } - -} diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/web/dto/Foo.java b/spring-security-basic-auth/src/main/java/org/baeldung/web/dto/Foo.java deleted file mode 100644 index 352045989d..0000000000 --- a/spring-security-basic-auth/src/main/java/org/baeldung/web/dto/Foo.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.baeldung.web.dto; - -import java.io.Serializable; - -public class Foo implements Serializable { - - public Foo() { - super(); - } - -} diff --git a/spring-security-basic-auth/src/main/resources/logback.xml b/spring-security-basic-auth/src/main/resources/logback.xml deleted file mode 100644 index 1146dade63..0000000000 --- a/spring-security-basic-auth/src/main/resources/logback.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml b/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml deleted file mode 100644 index b0d483768b..0000000000 --- a/spring-security-basic-auth/src/main/resources/webSecurityConfig.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/webapp/WEB-INF/mvc-servlet.xml b/spring-security-basic-auth/src/main/webapp/WEB-INF/mvc-servlet.xml deleted file mode 100644 index eb7ce7b5f8..0000000000 --- a/spring-security-basic-auth/src/main/webapp/WEB-INF/mvc-servlet.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/webapp/WEB-INF/view/homepage.jsp b/spring-security-basic-auth/src/main/webapp/WEB-INF/view/homepage.jsp deleted file mode 100644 index 7cc14b5dcd..0000000000 --- a/spring-security-basic-auth/src/main/webapp/WEB-INF/view/homepage.jsp +++ /dev/null @@ -1,7 +0,0 @@ - - - - -

This is the body of the sample view

- - \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/webapp/WEB-INF/web.xml b/spring-security-basic-auth/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 77a830c6d5..0000000000 --- a/spring-security-basic-auth/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - Spring Security Basic Auth Application - - - contextClass - org.springframework.web.context.support.AnnotationConfigWebApplicationContext - - - contextConfigLocation - org.baeldung.spring - - - - org.springframework.web.context.ContextLoaderListener - - - - mvc - org.springframework.web.servlet.DispatcherServlet - 1 - - - mvc - / - - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - springSecurityFilterChain - /* - - - - index.html - - - \ No newline at end of file diff --git a/spring-security-basic-auth/src/test/resources/.gitignore b/spring-security-basic-auth/src/test/resources/.gitignore deleted file mode 100644 index 83c05e60c8..0000000000 --- a/spring-security-basic-auth/src/test/resources/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -*.class - -#folders# -/target -/neoDb* -/data -/src/main/webapp/WEB-INF/classes -*/META-INF/* - -# Packaged files # -*.jar -*.war -*.ear \ No newline at end of file diff --git a/spring-security-basic-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java b/spring-security-mvc-digest-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java similarity index 96% rename from spring-security-basic-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java rename to spring-security-mvc-digest-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java index 968237227f..c51c0a0bc8 100644 --- a/spring-security-basic-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java +++ b/spring-security-mvc-digest-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java @@ -1,4 +1,4 @@ -package org.baeldung.security.basic; +package org.baeldung.basic; import java.io.IOException; import java.io.PrintWriter; diff --git a/spring-security-rest-basic-auth/README.md b/spring-security-rest-basic-auth/README.md index 328f46ed46..43ab08b8ca 100644 --- a/spring-security-rest-basic-auth/README.md +++ b/spring-security-rest-basic-auth/README.md @@ -9,4 +9,5 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - [RestTemplate with Basic Authentication in Spring](http://www.baeldung.com/2012/04/16/how-to-use-resttemplate-with-basic-authentication-in-spring-3-1) - [HttpClient Timeout](http://www.baeldung.com/httpclient-timeout) - [HttpClient with SSL](http://www.baeldung.com/httpclient-ssl) -- [Writing a Custom Filter in Spring Security](http://www.baeldung.com/spring-security-custom-filter) \ No newline at end of file +- [Writing a Custom Filter in Spring Security](http://www.baeldung.com/spring-security-custom-filter) +- [Spring Security Basic Authentication](http://www.baeldung.com/spring-security-basic-authentication) \ No newline at end of file diff --git a/spring-security-mvc-digest-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java b/spring-security-rest-basic-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java similarity index 96% rename from spring-security-mvc-digest-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java rename to spring-security-rest-basic-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java index 968237227f..6e580e7a22 100644 --- a/spring-security-mvc-digest-auth/src/main/java/org/baeldung/security/basic/MyBasicAuthenticationEntryPoint.java +++ b/spring-security-rest-basic-auth/src/main/java/org/baeldung/basic/MyBasicAuthenticationEntryPoint.java @@ -1,16 +1,15 @@ -package org.baeldung.security.basic; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +package org.baeldung.basic; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; import org.springframework.stereotype.Component; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + @Component public class MyBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { From 092d883dde466c9cbbf210db6569df8b5e5a8bd0 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Fri, 24 Mar 2017 20:12:13 +0100 Subject: [PATCH 041/102] Revert "Spring State Machine" (#1485) * Revert "Merge modules (#1471)" This reverts commit 11cdf67fad28587f3be0c1c4ca498ff541de81e2. * Revert "(BAEL-746) How to Copy an Array in Java (#1474)" This reverts commit 44f5742f16c7020de4487670f33a1e6527137308. * Revert "Bs santosh spring mybatis (#1479)" This reverts commit 3140ea166d05c59b605eb3ac0b5d55a116344c6e. * Revert "Spring State Machine (#1424)" This reverts commit 319dd2653a073f310db05b6121696ebfa7049968. --- pom.xml | 5 +- spring-state-machine/bpmn/forkjoin.bpmn | 116 ------------------ spring-state-machine/bpmn/img/forkjoin.png | Bin 50788 -> 0 bytes spring-state-machine/bpmn/img/simple.png | Bin 22706 -> 0 bytes spring-state-machine/bpmn/simple.bpmn | 76 ------------ spring-state-machine/pom.xml | 31 ----- .../ApplicationReviewEvents.java | 5 - .../ApplicationReviewStates.java | 5 - .../ForkJoinStateMachineConfiguration.java | 74 ----------- ...HierarchicalStateMachineConfiguration.java | 47 ------- .../JunctionStateMachineConfiguration.java | 60 --------- .../SimpleEnumStateMachineConfiguration.java | 53 -------- .../SimpleStateMachineConfiguration.java | 105 ---------------- .../config/StateMachineListener.java | 16 --- .../ForkJoinStateMachineTest.java | 45 ------- .../HierarchicalStateMachineTest.java | 37 ------ .../JunctionStateMachineTest.java | 24 ---- .../statemachine/StateEnumMachineTest.java | 33 ----- .../statemachine/StateMachineBuilderTest.java | 35 ------ .../spring/statemachine/StateMachineTest.java | 47 ------- 20 files changed, 1 insertion(+), 813 deletions(-) delete mode 100644 spring-state-machine/bpmn/forkjoin.bpmn delete mode 100644 spring-state-machine/bpmn/img/forkjoin.png delete mode 100644 spring-state-machine/bpmn/img/simple.png delete mode 100644 spring-state-machine/bpmn/simple.bpmn delete mode 100644 spring-state-machine/pom.xml delete mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java delete mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java delete mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java delete mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java delete mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java delete mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java delete mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java delete mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java delete mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java delete mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java delete mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java delete mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java delete mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java delete mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java diff --git a/pom.xml b/pom.xml index 929f98a674..791430ba0e 100644 --- a/pom.xml +++ b/pom.xml @@ -187,7 +187,6 @@ spring-sleuth spring-social-login spring-spel - spring-state-machine spring-thymeleaf spring-userservice spring-zuul @@ -210,9 +209,7 @@ rabbitmq vertx - - - + diff --git a/spring-state-machine/bpmn/forkjoin.bpmn b/spring-state-machine/bpmn/forkjoin.bpmn deleted file mode 100644 index 0cb060f74b..0000000000 --- a/spring-state-machine/bpmn/forkjoin.bpmn +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-state-machine/bpmn/img/forkjoin.png b/spring-state-machine/bpmn/img/forkjoin.png deleted file mode 100644 index 642ab6949d18b49012529d7c5b9b14f909c9d701..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50788 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�gf@SIUg-QO`umQ3S)QlWiV3@kS`RLu_ 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 D__yV@ diff --git a/spring-state-machine/bpmn/img/simple.png b/spring-state-machine/bpmn/img/simple.png deleted file mode 100644 index 7a79bf1d895a401b2b59d4b093e770f3546bc3d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22706 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 AC;$Ke diff --git a/spring-state-machine/bpmn/simple.bpmn b/spring-state-machine/bpmn/simple.bpmn deleted file mode 100644 index 8ed463e9f9..0000000000 --- a/spring-state-machine/bpmn/simple.bpmn +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-state-machine/pom.xml b/spring-state-machine/pom.xml deleted file mode 100644 index 5393626083..0000000000 --- a/spring-state-machine/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - parent-modules - com.baeldung - 1.0.0-SNAPSHOT - - 4.0.0 - - baeldung-spring-state-machine - - 1.8 - 1.8 - - - - - org.springframework.statemachine - spring-statemachine-core - 1.2.3.RELEASE - - - junit - junit - 4.11 - test - - - \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java deleted file mode 100644 index 971fc5dde7..0000000000 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.spring.stateMachine.applicationReview; - -public enum ApplicationReviewEvents { - APPROVE, REJECT -} diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java deleted file mode 100644 index 1df2db1f86..0000000000 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.spring.stateMachine.applicationReview; - -public enum ApplicationReviewStates { - PEER_REVIEW, PRINCIPAL_REVIEW, APPROVED, REJECTED -} diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java deleted file mode 100644 index c55104a627..0000000000 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.baeldung.spring.stateMachine.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.statemachine.config.EnableStateMachine; -import org.springframework.statemachine.config.StateMachineConfigurerAdapter; -import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; -import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; -import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; -import org.springframework.statemachine.guard.Guard; - -@Configuration -@EnableStateMachine -public class ForkJoinStateMachineConfiguration extends StateMachineConfigurerAdapter { - - @Override - public void configure(StateMachineConfigurationConfigurer config) - throws Exception { - config - .withConfiguration() - .autoStartup(true) - .listener(new StateMachineListener()); - } - - @Override - public void configure(StateMachineStateConfigurer states) throws Exception { - states - .withStates() - .initial("SI") - .fork("SFork") - .join("SJoin") - .end("SF") - .and() - .withStates() - .parent("SFork") - .initial("Sub1-1") - .end("Sub1-2") - .and() - .withStates() - .parent("SFork") - .initial("Sub2-1") - .end("Sub2-2"); - } - - @Override - public void configure(StateMachineTransitionConfigurer transitions) throws Exception { - transitions.withExternal() - .source("SI").target("SFork").event("E1") - .and().withExternal() - .source("Sub1-1").target("Sub1-2").event("sub1") - .and().withExternal() - .source("Sub2-1").target("Sub2-2").event("sub2") - .and() - .withFork() - .source("SFork") - .target("Sub1-1") - .target("Sub2-1") - .and() - .withJoin() - .source("Sub1-2") - .source("Sub2-2") - .target("SJoin"); - } - - @Bean - public Guard mediumGuard() { - return (ctx) -> false; - } - - @Bean - public Guard highGuard() { - return (ctx) -> false; - } -} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java deleted file mode 100644 index 708dbd3077..0000000000 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.baeldung.spring.stateMachine.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.statemachine.config.EnableStateMachine; -import org.springframework.statemachine.config.StateMachineConfigurerAdapter; -import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; -import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; -import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; - -@Configuration -@EnableStateMachine -public class HierarchicalStateMachineConfiguration extends StateMachineConfigurerAdapter { - - @Override - public void configure(StateMachineConfigurationConfigurer config) - throws Exception { - config - .withConfiguration() - .autoStartup(true) - .listener(new StateMachineListener()); - } - - @Override - public void configure(StateMachineStateConfigurer states) throws Exception { - states - .withStates() - .initial("SI") - .state("SI") - .end("SF") - .and() - .withStates() - .parent("SI") - .initial("SUB1") - .state("SUB2") - .end("SUBEND"); - } - - @Override - public void configure(StateMachineTransitionConfigurer transitions) throws Exception { - transitions.withExternal() - .source("SI").target("SF").event("end") - .and().withExternal() - .source("SUB1").target("SUB2").event("se1") - .and().withExternal() - .source("SUB2").target("SUBEND").event("s-end"); - } -} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java deleted file mode 100644 index e1bae10fb7..0000000000 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.baeldung.spring.stateMachine.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.statemachine.config.EnableStateMachine; -import org.springframework.statemachine.config.StateMachineConfigurerAdapter; -import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; -import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; -import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; -import org.springframework.statemachine.guard.Guard; - -@Configuration -@EnableStateMachine -public class JunctionStateMachineConfiguration extends StateMachineConfigurerAdapter { - - @Override - public void configure(StateMachineConfigurationConfigurer config) - throws Exception { - config - .withConfiguration() - .autoStartup(true) - .listener(new StateMachineListener()); - } - - @Override - public void configure(StateMachineStateConfigurer states) throws Exception { - states - .withStates() - .initial("SI") - .junction("SJ") - .state("high") - .state("medium") - .state("low") - .end("SF"); - } - - @Override - public void configure(StateMachineTransitionConfigurer transitions) throws Exception { - transitions.withExternal() - .source("SI").target("SJ").event("E1") - .and() - .withJunction() - .source("SJ") - .first("high", highGuard()) - .then("medium", mediumGuard()) - .last("low") - .and().withExternal() - .source("low").target("SF").event("end"); - } - - @Bean - public Guard mediumGuard() { - return (ctx) -> false; - } - - @Bean - public Guard highGuard() { - return (ctx) -> false; - } -} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java deleted file mode 100644 index 4e11851644..0000000000 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.baeldung.spring.stateMachine.config; - -import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewEvents; -import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewStates; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.statemachine.action.Action; -import org.springframework.statemachine.config.EnableStateMachine; -import org.springframework.statemachine.config.StateMachineConfigurerAdapter; -import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; -import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; -import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; -import org.springframework.statemachine.guard.Guard; - -import java.util.Arrays; -import java.util.HashSet; - -@Configuration -@EnableStateMachine -public class SimpleEnumStateMachineConfiguration extends StateMachineConfigurerAdapter { - - @Override - public void configure(StateMachineConfigurationConfigurer config) - throws Exception { - config - .withConfiguration() - .autoStartup(true) - .listener(new StateMachineListener()); - } - - @Override - public void configure(StateMachineStateConfigurer states) throws Exception { - states - .withStates() - .initial(ApplicationReviewStates.PEER_REVIEW) - .state(ApplicationReviewStates.PRINCIPAL_REVIEW) - .end(ApplicationReviewStates.APPROVED) - .end(ApplicationReviewStates.REJECTED); - - } - - @Override - public void configure(StateMachineTransitionConfigurer transitions) throws Exception { - transitions.withExternal() - .source(ApplicationReviewStates.PEER_REVIEW).target(ApplicationReviewStates.PRINCIPAL_REVIEW).event(ApplicationReviewEvents.APPROVE) - .and().withExternal() - .source(ApplicationReviewStates.PRINCIPAL_REVIEW).target(ApplicationReviewStates.APPROVED).event(ApplicationReviewEvents.APPROVE) - .and().withExternal() - .source(ApplicationReviewStates.PEER_REVIEW).target(ApplicationReviewStates.REJECTED).event(ApplicationReviewEvents.REJECT) - .and().withExternal() - .source(ApplicationReviewStates.PRINCIPAL_REVIEW).target(ApplicationReviewStates.REJECTED).event(ApplicationReviewEvents.REJECT); - } -} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java deleted file mode 100644 index fe4e0f82ce..0000000000 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.baeldung.spring.stateMachine.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.statemachine.action.Action; -import org.springframework.statemachine.config.EnableStateMachine; -import org.springframework.statemachine.config.StateMachineConfigurerAdapter; -import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; -import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; -import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; -import org.springframework.statemachine.guard.Guard; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.logging.Logger; - -@Configuration -@EnableStateMachine -public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapter { - - public static final Logger LOGGER = Logger.getLogger(SimpleStateMachineConfiguration.class.getName()); - - @Override - public void configure(StateMachineConfigurationConfigurer config) - throws Exception { - config - .withConfiguration() - .autoStartup(true) - .listener(new StateMachineListener()); - } - - @Override - public void configure(StateMachineStateConfigurer states) throws Exception { - states - .withStates() - .initial("SI") - .end("SF") - .states(new HashSet<>(Arrays.asList("S1", "S2"))) - .state("S4", executeAction(), errorAction()) - .stateEntry("S3", entryAction()) - .stateDo("S3", executeAction()) - .stateExit("S3", exitAction()); - - } - - @Override - public void configure(StateMachineTransitionConfigurer transitions) throws Exception { - transitions.withExternal() - .source("SI").target("S1").event("E1").action(initAction()) - .and().withExternal() - .source("S1").target("S2").event("E2") - .and().withExternal() - .source("SI").target("S3").event("E3") - .and().withExternal() - .source("S3").target("S4").event("E4").guard(simpleGuard()) - .and().withExternal() - .source("S2").target("SF").event("end"); - } - - @Bean - public Guard simpleGuard() { - return (ctx) -> { - int approvalCount = (int) ctx.getExtendedState().getVariables().getOrDefault("approvalCount", 0); - return approvalCount > 0; - }; - } - - @Bean - public Action entryAction() { - return (ctx) -> { - LOGGER.info("Entry " + ctx.getTarget().getId()); - }; - } - - @Bean - public Action executeAction() { - return (ctx) -> { - LOGGER.info("Do " + ctx.getTarget().getId()); - int approvals = (int) ctx.getExtendedState().getVariables().getOrDefault("approvalCount", 0); - approvals++; - ctx.getExtendedState().getVariables().put("approvalCount", approvals); - }; - } - - @Bean - public Action exitAction() { - return (ctx) -> { - LOGGER.info("Exit " + ctx.getSource().getId() + " -> " + ctx.getTarget().getId()); - }; - } - - @Bean - public Action errorAction() { - return (ctx) -> { - LOGGER.info("Error " + ctx.getSource().getId() + ctx.getException()); - }; - } - - @Bean - public Action initAction() { - return (ctx) -> { - LOGGER.info(ctx.getTarget().getId()); - }; - } -} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java deleted file mode 100644 index bb7859c683..0000000000 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.baeldung.spring.stateMachine.config; - -import org.springframework.statemachine.listener.StateMachineListenerAdapter; -import org.springframework.statemachine.state.State; - -import java.util.logging.Logger; - -public class StateMachineListener extends StateMachineListenerAdapter { - - public static final Logger LOGGER = Logger.getLogger(StateMachineListener.class.getName()); - - @Override - public void stateChanged(State from, State to) { - LOGGER.info(String.format("Transitioned from %s to %s%n", from == null ? "none" : from.getId(), to.getId())); - } -} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java deleted file mode 100644 index 416da5f0fe..0000000000 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.baeldung.spring.stateMachine; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; - -import org.junit.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.statemachine.StateMachine; - -import com.baeldung.spring.stateMachine.config.ForkJoinStateMachineConfiguration; - -public class ForkJoinStateMachineTest { - - @Test - public void whenForkStateEntered_thenMultipleSubStatesEntered() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ForkJoinStateMachineConfiguration.class); - StateMachine stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); - - boolean success = stateMachine.sendEvent("E1"); - - assertTrue(success); - - assertTrue(Arrays.asList("SFork", "Sub1-1", "Sub2-1").containsAll(stateMachine.getState().getIds())); - } - - @Test - public void whenAllConfiguredJoinEntryStatesAreEntered_thenTransitionToJoinState() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ForkJoinStateMachineConfiguration.class); - StateMachine stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); - - boolean success = stateMachine.sendEvent("E1"); - - assertTrue(success); - - assertTrue(Arrays.asList("SFork", "Sub1-1", "Sub2-1").containsAll(stateMachine.getState().getIds())); - - assertTrue(stateMachine.sendEvent("sub1")); - assertTrue(stateMachine.sendEvent("sub2")); - assertEquals("SJoin", stateMachine.getState().getId()); - } -} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java deleted file mode 100644 index 3557a63211..0000000000 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.baeldung.spring.stateMachine; - -import static org.junit.Assert.assertEquals; - -import java.util.Arrays; - -import org.junit.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.statemachine.StateMachine; - -import com.baeldung.spring.stateMachine.config.HierarchicalStateMachineConfiguration; - -public class HierarchicalStateMachineTest { - - @Test - public void whenTransitionToSubMachine_thenSubStateIsEntered() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(HierarchicalStateMachineConfiguration.class); - StateMachine stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); - - - assertEquals(Arrays.asList("SI", "SUB1"), stateMachine.getState().getIds()); - - stateMachine.sendEvent("se1"); - - assertEquals(Arrays.asList("SI", "SUB2"), stateMachine.getState().getIds()); - - stateMachine.sendEvent("s-end"); - - assertEquals(Arrays.asList("SI", "SUBEND"), stateMachine.getState().getIds()); - - stateMachine.sendEvent("end"); - - assertEquals(1, stateMachine.getState().getIds().size()); - assertEquals("SF", stateMachine.getState().getId()); - } -} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java deleted file mode 100644 index d0c1225c9b..0000000000 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.baeldung.spring.stateMachine; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.statemachine.StateMachine; - -import com.baeldung.spring.stateMachine.config.JunctionStateMachineConfiguration; - -public class JunctionStateMachineTest { - - @Test - public void whenTransitioningToJunction_thenArriveAtSubJunctionNode() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JunctionStateMachineConfiguration.class); - StateMachine stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); - - stateMachine.sendEvent("E1"); - Assert.assertEquals("low", stateMachine.getState().getId()); - - stateMachine.sendEvent("end"); - Assert.assertEquals("SF", stateMachine.getState().getId()); - } -} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java deleted file mode 100644 index 1fd7bd85f0..0000000000 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.baeldung.spring.stateMachine; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.statemachine.StateMachine; - -import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewEvents; -import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewStates; -import com.baeldung.spring.stateMachine.config.SimpleEnumStateMachineConfiguration; - -public class StateEnumMachineTest { - - private StateMachine stateMachine; - - @Before - public void setUp() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SimpleEnumStateMachineConfiguration.class); - stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); - } - - @Test - public void whenStateMachineConfiguredWithEnums_thenStateMachineAcceptsEnumEvents() { - assertTrue(stateMachine.sendEvent(ApplicationReviewEvents.APPROVE)); - assertEquals(ApplicationReviewStates.PRINCIPAL_REVIEW, stateMachine.getState().getId()); - assertTrue(stateMachine.sendEvent(ApplicationReviewEvents.REJECT)); - assertEquals(ApplicationReviewStates.REJECTED, stateMachine.getState().getId()); - } -} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java deleted file mode 100644 index cdd1e951e0..0000000000 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.baeldung.spring.stateMachine; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import org.springframework.statemachine.StateMachine; -import org.springframework.statemachine.config.StateMachineBuilder; - -public class StateMachineBuilderTest { - - @Test - public void whenUseStateMachineBuilder_thenBuildSuccessAndMachineWorks() throws Exception { - StateMachineBuilder.Builder builder = StateMachineBuilder.builder(); - builder.configureStates().withStates() - .initial("SI") - .state("S1") - .end("SF"); - - builder.configureTransitions() - .withExternal() - .source("SI").target("S1").event("E1") - .and().withExternal() - .source("S1").target("SF").event("E2"); - - StateMachine machine = builder.build(); - - machine.start(); - - machine.sendEvent("E1"); - assertEquals("S1", machine.getState().getId()); - - machine.sendEvent("E2"); - assertEquals("SF", machine.getState().getId()); - } -} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java deleted file mode 100644 index 1b442bf994..0000000000 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.baeldung.spring.stateMachine; - -import static org.junit.Assert.assertEquals; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.statemachine.StateMachine; - -import com.baeldung.spring.stateMachine.config.SimpleStateMachineConfiguration; - -public class StateMachineTest { - - private StateMachine stateMachine; - - @Before - public void setUp() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SimpleStateMachineConfiguration.class); - stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); - } - - @Test - public void whenSimpleStringStateMachineEvents_thenEndState() { - assertEquals("SI", stateMachine.getState().getId()); - - stateMachine.sendEvent("E1"); - assertEquals("S1", stateMachine.getState().getId()); - - stateMachine.sendEvent("E2"); - assertEquals("S2", stateMachine.getState().getId()); - - stateMachine.sendEvent("end"); - assertEquals("SF", stateMachine.getState().getId()); - - } - - @Test - public void whenSimpleStringMachineActionState_thenActionExecuted() { - stateMachine.sendEvent("E3"); - assertEquals("S3", stateMachine.getState().getId()); - - stateMachine.sendEvent("E4"); - assertEquals("S4", stateMachine.getState().getId()); - assertEquals(2, stateMachine.getExtendedState().getVariables().get("approvalCount")); - } -} From 2aca6e085b1fddf6ba6a6fa621f1475aeaddd1b7 Mon Sep 17 00:00:00 2001 From: Yasin Date: Sat, 25 Mar 2017 01:57:45 +0530 Subject: [PATCH 042/102] BAEL-745 Quick Math.pow example (#1482) * yasin.bhojawala@gmail.com Evaluation article on Different Types of Bean Injection in Spring * Revert "yasin.bhojawala@gmail.com" This reverts commit 963cc51a7a15b75b550108fe4e198cd65a274032. * Fixing compilation error and removing unused import * Introduction to Java9 StackWalking API - yasin.bhojawala@gmail.com Code examples for the article "Introduction to Java9 StackWalking API" * BAEL-608 Introduction to Java9 StackWalking API * BAEL-608 Introduction to Java9 StackWalking API changing the test names to BDD style * BAEL-608 Introduction to Java9 StackWalking API correcting the typo * BAEL-608 Introduction to Java9 StackWalking API improving method names * BAEL-608 Introduction to Java9 StackWalking API test method names improvements * BAEL-718 Quick intro to javatuples * merging pom from master * BAEL-722 Intro to JSONassert * BAEL-722 Intro to JSONassert Updated to 1.5.0 * BAEL-745 Quick Math.pow example --- .../java/com/baeldung/pow/PowerExample.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/pow/PowerExample.java diff --git a/core-java/src/main/java/com/baeldung/pow/PowerExample.java b/core-java/src/main/java/com/baeldung/pow/PowerExample.java new file mode 100644 index 0000000000..5be5376e6a --- /dev/null +++ b/core-java/src/main/java/com/baeldung/pow/PowerExample.java @@ -0,0 +1,19 @@ +package com.baeldung.pow; + +import java.text.DecimalFormat; + +public class PowerExample { + + public static void main(String[] args) { + + int intResult = (int) Math.pow(2, 3); + System.out.println("Math.pow(2, 3) = " + intResult); + + double dblResult = Math.pow(4.2, 3); + System.out.println("Math.pow(4.2, 3) = " + Math.pow(4.2, 3)); + + DecimalFormat df = new DecimalFormat(".00"); + System.out.println("Math.pow(4.2, 3) rounded = " + df.format(dblResult)); + + } +} From c3764e3f443e8a47ddfe7932f8e5b5f1c91e7afe Mon Sep 17 00:00:00 2001 From: Abhinab Kanrar Date: Sat, 25 Mar 2017 03:32:42 +0530 Subject: [PATCH 043/102] CORS in JAX-RS (#1484) * rest with spark java * 4 * Update Application.java * indentation changes * spring @requestmapping shortcuts * removing spring requestmapping and pushing spring-mvc-java * Joining/Splitting Strings with Java and Stream API * adding more join/split functionality * changing package name * testcase change * adding webutils * adding testcase for WebUtils and ServletRequestUtils * adding testcase * spring-security-stormpath * adding ratpack module * adding pom.xml * adding following modules with updated testcase : DB, Filter, Json * adding spring-boot custom banner tutorial * changing banner format in plain text * Delete banner.txt~ * Delete b.txt~ * CORS in JAX-RS --- .../java/com/baeldung/filter/CorsFilter.java | 22 ++++ .../com/baeldung/server/MovieCrudService.java | 110 ++++++++++-------- resteasy/src/main/webapp/script.js | 16 +++ 3 files changed, 99 insertions(+), 49 deletions(-) create mode 100644 resteasy/src/main/java/com/baeldung/filter/CorsFilter.java create mode 100644 resteasy/src/main/webapp/script.js diff --git a/resteasy/src/main/java/com/baeldung/filter/CorsFilter.java b/resteasy/src/main/java/com/baeldung/filter/CorsFilter.java new file mode 100644 index 0000000000..266707fb9f --- /dev/null +++ b/resteasy/src/main/java/com/baeldung/filter/CorsFilter.java @@ -0,0 +1,22 @@ +package com.baeldung.filter; + +import java.io.IOException; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.ext.Provider; + +@Provider +public class CorsFilter implements ContainerResponseFilter { + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + responseContext.getHeaders().add("Access-Control-Allow-Origin", "*"); + responseContext.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); + responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true"); + responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); + } + +} diff --git a/resteasy/src/main/java/com/baeldung/server/MovieCrudService.java b/resteasy/src/main/java/com/baeldung/server/MovieCrudService.java index b7f3215f3f..5a623a2dc6 100644 --- a/resteasy/src/main/java/com/baeldung/server/MovieCrudService.java +++ b/resteasy/src/main/java/com/baeldung/server/MovieCrudService.java @@ -13,71 +13,83 @@ import java.util.stream.Collectors; @Path("/movies") public class MovieCrudService { - private Map inventory = new HashMap(); + private Map inventory = new HashMap(); - @GET - @Path("/getinfo") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Movie movieByImdbId(@QueryParam("imdbId") String imdbId) { + @GET + @Path("/") + @Produces({ MediaType.TEXT_PLAIN }) + public Response index() { + return Response.status(200).header("Access-Control-Allow-Origin", "*") + .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization") + .header("Access-Control-Allow-Credentials", "true") + .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD").entity("").build(); + } - System.out.println("*** Calling getinfo for a given ImdbID***"); + @GET + @Path("/getinfo") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Movie movieByImdbId(@QueryParam("imdbId") String imdbId) { - if (inventory.containsKey(imdbId)) { - return inventory.get(imdbId); - } else - return null; - } + System.out.println("*** Calling getinfo for a given ImdbID***"); - @POST - @Path("/addmovie") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response addMovie(Movie movie) { + if (inventory.containsKey(imdbId)) { + return inventory.get(imdbId); + } else + return null; + } - System.out.println("*** Calling addMovie ***"); + @POST + @Path("/addmovie") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response addMovie(Movie movie) { - if (null != inventory.get(movie.getImdbId())) { - return Response.status(Response.Status.NOT_MODIFIED).entity("Movie is Already in the database.").build(); - } + System.out.println("*** Calling addMovie ***"); - inventory.put(movie.getImdbId(), movie); - return Response.status(Response.Status.CREATED).build(); - } + if (null != inventory.get(movie.getImdbId())) { + return Response.status(Response.Status.NOT_MODIFIED).entity("Movie is Already in the database.").build(); + } - @PUT - @Path("/updatemovie") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response updateMovie(Movie movie) { + inventory.put(movie.getImdbId(), movie); + return Response.status(Response.Status.CREATED).build(); + } - System.out.println("*** Calling updateMovie ***"); + @PUT + @Path("/updatemovie") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response updateMovie(Movie movie) { - if (null == inventory.get(movie.getImdbId())) { - return Response.status(Response.Status.NOT_MODIFIED).entity("Movie is not in the database.\nUnable to Update").build(); - } + System.out.println("*** Calling updateMovie ***"); - inventory.put(movie.getImdbId(), movie); - return Response.status(Response.Status.OK).build(); - } + if (null == inventory.get(movie.getImdbId())) { + return Response.status(Response.Status.NOT_MODIFIED) + .entity("Movie is not in the database.\nUnable to Update").build(); + } - @DELETE - @Path("/deletemovie") - public Response deleteMovie(@QueryParam("imdbId") String imdbId) { + inventory.put(movie.getImdbId(), movie); + return Response.status(Response.Status.OK).build(); + } - System.out.println("*** Calling deleteMovie ***"); + @DELETE + @Path("/deletemovie") + public Response deleteMovie(@QueryParam("imdbId") String imdbId) { - if (null == inventory.get(imdbId)) { - return Response.status(Response.Status.NOT_FOUND).entity("Movie is not in the database.\nUnable to Delete").build(); - } + System.out.println("*** Calling deleteMovie ***"); - inventory.remove(imdbId); - return Response.status(Response.Status.OK).build(); - } + if (null == inventory.get(imdbId)) { + return Response.status(Response.Status.NOT_FOUND).entity("Movie is not in the database.\nUnable to Delete") + .build(); + } - @GET - @Path("/listmovies") - @Produces({ "application/json" }) - public List listMovies() { + inventory.remove(imdbId); + return Response.status(Response.Status.OK).build(); + } - return inventory.values().stream().collect(Collectors.toCollection(ArrayList::new)); - } + @GET + @Path("/listmovies") + @Produces({ "application/json" }) + public List listMovies() { + + return inventory.values().stream().collect(Collectors.toCollection(ArrayList::new)); + } } diff --git a/resteasy/src/main/webapp/script.js b/resteasy/src/main/webapp/script.js new file mode 100644 index 0000000000..88198887b0 --- /dev/null +++ b/resteasy/src/main/webapp/script.js @@ -0,0 +1,16 @@ +function call(url, type, data) { + var request = $.ajax({ + url : url, + method : "GET", + data : (data) ? JSON.stringify(data) : "", + dataType : type + }); + + request.done(function(resp) { + console.log(resp); + }); + + request.fail(function(jqXHR, textStatus) { + console.log("Request failed: " + textStatus); + }); +}; \ No newline at end of file From 9472f0dd65885d49f365256e88c594009a460f0a Mon Sep 17 00:00:00 2001 From: mujahid Date: Sat, 25 Mar 2017 15:18:20 +0800 Subject: [PATCH 044/102] BAEL-347 - change solr-fulltext-search to solr (#1462) * solr-fulltext-search module created * solr-fulltext-search modue created * solr-fulltext-search change s * pom changes merged from upstream * removed integration tests from mvn build * Refactoring test class * removed test profile * module solr-fulltext-search changed to solr * module solr added in parent pom * delete file * removed unused file, changed test to default solr port --- pom.xml | 2 +- .../solr/fulltexh/search/model/Product.java | 5 - .../search/service/SolrSearchService.java | 5 - .../ItemSearchServiceIntegrationTest.java | 374 ------------------ {solr-fulltext-search => solr}/pom.xml | 4 +- .../solr/fulltext/search/model/Item.java | 0 .../search/service/ItemSearchService.java | 0 .../search/service/ItemSearchServiceImpl.java | 0 .../service/ItemSearchServiceLiveTest.java | 2 +- 9 files changed, 4 insertions(+), 388 deletions(-) delete mode 100644 solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/model/Product.java delete mode 100644 solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/service/SolrSearchService.java delete mode 100644 solr-fulltext-search/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceIntegrationTest.java rename {solr-fulltext-search => solr}/pom.xml (95%) rename {solr-fulltext-search => solr}/src/main/java/com/baeldung/solr/fulltext/search/model/Item.java (100%) rename {solr-fulltext-search => solr}/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchService.java (100%) rename {solr-fulltext-search => solr}/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceImpl.java (100%) rename {solr-fulltext-search => solr}/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java (99%) diff --git a/pom.xml b/pom.xml index 791430ba0e..4796a11ac4 100644 --- a/pom.xml +++ b/pom.xml @@ -109,7 +109,7 @@ rxjava selenium-junit-testng - solr-fulltext-search + solr spark-java spring-akka diff --git a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/model/Product.java b/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/model/Product.java deleted file mode 100644 index b5fb334282..0000000000 --- a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/model/Product.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.solr.fulltexh.search.model; - -public class Product { - -} diff --git a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/service/SolrSearchService.java b/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/service/SolrSearchService.java deleted file mode 100644 index aa511f0e1b..0000000000 --- a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltexh/search/service/SolrSearchService.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.solr.fulltexh.search.service; - -public interface SolrSearchService { - -} diff --git a/solr-fulltext-search/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceIntegrationTest.java b/solr-fulltext-search/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceIntegrationTest.java deleted file mode 100644 index 94661ffc2e..0000000000 --- a/solr-fulltext-search/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceIntegrationTest.java +++ /dev/null @@ -1,374 +0,0 @@ -package com.baeldung.solr.fulltext.search.service; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import java.util.List; -import java.util.Map; - -import org.apache.solr.client.solrj.SolrClient; -import org.apache.solr.client.solrj.SolrQuery; -import org.apache.solr.client.solrj.impl.HttpSolrClient; -import org.apache.solr.client.solrj.response.FacetField.Count; -import org.apache.solr.client.solrj.response.QueryResponse; -import org.apache.solr.client.solrj.response.RangeFacet; -import org.apache.solr.client.solrj.response.SpellCheckResponse; -import org.apache.solr.client.solrj.response.SpellCheckResponse.Suggestion; -import org.apache.solr.client.solrj.response.SuggesterResponse; -import org.apache.solr.common.SolrDocument; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.baeldung.solr.fulltext.search.model.Item; - -public class ItemSearchServiceIntegrationTest { - - private static SolrClient solrClient; - private static ItemSearchService itemSearchService; - private static final String solrUrl = "http://localhost:8987/solr/item"; - - @BeforeClass - public static void initBeans() throws Exception { - solrClient = new HttpSolrClient.Builder(solrUrl).build(); - itemSearchService = new ItemSearchServiceImpl(solrClient); - - solrClient.commit(); - } - - @Before - public void clearSolrData() throws Exception { - solrClient.deleteByQuery("*:*"); - } - - @Test - public void whenIndexing_thenAvailableOnRetrieval() throws Exception { - itemSearchService.index("hm0001", "Washing Machine", "Home Appliances", 450f); - final SolrDocument indexedDoc = solrClient.getById("hm0001"); - assertEquals("hm0001", indexedDoc.get("id")); - } - - @Test - public void whenIndexingBean_thenAvailableOnRetrieval() throws Exception { - Item item = new Item(); - item.setId("hm0002"); - item.setCategory("Televisions"); - item.setDescription("LED TV 32"); - item.setPrice(500); - itemSearchService.indexBean(item); - } - - @Test - public void whenSearchingByBasicQuery_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); - itemSearchService.index("hm0003", "LED TV 32", "Brand1 Home Appliances", 450f); - - SolrQuery query = new SolrQuery(); - query.setQuery("brand1"); - query.setStart(0); - query.setRows(10); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(3, items.size()); - - } - - @Test - public void whenSearchingWithWildCard_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); - itemSearchService.index("hm0003", "LED TV 32", "Brand1 Home Appliances", 450f); - - SolrQuery query = new SolrQuery(); - query.setQuery("*rand?"); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(3, items.size()); - } - - @Test - public void whenSearchingWithLogicalOperators_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); - itemSearchService.index("hm0003", "Brand2 LED TV 32", "Washing Appliances", 450f); - - SolrQuery query = new SolrQuery(); - query.setQuery("brand1 AND (Washing OR Refrigerator)"); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(2, items.size()); - } - - @Test - public void whenSearchingWithFields_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("0001", "Brand1 Washing Machine", "Home Appliances", 450f); - itemSearchService.index("0002", "Brand1 Refrigerator", "Home Appliances", 450f); - itemSearchService.index("0003", "Brand2 LED TV 32", "Brand1 Washing Home Appliances", 450f); - - SolrQuery query = new SolrQuery(); - query.setQuery("description:Brand* AND category:*Washing*"); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(1, items.size()); - } - - @Test - public void whenSearchingWithPhrase_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); - itemSearchService.index("hm0003", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); - - SolrQuery query = new SolrQuery(); - query.setQuery("washing MachIne"); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(2, items.size()); - } - - @Test - public void whenSearchingWithRealPhrase_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); - itemSearchService.index("hm0003", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); - - SolrQuery query = new SolrQuery(); - query.setQuery("\"washing machine\""); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(1, items.size()); - } - - @Test - public void whenSearchingPhraseWithProximity_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 450f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 450f); - itemSearchService.index("hm0003", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); - - SolrQuery query = new SolrQuery(); - query.setQuery("\"Washing equipment\"~2"); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(1, items.size()); - } - - @Test - public void whenSearchingWithPriceRange_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); - itemSearchService.index("hm0003", "Brand2 Dishwasher", "Home Appliances", 200f); - itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); - - SolrQuery query = new SolrQuery(); - query.setQuery("price:[100 TO 300]"); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(3, items.size()); - } - - @Test - public void whenSearchingWithPriceRangeInclusiveExclusive_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); - itemSearchService.index("hm0003", "Brand2 Dishwasher", "Home Appliances", 200f); - itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing tools and equipment ", 450f); - - SolrQuery query = new SolrQuery(); - query.setQuery("price:{100 TO 300]"); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(2, items.size()); - } - - @Test - public void whenSearchingWithFilterQuery_thenAllMatchingItemsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); - itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "Home Appliances", 200f); - itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing tools and equipment ", 250f); - - SolrQuery query = new SolrQuery(); - query.setQuery("price:[100 TO 300]"); - query.addFilterQuery("description:Brand1", "category:Home Appliances"); - - QueryResponse response = solrClient.query(query); - List items = response.getBeans(Item.class); - - assertEquals(2, items.size()); - } - - @Test - public void whenSearchingWithFacetFields_thenAllMatchingFacetsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "CategoryA", 100f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "CategoryA", 300f); - itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "CategoryB", 200f); - itemSearchService.index("hm0004", "Brand2 Dishwasher", "CategoryB", 250f); - - SolrQuery query = new SolrQuery(); - query.setQuery("*:*"); - query.addFacetField("category"); - - QueryResponse response = solrClient.query(query); - List facetResults = response.getFacetField("category").getValues(); - - assertEquals(2, facetResults.size()); - - for (Count count : facetResults) { - if ("categorya".equalsIgnoreCase(count.getName())) { - assertEquals(2, count.getCount()); - } else if ("categoryb".equalsIgnoreCase(count.getName())) { - assertEquals(2, count.getCount()); - } else { - fail("unexpected category"); - } - } - } - - @Test - public void whenSearchingWithFacetQuery_thenAllMatchingFacetsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "CategoryA", 100f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "CategoryA", 300f); - itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "CategoryB", 200f); - itemSearchService.index("hm0004", "Brand2 Dishwasher", "CategoryB", 250f); - - SolrQuery query = new SolrQuery(); - query.setQuery("*:*"); - - query.addFacetQuery("Washing OR Refrigerator"); - query.addFacetQuery("Brand2"); - - QueryResponse response = solrClient.query(query); - Map facetQueryMap = response.getFacetQuery(); - - assertEquals(2, facetQueryMap.size()); - - for (Map.Entry entry : facetQueryMap.entrySet()) { - String facetQuery = entry.getKey(); - - if ("Washing OR Refrigerator".equals(facetQuery)) { - assertEquals(Integer.valueOf(2), entry.getValue()); - } else if ("Brand2".equals(facetQuery)) { - assertEquals(Integer.valueOf(2), entry.getValue()); - } else { - fail("unexpected query"); - } - - } - } - - @Test - public void whenSearchingWithFacetRange_thenAllMatchingFacetsShouldAvialble() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "CategoryA", 100f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "CategoryA", 125f); - itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "CategoryB", 150f); - itemSearchService.index("hm0004", "Brand2 Dishwasher", "CategoryB", 250f); - - SolrQuery query = new SolrQuery(); - query.setQuery("*:*"); - - query.addNumericRangeFacet("price", 100, 275, 25); - - QueryResponse response = solrClient.query(query); - List rangeFacets = response.getFacetRanges().get(0).getCounts(); - - assertEquals(7, rangeFacets.size()); - } - - @Test - public void whenSearchingWithHitHighlighting_thenKeywordsShouldBeHighlighted() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); - itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "Home Appliances", 200f); - itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing equipments", 250f); - - SolrQuery query = new SolrQuery(); - query.setQuery("Appliances"); - query.setHighlight(true); - query.addHighlightField("category"); - query.setHighlightSimplePre(""); - query.setHighlightSimplePost(""); - QueryResponse response = solrClient.query(query); - - Map>> hitHighlightedMap = response.getHighlighting(); - Map> highlightedFieldMap = hitHighlightedMap.get("hm0001"); - List highlightedList = highlightedFieldMap.get("category"); - String highLightedText = highlightedList.get(0); - - assertEquals("Home Appliances", highLightedText); - - } - - @Test - public void whenSearchingWithKeywordWithMistake_thenSpellingSuggestionsShouldBeReturned() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); - itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "Home Appliances", 200f); - itemSearchService.index("hm0004", "Brand2 Dishwasher", "Washing equipments", 250f); - - SolrQuery query = new SolrQuery(); - query.setQuery("hme"); - query.set("spellcheck", "on"); - QueryResponse response = solrClient.query(query); - - SpellCheckResponse spellCheckResponse = response.getSpellCheckResponse(); - - assertEquals(false, spellCheckResponse.isCorrectlySpelled()); - - Suggestion suggestion = spellCheckResponse.getSuggestions().get(0); - - assertEquals("hme", suggestion.getToken()); - - List alternatives = suggestion.getAlternatives(); - String alternative = alternatives.get(0); - - assertEquals("home", alternative); - } - - @Test - public void whenSearchingWithIncompleteKeyword_thenKeywordSuggestionsShouldBeReturned() throws Exception { - itemSearchService.index("hm0001", "Brand1 Washing Machine", "Home Appliances", 100f); - itemSearchService.index("hm0002", "Brand1 Refrigerator", "Home Appliances", 300f); - itemSearchService.index("hm0003", "Brand2 Ceiling Fan", "Home Appliances", 200f); - itemSearchService.index("hm0004", "Brand2 Dishwasher", "Home washing equipments", 250f); - - SolrQuery query = new SolrQuery(); - query.setRequestHandler("/suggest"); - query.set("suggest", "true"); - query.set("suggest.build", "true"); - query.set("suggest.dictionary", "mySuggester"); - query.set("suggest.q", "Hom"); - QueryResponse response = solrClient.query(query); - - SuggesterResponse suggesterResponse = response.getSuggesterResponse(); - Map> suggestedTerms = suggesterResponse.getSuggestedTerms(); - List suggestions = suggestedTerms.get("mySuggester"); - - assertEquals(2, suggestions.size()); - - for (String term : suggestions) { - if (!"Home Appliances".equals(term) && !"Home washing equipments".equals(term)) { - fail("Unexpected suggestions"); - } - } - - } - -} diff --git a/solr-fulltext-search/pom.xml b/solr/pom.xml similarity index 95% rename from solr-fulltext-search/pom.xml rename to solr/pom.xml index 5b2950d64c..e784d87157 100644 --- a/solr-fulltext-search/pom.xml +++ b/solr/pom.xml @@ -3,10 +3,10 @@ 4.0.0 com.baeldung - solr-fulltext-search + solr 0.0.1-SNAPSHOT jar - solr-fulltext-search + solr diff --git a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltext/search/model/Item.java b/solr/src/main/java/com/baeldung/solr/fulltext/search/model/Item.java similarity index 100% rename from solr-fulltext-search/src/main/java/com/baeldung/solr/fulltext/search/model/Item.java rename to solr/src/main/java/com/baeldung/solr/fulltext/search/model/Item.java diff --git a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchService.java b/solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchService.java similarity index 100% rename from solr-fulltext-search/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchService.java rename to solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchService.java diff --git a/solr-fulltext-search/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceImpl.java b/solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceImpl.java similarity index 100% rename from solr-fulltext-search/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceImpl.java rename to solr/src/main/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceImpl.java diff --git a/solr-fulltext-search/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java b/solr/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java similarity index 99% rename from solr-fulltext-search/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java rename to solr/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java index 1489d40787..3704f4c34c 100644 --- a/solr-fulltext-search/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java +++ b/solr/src/test/java/com/baeldung/solr/fulltext/search/service/ItemSearchServiceLiveTest.java @@ -26,7 +26,7 @@ public class ItemSearchServiceLiveTest { private static SolrClient solrClient; private static ItemSearchService itemSearchService; - private static final String solrUrl = "http://localhost:8987/solr/item"; + private static final String solrUrl = "http://localhost:8983/solr/item"; @BeforeClass public static void initBeans() throws Exception { From b6e271f1f0f111e02ae6141918fb32cae265067f Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 25 Mar 2017 09:25:51 +0100 Subject: [PATCH 045/102] spring-custom-aop -> spring-aop (#1489) --- pom.xml | 1 + {spring-custom-aop => spring-aop}/pom.xml | 0 .../src/main/java/org/baeldung/Application.java | 0 .../src/main/java/org/baeldung/ExampleAspect.java | 0 .../src/main/java/org/baeldung/LogExecutionTime.java | 0 .../src/main/java/org/baeldung/Service.java | 0 .../src/test/java/org/baeldung/CustomAnnotationTest.java | 0 7 files changed, 1 insertion(+) rename {spring-custom-aop => spring-aop}/pom.xml (100%) rename {spring-custom-aop => spring-aop}/src/main/java/org/baeldung/Application.java (100%) rename {spring-custom-aop => spring-aop}/src/main/java/org/baeldung/ExampleAspect.java (100%) rename {spring-custom-aop => spring-aop}/src/main/java/org/baeldung/LogExecutionTime.java (100%) rename {spring-custom-aop => spring-aop}/src/main/java/org/baeldung/Service.java (100%) rename {spring-custom-aop => spring-aop}/src/test/java/org/baeldung/CustomAnnotationTest.java (100%) diff --git a/pom.xml b/pom.xml index 4796a11ac4..187ff60dd2 100644 --- a/pom.xml +++ b/pom.xml @@ -123,6 +123,7 @@ spring-cloud spring-core spring-cucumber + spring-aop spring-data-cassandra spring-data-couchbase-2 spring-data-dynamodb diff --git a/spring-custom-aop/pom.xml b/spring-aop/pom.xml similarity index 100% rename from spring-custom-aop/pom.xml rename to spring-aop/pom.xml diff --git a/spring-custom-aop/src/main/java/org/baeldung/Application.java b/spring-aop/src/main/java/org/baeldung/Application.java similarity index 100% rename from spring-custom-aop/src/main/java/org/baeldung/Application.java rename to spring-aop/src/main/java/org/baeldung/Application.java diff --git a/spring-custom-aop/src/main/java/org/baeldung/ExampleAspect.java b/spring-aop/src/main/java/org/baeldung/ExampleAspect.java similarity index 100% rename from spring-custom-aop/src/main/java/org/baeldung/ExampleAspect.java rename to spring-aop/src/main/java/org/baeldung/ExampleAspect.java diff --git a/spring-custom-aop/src/main/java/org/baeldung/LogExecutionTime.java b/spring-aop/src/main/java/org/baeldung/LogExecutionTime.java similarity index 100% rename from spring-custom-aop/src/main/java/org/baeldung/LogExecutionTime.java rename to spring-aop/src/main/java/org/baeldung/LogExecutionTime.java diff --git a/spring-custom-aop/src/main/java/org/baeldung/Service.java b/spring-aop/src/main/java/org/baeldung/Service.java similarity index 100% rename from spring-custom-aop/src/main/java/org/baeldung/Service.java rename to spring-aop/src/main/java/org/baeldung/Service.java diff --git a/spring-custom-aop/src/test/java/org/baeldung/CustomAnnotationTest.java b/spring-aop/src/test/java/org/baeldung/CustomAnnotationTest.java similarity index 100% rename from spring-custom-aop/src/test/java/org/baeldung/CustomAnnotationTest.java rename to spring-aop/src/test/java/org/baeldung/CustomAnnotationTest.java From efc2043e5285cfa3067a86b2cd1c19c7bacc73d3 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 25 Mar 2017 10:37:00 +0100 Subject: [PATCH 046/102] KafkaProducerConfig refactor (#1488) --- pom.xml | 1 + .../spring/kafka/KafkaProducerConfig.java | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 187ff60dd2..5c85e94546 100644 --- a/pom.xml +++ b/pom.xml @@ -144,6 +144,7 @@ spring-jms spring-jooq spring-jpa + spring-kafka spring-katharsis spring-ldap spring-mockito diff --git a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java index 84d57c9e92..7e2527b36e 100644 --- a/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java +++ b/spring-kafka/src/main/java/com/baeldung/spring/kafka/KafkaProducerConfig.java @@ -1,8 +1,5 @@ package com.baeldung.spring.kafka; -import java.util.HashMap; -import java.util.Map; - import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringSerializer; import org.springframework.beans.factory.annotation.Value; @@ -13,6 +10,9 @@ import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.core.ProducerFactory; import org.springframework.kafka.support.serializer.JsonSerializer; +import java.util.HashMap; +import java.util.Map; + @Configuration public class KafkaProducerConfig { @@ -21,30 +21,30 @@ public class KafkaProducerConfig { @Bean public ProducerFactory producerFactory() { - Map configProps = new HashMap(); + Map configProps = new HashMap<>(); configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - return new DefaultKafkaProducerFactory(configProps); + return new DefaultKafkaProducerFactory<>(configProps); } @Bean public KafkaTemplate kafkaTemplate() { - return new KafkaTemplate(producerFactory()); + return new KafkaTemplate<>(producerFactory()); } @Bean public ProducerFactory greetingProducerFactory() { - Map configProps = new HashMap(); + Map configProps = new HashMap<>(); configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); - return new DefaultKafkaProducerFactory(configProps); + return new DefaultKafkaProducerFactory<>(configProps); } @Bean public KafkaTemplate greetingKafkaTemplate() { - return new KafkaTemplate(greetingProducerFactory()); + return new KafkaTemplate<>(greetingProducerFactory()); } } From 669f4d6dce4d528dd6c8ad7382ebd33c2b6a71db Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 25 Mar 2017 11:00:16 +0100 Subject: [PATCH 047/102] Further build improvements (#1486) --- javaslang/pom.xml | 12 ++++++++++++ ...st.java => PropertyBasedLongRunningUnitTest.java} | 2 +- metrics/pom.xml | 12 ++++++++++++ ...{MetricsTest.java => MetricsIntegrationTest.java} | 2 +- 4 files changed, 26 insertions(+), 2 deletions(-) rename javaslang/src/test/java/com/baeldung/javaslang/{PropertyBasedTest.java => PropertyBasedLongRunningUnitTest.java} (97%) rename metrics/src/test/java/com/baeldung/metrics/core/{MetricsTest.java => MetricsIntegrationTest.java} (99%) diff --git a/javaslang/pom.xml b/javaslang/pom.xml index 941aac0802..3770cce548 100644 --- a/javaslang/pom.xml +++ b/javaslang/pom.xml @@ -44,6 +44,18 @@ 1.8 + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + + true + + \ No newline at end of file diff --git a/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java b/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedLongRunningUnitTest.java similarity index 97% rename from javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java rename to javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedLongRunningUnitTest.java index 43f3d6e6a0..2bbc92563e 100644 --- a/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedTest.java +++ b/javaslang/src/test/java/com/baeldung/javaslang/PropertyBasedLongRunningUnitTest.java @@ -11,7 +11,7 @@ import java.util.function.Predicate; import static javaslang.API.*; -public class PropertyBasedTest { +public class PropertyBasedLongRunningUnitTest { private static Predicate divisibleByTwo = i -> i % 2 == 0; private static Predicate divisibleByFive = i -> i % 5 == 0; diff --git a/metrics/pom.xml b/metrics/pom.xml index 0f5cb2f135..6111ebf30b 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -69,6 +69,18 @@ ${maven.compiler.target}
+ + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + + true + +
diff --git a/metrics/src/test/java/com/baeldung/metrics/core/MetricsTest.java b/metrics/src/test/java/com/baeldung/metrics/core/MetricsIntegrationTest.java similarity index 99% rename from metrics/src/test/java/com/baeldung/metrics/core/MetricsTest.java rename to metrics/src/test/java/com/baeldung/metrics/core/MetricsIntegrationTest.java index e876de6e65..352911f1e1 100644 --- a/metrics/src/test/java/com/baeldung/metrics/core/MetricsTest.java +++ b/metrics/src/test/java/com/baeldung/metrics/core/MetricsIntegrationTest.java @@ -11,7 +11,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; -public class MetricsTest { +public class MetricsIntegrationTest { @Test public void whenMarkMeter_thenCorrectRates() throws InterruptedException { Meter meter = new Meter(); From 21f9df63304a695cdcbb714fa1138b2b610d6751 Mon Sep 17 00:00:00 2001 From: Tomasz Lelek Date: Sat, 25 Mar 2017 11:14:09 +0100 Subject: [PATCH 048/102] Bael 518 protobuffer (#1400) * BEEL-518 code for protobuf article * BEEL-518 add generated protobuf class * BEEL-550 use newest version of protobuff * BAEL-518 Small refactoring in protobuffer module * BEEL-518 simpler protobuf example * BEEL-518 proper package --- book | 0 ...638124c-9a1b-4d25-8fce-cc223d472e77.events | Bin 0 -> 663 bytes ...2ba9cbe-1a44-428e-a710-13b1bdc67c4b.events | Bin 0 -> 675 bytes ...f420ffc-0c3b-403e-bb8c-66cf499c773e.events | Bin 0 -> 714 bytes ...72a057b-adea-4c69-83a0-0431318823e7.events | Bin 0 -> 714 bytes .../baeldung/protobuf/AddressBookProtos.java | 1488 +++-------------- .../src/main/resources/addressbook.proto | 13 +- .../com/baeldung/protobuf/ProtobufTest.java | 21 +- .../cache/2d9345a30d2cc31bb3091d70a8ef6c18.0 | 24 + .../cache/2d9345a30d2cc31bb3091d70a8ef6c18.1 | 39 + .../cache/4b217e04ba52215f3a6b64d28f6729c6.0 | 13 + .../cache/4b217e04ba52215f3a6b64d28f6729c6.1 | 7 + spring-rest/src/test/resources/cache/journal | 12 + 13 files changed, 334 insertions(+), 1283 deletions(-) create mode 100644 book create mode 100644 events/MessagesAggregate/0638124c-9a1b-4d25-8fce-cc223d472e77.events create mode 100644 events/MessagesAggregate/d2ba9cbe-1a44-428e-a710-13b1bdc67c4b.events create mode 100644 events/ToDoItem/bf420ffc-0c3b-403e-bb8c-66cf499c773e.events create mode 100644 events/ToDoItem/e72a057b-adea-4c69-83a0-0431318823e7.events create mode 100644 spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.0 create mode 100644 spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.1 create mode 100644 spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.0 create mode 100644 spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.1 create mode 100644 spring-rest/src/test/resources/cache/journal diff --git a/book b/book new file mode 100644 index 0000000000..e69de29bb2 diff --git a/events/MessagesAggregate/0638124c-9a1b-4d25-8fce-cc223d472e77.events b/events/MessagesAggregate/0638124c-9a1b-4d25-8fce-cc223d472e77.events new file mode 100644 index 0000000000000000000000000000000000000000..d3ce8b9cea0dd432ac369a37c2281c78d9b98a7e GIT binary patch literal 663 zcmbV|O-jT-5QW>lR}iwuMzoUZpQL+2la094g?NEVcV!%K5-_dI0X&Q6bPV`|2s5#k zkM~|ZJ|S`wRy{gK;D8;Ns1!I899XF=>B?F{Db5s13#b9>d#M{$Hxi55sSA`1qR6q< zPAU^%MRG2w!1aM41f_H|R^De`pnD?D4^6&f`hoWKAlfhgLaqndeHZ@*_YUaJ B!ZiQ@ literal 0 HcmV?d00001 diff --git a/events/MessagesAggregate/d2ba9cbe-1a44-428e-a710-13b1bdc67c4b.events b/events/MessagesAggregate/d2ba9cbe-1a44-428e-a710-13b1bdc67c4b.events new file mode 100644 index 0000000000000000000000000000000000000000..2ab0ec469f90efe0f09ac43380e620738125a0ce GIT binary patch literal 675 zcmbV|%}T>S6otpVuOMX6O|dtbJ2TECVTcP6+_;gxz@3@fT4)<+Qf>D>kxytW_=5Zfq;`tB*0x5>wYHZMYbnJj zsX$c>3`oX+Q6&R8lz=*5NYT1zg7~;eY*%?8UZ(AOp3|;f=lsO$zRT~q>uAn58>XJq z?R$6_z4}uojbEhZFGRi=ioWMv-`w$X*-o;@_BmU0*}mOwvPApqtcI~K4h>(N#4vE5 z`xW;DiFzs;Ax2t87g9)2&YT7lm4b>Z1IcKL<)Ar*@VjYa^gm3WiSaSy2c~bDUyf@2 P?4RiPsJpMnKgGQPZ1>2E literal 0 HcmV?d00001 diff --git a/events/ToDoItem/bf420ffc-0c3b-403e-bb8c-66cf499c773e.events b/events/ToDoItem/bf420ffc-0c3b-403e-bb8c-66cf499c773e.events new file mode 100644 index 0000000000000000000000000000000000000000..d805fc253e397456a33c8b6b1d5de5b42af14043 GIT binary patch literal 714 zcmbV|zfQw25QojqD0Xq?%h7JV7VWa4wZIZDKbZ8x1G`)uw4yLanf^6zW@SR)}or zljN9E01U!|L_y$v7eJ|yq%lEjLF~66b z`0zFTPJP(3GQBN1=5_2(;j;D5v5j;H`(amxJoS%6aZ&Oj=0$lP4z~WgwDoy7^AFc{ zq~ajD;_9BUWowLYNlS==&&Fbh(3#YL);uXqE^5>LuU(_KK>HOtfjN)6Eggw}KSBIa O>O%GB!p#bgm3{(Qui1M5 literal 0 HcmV?d00001 diff --git a/events/ToDoItem/e72a057b-adea-4c69-83a0-0431318823e7.events b/events/ToDoItem/e72a057b-adea-4c69-83a0-0431318823e7.events new file mode 100644 index 0000000000000000000000000000000000000000..3d67b74ced4494b9f3af4f38b6bef9622c565329 GIT binary patch literal 714 zcmbWzJx&8L5CveCo(s|90)j+6E}nP~`%%_5>nj*HK~+N8vP_jRk>3B#IU(=vG1IB*R{7Y%t8QQRn$EuaQ$R#GmsTG+NS5^qs15XGEZ zMQGa|C^3PFjR&U*0gaYgI;S+Z;&&kCq3VgZ>1BMX=q-;Gzw%fP)hgfTW#Qp&&r~=) ze7%0BKEhdrepeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - java.util.List - getPhonesList(); + java.util.List + getNumbersList(); /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - AddressBookProtos.Person.PhoneNumber getPhones(int index); + int getNumbersCount(); /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - int getPhonesCount(); + java.lang.String getNumbers(int index); /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - java.util.List - getPhonesOrBuilderList(); - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - AddressBookProtos.Person.PhoneNumberOrBuilder getPhonesOrBuilder( - int index); + com.google.protobuf.ByteString + getNumbersBytes(int index); } /** @@ -108,7 +102,7 @@ public final class AddressBookProtos { name_ = ""; id_ = 0; email_ = ""; - phones_ = java.util.Collections.emptyList(); + numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; } @java.lang.Override @@ -158,12 +152,12 @@ public final class AddressBookProtos { break; } case 34: { + com.google.protobuf.ByteString bs = input.readBytes(); if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - phones_ = new java.util.ArrayList(); + numbers_ = new com.google.protobuf.LazyStringArrayList(); mutable_bitField0_ |= 0x00000008; } - phones_.add( - input.readMessage(AddressBookProtos.Person.PhoneNumber.PARSER, extensionRegistry)); + numbers_.add(bs); break; } } @@ -175,7 +169,7 @@ public final class AddressBookProtos { e).setUnfinishedMessage(this); } finally { if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { - phones_ = java.util.Collections.unmodifiableList(phones_); + numbers_ = numbers_.getUnmodifiableView(); } this.unknownFields = unknownFields.build(); makeExtensionsImmutable(); @@ -184,819 +178,14 @@ public final class AddressBookProtos { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return AddressBookProtos.internal_static_protobuf_Person_descriptor; + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_descriptor; } protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { - return AddressBookProtos.internal_static_protobuf_Person_fieldAccessorTable + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_fieldAccessorTable .ensureFieldAccessorsInitialized( - AddressBookProtos.Person.class, AddressBookProtos.Person.Builder.class); - } - - /** - * Protobuf enum {@code protobuf.Person.PhoneType} - */ - public enum PhoneType - implements com.google.protobuf.ProtocolMessageEnum { - /** - * MOBILE = 0; - */ - MOBILE(0), - /** - * HOME = 1; - */ - HOME(1), - /** - * WORK = 2; - */ - WORK(2),; - - /** - * MOBILE = 0; - */ - public static final int MOBILE_VALUE = 0; - /** - * HOME = 1; - */ - public static final int HOME_VALUE = 1; - /** - * WORK = 2; - */ - public static final int WORK_VALUE = 2; - - - public final int getNumber() { - return value; - } - - /** - * @deprecated Use {@link #forNumber(int)} instead. - */ - @java.lang.Deprecated - public static PhoneType valueOf(int value) { - return forNumber(value); - } - - public static PhoneType forNumber(int value) { - switch (value) { - case 0: - return MOBILE; - case 1: - return HOME; - case 2: - return WORK; - default: - return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - - private static final com.google.protobuf.Internal.EnumLiteMap< - PhoneType> internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - public PhoneType findValueByNumber(int number) { - return PhoneType.forNumber(number); - } - }; - - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(ordinal()); - } - - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return AddressBookProtos.Person.getDescriptor().getEnumTypes().get(0); - } - - private static final PhoneType[] VALUES = values(); - - public static PhoneType valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int value; - - private PhoneType(int value) { - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:protobuf.Person.PhoneType) - } - - public interface PhoneNumberOrBuilder extends - // @@protoc_insertion_point(interface_extends:protobuf.Person.PhoneNumber) - com.google.protobuf.MessageOrBuilder { - - /** - * required string number = 1; - */ - boolean hasNumber(); - - /** - * required string number = 1; - */ - java.lang.String getNumber(); - - /** - * required string number = 1; - */ - com.google.protobuf.ByteString - getNumberBytes(); - - /** - * optional .protobuf.Person.PhoneType type = 2 [default = HOME]; - */ - boolean hasType(); - - /** - * optional .protobuf.Person.PhoneType type = 2 [default = HOME]; - */ - AddressBookProtos.Person.PhoneType getType(); - } - - /** - * Protobuf type {@code protobuf.Person.PhoneNumber} - */ - public static final class PhoneNumber extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:protobuf.Person.PhoneNumber) - PhoneNumberOrBuilder { - // Use PhoneNumber.newBuilder() to construct. - private PhoneNumber(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - - private PhoneNumber() { - number_ = ""; - type_ = 1; - } - - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - - private PhoneNumber( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - this(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - com.google.protobuf.ByteString bs = input.readBytes(); - bitField0_ |= 0x00000001; - number_ = bs; - break; - } - case 16: { - int rawValue = input.readEnum(); - AddressBookProtos.Person.PhoneType value = AddressBookProtos.Person.PhoneType.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(2, rawValue); - } else { - bitField0_ |= 0x00000002; - type_ = rawValue; - } - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return AddressBookProtos.internal_static_protobuf_Person_PhoneNumber_descriptor; - } - - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return AddressBookProtos.internal_static_protobuf_Person_PhoneNumber_fieldAccessorTable - .ensureFieldAccessorsInitialized( - AddressBookProtos.Person.PhoneNumber.class, AddressBookProtos.Person.PhoneNumber.Builder.class); - } - - private int bitField0_; - public static final int NUMBER_FIELD_NUMBER = 1; - private volatile java.lang.Object number_; - - /** - * required string number = 1; - */ - public boolean hasNumber() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - - /** - * required string number = 1; - */ - public java.lang.String getNumber() { - java.lang.Object ref = number_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - number_ = s; - } - return s; - } - } - - /** - * required string number = 1; - */ - public com.google.protobuf.ByteString - getNumberBytes() { - java.lang.Object ref = number_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - number_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int TYPE_FIELD_NUMBER = 2; - private int type_; - - /** - * optional .protobuf.Person.PhoneType type = 2 [default = HOME]; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - - /** - * optional .protobuf.Person.PhoneType type = 2 [default = HOME]; - */ - public AddressBookProtos.Person.PhoneType getType() { - AddressBookProtos.Person.PhoneType result = AddressBookProtos.Person.PhoneType.valueOf(type_); - return result == null ? AddressBookProtos.Person.PhoneType.HOME : result; - } - - private byte memoizedIsInitialized = -1; - - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - if (!hasNumber()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - if (((bitField0_ & 0x00000001) == 0x00000001)) { - com.google.protobuf.GeneratedMessageV3.writeString(output, 1, number_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeEnum(2, type_); - } - unknownFields.writeTo(output); - } - - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, number_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(2, type_); - } - size += unknownFields.getSerializedSize(); - memoizedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof AddressBookProtos.Person.PhoneNumber)) { - return super.equals(obj); - } - AddressBookProtos.Person.PhoneNumber other = (AddressBookProtos.Person.PhoneNumber) obj; - - boolean result = true; - result = result && (hasNumber() == other.hasNumber()); - if (hasNumber()) { - result = result && getNumber() - .equals(other.getNumber()); - } - result = result && (hasType() == other.hasType()); - if (hasType()) { - result = result && type_ == other.type_; - } - result = result && unknownFields.equals(other.unknownFields); - return result; - } - - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptorForType().hashCode(); - if (hasNumber()) { - hash = (37 * hash) + NUMBER_FIELD_NUMBER; - hash = (53 * hash) + getNumber().hashCode(); - } - if (hasType()) { - hash = (37 * hash) + TYPE_FIELD_NUMBER; - hash = (53 * hash) + type_; - } - hash = (29 * hash) + unknownFields.hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static AddressBookProtos.Person.PhoneNumber parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - - public static AddressBookProtos.Person.PhoneNumber parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - - public static AddressBookProtos.Person.PhoneNumber parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - - public static AddressBookProtos.Person.PhoneNumber parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - - public static AddressBookProtos.Person.PhoneNumber parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - - public static AddressBookProtos.Person.PhoneNumber parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static AddressBookProtos.Person.PhoneNumber parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - - public static AddressBookProtos.Person.PhoneNumber parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - - public static AddressBookProtos.Person.PhoneNumber parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - - public static AddressBookProtos.Person.PhoneNumber parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public Builder newBuilderForType() { - return newBuilder(); - } - - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - - public static Builder newBuilder(AddressBookProtos.Person.PhoneNumber prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - - /** - * Protobuf type {@code protobuf.Person.PhoneNumber} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:protobuf.Person.PhoneNumber) - AddressBookProtos.Person.PhoneNumberOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return AddressBookProtos.internal_static_protobuf_Person_PhoneNumber_descriptor; - } - - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return AddressBookProtos.internal_static_protobuf_Person_PhoneNumber_fieldAccessorTable - .ensureFieldAccessorsInitialized( - AddressBookProtos.Person.PhoneNumber.class, AddressBookProtos.Person.PhoneNumber.Builder.class); - } - - // Construct using AddressBookProtos.Person.PhoneNumber.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessageV3 - .alwaysUseFieldBuilders) { - } - } - - public Builder clear() { - super.clear(); - number_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - type_ = 1; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return AddressBookProtos.internal_static_protobuf_Person_PhoneNumber_descriptor; - } - - public AddressBookProtos.Person.PhoneNumber getDefaultInstanceForType() { - return AddressBookProtos.Person.PhoneNumber.getDefaultInstance(); - } - - public AddressBookProtos.Person.PhoneNumber build() { - AddressBookProtos.Person.PhoneNumber result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public AddressBookProtos.Person.PhoneNumber buildPartial() { - AddressBookProtos.Person.PhoneNumber result = new AddressBookProtos.Person.PhoneNumber(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.number_ = number_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.type_ = type_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder clone() { - return (Builder) super.clone(); - } - - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.setField(field, value); - } - - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return (Builder) super.clearField(field); - } - - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return (Builder) super.clearOneof(oneof); - } - - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, Object value) { - return (Builder) super.setRepeatedField(field, index, value); - } - - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.addRepeatedField(field, value); - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof AddressBookProtos.Person.PhoneNumber) { - return mergeFrom((AddressBookProtos.Person.PhoneNumber) other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(AddressBookProtos.Person.PhoneNumber other) { - if (other == AddressBookProtos.Person.PhoneNumber.getDefaultInstance()) - return this; - if (other.hasNumber()) { - bitField0_ |= 0x00000001; - number_ = other.number_; - onChanged(); - } - if (other.hasType()) { - setType(other.getType()); - } - this.mergeUnknownFields(other.unknownFields); - onChanged(); - return this; - } - - public final boolean isInitialized() { - if (!hasNumber()) { - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - AddressBookProtos.Person.PhoneNumber parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (AddressBookProtos.Person.PhoneNumber) e.getUnfinishedMessage(); - throw e.unwrapIOException(); - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - - private int bitField0_; - - private java.lang.Object number_ = ""; - - /** - * required string number = 1; - */ - public boolean hasNumber() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - - /** - * required string number = 1; - */ - public java.lang.String getNumber() { - java.lang.Object ref = number_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - number_ = s; - } - return s; - } else { - return (java.lang.String) ref; - } - } - - /** - * required string number = 1; - */ - public com.google.protobuf.ByteString - getNumberBytes() { - java.lang.Object ref = number_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - number_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - /** - * required string number = 1; - */ - public Builder setNumber( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - number_ = value; - onChanged(); - return this; - } - - /** - * required string number = 1; - */ - public Builder clearNumber() { - bitField0_ = (bitField0_ & ~0x00000001); - number_ = getDefaultInstance().getNumber(); - onChanged(); - return this; - } - - /** - * required string number = 1; - */ - public Builder setNumberBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - number_ = value; - onChanged(); - return this; - } - - private int type_ = 1; - - /** - * optional .protobuf.Person.PhoneType type = 2 [default = HOME]; - */ - public boolean hasType() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - - /** - * optional .protobuf.Person.PhoneType type = 2 [default = HOME]; - */ - public AddressBookProtos.Person.PhoneType getType() { - AddressBookProtos.Person.PhoneType result = AddressBookProtos.Person.PhoneType.valueOf(type_); - return result == null ? AddressBookProtos.Person.PhoneType.HOME : result; - } - - /** - * optional .protobuf.Person.PhoneType type = 2 [default = HOME]; - */ - public Builder setType(AddressBookProtos.Person.PhoneType value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - type_ = value.getNumber(); - onChanged(); - return this; - } - - /** - * optional .protobuf.Person.PhoneType type = 2 [default = HOME]; - */ - public Builder clearType() { - bitField0_ = (bitField0_ & ~0x00000002); - type_ = 1; - onChanged(); - return this; - } - - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.setUnknownFields(unknownFields); - } - - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.mergeUnknownFields(unknownFields); - } - - - // @@protoc_insertion_point(builder_scope:protobuf.Person.PhoneNumber) - } - - // @@protoc_insertion_point(class_scope:protobuf.Person.PhoneNumber) - private static final AddressBookProtos.Person.PhoneNumber DEFAULT_INSTANCE; - - static { - DEFAULT_INSTANCE = new AddressBookProtos.Person.PhoneNumber(); - } - - public static AddressBookProtos.Person.PhoneNumber getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - @java.lang.Deprecated - public static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - public PhoneNumber parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new PhoneNumber(input, extensionRegistry); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public AddressBookProtos.Person.PhoneNumber getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - + com.baeldung.protobuf.AddressBookProtos.Person.class, com.baeldung.protobuf.AddressBookProtos.Person.Builder.class); } private int bitField0_; @@ -1107,44 +296,37 @@ public final class AddressBookProtos { } } - public static final int PHONES_FIELD_NUMBER = 4; - private java.util.List phones_; + public static final int NUMBERS_FIELD_NUMBER = 4; + private com.google.protobuf.LazyStringList numbers_; /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public java.util.List getPhonesList() { - return phones_; + public com.google.protobuf.ProtocolStringList + getNumbersList() { + return numbers_; } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public java.util.List - getPhonesOrBuilderList() { - return phones_; + public int getNumbersCount() { + return numbers_.size(); } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public int getPhonesCount() { - return phones_.size(); + public java.lang.String getNumbers(int index) { + return numbers_.get(index); } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public AddressBookProtos.Person.PhoneNumber getPhones(int index) { - return phones_.get(index); - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public AddressBookProtos.Person.PhoneNumberOrBuilder getPhonesOrBuilder( - int index) { - return phones_.get(index); + public com.google.protobuf.ByteString + getNumbersBytes(int index) { + return numbers_.getByteString(index); } private byte memoizedIsInitialized = -1; @@ -1162,12 +344,6 @@ public final class AddressBookProtos { memoizedIsInitialized = 0; return false; } - for (int i = 0; i < getPhonesCount(); i++) { - if (!getPhones(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } memoizedIsInitialized = 1; return true; } @@ -1183,8 +359,8 @@ public final class AddressBookProtos { if (((bitField0_ & 0x00000004) == 0x00000004)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 3, email_); } - for (int i = 0; i < phones_.size(); i++) { - output.writeMessage(4, phones_.get(i)); + for (int i = 0; i < numbers_.size(); i++) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 4, numbers_.getRaw(i)); } unknownFields.writeTo(output); } @@ -1204,9 +380,13 @@ public final class AddressBookProtos { if (((bitField0_ & 0x00000004) == 0x00000004)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, email_); } - for (int i = 0; i < phones_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, phones_.get(i)); + { + int dataSize = 0; + for (int i = 0; i < numbers_.size(); i++) { + dataSize += computeStringSizeNoTag(numbers_.getRaw(i)); + } + size += dataSize; + size += 1 * getNumbersList().size(); } size += unknownFields.getSerializedSize(); memoizedSize = size; @@ -1220,10 +400,10 @@ public final class AddressBookProtos { if (obj == this) { return true; } - if (!(obj instanceof AddressBookProtos.Person)) { + if (!(obj instanceof com.baeldung.protobuf.AddressBookProtos.Person)) { return super.equals(obj); } - AddressBookProtos.Person other = (AddressBookProtos.Person) obj; + com.baeldung.protobuf.AddressBookProtos.Person other = (com.baeldung.protobuf.AddressBookProtos.Person) obj; boolean result = true; result = result && (hasName() == other.hasName()); @@ -1241,8 +421,8 @@ public final class AddressBookProtos { result = result && getEmail() .equals(other.getEmail()); } - result = result && getPhonesList() - .equals(other.getPhonesList()); + result = result && getNumbersList() + .equals(other.getNumbersList()); result = result && unknownFields.equals(other.unknownFields); return result; } @@ -1266,47 +446,47 @@ public final class AddressBookProtos { hash = (37 * hash) + EMAIL_FIELD_NUMBER; hash = (53 * hash) + getEmail().hashCode(); } - if (getPhonesCount() > 0) { - hash = (37 * hash) + PHONES_FIELD_NUMBER; - hash = (53 * hash) + getPhonesList().hashCode(); + if (getNumbersCount() > 0) { + hash = (37 * hash) + NUMBERS_FIELD_NUMBER; + hash = (53 * hash) + getNumbersList().hashCode(); } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; } - public static AddressBookProtos.Person parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static AddressBookProtos.Person parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static AddressBookProtos.Person parseFrom(byte[] data) + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static AddressBookProtos.Person parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static AddressBookProtos.Person parseFrom(java.io.InputStream input) + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } - public static AddressBookProtos.Person parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -1314,13 +494,13 @@ public final class AddressBookProtos { .parseWithIOException(PARSER, input, extensionRegistry); } - public static AddressBookProtos.Person parseDelimitedFrom(java.io.InputStream input) + public static com.baeldung.protobuf.AddressBookProtos.Person parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseDelimitedWithIOException(PARSER, input); } - public static AddressBookProtos.Person parseDelimitedFrom( + public static com.baeldung.protobuf.AddressBookProtos.Person parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -1328,14 +508,14 @@ public final class AddressBookProtos { .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } - public static AddressBookProtos.Person parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } - public static AddressBookProtos.Person parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.Person parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -1351,7 +531,7 @@ public final class AddressBookProtos { return DEFAULT_INSTANCE.toBuilder(); } - public static Builder newBuilder(AddressBookProtos.Person prototype) { + public static Builder newBuilder(com.baeldung.protobuf.AddressBookProtos.Person prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @@ -1373,20 +553,20 @@ public final class AddressBookProtos { public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:protobuf.Person) - AddressBookProtos.PersonOrBuilder { + com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return AddressBookProtos.internal_static_protobuf_Person_descriptor; + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_descriptor; } protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { - return AddressBookProtos.internal_static_protobuf_Person_fieldAccessorTable + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_fieldAccessorTable .ensureFieldAccessorsInitialized( - AddressBookProtos.Person.class, AddressBookProtos.Person.Builder.class); + com.baeldung.protobuf.AddressBookProtos.Person.class, com.baeldung.protobuf.AddressBookProtos.Person.Builder.class); } - // Construct using AddressBookProtos.Person.newBuilder() + // Construct using com.baeldung.protobuf.AddressBookProtos.Person.newBuilder() private Builder() { maybeForceBuilderInitialization(); } @@ -1400,7 +580,6 @@ public final class AddressBookProtos { private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessageV3 .alwaysUseFieldBuilders) { - getPhonesFieldBuilder(); } } @@ -1412,34 +591,30 @@ public final class AddressBookProtos { bitField0_ = (bitField0_ & ~0x00000002); email_ = ""; bitField0_ = (bitField0_ & ~0x00000004); - if (phonesBuilder_ == null) { - phones_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - phonesBuilder_.clear(); - } + numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); return this; } public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return AddressBookProtos.internal_static_protobuf_Person_descriptor; + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_Person_descriptor; } - public AddressBookProtos.Person getDefaultInstanceForType() { - return AddressBookProtos.Person.getDefaultInstance(); + public com.baeldung.protobuf.AddressBookProtos.Person getDefaultInstanceForType() { + return com.baeldung.protobuf.AddressBookProtos.Person.getDefaultInstance(); } - public AddressBookProtos.Person build() { - AddressBookProtos.Person result = buildPartial(); + public com.baeldung.protobuf.AddressBookProtos.Person build() { + com.baeldung.protobuf.AddressBookProtos.Person result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } - public AddressBookProtos.Person buildPartial() { - AddressBookProtos.Person result = new AddressBookProtos.Person(this); + public com.baeldung.protobuf.AddressBookProtos.Person buildPartial() { + com.baeldung.protobuf.AddressBookProtos.Person result = new com.baeldung.protobuf.AddressBookProtos.Person(this); int from_bitField0_ = bitField0_; int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) == 0x00000001)) { @@ -1454,15 +629,11 @@ public final class AddressBookProtos { to_bitField0_ |= 0x00000004; } result.email_ = email_; - if (phonesBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - phones_ = java.util.Collections.unmodifiableList(phones_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.phones_ = phones_; - } else { - result.phones_ = phonesBuilder_.build(); + if (((bitField0_ & 0x00000008) == 0x00000008)) { + numbers_ = numbers_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000008); } + result.numbers_ = numbers_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -1501,16 +672,16 @@ public final class AddressBookProtos { } public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof AddressBookProtos.Person) { - return mergeFrom((AddressBookProtos.Person) other); + if (other instanceof com.baeldung.protobuf.AddressBookProtos.Person) { + return mergeFrom((com.baeldung.protobuf.AddressBookProtos.Person) other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(AddressBookProtos.Person other) { - if (other == AddressBookProtos.Person.getDefaultInstance()) return this; + public Builder mergeFrom(com.baeldung.protobuf.AddressBookProtos.Person other) { + if (other == com.baeldung.protobuf.AddressBookProtos.Person.getDefaultInstance()) return this; if (other.hasName()) { bitField0_ |= 0x00000001; name_ = other.name_; @@ -1524,31 +695,15 @@ public final class AddressBookProtos { email_ = other.email_; onChanged(); } - if (phonesBuilder_ == null) { - if (!other.phones_.isEmpty()) { - if (phones_.isEmpty()) { - phones_ = other.phones_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensurePhonesIsMutable(); - phones_.addAll(other.phones_); - } - onChanged(); - } - } else { - if (!other.phones_.isEmpty()) { - if (phonesBuilder_.isEmpty()) { - phonesBuilder_.dispose(); - phonesBuilder_ = null; - phones_ = other.phones_; - bitField0_ = (bitField0_ & ~0x00000008); - phonesBuilder_ = - com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? - getPhonesFieldBuilder() : null; - } else { - phonesBuilder_.addAllMessages(other.phones_); - } + if (!other.numbers_.isEmpty()) { + if (numbers_.isEmpty()) { + numbers_ = other.numbers_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureNumbersIsMutable(); + numbers_.addAll(other.numbers_); } + onChanged(); } this.mergeUnknownFields(other.unknownFields); onChanged(); @@ -1562,11 +717,6 @@ public final class AddressBookProtos { if (!hasId()) { return false; } - for (int i = 0; i < getPhonesCount(); i++) { - if (!getPhones(i).isInitialized()) { - return false; - } - } return true; } @@ -1574,11 +724,11 @@ public final class AddressBookProtos { com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - AddressBookProtos.Person parsedMessage = null; + com.baeldung.protobuf.AddressBookProtos.Person parsedMessage = null; try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (AddressBookProtos.Person) e.getUnfinishedMessage(); + parsedMessage = (com.baeldung.protobuf.AddressBookProtos.Person) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { if (parsedMessage != null) { @@ -1790,266 +940,109 @@ public final class AddressBookProtos { return this; } - private java.util.List phones_ = - java.util.Collections.emptyList(); + private com.google.protobuf.LazyStringList numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensurePhonesIsMutable() { + private void ensureNumbersIsMutable() { if (!((bitField0_ & 0x00000008) == 0x00000008)) { - phones_ = new java.util.ArrayList(phones_); + numbers_ = new com.google.protobuf.LazyStringArrayList(numbers_); bitField0_ |= 0x00000008; } } - private com.google.protobuf.RepeatedFieldBuilderV3< - AddressBookProtos.Person.PhoneNumber, AddressBookProtos.Person.PhoneNumber.Builder, AddressBookProtos.Person.PhoneNumberOrBuilder> phonesBuilder_; - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public java.util.List getPhonesList() { - if (phonesBuilder_ == null) { - return java.util.Collections.unmodifiableList(phones_); - } else { - return phonesBuilder_.getMessageList(); - } + public com.google.protobuf.ProtocolStringList + getNumbersList() { + return numbers_.getUnmodifiableView(); } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public int getPhonesCount() { - if (phonesBuilder_ == null) { - return phones_.size(); - } else { - return phonesBuilder_.getCount(); - } + public int getNumbersCount() { + return numbers_.size(); } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public AddressBookProtos.Person.PhoneNumber getPhones(int index) { - if (phonesBuilder_ == null) { - return phones_.get(index); - } else { - return phonesBuilder_.getMessage(index); - } + public java.lang.String getNumbers(int index) { + return numbers_.get(index); } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public Builder setPhones( - int index, AddressBookProtos.Person.PhoneNumber value) { - if (phonesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensurePhonesIsMutable(); - phones_.set(index, value); - onChanged(); - } else { - phonesBuilder_.setMessage(index, value); + public com.google.protobuf.ByteString + getNumbersBytes(int index) { + return numbers_.getByteString(index); + } + + /** + * repeated string numbers = 4; + */ + public Builder setNumbers( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); } + ensureNumbersIsMutable(); + numbers_.set(index, value); + onChanged(); return this; } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public Builder setPhones( - int index, AddressBookProtos.Person.PhoneNumber.Builder builderForValue) { - if (phonesBuilder_ == null) { - ensurePhonesIsMutable(); - phones_.set(index, builderForValue.build()); - onChanged(); - } else { - phonesBuilder_.setMessage(index, builderForValue.build()); + public Builder addNumbers( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); } + ensureNumbersIsMutable(); + numbers_.add(value); + onChanged(); return this; } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public Builder addPhones(AddressBookProtos.Person.PhoneNumber value) { - if (phonesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensurePhonesIsMutable(); - phones_.add(value); - onChanged(); - } else { - phonesBuilder_.addMessage(value); - } + public Builder addAllNumbers( + java.lang.Iterable values) { + ensureNumbersIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, numbers_); + onChanged(); return this; } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public Builder addPhones( - int index, AddressBookProtos.Person.PhoneNumber value) { - if (phonesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensurePhonesIsMutable(); - phones_.add(index, value); - onChanged(); - } else { - phonesBuilder_.addMessage(index, value); - } + public Builder clearNumbers() { + numbers_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); return this; } /** - * repeated .protobuf.Person.PhoneNumber phones = 4; + * repeated string numbers = 4; */ - public Builder addPhones( - AddressBookProtos.Person.PhoneNumber.Builder builderForValue) { - if (phonesBuilder_ == null) { - ensurePhonesIsMutable(); - phones_.add(builderForValue.build()); - onChanged(); - } else { - phonesBuilder_.addMessage(builderForValue.build()); + public Builder addNumbersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); } + ensureNumbersIsMutable(); + numbers_.add(value); + onChanged(); return this; } - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public Builder addPhones( - int index, AddressBookProtos.Person.PhoneNumber.Builder builderForValue) { - if (phonesBuilder_ == null) { - ensurePhonesIsMutable(); - phones_.add(index, builderForValue.build()); - onChanged(); - } else { - phonesBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public Builder addAllPhones( - java.lang.Iterable values) { - if (phonesBuilder_ == null) { - ensurePhonesIsMutable(); - com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, phones_); - onChanged(); - } else { - phonesBuilder_.addAllMessages(values); - } - return this; - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public Builder clearPhones() { - if (phonesBuilder_ == null) { - phones_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - phonesBuilder_.clear(); - } - return this; - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public Builder removePhones(int index) { - if (phonesBuilder_ == null) { - ensurePhonesIsMutable(); - phones_.remove(index); - onChanged(); - } else { - phonesBuilder_.remove(index); - } - return this; - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public AddressBookProtos.Person.PhoneNumber.Builder getPhonesBuilder( - int index) { - return getPhonesFieldBuilder().getBuilder(index); - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public AddressBookProtos.Person.PhoneNumberOrBuilder getPhonesOrBuilder( - int index) { - if (phonesBuilder_ == null) { - return phones_.get(index); - } else { - return phonesBuilder_.getMessageOrBuilder(index); - } - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public java.util.List - getPhonesOrBuilderList() { - if (phonesBuilder_ != null) { - return phonesBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(phones_); - } - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public AddressBookProtos.Person.PhoneNumber.Builder addPhonesBuilder() { - return getPhonesFieldBuilder().addBuilder( - AddressBookProtos.Person.PhoneNumber.getDefaultInstance()); - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public AddressBookProtos.Person.PhoneNumber.Builder addPhonesBuilder( - int index) { - return getPhonesFieldBuilder().addBuilder( - index, AddressBookProtos.Person.PhoneNumber.getDefaultInstance()); - } - - /** - * repeated .protobuf.Person.PhoneNumber phones = 4; - */ - public java.util.List - getPhonesBuilderList() { - return getPhonesFieldBuilder().getBuilderList(); - } - - private com.google.protobuf.RepeatedFieldBuilderV3< - AddressBookProtos.Person.PhoneNumber, AddressBookProtos.Person.PhoneNumber.Builder, AddressBookProtos.Person.PhoneNumberOrBuilder> - getPhonesFieldBuilder() { - if (phonesBuilder_ == null) { - phonesBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< - AddressBookProtos.Person.PhoneNumber, AddressBookProtos.Person.PhoneNumber.Builder, AddressBookProtos.Person.PhoneNumberOrBuilder>( - phones_, - ((bitField0_ & 0x00000008) == 0x00000008), - getParentForChildren(), - isClean()); - phones_ = null; - } - return phonesBuilder_; - } - public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); @@ -2065,13 +1058,13 @@ public final class AddressBookProtos { } // @@protoc_insertion_point(class_scope:protobuf.Person) - private static final AddressBookProtos.Person DEFAULT_INSTANCE; + private static final com.baeldung.protobuf.AddressBookProtos.Person DEFAULT_INSTANCE; static { - DEFAULT_INSTANCE = new AddressBookProtos.Person(); + DEFAULT_INSTANCE = new com.baeldung.protobuf.AddressBookProtos.Person(); } - public static AddressBookProtos.Person getDefaultInstance() { + public static com.baeldung.protobuf.AddressBookProtos.Person getDefaultInstance() { return DEFAULT_INSTANCE; } @@ -2095,7 +1088,7 @@ public final class AddressBookProtos { return PARSER; } - public AddressBookProtos.Person getDefaultInstanceForType() { + public com.baeldung.protobuf.AddressBookProtos.Person getDefaultInstanceForType() { return DEFAULT_INSTANCE; } @@ -2108,13 +1101,13 @@ public final class AddressBookProtos { /** * repeated .protobuf.Person people = 1; */ - java.util.List + java.util.List getPeopleList(); /** * repeated .protobuf.Person people = 1; */ - AddressBookProtos.Person getPeople(int index); + com.baeldung.protobuf.AddressBookProtos.Person getPeople(int index); /** * repeated .protobuf.Person people = 1; @@ -2124,13 +1117,13 @@ public final class AddressBookProtos { /** * repeated .protobuf.Person people = 1; */ - java.util.List + java.util.List getPeopleOrBuilderList(); /** * repeated .protobuf.Person people = 1; */ - AddressBookProtos.PersonOrBuilder getPeopleOrBuilder( + com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder getPeopleOrBuilder( int index); } @@ -2181,11 +1174,11 @@ public final class AddressBookProtos { } case 10: { if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { - people_ = new java.util.ArrayList(); + people_ = new java.util.ArrayList(); mutable_bitField0_ |= 0x00000001; } people_.add( - input.readMessage(AddressBookProtos.Person.PARSER, extensionRegistry)); + input.readMessage(com.baeldung.protobuf.AddressBookProtos.Person.PARSER, extensionRegistry)); break; } } @@ -2206,30 +1199,30 @@ public final class AddressBookProtos { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return AddressBookProtos.internal_static_protobuf_AddressBook_descriptor; + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_descriptor; } protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { - return AddressBookProtos.internal_static_protobuf_AddressBook_fieldAccessorTable + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_fieldAccessorTable .ensureFieldAccessorsInitialized( - AddressBookProtos.AddressBook.class, AddressBookProtos.AddressBook.Builder.class); + com.baeldung.protobuf.AddressBookProtos.AddressBook.class, com.baeldung.protobuf.AddressBookProtos.AddressBook.Builder.class); } public static final int PEOPLE_FIELD_NUMBER = 1; - private java.util.List people_; + private java.util.List people_; /** * repeated .protobuf.Person people = 1; */ - public java.util.List getPeopleList() { + public java.util.List getPeopleList() { return people_; } /** * repeated .protobuf.Person people = 1; */ - public java.util.List + public java.util.List getPeopleOrBuilderList() { return people_; } @@ -2244,14 +1237,14 @@ public final class AddressBookProtos { /** * repeated .protobuf.Person people = 1; */ - public AddressBookProtos.Person getPeople(int index) { + public com.baeldung.protobuf.AddressBookProtos.Person getPeople(int index) { return people_.get(index); } /** * repeated .protobuf.Person people = 1; */ - public AddressBookProtos.PersonOrBuilder getPeopleOrBuilder( + public com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder getPeopleOrBuilder( int index) { return people_.get(index); } @@ -2302,10 +1295,10 @@ public final class AddressBookProtos { if (obj == this) { return true; } - if (!(obj instanceof AddressBookProtos.AddressBook)) { + if (!(obj instanceof com.baeldung.protobuf.AddressBookProtos.AddressBook)) { return super.equals(obj); } - AddressBookProtos.AddressBook other = (AddressBookProtos.AddressBook) obj; + com.baeldung.protobuf.AddressBookProtos.AddressBook other = (com.baeldung.protobuf.AddressBookProtos.AddressBook) obj; boolean result = true; result = result && getPeopleList() @@ -2330,38 +1323,38 @@ public final class AddressBookProtos { return hash; } - public static AddressBookProtos.AddressBook parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static AddressBookProtos.AddressBook parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static AddressBookProtos.AddressBook parseFrom(byte[] data) + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static AddressBookProtos.AddressBook parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static AddressBookProtos.AddressBook parseFrom(java.io.InputStream input) + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } - public static AddressBookProtos.AddressBook parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -2369,13 +1362,13 @@ public final class AddressBookProtos { .parseWithIOException(PARSER, input, extensionRegistry); } - public static AddressBookProtos.AddressBook parseDelimitedFrom(java.io.InputStream input) + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseDelimitedWithIOException(PARSER, input); } - public static AddressBookProtos.AddressBook parseDelimitedFrom( + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -2383,14 +1376,14 @@ public final class AddressBookProtos { .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } - public static AddressBookProtos.AddressBook parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } - public static AddressBookProtos.AddressBook parseFrom( + public static com.baeldung.protobuf.AddressBookProtos.AddressBook parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -2406,7 +1399,7 @@ public final class AddressBookProtos { return DEFAULT_INSTANCE.toBuilder(); } - public static Builder newBuilder(AddressBookProtos.AddressBook prototype) { + public static Builder newBuilder(com.baeldung.protobuf.AddressBookProtos.AddressBook prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @@ -2428,20 +1421,20 @@ public final class AddressBookProtos { public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:protobuf.AddressBook) - AddressBookProtos.AddressBookOrBuilder { + com.baeldung.protobuf.AddressBookProtos.AddressBookOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return AddressBookProtos.internal_static_protobuf_AddressBook_descriptor; + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_descriptor; } protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { - return AddressBookProtos.internal_static_protobuf_AddressBook_fieldAccessorTable + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_fieldAccessorTable .ensureFieldAccessorsInitialized( - AddressBookProtos.AddressBook.class, AddressBookProtos.AddressBook.Builder.class); + com.baeldung.protobuf.AddressBookProtos.AddressBook.class, com.baeldung.protobuf.AddressBookProtos.AddressBook.Builder.class); } - // Construct using AddressBookProtos.AddressBook.newBuilder() + // Construct using com.baeldung.protobuf.AddressBookProtos.AddressBook.newBuilder() private Builder() { maybeForceBuilderInitialization(); } @@ -2472,23 +1465,23 @@ public final class AddressBookProtos { public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return AddressBookProtos.internal_static_protobuf_AddressBook_descriptor; + return com.baeldung.protobuf.AddressBookProtos.internal_static_protobuf_AddressBook_descriptor; } - public AddressBookProtos.AddressBook getDefaultInstanceForType() { - return AddressBookProtos.AddressBook.getDefaultInstance(); + public com.baeldung.protobuf.AddressBookProtos.AddressBook getDefaultInstanceForType() { + return com.baeldung.protobuf.AddressBookProtos.AddressBook.getDefaultInstance(); } - public AddressBookProtos.AddressBook build() { - AddressBookProtos.AddressBook result = buildPartial(); + public com.baeldung.protobuf.AddressBookProtos.AddressBook build() { + com.baeldung.protobuf.AddressBookProtos.AddressBook result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } - public AddressBookProtos.AddressBook buildPartial() { - AddressBookProtos.AddressBook result = new AddressBookProtos.AddressBook(this); + public com.baeldung.protobuf.AddressBookProtos.AddressBook buildPartial() { + com.baeldung.protobuf.AddressBookProtos.AddressBook result = new com.baeldung.protobuf.AddressBookProtos.AddressBook(this); int from_bitField0_ = bitField0_; if (peopleBuilder_ == null) { if (((bitField0_ & 0x00000001) == 0x00000001)) { @@ -2536,16 +1529,16 @@ public final class AddressBookProtos { } public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof AddressBookProtos.AddressBook) { - return mergeFrom((AddressBookProtos.AddressBook) other); + if (other instanceof com.baeldung.protobuf.AddressBookProtos.AddressBook) { + return mergeFrom((com.baeldung.protobuf.AddressBookProtos.AddressBook) other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(AddressBookProtos.AddressBook other) { - if (other == AddressBookProtos.AddressBook.getDefaultInstance()) return this; + public Builder mergeFrom(com.baeldung.protobuf.AddressBookProtos.AddressBook other) { + if (other == com.baeldung.protobuf.AddressBookProtos.AddressBook.getDefaultInstance()) return this; if (peopleBuilder_ == null) { if (!other.people_.isEmpty()) { if (people_.isEmpty()) { @@ -2590,11 +1583,11 @@ public final class AddressBookProtos { com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - AddressBookProtos.AddressBook parsedMessage = null; + com.baeldung.protobuf.AddressBookProtos.AddressBook parsedMessage = null; try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (AddressBookProtos.AddressBook) e.getUnfinishedMessage(); + parsedMessage = (com.baeldung.protobuf.AddressBookProtos.AddressBook) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { if (parsedMessage != null) { @@ -2606,23 +1599,23 @@ public final class AddressBookProtos { private int bitField0_; - private java.util.List people_ = + private java.util.List people_ = java.util.Collections.emptyList(); private void ensurePeopleIsMutable() { if (!((bitField0_ & 0x00000001) == 0x00000001)) { - people_ = new java.util.ArrayList(people_); + people_ = new java.util.ArrayList(people_); bitField0_ |= 0x00000001; } } private com.google.protobuf.RepeatedFieldBuilderV3< - AddressBookProtos.Person, AddressBookProtos.Person.Builder, AddressBookProtos.PersonOrBuilder> peopleBuilder_; + com.baeldung.protobuf.AddressBookProtos.Person, com.baeldung.protobuf.AddressBookProtos.Person.Builder, com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder> peopleBuilder_; /** * repeated .protobuf.Person people = 1; */ - public java.util.List getPeopleList() { + public java.util.List getPeopleList() { if (peopleBuilder_ == null) { return java.util.Collections.unmodifiableList(people_); } else { @@ -2644,7 +1637,7 @@ public final class AddressBookProtos { /** * repeated .protobuf.Person people = 1; */ - public AddressBookProtos.Person getPeople(int index) { + public com.baeldung.protobuf.AddressBookProtos.Person getPeople(int index) { if (peopleBuilder_ == null) { return people_.get(index); } else { @@ -2656,7 +1649,7 @@ public final class AddressBookProtos { * repeated .protobuf.Person people = 1; */ public Builder setPeople( - int index, AddressBookProtos.Person value) { + int index, com.baeldung.protobuf.AddressBookProtos.Person value) { if (peopleBuilder_ == null) { if (value == null) { throw new NullPointerException(); @@ -2674,7 +1667,7 @@ public final class AddressBookProtos { * repeated .protobuf.Person people = 1; */ public Builder setPeople( - int index, AddressBookProtos.Person.Builder builderForValue) { + int index, com.baeldung.protobuf.AddressBookProtos.Person.Builder builderForValue) { if (peopleBuilder_ == null) { ensurePeopleIsMutable(); people_.set(index, builderForValue.build()); @@ -2688,7 +1681,7 @@ public final class AddressBookProtos { /** * repeated .protobuf.Person people = 1; */ - public Builder addPeople(AddressBookProtos.Person value) { + public Builder addPeople(com.baeldung.protobuf.AddressBookProtos.Person value) { if (peopleBuilder_ == null) { if (value == null) { throw new NullPointerException(); @@ -2706,7 +1699,7 @@ public final class AddressBookProtos { * repeated .protobuf.Person people = 1; */ public Builder addPeople( - int index, AddressBookProtos.Person value) { + int index, com.baeldung.protobuf.AddressBookProtos.Person value) { if (peopleBuilder_ == null) { if (value == null) { throw new NullPointerException(); @@ -2724,7 +1717,7 @@ public final class AddressBookProtos { * repeated .protobuf.Person people = 1; */ public Builder addPeople( - AddressBookProtos.Person.Builder builderForValue) { + com.baeldung.protobuf.AddressBookProtos.Person.Builder builderForValue) { if (peopleBuilder_ == null) { ensurePeopleIsMutable(); people_.add(builderForValue.build()); @@ -2739,7 +1732,7 @@ public final class AddressBookProtos { * repeated .protobuf.Person people = 1; */ public Builder addPeople( - int index, AddressBookProtos.Person.Builder builderForValue) { + int index, com.baeldung.protobuf.AddressBookProtos.Person.Builder builderForValue) { if (peopleBuilder_ == null) { ensurePeopleIsMutable(); people_.add(index, builderForValue.build()); @@ -2754,7 +1747,7 @@ public final class AddressBookProtos { * repeated .protobuf.Person people = 1; */ public Builder addAllPeople( - java.lang.Iterable values) { + java.lang.Iterable values) { if (peopleBuilder_ == null) { ensurePeopleIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll( @@ -2797,7 +1790,7 @@ public final class AddressBookProtos { /** * repeated .protobuf.Person people = 1; */ - public AddressBookProtos.Person.Builder getPeopleBuilder( + public com.baeldung.protobuf.AddressBookProtos.Person.Builder getPeopleBuilder( int index) { return getPeopleFieldBuilder().getBuilder(index); } @@ -2805,7 +1798,7 @@ public final class AddressBookProtos { /** * repeated .protobuf.Person people = 1; */ - public AddressBookProtos.PersonOrBuilder getPeopleOrBuilder( + public com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder getPeopleOrBuilder( int index) { if (peopleBuilder_ == null) { return people_.get(index); @@ -2817,7 +1810,7 @@ public final class AddressBookProtos { /** * repeated .protobuf.Person people = 1; */ - public java.util.List + public java.util.List getPeopleOrBuilderList() { if (peopleBuilder_ != null) { return peopleBuilder_.getMessageOrBuilderList(); @@ -2829,34 +1822,34 @@ public final class AddressBookProtos { /** * repeated .protobuf.Person people = 1; */ - public AddressBookProtos.Person.Builder addPeopleBuilder() { + public com.baeldung.protobuf.AddressBookProtos.Person.Builder addPeopleBuilder() { return getPeopleFieldBuilder().addBuilder( - AddressBookProtos.Person.getDefaultInstance()); + com.baeldung.protobuf.AddressBookProtos.Person.getDefaultInstance()); } /** * repeated .protobuf.Person people = 1; */ - public AddressBookProtos.Person.Builder addPeopleBuilder( + public com.baeldung.protobuf.AddressBookProtos.Person.Builder addPeopleBuilder( int index) { return getPeopleFieldBuilder().addBuilder( - index, AddressBookProtos.Person.getDefaultInstance()); + index, com.baeldung.protobuf.AddressBookProtos.Person.getDefaultInstance()); } /** * repeated .protobuf.Person people = 1; */ - public java.util.List + public java.util.List getPeopleBuilderList() { return getPeopleFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilderV3< - AddressBookProtos.Person, AddressBookProtos.Person.Builder, AddressBookProtos.PersonOrBuilder> + com.baeldung.protobuf.AddressBookProtos.Person, com.baeldung.protobuf.AddressBookProtos.Person.Builder, com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder> getPeopleFieldBuilder() { if (peopleBuilder_ == null) { peopleBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< - AddressBookProtos.Person, AddressBookProtos.Person.Builder, AddressBookProtos.PersonOrBuilder>( + com.baeldung.protobuf.AddressBookProtos.Person, com.baeldung.protobuf.AddressBookProtos.Person.Builder, com.baeldung.protobuf.AddressBookProtos.PersonOrBuilder>( people_, ((bitField0_ & 0x00000001) == 0x00000001), getParentForChildren(), @@ -2881,13 +1874,13 @@ public final class AddressBookProtos { } // @@protoc_insertion_point(class_scope:protobuf.AddressBook) - private static final AddressBookProtos.AddressBook DEFAULT_INSTANCE; + private static final com.baeldung.protobuf.AddressBookProtos.AddressBook DEFAULT_INSTANCE; static { - DEFAULT_INSTANCE = new AddressBookProtos.AddressBook(); + DEFAULT_INSTANCE = new com.baeldung.protobuf.AddressBookProtos.AddressBook(); } - public static AddressBookProtos.AddressBook getDefaultInstance() { + public static com.baeldung.protobuf.AddressBookProtos.AddressBook getDefaultInstance() { return DEFAULT_INSTANCE; } @@ -2911,7 +1904,7 @@ public final class AddressBookProtos { return PARSER; } - public AddressBookProtos.AddressBook getDefaultInstanceForType() { + public com.baeldung.protobuf.AddressBookProtos.AddressBook getDefaultInstanceForType() { return DEFAULT_INSTANCE; } @@ -2922,11 +1915,6 @@ public final class AddressBookProtos { private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_protobuf_Person_fieldAccessorTable; - private static final com.google.protobuf.Descriptors.Descriptor - internal_static_protobuf_Person_PhoneNumber_descriptor; - private static final - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internal_static_protobuf_Person_PhoneNumber_fieldAccessorTable; private static final com.google.protobuf.Descriptors.Descriptor internal_static_protobuf_AddressBook_descriptor; private static final @@ -2943,15 +1931,11 @@ public final class AddressBookProtos { static { java.lang.String[] descriptorData = { - "\n\020routeguide.proto\022\010protobuf\"\333\001\n\006Person\022" + - "\014\n\004name\030\001 \002(\t\022\n\n\002id\030\002 \002(\005\022\r\n\005email\030\003 \001(\t" + - "\022,\n\006phones\030\004 \003(\0132\034.protobuf.Person.Phone" + - "Number\032M\n\013PhoneNumber\022\016\n\006number\030\001 \002(\t\022.\n" + - "\004type\030\002 \001(\0162\032.protobuf.Person.PhoneType:" + - "\004HOME\"+\n\tPhoneType\022\n\n\006MOBILE\020\000\022\010\n\004HOME\020\001" + - "\022\010\n\004WORK\020\002\"/\n\013AddressBook\022 \n\006people\030\001 \003(" + - "\0132\020.protobuf.PersonB*\n\025com.baeldung.prot" + - "obufB\021AddressBookProtos" + "\n\020routeguide.proto\022\010protobuf\"B\n\006Person\022\014" + + "\n\004name\030\001 \002(\t\022\n\n\002id\030\002 \002(\005\022\r\n\005email\030\003 \001(\t\022" + + "\017\n\007numbers\030\004 \003(\t\"/\n\013AddressBook\022 \n\006peopl" + + "e\030\001 \003(\0132\020.protobuf.PersonB*\n\025com.baeldun" + + "g.protobufB\021AddressBookProtos" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -2970,13 +1954,7 @@ public final class AddressBookProtos { internal_static_protobuf_Person_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_protobuf_Person_descriptor, - new java.lang.String[]{"Name", "Id", "Email", "Phones",}); - internal_static_protobuf_Person_PhoneNumber_descriptor = - internal_static_protobuf_Person_descriptor.getNestedTypes().get(0); - internal_static_protobuf_Person_PhoneNumber_fieldAccessorTable = new - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_protobuf_Person_PhoneNumber_descriptor, - new java.lang.String[]{"Number", "Type",}); + new java.lang.String[]{"Name", "Id", "Email", "Numbers",}); internal_static_protobuf_AddressBook_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_protobuf_AddressBook_fieldAccessorTable = new @@ -2985,5 +1963,5 @@ public final class AddressBookProtos { new java.lang.String[]{"People",}); } - // @@protoc_insertion_point(outer_class_scope) +// @@protoc_insertion_point(outer_class_scope) } \ No newline at end of file diff --git a/protobuffer/src/main/resources/addressbook.proto b/protobuffer/src/main/resources/addressbook.proto index 1c0946b7f7..fe3f9c4174 100644 --- a/protobuffer/src/main/resources/addressbook.proto +++ b/protobuffer/src/main/resources/addressbook.proto @@ -8,18 +8,7 @@ message Person { required int32 id = 2; optional string email = 3; - enum PhoneType { - MOBILE = 0; - HOME = 1; - WORK = 2; - } - - message PhoneNumber { - required string number = 1; - optional PhoneType type = 2 [default = HOME]; - } - - repeated PhoneNumber phones = 4; + repeated string numbers = 4; } message AddressBook { diff --git a/protobuffer/src/test/java/com/baeldung/protobuf/ProtobufTest.java b/protobuffer/src/test/java/com/baeldung/protobuf/ProtobufTest.java index 74447eb4a4..f0d64092f0 100644 --- a/protobuffer/src/test/java/com/baeldung/protobuf/ProtobufTest.java +++ b/protobuffer/src/test/java/com/baeldung/protobuf/ProtobufTest.java @@ -28,24 +28,18 @@ public class ProtobufTest { int id = new Random().nextInt(); String name = "Michael Program"; String number = "01234567890"; - AddressBookProtos.Person.PhoneType type = AddressBookProtos.Person.PhoneType.HOME; AddressBookProtos.Person person = AddressBookProtos.Person.newBuilder() .setId(id) .setName(name) .setEmail(email) - .addPhones( - AddressBookProtos.Person.PhoneNumber.newBuilder() - .setNumber(number) - .setType(type)) + .addNumbers(number) .build(); //then assertEquals(person.getEmail(), email); assertEquals(person.getId(), id); assertEquals(person.getName(), name); - assertEquals(person.getPhones(0).getNumber(), number); - assertEquals(person.getPhones(0).getType(), type); - assertEquals(person.getPhonesList().size(), 1); + assertEquals(person.getNumbers(0), number); } @@ -56,16 +50,12 @@ public class ProtobufTest { int id = new Random().nextInt(); String name = "Michael Program"; String number = "01234567890"; - AddressBookProtos.Person.PhoneType type = AddressBookProtos.Person.PhoneType.HOME; AddressBookProtos.Person person = AddressBookProtos.Person.newBuilder() .setId(id) .setName(name) .setEmail(email) - .addPhones( - AddressBookProtos.Person.PhoneNumber.newBuilder() - .setNumber(number) - .setType(type)) + .addNumbers(number) .build(); //when @@ -82,9 +72,8 @@ public class ProtobufTest { assertEquals(deserialized.getPeople(0).getEmail(), email); assertEquals(deserialized.getPeople(0).getId(), id); assertEquals(deserialized.getPeople(0).getName(), name); - assertEquals(deserialized.getPeople(0).getPhones(0).getNumber(), number); - assertEquals(deserialized.getPeople(0).getPhones(0).getType(), type); - assertEquals(deserialized.getPeople(0).getPhonesList().size(), 1); + assertEquals(deserialized.getPeople(0).getNumbers(0), number); + } } diff --git a/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.0 b/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.0 new file mode 100644 index 0000000000..78d5d3fcf3 --- /dev/null +++ b/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.0 @@ -0,0 +1,24 @@ +https://publicobject.com/helloworld.txt +GET +0 +HTTP/1.1 200 OK +10 +Server: nginx/1.10.0 (Ubuntu) +Date: Thu, 09 Mar 2017 10:17:25 GMT +Content-Type: text/plain +Content-Length: 1759 +Last-Modified: Tue, 27 May 2014 02:35:47 GMT +Connection: keep-alive +ETag: "5383fa03-6df" +Accept-Ranges: bytes +OkHttp-Sent-Millis: 1489054646765 +OkHttp-Received-Millis: 1489054646966 + +TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +4 +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 +0 +TLSv1.2 diff --git a/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.1 b/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.1 new file mode 100644 index 0000000000..83db2312f0 --- /dev/null +++ b/spring-rest/src/test/resources/cache/2d9345a30d2cc31bb3091d70a8ef6c18.1 @@ -0,0 +1,39 @@ + + \\ // + \\ .ooo. // + .@@@@@@@@@. + :@@@@@@@@@@@@@: + :@@. '@@@@@' .@@: + @@@@@@@@@@@@@@@@@ + @@@@@@@@@@@@@@@@@ + + :@@ :@@@@@@@@@@@@@@@@@. @@: + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@ '@@@@@@@@@@@@@@@@@, @@@ + @@@@@@@@@@@@@@@@@ + '@@@@@@@@@@@@@@@' + @@@@ @@@@ + @@@@ @@@@ + @@@@ @@@@ + '@@' '@@' + + :@@@. + .@@@@@@@: +@@ `@@ @@` @@ @@ + .@@@@'@@@@: +@@ `@@ @@` @@ @@ + @@@ @@@ +@@ `@@ @@` @@ @@ + .@@ @@: +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@;@@@@@ + @@@ @@@ +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@@@@@@@@ + @@@ @@@ +@@ @@@ `@@@@@@@@@@` @@ @@ @@@ :@@ + @@@ @@@ +@@@@@ `@@@@@@@@@@` @@ @@ @@# @@+ + @@@ @@@ +@@@@@+ `@@ @@` @@ @@ @@: @@# + @@: .@@` +@@@+@@ `@@ @@` @@ @@ @@# @@+ + @@@. .@@@ +@@ @@@ `@@ @@` @@ @@ @@@ ,@@ + @@@@@@@@@ +@@ @@@ `@@ @@` @@@@ @@@@ @@@@#@@@@ + @@@@@@@ +@@ #@@ `@@ @@` @@@@: @@@@: @@'@@@@@ + @@: + @@: + @@: diff --git a/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.0 b/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.0 new file mode 100644 index 0000000000..82c93f0a86 --- /dev/null +++ b/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.0 @@ -0,0 +1,13 @@ +http://publicobject.com/helloworld.txt +GET +0 +HTTP/1.1 301 Moved Permanently +8 +Server: nginx/1.10.0 (Ubuntu) +Date: Thu, 09 Mar 2017 10:17:25 GMT +Content-Type: text/html +Content-Length: 194 +Connection: keep-alive +Location: https://publicobject.com/helloworld.txt +OkHttp-Sent-Millis: 1489054646977 +OkHttp-Received-Millis: 1489054647185 diff --git a/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.1 b/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.1 new file mode 100644 index 0000000000..acf72eabe3 --- /dev/null +++ b/spring-rest/src/test/resources/cache/4b217e04ba52215f3a6b64d28f6729c6.1 @@ -0,0 +1,7 @@ + +301 Moved Permanently + +

301 Moved Permanently

+
nginx/1.10.0 (Ubuntu)
+ + diff --git a/spring-rest/src/test/resources/cache/journal b/spring-rest/src/test/resources/cache/journal new file mode 100644 index 0000000000..44b709c179 --- /dev/null +++ b/spring-rest/src/test/resources/cache/journal @@ -0,0 +1,12 @@ +libcore.io.DiskLruCache +1 +201105 +2 + +DIRTY 4b217e04ba52215f3a6b64d28f6729c6 +CLEAN 4b217e04ba52215f3a6b64d28f6729c6 333 194 +DIRTY 2d9345a30d2cc31bb3091d70a8ef6c18 +CLEAN 2d9345a30d2cc31bb3091d70a8ef6c18 7618 1759 +READ 4b217e04ba52215f3a6b64d28f6729c6 +DIRTY 4b217e04ba52215f3a6b64d28f6729c6 +CLEAN 4b217e04ba52215f3a6b64d28f6729c6 333 194 From 9b48f77c8d1f48aef50f6936b24d0b94f607b550 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 25 Mar 2017 11:47:14 +0100 Subject: [PATCH 049/102] ContactNumberValidator refactor (#1491) * ContactNumberValidator refactor * Refactor controllers --- .../customvalidator/ContactNumberValidator.java | 5 +---- .../com/baeldung/web/controller/UserController.java | 7 ++++--- .../web/controller/ValidatedPhoneController.java | 13 ++++++------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java index 713b7429cf..dea6b9099b 100644 --- a/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java +++ b/spring-mvc-java/src/main/java/com/baeldung/customvalidator/ContactNumberValidator.java @@ -11,10 +11,7 @@ public class ContactNumberValidator implements ConstraintValidator 8) && (contactField.length() < 14); + return contactField != null && contactField.matches("[0-9]+") && (contactField.length() > 8) && (contactField.length() < 14); } } diff --git a/spring-mvc-java/src/main/java/com/baeldung/web/controller/UserController.java b/spring-mvc-java/src/main/java/com/baeldung/web/controller/UserController.java index fda159f204..e72ec06830 100644 --- a/spring-mvc-java/src/main/java/com/baeldung/web/controller/UserController.java +++ b/spring-mvc-java/src/main/java/com/baeldung/web/controller/UserController.java @@ -3,15 +3,16 @@ package com.baeldung.web.controller; import com.baeldung.model.User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/") public class UserController { - @RequestMapping(value = "/", method = RequestMethod.GET) + @GetMapping("/") public String showForm(final Model model) { final User user = new User(); user.setFirstname("John"); @@ -21,7 +22,7 @@ public class UserController { return "index"; } - @RequestMapping(value = "/processForm", method = RequestMethod.POST) + @PostMapping("/processForm") public String processForm(@ModelAttribute(value = "user") final User user, final Model model) { // Insert User into DB model.addAttribute("name", user.getFirstname() + " " + user.getLastname()); diff --git a/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java b/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java index 54b0e19e60..8c6cfcd3be 100644 --- a/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java +++ b/spring-mvc-java/src/main/java/com/baeldung/web/controller/ValidatedPhoneController.java @@ -1,27 +1,26 @@ package com.baeldung.web.controller; -import javax.validation.Valid; - +import com.baeldung.model.ValidatedPhone; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import com.baeldung.model.ValidatedPhone; +import javax.validation.Valid; @Controller @EnableWebMvc public class ValidatedPhoneController { - @RequestMapping(value = "/validatePhone", method = RequestMethod.GET) + @GetMapping("/validatePhone") public String loadFormPage(Model m) { m.addAttribute("validatedPhone", new ValidatedPhone()); return "phoneHome"; } - @RequestMapping(value = "/addValidatePhone", method = RequestMethod.POST) + @PostMapping("/addValidatePhone") public String submitForm(@Valid ValidatedPhone validatedPhone, BindingResult result, Model m) { if (result.hasErrors()) { return "phoneHome"; From e62f8fa3d8ce4e8671c59065401cccc58384f016 Mon Sep 17 00:00:00 2001 From: buddhini81 Date: Sun, 26 Mar 2017 00:20:46 +0530 Subject: [PATCH 050/102] Add javaEE annotation sample project (#1481) * Delete AccountServlet.java * Delete BankAppServletContextListener.java * Delete LogInFilter.java * Delete UploadCustomerDocumentsServlet.java * Delete index.jsp * Delete login.jsp * Delete upload.jsp * Delete web.xml * Create javaeeannotations * Delete javaeeannotations * commit javaEE annotations project --- .../JavaEEAnnotationsSample/pom.xml | 57 ++++++++++++++++++ .../javaeeannotations}/AccountServlet.java | 13 ++--- .../BankAppServletContextListener.java | 34 +++++------ .../DepositRequestListener.java | 20 +++++++ .../javaeeannotations}/LogInFilter.java | 8 +-- .../UploadCustomerDocumentsServlet.java | 58 +++++++++---------- .../src/main}/webapp/index.jsp | 30 +++++----- .../src/main}/webapp/login.jsp | 22 +++---- .../src/main}/webapp/upload.jsp | 30 +++++----- .../src/main/webapp}/web.xml | 20 +++---- 10 files changed, 183 insertions(+), 109 deletions(-) create mode 100644 jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/pom.xml rename jee7/src/main/java/com/baeldung/javaeeannotations/{ => JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations}/AccountServlet.java (85%) rename jee7/src/main/java/com/baeldung/javaeeannotations/{ => JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations}/BankAppServletContextListener.java (96%) create mode 100644 jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/DepositRequestListener.java rename jee7/src/main/java/com/baeldung/javaeeannotations/{ => JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations}/LogInFilter.java (96%) rename jee7/src/main/java/com/baeldung/javaeeannotations/{ => JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations}/UploadCustomerDocumentsServlet.java (96%) rename jee7/src/main/{ => java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main}/webapp/index.jsp (79%) rename jee7/src/main/{ => java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main}/webapp/login.jsp (97%) rename jee7/src/main/{ => java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main}/webapp/upload.jsp (97%) rename jee7/src/main/{webapp/WEB-INF => java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp}/web.xml (97%) diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/pom.xml b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/pom.xml new file mode 100644 index 0000000000..b4bb243559 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + com.baeldung.javaeeannotations + JavaEEAnnotationsSample + 0.0.1-SNAPSHOT + war + JavaEEAnnotationsSample + JavaEEAnnotationsSample + + + + + javax.annotation + javax.annotation-api + 1.3 + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + javax.servlet.jsp + jsp-api + 2.1 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.2 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-war-plugin + 2.4 + + src/main/webapp + SpringFieldConstructorInjection + false + + + + + JavaEEAnnotationsSample + + \ No newline at end of file diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java similarity index 85% rename from jee7/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java index e3f1667595..e24eb307bb 100644 --- a/jee7/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/AccountServlet.java @@ -5,9 +5,6 @@ import java.io.PrintWriter; import javax.servlet.ServletConfig; import javax.servlet.ServletException; -import javax.servlet.annotation.HttpConstraint; -import javax.servlet.annotation.HttpMethodConstraint; -import javax.servlet.annotation.ServletSecurity; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; @@ -19,20 +16,20 @@ import javax.servlet.http.HttpServletResponse; urlPatterns = {"/account", "/bankAccount" }, initParams = { @WebInitParam(name = "type", value = "savings") } ) -@ServletSecurity( +/*@ServletSecurity( value = @HttpConstraint(rolesAllowed = {"admin"}), httpMethodConstraints = {@HttpMethodConstraint(value = "POST", rolesAllowed = {"admin"})} - ) + )*/ public class AccountServlet extends javax.servlet.http.HttpServlet { String accountType = null; - @Override public void init(ServletConfig config) throws ServletException { accountType = config.getInitParameter("type"); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + PrintWriter writer = response.getWriter(); writer.println("Hello, I am an AccountServlet!"); writer.flush(); @@ -49,8 +46,8 @@ public class AccountServlet extends javax.servlet.http.HttpServlet { PrintWriter writer = response.getWriter(); writer.println(" Balance of " + accountType + " account is: " + - accountBalance + "
This account bares an interest rate of " + interestRate + - " % "); + accountBalance + "
This account bares an interest rate of " + interestRate + + " % "); writer.flush(); } diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java similarity index 96% rename from jee7/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java index 6b43dd8a84..ee1b624cd1 100644 --- a/jee7/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/BankAppServletContextListener.java @@ -1,17 +1,17 @@ -package com.baeldung.javaeeannotations; - -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.servlet.annotation.WebListener; - -@WebListener -public class BankAppServletContextListener implements ServletContextListener { - - public void contextInitialized(ServletContextEvent sce) { - sce.getServletContext().setAttribute("ATTR_DEFAULT_LANGUAGE", "english"); - } - - public void contextDestroyed(ServletContextEvent sce) { - System.out.println("CONTEXT DESTROYED"); - } -} +package com.baeldung.javaeeannotations; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +@WebListener +public class BankAppServletContextListener implements ServletContextListener { + + public void contextInitialized(ServletContextEvent sce) { + sce.getServletContext().setAttribute("ATTR_DEFAULT_LANGUAGE", "english"); + } + + public void contextDestroyed(ServletContextEvent sce) { + System.out.println("CONTEXT DESTROYED"); + } +} diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/DepositRequestListener.java b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/DepositRequestListener.java new file mode 100644 index 0000000000..f361c84b97 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/DepositRequestListener.java @@ -0,0 +1,20 @@ +package com.baeldung.javaeeannotations; + +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.annotation.WebListener; +import javax.servlet.http.HttpServletRequest; + +@WebListener +public class DepositRequestListener implements ServletRequestListener { + + public void requestDestroyed(ServletRequestEvent event) { + + } + + public void requestInitialized(ServletRequestEvent evet) { + HttpServletRequest req = (HttpServletRequest)evet.getServletRequest(); + req.setAttribute("interest", new Double(10)); + } + +} diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/LogInFilter.java b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/LogInFilter.java similarity index 96% rename from jee7/src/main/java/com/baeldung/javaeeannotations/LogInFilter.java rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/LogInFilter.java index 4e4aef2672..5ee420f6a2 100644 --- a/jee7/src/main/java/com/baeldung/javaeeannotations/LogInFilter.java +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/LogInFilter.java @@ -11,13 +11,13 @@ import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -@WebFilter( +/*@WebFilter( urlPatterns = "/bankAccount/*", filterName = "LogInFilter", description = "Filter all account transaction URLs" - ) + )*/ public class LogInFilter implements javax.servlet.Filter { - @Override + public void init(FilterConfig filterConfig) throws ServletException { } @@ -29,8 +29,8 @@ public class LogInFilter implements javax.servlet.Filter { chain.doFilter(request, response); } - @Override public void destroy() { + } } diff --git a/jee7/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java similarity index 96% rename from jee7/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java index 8a6c709b81..3a139ad7cc 100644 --- a/jee7/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/java/com/baeldung/javaeeannotations/UploadCustomerDocumentsServlet.java @@ -1,29 +1,29 @@ -package com.baeldung.javaeeannotations; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.annotation.MultipartConfig; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.Part; - -@WebServlet(urlPatterns = { "/uploadCustDocs" }) -@MultipartConfig( - fileSizeThreshold = 1024 * 1024 * 20, - maxFileSize = 1024 * 1024 * 20, - maxRequestSize = 1024 * 1024 * 25, - location = "D:/custDocs" - ) -public class UploadCustomerDocumentsServlet extends HttpServlet { - - protected void doPost( - HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - for (Part part : request.getParts()) { - part.write("myFile"); - } - } - -} +package com.baeldung.javaeeannotations; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; + +@WebServlet(urlPatterns = { "/uploadCustDocs" }) +@MultipartConfig( + fileSizeThreshold = 1024 * 1024 * 20, + maxFileSize = 1024 * 1024 * 20, + maxRequestSize = 1024 * 1024 * 25, + location = "D:/custDocs" + ) +public class UploadCustomerDocumentsServlet extends HttpServlet { + + protected void doPost( + HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + for (Part part : request.getParts()) { + part.write("myFile"); + } + } + +} diff --git a/jee7/src/main/webapp/index.jsp b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/index.jsp similarity index 79% rename from jee7/src/main/webapp/index.jsp rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/index.jsp index 0c389ef5b1..c49dec859e 100644 --- a/jee7/src/main/webapp/index.jsp +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/index.jsp @@ -1,16 +1,16 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - - -My Account - - -
- Width: -    - -
- +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + +My Account + + +
+ Amount: +    + +
+ \ No newline at end of file diff --git a/jee7/src/main/webapp/login.jsp b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/login.jsp similarity index 97% rename from jee7/src/main/webapp/login.jsp rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/login.jsp index 885df0c3d9..6892cb0420 100644 --- a/jee7/src/main/webapp/login.jsp +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/login.jsp @@ -1,12 +1,12 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - - -Login - - -Login Here... - +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + +Login + + +Login Here... + \ No newline at end of file diff --git a/jee7/src/main/webapp/upload.jsp b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/upload.jsp similarity index 97% rename from jee7/src/main/webapp/upload.jsp rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/upload.jsp index 020483b99f..3601322ef0 100644 --- a/jee7/src/main/webapp/upload.jsp +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/upload.jsp @@ -1,16 +1,16 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - - -Insert title here - - -
- -
- -
- +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + +Insert title here + + +
+ +
+ +
+ \ No newline at end of file diff --git a/jee7/src/main/webapp/WEB-INF/web.xml b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/web.xml similarity index 97% rename from jee7/src/main/webapp/WEB-INF/web.xml rename to jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/web.xml index 0a3d84d2d4..f01eb341e4 100644 --- a/jee7/src/main/webapp/WEB-INF/web.xml +++ b/jee7/src/main/java/com/baeldung/javaeeannotations/JavaEEAnnotationsSample/src/main/webapp/web.xml @@ -1,11 +1,11 @@ - - - - BASIC - default - - + + + + BASIC + default + + \ No newline at end of file From de29cd56327f0b532d587053f1c997e07d6c88c6 Mon Sep 17 00:00:00 2001 From: baljeet20 Date: Sun, 26 Mar 2017 01:53:04 +0530 Subject: [PATCH 051/102] BAEL-750 Added Java configuration (#1494) * BAEL-750 A guide to gemfire with spring data * BAEL-750 A guide to gemfire with spring data * BAEL-750 A guide to gemfire with spring data --- pom.xml | 3 +- spring-data-gemfire/pom.xml | 97 ++++++++++++++++++ .../gemfire/function/FunctionExecution.java | 16 +++ .../data/gemfire/function/FunctionImpl.java | 18 ++++ .../function/GemfireConfiguration.java | 65 ++++++++++++ .../spring/data/gemfire/model/Employee.java | 41 ++++++++ .../repository/EmployeeRepository.java | 17 ++++ .../main/resources/application-context.xml | 20 ++++ .../repository/EmployeeRepositoryTest.java | 98 +++++++++++++++++++ 9 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 spring-data-gemfire/pom.xml create mode 100644 spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionExecution.java create mode 100644 spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionImpl.java create mode 100644 spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/GemfireConfiguration.java create mode 100644 spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/model/Employee.java create mode 100644 spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepository.java create mode 100644 spring-data-gemfire/src/main/resources/application-context.xml create mode 100644 spring-data-gemfire/src/test/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepositoryTest.java diff --git a/pom.xml b/pom.xml index 5c85e94546..e0295e5c72 100644 --- a/pom.xml +++ b/pom.xml @@ -211,7 +211,8 @@ rabbitmq vertx - + spring-data-gemfire + diff --git a/spring-data-gemfire/pom.xml b/spring-data-gemfire/pom.xml new file mode 100644 index 0000000000..f387b04651 --- /dev/null +++ b/spring-data-gemfire/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + com.baeldung + spring-data-gemfire + 1.0.0-SNAPSHOT + jar + + 2.19.1 + 1.7.4.RELEASE + 7.0.1 + 1.0 + 4.12 + 1.3 + 4.3.0.RELEASE + 4.3.0.RELEASE + + + + + org.springframework.data + spring-data-gemfire + ${spring-data-gemfire-version} + + + + com.gemstone.gemfire + gemfire + ${gemfire-version} + + + + com.google.collections + google-collections + ${google-collections-version} + + + + junit + junit + ${junit-version} + test + + + org.hamcrest + hamcrest-core + + + + + org.hamcrest + hamcrest-library + ${hamcrest-library-version} + test + + + org.springframework + spring-context + ${spring-context-version} + + + org.springframework + spring-test + ${spring-test-version} + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + + + + + spring-snapshots + Spring Snapshots + http://repo.spring.io/libs-snapshot + + true + + + + gemstone + http://dist.gemstone.com.s3.amazonaws.com/maven/release/ + + + + \ No newline at end of file diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionExecution.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionExecution.java new file mode 100644 index 0000000000..a3ddf32414 --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionExecution.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.data.gemfire.function; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.gemfire.function.annotation.FunctionId; +import org.springframework.data.gemfire.function.annotation.OnRegion; +import org.springframework.data.gemfire.function.annotation.RegionData; + +import java.util.Map; +import java.util.Set; + +@OnRegion(region="employee") +public interface FunctionExecution { + @FunctionId("greeting") + public void execute(String message); + +} diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionImpl.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionImpl.java new file mode 100644 index 0000000000..9fc6a2155d --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/FunctionImpl.java @@ -0,0 +1,18 @@ +package com.baeldung.spring.data.gemfire.function; + +import org.springframework.data.gemfire.function.annotation.GemfireFunction; +import org.springframework.stereotype.Component; + +@Component +public class FunctionImpl { + + @GemfireFunction + public void greeting(String message){ + System.out.println("Message "+message); + } + + @GemfireFunction + public String sayHello(String message){ + return "Hello "+message; + } +} diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/GemfireConfiguration.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/GemfireConfiguration.java new file mode 100644 index 0000000000..0049f82ddc --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/function/GemfireConfiguration.java @@ -0,0 +1,65 @@ +package com.baeldung.spring.data.gemfire.function; + +import com.baeldung.spring.data.gemfire.model.Employee; +import com.baeldung.spring.data.gemfire.repository.EmployeeRepository; +import com.gemstone.gemfire.cache.DataPolicy; +import com.gemstone.gemfire.cache.GemFireCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.gemfire.CacheFactoryBean; +import org.springframework.data.gemfire.LocalRegionFactoryBean; +import org.springframework.data.gemfire.function.config.EnableGemfireFunctionExecutions; +import org.springframework.data.gemfire.function.config.EnableGemfireFunctions; +import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories; +import java.util.Properties; + +@Configuration +@ComponentScan +@EnableGemfireRepositories(basePackages = "com.baeldung.spring.data.gemfire.repository") +@EnableGemfireFunctions +@EnableGemfireFunctionExecutions(basePackages = "com.baeldung.spring.data.gemfire.function") +public class GemfireConfiguration { + + @Autowired + EmployeeRepository employeeRepository; + + @Autowired + FunctionExecution functionExecution; + + + @Bean + Properties gemfireProperties() { + Properties gemfireProperties = new Properties(); + gemfireProperties.setProperty("name", "SpringDataGemFireApplication"); + gemfireProperties.setProperty("mcast-port", "0"); + gemfireProperties.setProperty("log-level", "config"); + return gemfireProperties; + } + + @Bean + @Autowired + CacheFactoryBean gemfireCache() { + CacheFactoryBean gemfireCache = new CacheFactoryBean(); + gemfireCache.setClose(true); + gemfireCache.setProperties(gemfireProperties()); + return gemfireCache; + } + + + @Bean(name="employee") + @Autowired + LocalRegionFactoryBean getEmployee(final GemFireCache cache) { + LocalRegionFactoryBean employeeRegion = new LocalRegionFactoryBean(); + employeeRegion.setCache(cache); + employeeRegion.setClose(false); + employeeRegion.setName("employee"); + employeeRegion.setPersistent(false); + employeeRegion.setDataPolicy(DataPolicy.PRELOADED); + return employeeRegion; + } + + +} + diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/model/Employee.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/model/Employee.java new file mode 100644 index 0000000000..9c29b28dca --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/model/Employee.java @@ -0,0 +1,41 @@ +package com.baeldung.spring.data.gemfire.model; + +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.gemfire.mapping.Region; + + +@Region("employee") +public class Employee { + + @Id + public String name; + public double salary; + + @PersistenceConstructor + public Employee(String name, double salary) { + this.name = name; + this.salary = salary; + } + + @Override + public String toString() { + return name + " is " + salary + " years old."; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getSalary() { + return salary; + } + + public void setSalary(int salary) { + this.salary = salary; + } +} \ No newline at end of file diff --git a/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepository.java b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepository.java new file mode 100644 index 0000000000..30799a2b99 --- /dev/null +++ b/spring-data-gemfire/src/main/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepository.java @@ -0,0 +1,17 @@ +package com.baeldung.spring.data.gemfire.repository; + +import com.baeldung.spring.data.gemfire.model.Employee; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface EmployeeRepository extends CrudRepository { + + Employee findByName(String name); + + Iterable findBySalaryGreaterThan(double salary); + + Iterable findBySalaryLessThan(double salary); + + Iterable findBySalaryGreaterThanAndSalaryLessThan(double salary1, double salary2); +} diff --git a/spring-data-gemfire/src/main/resources/application-context.xml b/spring-data-gemfire/src/main/resources/application-context.xml new file mode 100644 index 0000000000..e618719b30 --- /dev/null +++ b/spring-data-gemfire/src/main/resources/application-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-data-gemfire/src/test/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepositoryTest.java b/spring-data-gemfire/src/test/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepositoryTest.java new file mode 100644 index 0000000000..8714ad2d81 --- /dev/null +++ b/spring-data-gemfire/src/test/java/com/baeldung/spring/data/gemfire/repository/EmployeeRepositoryTest.java @@ -0,0 +1,98 @@ +package com.baeldung.spring.data.gemfire.repository; + +import com.baeldung.spring.data.gemfire.function.FunctionExecution; +import com.baeldung.spring.data.gemfire.function.GemfireConfiguration; +import com.baeldung.spring.data.gemfire.model.Employee; +import com.google.common.collect.Lists; +import org.junit.After; +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 org.springframework.test.context.support.AnnotationConfigContextLoader; + +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=GemfireConfiguration.class, loader=AnnotationConfigContextLoader.class) +public class EmployeeRepositoryTest { + + @Autowired + EmployeeRepository employeeRepository; + + @Autowired + FunctionExecution execution; + + @Test + public void whenEmployeeIsSaved_ThenAbleToRead(){ + Employee employee=new Employee("John Davidson",4550.00); + employeeRepository.save(employee); + + List employees= Lists.newArrayList(employeeRepository.findAll()); + + assertEquals(1, employees.size()); + } + + @Test + public void whenSalaryGreaterThan_ThenEmployeeFound(){ + + Employee employee=new Employee("John Davidson",4550.00); + Employee employee1=new Employee("Adam Davidson",3500.00); + Employee employee2=new Employee("Chris Davidson",5600.00); + + employeeRepository.save(employee); + employeeRepository.save(employee1); + employeeRepository.save(employee2); + + List employees= Lists.newArrayList(employeeRepository.findBySalaryGreaterThan(4000.00)); + + assertEquals(2,employees.size()); + + } + + @Test + public void whenSalaryLessThan_ThenEmployeeFound(){ + + Employee employee=new Employee("John Davidson",4550.00); + Employee employee1=new Employee("Adam Davidson",3500.00); + Employee employee2=new Employee("Chris Davidson",5600.00); + + employeeRepository.save(employee); + employeeRepository.save(employee1); + employeeRepository.save(employee2); + + List employees= Lists.newArrayList(employeeRepository.findBySalaryLessThan(4000)); + + assertEquals(1,employees.size()); + + } + @Test + public void whenSalaryBetween_ThenEmployeeFound(){ + + Employee employee=new Employee("John Davidson",4550.00); + Employee employee1=new Employee("Adam Davidson",3500.00); + Employee employee2=new Employee("Chris Davidson",5600.00); + + employeeRepository.save(employee); + employeeRepository.save(employee1); + employeeRepository.save(employee2); + + List employees= Lists.newArrayList(employeeRepository.findBySalaryGreaterThanAndSalaryLessThan(3500,5000)); + + assertEquals(1,employees.size()); + + } + + @Test + public void whenFunctionExecutedFromClient_ThenFunctionExecutedOnServer(){ + execution.execute("Hello World"); + } + + @After + public void cleanup(){ + employeeRepository.deleteAll(); + } +} From 052c149b0edb156eba9244d708d5b3a958d15e4f Mon Sep 17 00:00:00 2001 From: lor6 Date: Sat, 25 Mar 2017 23:12:17 +0200 Subject: [PATCH 052/102] in memory test (#1476) * in memory test * update dependencies * rename db * remove create db * update version --- spring-jpa/pom.xml | 8 +-- .../org/baeldung/config/StudentJpaConfig.java | 68 +++++++++++++++++++ .../persistence/dao/StudentRepository.java | 8 +++ .../baeldung/persistence/model/Student.java | 38 +++++++++++ .../resources/persistence-student.properties | 11 +++ .../repository/InMemoryDBTest.java | 38 +++++++++++ .../resources/persistence-student.properties | 9 +++ 7 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java create mode 100644 spring-jpa/src/main/java/org/baeldung/persistence/dao/StudentRepository.java create mode 100644 spring-jpa/src/main/java/org/baeldung/persistence/model/Student.java create mode 100644 spring-jpa/src/main/resources/persistence-student.properties create mode 100644 spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBTest.java create mode 100644 spring-jpa/src/test/resources/persistence-student.properties diff --git a/spring-jpa/pom.xml b/spring-jpa/pom.xml index 2229d64abe..3ca373acea 100644 --- a/spring-jpa/pom.xml +++ b/spring-jpa/pom.xml @@ -196,14 +196,14 @@ - 4.3.4.RELEASE + 4.3.7.RELEASE 3.21.0-GA - 5.2.5.Final + 5.2.9.Final 5.1.40 - 1.10.5.RELEASE - 1.4.193 + 1.11.1.RELEASE + 1.4.194 1.2 diff --git a/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java b/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java new file mode 100644 index 0000000000..a40f180a62 --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/config/StudentJpaConfig.java @@ -0,0 +1,68 @@ +package org.baeldung.config; + +import java.util.Properties; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration +@EnableJpaRepositories(basePackages = "org.baeldung.persistence.dao") +@PropertySource("persistence-student.properties") +@EnableTransactionManagement +public class StudentJpaConfig { + + @Autowired + private Environment env; + + @Bean + public DataSource dataSource() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); + dataSource.setUrl(env.getProperty("jdbc.url")); + dataSource.setUsername(env.getProperty("jdbc.user")); + dataSource.setPassword(env.getProperty("jdbc.pass")); + + return dataSource; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan(new String[] { "org.baeldung.persistence.model" }); + em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + em.setJpaProperties(additionalProperties()); + return em; + } + + @Bean + JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory); + return transactionManager; + } + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + + hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); + hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); + hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql")); + hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache")); + hibernateProperties.setProperty("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache")); + + return hibernateProperties; + } +} diff --git a/spring-jpa/src/main/java/org/baeldung/persistence/dao/StudentRepository.java b/spring-jpa/src/main/java/org/baeldung/persistence/dao/StudentRepository.java new file mode 100644 index 0000000000..af484b442c --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/persistence/dao/StudentRepository.java @@ -0,0 +1,8 @@ +package org.baeldung.persistence.dao; + +import org.springframework.data.jpa.repository.JpaRepository; + +import org.baeldung.persistence.model.Student; + +public interface StudentRepository extends JpaRepository { +} diff --git a/spring-jpa/src/main/java/org/baeldung/persistence/model/Student.java b/spring-jpa/src/main/java/org/baeldung/persistence/model/Student.java new file mode 100644 index 0000000000..437eeac5bb --- /dev/null +++ b/spring-jpa/src/main/java/org/baeldung/persistence/model/Student.java @@ -0,0 +1,38 @@ +package org.baeldung.persistence.model; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class Student { + + @Id + private long id; + private String name; + + public Student() { + } + + public Student(long id, String name) { + super(); + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-jpa/src/main/resources/persistence-student.properties b/spring-jpa/src/main/resources/persistence-student.properties new file mode 100644 index 0000000000..4e80b836d2 --- /dev/null +++ b/spring-jpa/src/main/resources/persistence-student.properties @@ -0,0 +1,11 @@ +jdbc.driverClassName=com.mysql.jdbc.Driver +jdbc.url=jdbc:mysql://localhost:3306/myDb +jdbc.user=tutorialuser +jdbc.pass=tutorialpass + +hibernate.dialect=org.hibernate.dialect.MySQL5Dialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create-drop + +hibernate.cache.use_second_level_cache=false +hibernate.cache.use_query_cache=false \ No newline at end of file diff --git a/spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBTest.java b/spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBTest.java new file mode 100644 index 0000000000..2c40c5b117 --- /dev/null +++ b/spring-jpa/src/test/java/org/baeldung/persistence/repository/InMemoryDBTest.java @@ -0,0 +1,38 @@ +package org.baeldung.persistence.repository; + +import javax.annotation.Resource; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.transaction.annotation.Transactional; + +import org.baeldung.config.StudentJpaConfig; +import org.baeldung.persistence.model.Student; +import org.baeldung.persistence.dao.StudentRepository; + +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { StudentJpaConfig.class }, loader = AnnotationConfigContextLoader.class) +@Transactional +public class InMemoryDBTest { + + @Resource + private StudentRepository studentRepository; + + private static final long ID = 1; + private static final String NAME="john"; + + @Test + public void givenStudent_whenSave_thenGetOk(){ + Student student = new Student(ID, NAME); + studentRepository.save(student); + + Student student2 = studentRepository.findOne(ID); + assertEquals("name incorrect", NAME, student2.getName()); + } + +} diff --git a/spring-jpa/src/test/resources/persistence-student.properties b/spring-jpa/src/test/resources/persistence-student.properties new file mode 100644 index 0000000000..21dcd5b4a0 --- /dev/null +++ b/spring-jpa/src/test/resources/persistence-student.properties @@ -0,0 +1,9 @@ +jdbc.driverClassName=org.h2.Driver +jdbc.url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1 + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create-drop + +hibernate.cache.use_second_level_cache=false +hibernate.cache.use_query_cache=false \ No newline at end of file From 52eb7c2bc42081b589b9a566f594fe3817acfaa5 Mon Sep 17 00:00:00 2001 From: Tomasz Lelek Date: Sun, 26 Mar 2017 15:56:20 +0200 Subject: [PATCH 053/102] Bael 738 (#1504) * BAEL-724 code for put/patch article * BAEL-724 fix typo * BAEL-728 more generic patch approach * BAEL-738 change url of PUT method * fix route confict --- .../repository/HeavyResourceRepository.java | 8 ++++++++ .../web/controller/HeavyResourceController.java | 14 +++++++------- .../controller/HeavyResourceControllerTest.java | 6 +++--- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java b/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java index 6de9e75450..5556d85f65 100644 --- a/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java +++ b/spring-rest/src/main/java/org/baeldung/repository/HeavyResourceRepository.java @@ -2,6 +2,7 @@ package org.baeldung.repository; import org.baeldung.web.dto.HeavyResource; import org.baeldung.web.dto.HeavyResourceAddressOnly; +import org.baeldung.web.dto.HeavyResourceAddressPartialUpdate; import java.util.Map; @@ -17,4 +18,11 @@ public class HeavyResourceRepository { public void save(Map updates, String id) { } + + public void save(HeavyResource heavyResource, String id) { + + } + public void save(HeavyResourceAddressOnly partialUpdate, String id) { + + } } diff --git a/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java b/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java index f2c4ffaa51..55616a6446 100644 --- a/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java +++ b/spring-rest/src/main/java/org/baeldung/web/controller/HeavyResourceController.java @@ -15,19 +15,19 @@ public class HeavyResourceController { private HeavyResourceRepository heavyResourceRepository = new HeavyResourceRepository(); - @RequestMapping(value = "/heavy", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity saveResource(@RequestBody HeavyResource heavyResource) { - heavyResourceRepository.save(heavyResource); + @RequestMapping(value = "/heavyresource/{id}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity saveResource(@RequestBody HeavyResource heavyResource, @PathVariable("id") String id) { + heavyResourceRepository.save(heavyResource, id); return ResponseEntity.ok("resource saved"); } - @RequestMapping(value = "/heavy", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity partialUpdateName(@RequestBody HeavyResourceAddressOnly partialUpdate) { - heavyResourceRepository.save(partialUpdate); + @RequestMapping(value = "/heavyresource/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity partialUpdateName(@RequestBody HeavyResourceAddressOnly partialUpdate, @PathVariable("id") String id) { + heavyResourceRepository.save(partialUpdate, id); return ResponseEntity.ok("resource address updated"); } - @RequestMapping(value = "/heavy/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(value = "/heavyresource2/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity partialUpdateGeneric(@RequestBody Map updates, @PathVariable("id") String id) { heavyResourceRepository.save(updates, id); diff --git a/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java b/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java index e283c5f5f6..a1f9e71bec 100644 --- a/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java +++ b/spring-rest/src/test/java/org/baeldung/web/controller/HeavyResourceControllerTest.java @@ -42,7 +42,7 @@ public class HeavyResourceControllerTest { @Test public void givenHeavyResource_whenSendPutRequest_thenCreateResource() throws Exception { - mockMvc.perform(put("/heavy") + mockMvc.perform(put("/heavyresource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString(new HeavyResource(1, "Tom", "Jackson", 12, "heaven street"))) ).andExpect(status().isOk()); @@ -50,7 +50,7 @@ public class HeavyResourceControllerTest { @Test public void givenNewAddressOfResource_whenExecutePatchRequest_thenUpdateResourcePartially() throws Exception { - mockMvc.perform(patch("/heavy") + mockMvc.perform(patch("/heavyresource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString(new HeavyResourceAddressOnly(1, "5th avenue"))) ).andExpect(status().isOk()); @@ -61,7 +61,7 @@ public class HeavyResourceControllerTest { HashMap updates = new HashMap<>(); updates.put("address", "5th avenue"); - mockMvc.perform(patch("/heavy/1") + mockMvc.perform(patch("/heavyresource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString(updates)) ).andExpect(status().isOk()); From d3e57d38a620f76a4d288fa9ea0e95efe15fc09a Mon Sep 17 00:00:00 2001 From: Tian Baoqiang Date: Mon, 27 Mar 2017 00:23:28 +0800 Subject: [PATCH 054/102] #BAEL-636 (#1468) * jira #BAEL-636 * remove redundant dependency spring-boot-starter-reactive --- spring-5/pom.xml | 11 ++ .../java/com/baeldung/functional/Actor.java | 23 +++ .../com/baeldung/functional/FormHandler.java | 50 ++++++ .../functional/FunctionalWebApplication.java | 80 +++++++++ .../functional/IndexRewriteFilter.java | 29 ++++ spring-5/src/main/resources/files/hello.txt | 1 + ...nctionalWebApplicationIntegrationTest.java | 154 ++++++++++++++++++ .../src/test/resources/baeldung-weekly.png | Bin 0 -> 22275 bytes 8 files changed, 348 insertions(+) create mode 100644 spring-5/src/main/java/com/baeldung/functional/Actor.java create mode 100644 spring-5/src/main/java/com/baeldung/functional/FormHandler.java create mode 100644 spring-5/src/main/java/com/baeldung/functional/FunctionalWebApplication.java create mode 100644 spring-5/src/main/java/com/baeldung/functional/IndexRewriteFilter.java create mode 100644 spring-5/src/main/resources/files/hello.txt create mode 100644 spring-5/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java create mode 100644 spring-5/src/test/resources/baeldung-weekly.png diff --git a/spring-5/pom.xml b/spring-5/pom.xml index eca36cc1d1..59bead4b73 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -35,6 +35,17 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-webflux + + + + + io.projectreactor + reactor-core + 3.0.6.BUILD-SNAPSHOT + diff --git a/spring-5/src/main/java/com/baeldung/functional/Actor.java b/spring-5/src/main/java/com/baeldung/functional/Actor.java new file mode 100644 index 0000000000..23c88b89e1 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/Actor.java @@ -0,0 +1,23 @@ +package com.baeldung.functional; + +class Actor { + private String firstname; + private String lastname; + + public Actor() { + } + + public Actor(String firstname, String lastname) { + this.firstname = firstname; + this.lastname = lastname; + } + + public String getFirstname() { + return firstname; + } + + public String getLastname() { + return lastname; + } + +} diff --git a/spring-5/src/main/java/com/baeldung/functional/FormHandler.java b/spring-5/src/main/java/com/baeldung/functional/FormHandler.java new file mode 100644 index 0000000000..c44db76f56 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/FormHandler.java @@ -0,0 +1,50 @@ +package com.baeldung.functional; + +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +import java.util.concurrent.atomic.AtomicLong; + +import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers; +import static org.springframework.web.reactive.function.BodyExtractors.toFormData; +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +public class FormHandler { + + Mono handleLogin(ServerRequest request) { + return request + .body(toFormData()) + .map(MultiValueMap::toSingleValueMap) + .map(formData -> { + System.out.println("form data: " + formData.toString()); + if ("baeldung".equals(formData.get("user")) && "you_know_what_to_do".equals(formData.get("token"))) { + return ok() + .body(Mono.just("welcome back!"), String.class) + .block(); + } + return ServerResponse + .badRequest() + .build() + .block(); + }); + } + + Mono handleUpload(ServerRequest request) { + return request + .body(toDataBuffers()) + .collectList() + .map(dataBuffers -> { + AtomicLong atomicLong = new AtomicLong(0); + dataBuffers.forEach(d -> atomicLong.addAndGet(d + .asByteBuffer() + .array().length)); + System.out.println("data length:" + atomicLong.get()); + return ok() + .body(fromObject(atomicLong.toString())) + .block(); + }); + } +} diff --git a/spring-5/src/main/java/com/baeldung/functional/FunctionalWebApplication.java b/spring-5/src/main/java/com/baeldung/functional/FunctionalWebApplication.java new file mode 100644 index 0000000000..573813b166 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/FunctionalWebApplication.java @@ -0,0 +1,80 @@ +package com.baeldung.functional; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; +import org.springframework.boot.web.server.WebServer; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.http.server.reactive.ServletHttpHandlerAdapter; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.WebHandler; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import reactor.core.publisher.Flux; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.RequestPredicates.*; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; +import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +public class FunctionalWebApplication { + + private static final Actor BRAD_PITT = new Actor("Brad", "Pitt"); + private static final Actor TOM_HANKS = new Actor("Tom", "Hanks"); + private static final List actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS)); + + private RouterFunction routingFunction() { + FormHandler formHandler = new FormHandler(); + + RouterFunction restfulRouter = route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest + .bodyToMono(Actor.class) + .doOnNext(actors::add) + .then(ok().build())); + + return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))) + .andRoute(POST("/login"), formHandler::handleLogin) + .andRoute(POST("/upload"), formHandler::handleUpload) + .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) + .andNest(path("/actor"), restfulRouter) + .filter((request, next) -> { + System.out.println("Before handler invocation: " + request.path()); + return next.handle(request); + }); + } + + WebServer start() throws Exception { + WebHandler webHandler = toHttpHandler(routingFunction()); + HttpHandler httpHandler = WebHttpHandlerBuilder + .webHandler(webHandler) + .prependFilter(new IndexRewriteFilter()) + .build(); + + Tomcat tomcat = new Tomcat(); + tomcat.setHostname("localhost"); + tomcat.setPort(9090); + Context rootContext = tomcat.addContext("", System.getProperty("java.io.tmpdir")); + ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(httpHandler); + Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet); + rootContext.addServletMappingDecoded("/", "httpHandlerServlet"); + + TomcatWebServer server = new TomcatWebServer(tomcat); + server.start(); + return server; + + } + + public static void main(String[] args) { + try { + new FunctionalWebApplication().start(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/spring-5/src/main/java/com/baeldung/functional/IndexRewriteFilter.java b/spring-5/src/main/java/com/baeldung/functional/IndexRewriteFilter.java new file mode 100644 index 0000000000..3e19f81943 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/IndexRewriteFilter.java @@ -0,0 +1,29 @@ +package com.baeldung.functional; + +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; + +class IndexRewriteFilter implements WebFilter { + + @Override + public Mono filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) { + ServerHttpRequest request = serverWebExchange.getRequest(); + if (request + .getURI() + .getPath() + .equals("/")) { + return webFilterChain.filter(serverWebExchange + .mutate() + .request(builder -> builder + .method(request.getMethod()) + .contextPath(request.getContextPath()) + .path("/test")) + .build()); + } + return webFilterChain.filter(serverWebExchange); + } + +} diff --git a/spring-5/src/main/resources/files/hello.txt b/spring-5/src/main/resources/files/hello.txt new file mode 100644 index 0000000000..b6fc4c620b --- /dev/null +++ b/spring-5/src/main/resources/files/hello.txt @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/spring-5/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java b/spring-5/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java new file mode 100644 index 0000000000..bf28ed1e7d --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java @@ -0,0 +1,154 @@ +package com.baeldung.functional; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.boot.web.server.WebServer; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.BodyInserters; + +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromResource; + +public class FunctionalWebApplicationIntegrationTest { + + private static WebTestClient client; + private static WebServer server; + + @BeforeClass + public static void setup() throws Exception { + server = new FunctionalWebApplication().start(); + client = WebTestClient + .bindToServer() + .baseUrl("http://localhost:" + server.getPort()) + .build(); + } + + @AfterClass + public static void destroy() { + server.stop(); + } + + @Test + public void givenRouter_whenGetTest_thenGotHelloWorld() throws Exception { + client + .get() + .uri("/test") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .value() + .isEqualTo("helloworld"); + } + + @Test + public void givenIndexFilter_whenRequestRoot_thenRewrittenToTest() throws Exception { + client + .get() + .uri("/") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .value() + .isEqualTo("helloworld"); + } + + @Test + public void givenLoginForm_whenPostValidToken_thenSuccess() throws Exception { + MultiValueMap formData = new LinkedMultiValueMap<>(1); + formData.add("user", "baeldung"); + formData.add("token", "you_know_what_to_do"); + + client + .post() + .uri("/login") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .exchange(BodyInserters.fromFormData(formData)) + .expectStatus() + .isOk() + .expectBody(String.class) + .value() + .isEqualTo("welcome back!"); + } + + @Test + public void givenLoginForm_whenRequestWithInvalidToken_thenFail() throws Exception { + MultiValueMap formData = new LinkedMultiValueMap<>(2); + formData.add("user", "baeldung"); + formData.add("token", "try_again"); + + client + .post() + .uri("/login") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .exchange(BodyInserters.fromFormData(formData)) + .expectStatus() + .isBadRequest(); + } + + @Test + public void givenUploadForm_whenRequestWithMultipartData_thenSuccess() throws Exception { + Resource resource = new ClassPathResource("/baeldung-weekly.png"); + client + .post() + .uri("/upload") + .contentType(MediaType.MULTIPART_FORM_DATA) + .exchange(fromResource(resource)) + .expectStatus() + .isOk() + .expectBody(String.class) + .value() + .isEqualTo(String.valueOf(resource.contentLength())); + } + + @Test + public void givenActors_whenAddActor_thenAdded() throws Exception { + client + .get() + .uri("/actor") + .exchange() + .expectStatus() + .isOk() + .expectBody(Actor.class) + .list() + .hasSize(2); + + client + .post() + .uri("/actor") + .exchange(fromObject(new Actor("Clint", "Eastwood"))) + .expectStatus() + .isOk(); + + client + .get() + .uri("/actor") + .exchange() + .expectStatus() + .isOk() + .expectBody(Actor.class) + .list() + .hasSize(3); + } + + @Test + public void givenResources_whenAccess_thenGot() throws Exception { + client + .get() + .uri("/files/hello.txt") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .value() + .isEqualTo("hello"); + } + +} diff --git a/spring-5/src/test/resources/baeldung-weekly.png b/spring-5/src/test/resources/baeldung-weekly.png new file mode 100644 index 0000000000000000000000000000000000000000..5a27d61dae718016a947083e01411ed1bc14f90e GIT binary patch literal 22275 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 Date: Sun, 26 Mar 2017 19:38:09 +0200 Subject: [PATCH 055/102] Update pom.xml (#1507) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e0295e5c72..0939ea91b8 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ javaxval jaxb jee7 - jhipster + jjwt jooq jpa-storedprocedure From 6322aa350f1c1a35d751463bd2bb57dbc03bf65e Mon Sep 17 00:00:00 2001 From: lor6 Date: Sun, 26 Mar 2017 21:19:07 +0300 Subject: [PATCH 056/102] Bael 737 v2 (#1509) * in memory test * update dependencies * rename db * remove create db * update version * update properties file --- spring-jpa/src/test/resources/persistence-student.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-jpa/src/test/resources/persistence-student.properties b/spring-jpa/src/test/resources/persistence-student.properties index 21dcd5b4a0..3b6b580630 100644 --- a/spring-jpa/src/test/resources/persistence-student.properties +++ b/spring-jpa/src/test/resources/persistence-student.properties @@ -3,7 +3,7 @@ jdbc.url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1 hibernate.dialect=org.hibernate.dialect.H2Dialect hibernate.show_sql=true -hibernate.hbm2ddl.auto=create-drop +hibernate.hbm2ddl.auto=create hibernate.cache.use_second_level_cache=false hibernate.cache.use_query_cache=false \ No newline at end of file From e6836c01c7b6fb0ab058e93f9ef46aa6540a9c6d Mon Sep 17 00:00:00 2001 From: Mohamed Sanaulla Date: Sun, 26 Mar 2017 23:57:36 +0300 Subject: [PATCH 057/102] sample for BAEL-747 Check if a number is prime in Java (#1508) --- .../primechecker/BigIntegerPrimeChecker.java | 13 ++++++++++ .../primechecker/BruteForcePrimeChecker.java | 13 ++++++++++ .../primechecker/OptimisedPrimeChecker.java | 15 ++++++++++++ .../baeldung/primechecker/PrimeChecker.java | 6 +++++ .../primechecker/PrimesPrimeChecker.java | 12 ++++++++++ .../BigIntegerPrimeCheckerTest.java | 23 ++++++++++++++++++ .../BruteForcePrimeCheckerTest.java | 24 +++++++++++++++++++ .../OptimisedPrimeCheckerTest.java | 23 ++++++++++++++++++ .../primechecker/PrimesPrimeCheckerTest.java | 22 +++++++++++++++++ 9 files changed, 151 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/primechecker/BigIntegerPrimeChecker.java create mode 100644 core-java/src/main/java/com/baeldung/primechecker/BruteForcePrimeChecker.java create mode 100644 core-java/src/main/java/com/baeldung/primechecker/OptimisedPrimeChecker.java create mode 100644 core-java/src/main/java/com/baeldung/primechecker/PrimeChecker.java create mode 100644 core-java/src/main/java/com/baeldung/primechecker/PrimesPrimeChecker.java create mode 100644 core-java/src/test/java/com/baeldung/primechecker/BigIntegerPrimeCheckerTest.java create mode 100644 core-java/src/test/java/com/baeldung/primechecker/BruteForcePrimeCheckerTest.java create mode 100644 core-java/src/test/java/com/baeldung/primechecker/OptimisedPrimeCheckerTest.java create mode 100644 core-java/src/test/java/com/baeldung/primechecker/PrimesPrimeCheckerTest.java diff --git a/core-java/src/main/java/com/baeldung/primechecker/BigIntegerPrimeChecker.java b/core-java/src/main/java/com/baeldung/primechecker/BigIntegerPrimeChecker.java new file mode 100644 index 0000000000..1ac4fed63f --- /dev/null +++ b/core-java/src/main/java/com/baeldung/primechecker/BigIntegerPrimeChecker.java @@ -0,0 +1,13 @@ +package com.baeldung.primechecker; + +import java.math.BigInteger; + +public class BigIntegerPrimeChecker implements PrimeChecker{ + + @Override + public boolean isPrime(int number) { + BigInteger bigInt = BigInteger.valueOf(number); + return bigInt.isProbablePrime(100); + } + +} diff --git a/core-java/src/main/java/com/baeldung/primechecker/BruteForcePrimeChecker.java b/core-java/src/main/java/com/baeldung/primechecker/BruteForcePrimeChecker.java new file mode 100644 index 0000000000..7a94479b8f --- /dev/null +++ b/core-java/src/main/java/com/baeldung/primechecker/BruteForcePrimeChecker.java @@ -0,0 +1,13 @@ +package com.baeldung.primechecker; + +import java.util.stream.IntStream; + +public class BruteForcePrimeChecker implements PrimeChecker{ + + @Override + public boolean isPrime(int number) { + return IntStream.range(2, number).filter(n -> (number % n == 0)).count() == 0; + } + + +} diff --git a/core-java/src/main/java/com/baeldung/primechecker/OptimisedPrimeChecker.java b/core-java/src/main/java/com/baeldung/primechecker/OptimisedPrimeChecker.java new file mode 100644 index 0000000000..40669f4181 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/primechecker/OptimisedPrimeChecker.java @@ -0,0 +1,15 @@ +package com.baeldung.primechecker; + +import java.util.stream.IntStream; + +public class OptimisedPrimeChecker implements PrimeChecker{ + + @Override + public boolean isPrime(int number) { + return IntStream.range(2, (int)Math.sqrt(number) + 1) + .filter(n -> (number % n == 0)) + .count() == 0; + } + + +} diff --git a/core-java/src/main/java/com/baeldung/primechecker/PrimeChecker.java b/core-java/src/main/java/com/baeldung/primechecker/PrimeChecker.java new file mode 100644 index 0000000000..22260268bc --- /dev/null +++ b/core-java/src/main/java/com/baeldung/primechecker/PrimeChecker.java @@ -0,0 +1,6 @@ +package com.baeldung.primechecker; + +public interface PrimeChecker { + + public boolean isPrime( int number ); +} diff --git a/core-java/src/main/java/com/baeldung/primechecker/PrimesPrimeChecker.java b/core-java/src/main/java/com/baeldung/primechecker/PrimesPrimeChecker.java new file mode 100644 index 0000000000..0c6a636612 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/primechecker/PrimesPrimeChecker.java @@ -0,0 +1,12 @@ +package com.baeldung.primechecker; + +import org.apache.commons.math3.primes.Primes; + +public class PrimesPrimeChecker implements PrimeChecker{ + + @Override + public boolean isPrime(int number) { + return Primes.isPrime(number); + } + +} diff --git a/core-java/src/test/java/com/baeldung/primechecker/BigIntegerPrimeCheckerTest.java b/core-java/src/test/java/com/baeldung/primechecker/BigIntegerPrimeCheckerTest.java new file mode 100644 index 0000000000..6a5228cc50 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/primechecker/BigIntegerPrimeCheckerTest.java @@ -0,0 +1,23 @@ +package com.baeldung.primechecker; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class BigIntegerPrimeCheckerTest { + + PrimeChecker primeChecker = new BigIntegerPrimeChecker(); + + @Test + public void givenPrimeNumber_whenCheckIsPrime_thenTrue(){ + assertTrue(primeChecker.isPrime(13)); + assertTrue(primeChecker.isPrime(1009)); + } + + @Test + public void givenNonPrimeNumber_whenCheckIsPrime_thenFalse(){ + assertTrue(!primeChecker.isPrime(50)); + assertTrue(!primeChecker.isPrime(1001)); + } + +} diff --git a/core-java/src/test/java/com/baeldung/primechecker/BruteForcePrimeCheckerTest.java b/core-java/src/test/java/com/baeldung/primechecker/BruteForcePrimeCheckerTest.java new file mode 100644 index 0000000000..7139373f5e --- /dev/null +++ b/core-java/src/test/java/com/baeldung/primechecker/BruteForcePrimeCheckerTest.java @@ -0,0 +1,24 @@ +package com.baeldung.primechecker; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class BruteForcePrimeCheckerTest { + + BruteForcePrimeChecker primeChecker = new BruteForcePrimeChecker(); + + @Test + public void givenPrimeNumber_whenCheckIsPrime_thenTrue(){ + assertTrue(primeChecker.isPrime(13)); + assertTrue(primeChecker.isPrime(1009)); + } + + @Test + public void givenNonPrimeNumber_whenCheckIsPrime_thenFalse(){ + assertTrue(!primeChecker.isPrime(50)); + assertTrue(!primeChecker.isPrime(1001)); + } + + + +} diff --git a/core-java/src/test/java/com/baeldung/primechecker/OptimisedPrimeCheckerTest.java b/core-java/src/test/java/com/baeldung/primechecker/OptimisedPrimeCheckerTest.java new file mode 100644 index 0000000000..bb4c06a53a --- /dev/null +++ b/core-java/src/test/java/com/baeldung/primechecker/OptimisedPrimeCheckerTest.java @@ -0,0 +1,23 @@ +package com.baeldung.primechecker; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class OptimisedPrimeCheckerTest { + + PrimeChecker primeChecker = new OptimisedPrimeChecker(); + + @Test + public void givenPrimeNumber_whenCheckIsPrime_thenTrue(){ + assertTrue(primeChecker.isPrime(13)); + assertTrue(primeChecker.isPrime(1009)); + } + + @Test + public void givenNonPrimeNumber_whenCheckIsPrime_thenFalse(){ + assertTrue(!primeChecker.isPrime(50)); + assertTrue(!primeChecker.isPrime(1001)); + } + +} diff --git a/core-java/src/test/java/com/baeldung/primechecker/PrimesPrimeCheckerTest.java b/core-java/src/test/java/com/baeldung/primechecker/PrimesPrimeCheckerTest.java new file mode 100644 index 0000000000..f8b194e855 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/primechecker/PrimesPrimeCheckerTest.java @@ -0,0 +1,22 @@ +package com.baeldung.primechecker; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class PrimesPrimeCheckerTest { + PrimeChecker primeChecker = new PrimesPrimeChecker(); + + @Test + public void givenPrimeNumber_whenCheckIsPrime_thenTrue() { + assertTrue(primeChecker.isPrime(13)); + assertTrue(primeChecker.isPrime(1009)); + } + + @Test + public void givenNonPrimeNumber_whenCheckIsPrime_thenFalse() { + assertTrue(!primeChecker.isPrime(50)); + assertTrue(!primeChecker.isPrime(1001)); + } + +} From 427077ebfabc170f31e771c5486cc7a0cb25737b Mon Sep 17 00:00:00 2001 From: Nancy Bosecker Date: Mon, 27 Mar 2017 05:46:58 -0700 Subject: [PATCH 058/102] Jackson Map Serialize/Deserialize (#1511) * Solr w Apache SolrJ * Solr w Apache SolrJ * updated test names and moved add to @before method * create apache-solrj module, moved code from spring-data-solr * More examples for indexing,delete,and query for solrj * More examples for indexing,delete,and query for solrj * Jackson Map Serialize/Deserialize * Jackson Map Serialize/Deserialize --- .../com/baeldung/jackson/entities/MyPair.java | 80 +++++++++++++++++++ .../serialization/MyPairSerializer.java | 25 ++++++ .../JacksonMapDeserializeTest.java | 63 +++++++++++++++ .../JacksonMapSerializeTest.java | 71 ++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 jackson/src/main/java/com/baeldung/jackson/entities/MyPair.java create mode 100644 jackson/src/main/java/com/baeldung/jackson/serialization/MyPairSerializer.java create mode 100644 jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java create mode 100644 jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java diff --git a/jackson/src/main/java/com/baeldung/jackson/entities/MyPair.java b/jackson/src/main/java/com/baeldung/jackson/entities/MyPair.java new file mode 100644 index 0000000000..ebe41890fe --- /dev/null +++ b/jackson/src/main/java/com/baeldung/jackson/entities/MyPair.java @@ -0,0 +1,80 @@ +package com.baeldung.jackson.entities; + +import com.fasterxml.jackson.annotation.JsonValue; + +public class MyPair { + + private String first; + private String second; + + public MyPair(String first, String second) { + this.first = first; + this.second = second; + } + + public MyPair(String both) { + String[] pairs = both.split("and"); + this.first = pairs[0].trim(); + this.second = pairs[1].trim(); + } + + @Override + @JsonValue + public String toString() { + return first + " and " + second; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((first == null) ? 0 : first.hashCode()); + result = prime * result + ((second == null) ? 0 : second.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof MyPair)) { + return false; + } + MyPair other = (MyPair) obj; + if (first == null) { + if (other.first != null) { + return false; + } + } else if (!first.equals(other.first)) { + return false; + } + if (second == null) { + if (other.second != null) { + return false; + } + } else if (!second.equals(other.second)) { + return false; + } + return true; + } + + public String getFirst() { + return first; + } + + public void setFirst(String first) { + this.first = first; + } + + public String getSecond() { + return second; + } + + public void setSecond(String second) { + this.second = second; + } +} \ No newline at end of file diff --git a/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairSerializer.java b/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairSerializer.java new file mode 100644 index 0000000000..68afb6c193 --- /dev/null +++ b/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairSerializer.java @@ -0,0 +1,25 @@ +package com.baeldung.jackson.serialization; + +import java.io.IOException; +import java.io.StringWriter; + +import com.baeldung.jackson.entities.MyPair; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; + +public class MyPairSerializer extends JsonSerializer { + + private final ObjectMapper mapper = new ObjectMapper(); + + @Override + public void serialize(MyPair value, JsonGenerator gen, + SerializerProvider serializers) throws IOException, + JsonProcessingException { + StringWriter writer = new StringWriter(); + mapper.writeValue(writer, value); + gen.writeFieldName(writer.toString()); + } +} \ No newline at end of file diff --git a/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java b/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java new file mode 100644 index 0000000000..3be3981c9b --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java @@ -0,0 +1,63 @@ +package com.baeldung.jackson.deserialization; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +import com.baeldung.jackson.entities.MyPair; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JacksonMapDeserializeTest { + + private Map map; + private Map cmap; + + @Test + public void whenSimpleMapDeserialize_thenCorrect() + throws JsonParseException, JsonMappingException, IOException { + + final String jsonInput = "{\"key\": \"value\"}"; + final ObjectMapper mapper = new ObjectMapper(); + TypeReference> typeRef = new TypeReference>() { + }; + + final Map map = mapper.readValue(jsonInput, typeRef); + + Assert.assertEquals("value", map.get("key")); + } + + @Test + public void whenObjectStringMapDeserialize_thenCorrect() + throws JsonParseException, JsonMappingException, IOException { + + final String jsonInput = "{\"Abbott and Costello\" : \"Comedy\"}"; + final ObjectMapper mapper = new ObjectMapper(); + + TypeReference> typeRef = new TypeReference>() { + }; + map = mapper.readValue(jsonInput, typeRef); + + Assert.assertEquals("Comedy", map.get(new MyPair("Abbott", "Costello"))); + } + + @Test + public void whenObjectObjectMapDeserialize_thenCorrect() + throws JsonParseException, JsonMappingException, IOException { + + final String jsonInput = "{\"Abbott and Costello\" : \"Comedy and 1940s\"}"; + final ObjectMapper mapper = new ObjectMapper(); + TypeReference> typeRef = new TypeReference>() { + }; + + cmap = mapper.readValue(jsonInput, typeRef); + + Assert.assertEquals(new MyPair("Comedy", "1940s"), + cmap.get(new MyPair("Abbott", "Costello"))); + } +} diff --git a/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java b/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java new file mode 100644 index 0000000000..71ba698e8e --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java @@ -0,0 +1,71 @@ +package com.baeldung.jackson.serialization; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +import com.baeldung.jackson.entities.MyPair; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.MapSerializer; + +public class JacksonMapSerializeTest { + + @JsonSerialize(keyUsing = MyPairSerializer.class) + private Map map; + + @JsonSerialize(keyUsing = MapSerializer.class) + private Map cmap; + + @JsonSerialize(keyUsing = MyPairSerializer.class) + private MyPair mapKey; + + @JsonSerialize(keyUsing = MyPairSerializer.class) + private MyPair mapValue; + + @Test + public void whenSimpleMapSerialize_thenCorrect() + throws JsonProcessingException { + + Map map = new HashMap(); + map.put("key", "value"); + + final ObjectMapper mapper = new ObjectMapper(); + final String jsonResult = mapper.writeValueAsString(map); + + Assert.assertEquals("{\"key\":\"value\"}", jsonResult); + } + + @Test + public void whenCustomObjectStringMapSerialize_thenCorrect() + throws JsonProcessingException { + + map = new HashMap(); + MyPair key = new MyPair("Abbott", "Costello"); + map.put(key, "Comedy"); + + final ObjectMapper mapper = new ObjectMapper(); + final String jsonResult = mapper.writeValueAsString(map); + + Assert.assertEquals("{\"Abbott and Costello\":\"Comedy\"}", jsonResult); + } + + @Test + public void whenCustomObjectObjectMapSerialize_thenCorrect() + throws JsonProcessingException { + + cmap = new HashMap(); + mapKey = new MyPair("Abbott", "Costello"); + mapValue = new MyPair("Comedy", "1940's"); + cmap.put(mapKey, mapValue); + + final ObjectMapper mapper = new ObjectMapper(); + final String jsonResult = mapper.writeValueAsString(cmap); + + Assert.assertEquals("{\"Abbott and Costello\":\"Comedy and 1940's\"}", + jsonResult); + } +} From c5806d4e058281142a0dbfee1abc2ec084c735ea Mon Sep 17 00:00:00 2001 From: KevinGilmore Date: Mon, 27 Mar 2017 08:46:04 -0500 Subject: [PATCH 059/102] BAEL-737 README (#1514) * Add files via upload * Update pom.xml * Update RunGuice.java * Update Communication.java * Update CommunicationMode.java * Update DefaultCommunicator.java * Update EmailCommunicationMode.java * Update IMCommunicationMode.java * Update SMSCommunicationMode.java * Update MessageLogger.java * Update MessageSentLoggable.java * Update AOPModule.java * Update BasicModule.java * Update CommunicationModel.java * Update Communicator.java * Update BasicModule.java * Update RunGuice.java * Update MessageLogger.java * Update Communicator.java * Update pom.xml * BAEL-278: Updated README.md * BAEL-554: Add and update README.md files * Update pom.xml * Update pom.xml * Update pom.xml * BAEL-345: fixed assertion * BAEL-109: Updated README.md * BAEL-345: Added README.md * Reinstating reactor-core module in root-level pom * BAEL-393: Adding guide-intro module to root pom * BAEL-9: Updated README.md * BAEL-157: README.md updated * Changed project name * Update RunGuice.java Removed references to message logging and output * Update Communication.java Removed message logging-related code * BAEL-566: Updated README.md * New project name * BAEL-393: removing guice-intro directory * BAEL-393: renamed module guice-intro to guice in root pom.xml * BAEL-393 and BAEL-541 README.md files * BAEL-731: Updated README.md * BAEL-680: renamed test methods * BAEL-714: Updated README.md * BAEL-737: Updated README.md --- spring-jpa/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-jpa/README.md b/spring-jpa/README.md index 313865e3f8..70f4404f98 100644 --- a/spring-jpa/README.md +++ b/spring-jpa/README.md @@ -13,6 +13,7 @@ - [Hibernate Second-Level Cache](http://www.baeldung.com/hibernate-second-level-cache) - [Spring, Hibernate and a JNDI Datasource](http://www.baeldung.com/spring-persistence-jpa-jndi-datasource) - [Deleting Objects with Hibernate](http://www.baeldung.com/delete-with-hibernate) +- [Self-Contained Testing Using an In-Memory Database](http://www.baeldung.com/spring-jpa-test-in-memory-database) ### Eclipse Config After importing the project into Eclipse, you may see the following error: From 365d75a8d7aca4a380926724c0ebe75d4d1396d8 Mon Sep 17 00:00:00 2001 From: Nancy Bosecker Date: Mon, 27 Mar 2017 09:44:59 -0700 Subject: [PATCH 060/102] Jackson version updated (#1516) * Solr w Apache SolrJ * Solr w Apache SolrJ * updated test names and moved add to @before method * create apache-solrj module, moved code from spring-data-solr * More examples for indexing,delete,and query for solrj * More examples for indexing,delete,and query for solrj * Jackson Map Serialize/Deserialize * Jackson Map Serialize/Deserialize * Jackson version update --- jackson/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jackson/pom.xml b/jackson/pom.xml index 881ba8e24c..8e627c146b 100644 --- a/jackson/pom.xml +++ b/jackson/pom.xml @@ -195,7 +195,7 @@ - 2.8.6 + 2.8.7 1.7.21 From 1c1c557a399327fc1cf031357c6c041c817a6760 Mon Sep 17 00:00:00 2001 From: Danil Kornishev Date: Mon, 27 Mar 2017 15:21:03 -0400 Subject: [PATCH 061/102] Spring State Machine (#1493) * Neo4j cleanup * Neo4j cleanup * Neo4j cleanup x2 * State Machine Init * cleanup * White background, Java Util Logging * Change to Logging * Static import of asserts. rename test methods * fix attempt for S3 -> S4 * Remove awaitility. add teardown * fix attempt for S3 -> S4 * fix attempt for S3 -> S4 Final --- pom.xml | 1 + spring-state-machine/bpmn/forkjoin.bpmn | 116 ++++++++++++++++++ spring-state-machine/bpmn/img/forkjoin.png | Bin 0 -> 50788 bytes spring-state-machine/bpmn/img/simple.png | Bin 0 -> 22706 bytes spring-state-machine/bpmn/simple.bpmn | 76 ++++++++++++ spring-state-machine/pom.xml | 31 +++++ .../ApplicationReviewEvents.java | 5 + .../ApplicationReviewStates.java | 5 + .../ForkJoinStateMachineConfiguration.java | 74 +++++++++++ ...HierarchicalStateMachineConfiguration.java | 47 +++++++ .../JunctionStateMachineConfiguration.java | 60 +++++++++ .../SimpleEnumStateMachineConfiguration.java | 53 ++++++++ .../SimpleStateMachineConfiguration.java | 105 ++++++++++++++++ .../config/StateMachineListener.java | 16 +++ .../ForkJoinStateMachineTest.java | 45 +++++++ .../HierarchicalStateMachineTest.java | 37 ++++++ .../JunctionStateMachineTest.java | 24 ++++ .../statemachine/StateEnumMachineTest.java | 33 +++++ .../statemachine/StateMachineBuilderTest.java | 35 ++++++ .../spring/statemachine/StateMachineTest.java | 51 ++++++++ 20 files changed, 814 insertions(+) create mode 100644 spring-state-machine/bpmn/forkjoin.bpmn create mode 100644 spring-state-machine/bpmn/img/forkjoin.png create mode 100644 spring-state-machine/bpmn/img/simple.png create mode 100644 spring-state-machine/bpmn/simple.bpmn create mode 100644 spring-state-machine/pom.xml create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java create mode 100644 spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java create mode 100644 spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java diff --git a/pom.xml b/pom.xml index 0939ea91b8..6b4511e45b 100644 --- a/pom.xml +++ b/pom.xml @@ -189,6 +189,7 @@ spring-sleuth spring-social-login spring-spel + spring-state-machine spring-thymeleaf spring-userservice spring-zuul diff --git a/spring-state-machine/bpmn/forkjoin.bpmn b/spring-state-machine/bpmn/forkjoin.bpmn new file mode 100644 index 0000000000..0cb060f74b --- /dev/null +++ b/spring-state-machine/bpmn/forkjoin.bpmn @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-state-machine/bpmn/img/forkjoin.png b/spring-state-machine/bpmn/img/forkjoin.png new file mode 100644 index 0000000000000000000000000000000000000000..642ab6949d18b49012529d7c5b9b14f909c9d701 GIT binary patch literal 50788 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�gf@SIUg-QO`umQ3S)QlWiV3@kS`RLu_ zHwe@b^en0N-B5{;t3HetA_L>(1cY5wi_f8aq}=_gUf2nG)Ajil!YWv&!c>Ai3HSjc ze0Koq*`x;-n{;zFBi_5Kqx+xKCyx}Uc>x~08vl?cKAOAj$y@yjnth5tAAy1lK0N^l zS^5yvlG)99GH4Ym;?c%HWP7J+bwV+y#XN~|orI%E2h#l+fz+WKn7T^l%EU}(_XpSz zYPp!D|DMM42kZ+Wz60-Zp-}pDZqGataE-y761NoUwMYyvh|E`fl>mNSL-FNZI?ExR z?2pl81Au`4dM-Wttr&d1piIHZx$BFQIAO3yD+Fc3a9Q1Atf@1B$>~Om@ZZwXt&j$6 zVV^sf2zvHw{~(yHlS6v|loJlgptIc%3U~Ct&-OLAn|*?b1xMhyYESBfy3P{GMDl=9 z6o+xYNHag?`W&u})j1j=W3}u8fBvcVcPefyQ2%=fNN8IlYh0$m zOfh{#OV*&#tq?J8imMF?T|KX)6~|*5h|fK+UZ(@fS3O;@HYBX3hMzm$y9%5#;xi8a zv7x;;10Fd{GI?*y%1A=s7C+U<}g>sxfEz~0g|jjz!JGlnRxYm?@I*caDnjvOHo3YxTXam5N%B2dax3{ zlCA^2(4WQ_7(>AcZx0GGXmH7$_dgt}vgk%X1Fd9haB~g-G1-sBoN-Ss;&%Lq7Z-c&QqFz_+@9OhbYM(uJSY_O0~Z?SL?Pe{J(Jbu ze|Dh6-R%l4TY4Tu4n40DaQ?oy1x+1;seGKB;2YDpi1yi+H-6eRtFnr6SZb4CD+l|7 z3UJjq%p9+=)xEgx2thfkJ46I*EO_9lf6N_}_#18O$aZY9Y5}!E@JM=65>uh-x?0T| z73F@%Ey`o#8=bd$tT*Z{h;Q{m9YmRx+U|%MV*l(~R%Fxl&e;JyU?K&E{{+~^Re-<8 zb{p8lF~lPf6OF+D5is_qu&@L%mHD5(Pn>zg&G56g=ql1KUKxj5DTF7_7kPcoMXF1>e;IrqNK9xi_*GkZRflB`^X_*37`Nv-Ck%G+9>m2)5#1dU{Uq-tfM|~2bX|c?<#kN zt@i@g0)fObenrg4`g2o}P(eoIL#QFL`r2PiAP#Z#k_)@!yafJhK*Q{G3UDRvptESi zdeA91ZuDQ@M-Ry(oo?}|zC4+8&hwG<4>cbSI9}(A|G0CkTJS1;PhGlms7!9*?*|w& zxEyV0vufvsm5l)TjTP_|I(Y`f$=DOxevFqOU#YSgp}8pALkQZyl(iAD_6{saH|QoK zJK{B&;0G>;U~Cia%L~#ING}7mY9fL|?V#~;aPzzBVqI|Q#ytQLVBfonHa3JCyjR2e zDUsH`KheRhf8T*y$RFvn@)dDyXKPBSQLe#D+z(#a0&u$2G0T3q&@a_d05J=hgV5X^ zY2XY8b_R<`;msxdQmxOZiP&X>e19_?VncJ_+jjse?|lg5(`~ii4Ka~xcvh>4e`%D2 zA{nPa!yMd1H!#|;zsh>xJ2=@M03_hzl^^KI?D+ot`cY=9CE&snI30o>lptm@7>XbT zh+g=&{Fz*dyEUeOdb5R(1OFCDzd=Bm9a2|=r7yN!0w`+FCkZF_Fg-J&*=C3N(ALKl zrjC1?6T0<7DZC735(%5QK^Kg=_6Hn)zqbNYPB$3>Z3m+y>-j{8XI?e%AafD?0$na^ zU_k8#>|Y16Z!T>{Pp$i3$G?BiaRzSAci^2|*NIL&{LP5}mMi$l0Fa(;Pm~mAdZyua zZ4%I3-7}lk0cV}qyizDyvWcfb$N;BtgO;iH3Yy5)`)c{vXZ?s;`Zgh;k{st$fEB&G z9|gb(luZNAtby}{bJWS17cir*DE48ifl5rsB5?2>QSzIdNZAKrUoi7kZ0LaC*4|J~J zfu{>z1K`1eug01;MGGK$5Eo3*RGcZn-nbl(|HjV5YXZ!L$$tw83}`4 zQ3#@Q&bi$Y1uu4)iCeyN^kQdB>YmU+Z&|c zv&T@HeG&(^AQP>T^^=E=pl}YmV5q13Fv>6wSyKUBp;7-kAu9YMv>TUJON!ETGe#-v z|H%swr-_XDc#--ES6eH}i?4OF-7=9{m&X-BwT;yLIbh4nR9^x65I4`Gcf&fl3W*AJ zfLAtu0}hUf;JKN*hTtTE(w_X@1sdd~6Qzf;rIwh4v6F=R7#|f<&=A0R8)iS&gJ9P4 zr}Udr#ITHIUnHAPk%NPS^cD8NXxl+r#wHfQ+c}IqY%LAiLtQB1Q-=mdBBTcZAuUg5 zaRo670kaaO%2=FhX6Tc z`!uG)fuGsju>z&+@0xvPAO>PKWme^bHI{bIjLRTk(wRp|L2+?&)vz8SOn0dc)`%?$ ziX2W8Pz5y6eV}iwpB(D%Pmlt{@4=6E6|<+gMXz42o$-t0qZ3~r6-&+70%@%l$aIjC z;X8&EKw0z|u{-&!gp>71;fH@kqT-Z40Nv>yvr-cFV}F* z-~wEV#@Yh>D#T&$wi-d&WYnH_sR*CErH6--Gp>f6@qY6l;3RRV0 zGZ@Q%h>!4`Hx4+S^z`imVvIh0vj9efI6jg#@JW{cL0=s0e12)R1`;rNI-NF$ec1!v zXSh#>!0tEz26z8odsiM0W!wEr5y=wbDZ4}wD#@0mBKuCVj3xWNlieuMqGZiB_Uuc@ zJ|v0k`_3SYZS4E{yY4~H^S?tAX*y3Td3bH3-C?|}%#!k~hb8_Zu3 ztg(*4&|$dY3CfU+0D;2k)(^(NQzCTO0J#n+?VE%LZ#iLQ0rHJAA_b4{_ByV90l?Uh zKo=31Y02C0^v=%>m(*d5=Qikqe#TPE{LH+pT%aVENt$cJt^6*uE>M(V|DEH^x(C;c zqXet#B4Tp_eM4mM-I#!lE3n3xN^tyNlC{xEXyO=2lQ_}sg6==2XMKqz+RA4fKlpJh zA~z93PQ9>u8i{zOAj{c|obmk?o(3;l5|@3`S(m-#x<*hBYvzCdu2~1OO_9IoW}7|< zAr)sRs3d>n=++2IQ@l5B5#5_E1x5?8qh{A2lsE};`3sP@9nEE6YgMejt|(sDKY%;m zHNPg0HBPQ8pO*V;?FYF+J-}MUbWBto$<*|0x$5aHM z1q~DZK1$-wB)y8*;YWUlDOoo*ihR$tL4p0c@`D$ehhqv4PQklKw}7=j38)E*=#$S7 zJ(5g*2#h3CVSf-(0C^uk$pyMQK`g86>N$l-hP#)O@ty>KX?w(YSu4y0>!+~9qx5tJ z_B$%{zVjSb{4`JybqWy|_3t;mCG!^V(pt?DuuHd`k)%ZP%=%Zyj2KDzFP$biubQUF zI871w+KYzhFRFEv6puv@1uPf|8dPjhPHjWB5(&O3&d(;I$c_>fgPCPVlGmSCwfn82 zG2}g;4$@)t#fDx9h%L!)!E{#;z=OQFhjzP1 zE;!VOjEOVhuA!G->GSr*Nyv4jiKK>3;~KXjtP}v@MHmsAm^W?|8&UVqS`oeex&|c;8Rd)c``AfMy(7#Fh{s`i*lj^W& zj%?TdfLvp}ymn*1r*W{K#~biA9^7}X_?qOgKCXVRv;4 z@6<={V$Wq>!nQ{zMk?r#)n+|Wm9wpu(UZ>R)S5+Yw|Lfwf2`c>NviYj1!q|C@Tqd) zLNs-`u-imD%zrn@GVI<)(0YMx(OgziX(^fXM2v>@{*zs%s0w0!lNFOY_+4fi5*fYO zoLW7WUsT@3I`wG$T6$W$azUWr#gu|+TRa?M0;siDu*SwZ6)SMo6tC;pEMQBjN3Y)U z_L9e`bu;N+?Qf7dJ9JHPu_-356?8c$%Y4R}+x&<`$k5{XpjC1I4L+C-ECX9WL;3t1Muhv>;Ll9MDI9###h^m#!C>q$0&?$vZGROCyU8oUV7-zssQC z?+8X&eP|W-VEDeucQXvP&>sg$;d^-2wB;pKOA46cELK^xf%#WWVoUSc!QM$ zuGo&i{AXiq;N<&vtuJ+Y< zB+gI=_qTNa0%sz!OH!YF4qJVl(Ww*oV0VII%0d7f@H)RBwrfKWOvsJ0Lfr->iNLI- zx3}EC20fu8j)q!Bze&SDoLTQR=?TX&!6@2Ef8-F&c^-LWy$*Wo8o!E$wMFl>nWpFQ zTlm1XWfi7c8C4^u;bSN;X$^$;T{$tfxQwXR;nkB`7LQ6Mu* z4o6~Xdm0l19|W}b2C7oIxw+YbrwPTD9n&HQ)B5!>)YRa9n1ykllV6iAMqmIz z&S>*pUg7)__vkO<<8pruZ{BL*p4O`%P_dRB)WMvv9; z6vht@&QH?3-gx%!?M?AVONV}$o6j0{hTUOzDGG=|?)}{o%2B5FXvQZ12WcLqj9@?XOL=OD#nEw zoJsvx_zNCc>TZgZ@3$>?KP%&8{R4faGl5cEHO(kAWE*SWj7Odt8X*6cy>4&kzyj;u zx-417G6}~n-Z(S(rhJe#Hc}aHikr`-R>a#^u<^TOD9t16%7y@S1bTwi`e}KFXVpQS zoij31$=celWGL|MF;oS5lR|aB+XJDjSWDvu>4KJCr+>opIQC8Fv01f>uyN~?DfMv& zNLNWJM;Za7=|u72LPXL1NcKvP+8hNTZ^eM5nnn8-mz-j^9ZB~N1Z-~JZ8c1&M)&2_ z6FGxZP=c-aJ+L(iDr&U8*rs!8%ngUa1#p}Rw+wJR=}Owd-MW0;%V7aDxdr|Vq-;h^ zS^-13nt)g^T(?x^G-e5F`e9unJde7rJPiM0PA2i z23B&ld|}#Fh%Du~_9zH+(0Suz`~zvu0>_|aPxxdhAOYyaF*7qOI*8`A*;sQBQ`xp1 zZuk)1^crp>P#%KJDtJFdr$lSuXDlId2jhdkBv8$X;T9hU+JOAYIcu!; zIub(Ae8=r1C>q27|11|hAegIB!193S`pBR;2;U1)yeWP>L@JU$4Dzs`BOIRS#w+I@UEcLe$gQ8=u} zE($c6L6*=^izp{uff-|V77$OH#!Uh=UM9+9{K3frfv%Yn!0%isyruxm1Syubbd?rM zJb>KV)rOcUyiSpcpp=5e(45b5u$LMg74~P^u0|&wiN|woA0OX3>Y!QB7=GovBW6HN zI)ak}=tXkgrj2-4TF$a*Ca5>n{H=F}A*d>Ys;qteY)j2NDPoLBsO1NAB>M7zSK57@ zO*@Uv-vX^&k#COlMoHFgB@3FGPn!mPcfaH=6z0Rm+uKv+Df+xhf)sdZYQ=QXI zd}FUn;?77z&{_|_6#v`tp9VR!SEH$H-p_Why6|b_>~1IK7X4opG(|{eJ_(gJVVmGUWx z-K+VL5-sz4IX5<&N+hI1Z+>*y7|W^&F1^XU=&4jy|D5~L8sckI*PF)dkt&R*#(uN; z%(4dwfA*Z2YwtWymimYV+D{n$7L7DTjqHsyCqBS}-`G7tD(^#ge(Yr*v=f=1ii%_b z7)l4>zQDgd`htW^oBAVQpt+Rx0*|t=?jaB%tM`7OJskxfOV=wg&ofA`1vjnig54O7 zXU_;K*RN}oB`5PJG!{3zcvtAAew7Rn$expod}B0j-)fav{CdNU&wfjCYwlNj#nkIf z2XS|#!KGg)x^^AbkvequTdHI27Q_m+(*5&Tp3emeQ{Z)6pcPPEsNA(K1>|Fv5V7L; zneK4Uj9e-{a{bAO8R|d zRxGk-<5`>jc>%*jO{BT~c4vf4zqHY64$n-rRN^Cc&gV-wH&hbd?QQ-1kiYkKvZK&K zh5rz~dWKFtNh#20u#sL@XR1O-;z{$M_8j|5 zol}QFaw@9f`!XSB&Ut@O>V?CV30mq#LQHesR!Hqiqf1ra2<<>-qTBv#N0F|U*+{l4 zTqdKtVsq-(9?8_8KzjA^*AK;Yh~eI|_DnZ-Dnbrz@`>JSuXsoF>0iExXwW~NC`B>I zbzpkR3AG3Op)v{(`G{16n>1w9sl%MYAYd*yHkP(kOU#2qt|e`qKrJSLG|GUnLxXXA zKhU1J*TO^JVD~Q>7Os!Eh+S!76MEuZtAir7LBp+UHItnZB4M?0oJDALsZ6FS&K zO77kk(lPf%qd2Y*ae9tmOqFHC?cR4Eb9J`{@ED2b`GXd9TI_a<770r{>&j?Xey-cp zQ+Xdg>^UsaDasyVAr#hTxLO_bAFCe4tg#Ayd@1StjSeyw%g} zis^j7IO>JwgH^fiN3Tp4=r%0Km8ZD9m<{&4HAtNO+~r$&zU}w#IB4&^wz@lG2L$X2XWbNw5WazM^}VL*tegJUK4Xn%sD2forxm!K!#?<~Xku43sJ?K6AkTRqiFJv(isCvr%lVTr39yTPgI~btqn)dt3vSRYq0%O+-ZzJ`Q>@3P%8ezfY!ELCoJC#PWeBgQ7H?+exXZ8U8yH`Ax6Mk?F4x@9!{^hb$98aRJ#GHYTa}9 z=0MGOjMmjfDY9Sd{3qE~&!_+-Kkpm^o%$5zKJwjZ1Q(tDs%{8lPXW!x>_MT$YrMav zTH|yrk(6r(kdOVNbrEIoU~b`+a+#%B=}c_&MAnE&|Lk7blKO676&?}sys%>Y8xe(_ zey{Qz!5p{c43BCE4P~14-w{o5^y}!@#f)^M$z0t%t+hxim+md^IU|^pJ5BL^=k$I_ z;qPR09%5s?#Yxa;#-#2u^4(NfJDvMNyu&J}kIo^c=1=Oqym3dcXTIKrA7hmu?tV>G zVC-pP9RLHS+~XFRt9U{0=JcC!McPr?Lt>nsv&vP-3M}g(up7kAi*4pciuHuLV~HM~ z!a=mK@y#W6!=_KSS3tcs@8Y*_46B;yWh7c1iDlhsK9oBwm6E@Y`B5>L zy=xcinLQ{k%v+{|7p0Zp5w@c5&O*cEdC*-`RalxHNc3cDp>u}rD{pO-h6S^O_~#hs zLOq3-dcCX2%0(UnGs4&>$}&ChEDxt%ZBl_3KYJ#%Ft#5W<4T8E%GQuBdA*v8kzB2z z^gs9X&hR2%ZRS@KqRQ|Dc}E7x?OLCUoasG+%j2&Dvy#G(trxO(-#isv{maw_7Y0#& z+EgsRf&o+MF@yv~Bxd>IQ9Qh6a>2!hHE)zAb^9zC>(|`=wALU`5l@sdYX&rT}S71eupbN)fQ~@k2(H#Z>VZSC*wO zZ0}p3WNO|Mr7Vl{OOauS$d>$x)u*4kNJxpW!+x$^F{s;arQ^|R@m})KqK<7BecPf+ ziYe~-=(S2eU!pf?<<~^N0~b_`{T`c&-bGN&K5ve=wX8sHbl|>fW%Jg}YVOyJI>OqW zSayF-HCnNKQ`60gHsiKw*-Z0Fm+H3JkV3xOPxEregOHh-XW@D&Vz0@Z*;@AnHyiWQ z8&_VXoE1c1PIUbq@M+$lVgw;uOq?YM1kcMTB--_STLdmzrK@FzLoPk4bRbrwz4|N) zZQ7OqkCoG_86wJeU7o6d4RDV|*m!-Ut(s`P6yzd`qccBT);sdm9GM~R(D!s4sNh%S+aZaq{(;!T}itI!zqntV1^CkD)V>4xes_A6%MoaX3#8U84pMI zBN*sO4xS2M-g}KnCvBYwK5f(R_}J|Xkd8N0m2kKPRslS5DGt5I5`2CzBE*r4EEM!3 zQv&BUdt_wfy-#hK>Rf^;$p{Pb4YPq$R2)3=>H74U_C%|2-eMi6mNbK6t53xbigHH~ z#l!vdwpOhHqy4_LRF>uGDjRUi{)$_yqjt)xAuGLg9@V*%qc+An^^)2S#3ZisZ@8ZH zB{@33yE7?!tGYDDAtxu+>U2+-#glLZ*Jfe$l~|lTI2R^`ft#)+3Lo|>ea5rkhD4t_B0QA?{udQGQW_> z|4=XCeIV(D+IJrj&UWvqzFEE{;(uncYWUDI@3-Ub;mAQ8&5+A2)V`zi2v9>);H5)Eqg?f6#})}CYe zvz7H>B&oak{D|x_!Dq_sX`!i7eG(|*nzC)HAl8J^PhA1|WirULU~FY?iKQ3W`eLf{ z@-r-Yp#R+aOsANvs4vptavH{~tkV=9T0#RfxthPU?H@WteBliY`&!W3utfN3FyYFj zj3;^Y`Az|1RV2Gou|51km&dnnMoH*lNGvdx6s$2GzFBbvI=~iDzuW)2_Rz%+u4Ojt z#3mIxtDNfGrGGwZHC}Vy9QS>o;|pS${=HS>NFMue%R{4b)lBtfw;76P0i&sfz;dgH zjSkj=b@CRa2i|j54}W%Y3dxzrDQQVZaB;j9(YZOOZbd~4N~do{#&C#;YnyC)TR4;03PQzb2jU06NeKO z5_@O2niaJ(3PhLL`V$;qu>-~uO>_4cS^-@7Jjfk zeY?;fupK!fY=)P@*W%hlOb`4jsF!gdlpZ=pRO4T6lN7zbmwe)sA)1X(UtPOf{5_Y*lo+(2^@zJZ2$`Msk?6^P@3^0w92pv4ZC_&Q5UdV0p4=+Lf`qbRV zCEz(d<|(W`9Go6!W``vd#wh`^&q4v3&b?xzEi#s_(SJdn_qtd0H9jX#t~Xm8$ytM z%CdNxNv`OV3l7&avT2^g2t)cN%4YSyyI2eXJgizS`vDBPf{GAS4+3J+<_$^w2+Q`` z4`Zbn-PU0tN!1RQQokRon4mRq#06Krb`^!@R2WBKXbf=J=0`64cBnN_ZyzJR;L0~u z&F(_5R4|jX5q}SR*#dx+M-fHwEa06_CNAh%#a#9eT!>T=Jog?AR`g09`q;o>?H5!} zM3Xe&X}sYN344a4nLK_BtXmw!E=q#yMS+!IGEPxqzn8qHFX1zaE=H2_%|q}UsiYae zl9C-)SC{^I9dz*z769Yj<88ry4cY?OyMQ63;QqA|)Y05p1KF1g7vr@`-3NT5{$NNS z@qr^kcJ2Ki)a>}WQ*aO9=hfF$Q$u%xi{S@_-o7|H#I1bVtyPSz7RuQ>g_91U#;O)} zh(ATb$G;5cgKkS4pp6}M{1T1_q4x7fIoMG`FBGW|dZH5T{s;3s9#0T61XBgSnz#); z2Nz+`qw2=&h&`v&P1%*=+X`4*kQ9P+pE>-#c{0Y6(JDhp6VkjQB!44(N53d)iF3g< zpgFJrJGk+iqQxtGFYNhGu8$9Zs%CzC)Bc-M=IEAiLyCW-2R)Ys;TbxMJc>+Cq=|A? zwW~IEYM_frZ(ln1-#E9A&sDITC%fb!?iM9q!rssyN}d2sfPpB5*x~$-9JD=x$6?nG z{t}FzaZFAW_aT0pJIh((a_sYiVbYzyF}!#G{(oQkg@9D?&oHsyJ$eCR^XmSI*Z>%J z`pLqv^n$ejP%bp-0b;KW{&C6CLNY)o*NWXy!#swIAXQiPrNHJpRi6>=kJ5J7Btp>) z0`3P&ia0LF|7-ul5Fzp7E|J9qig~pUrrZZH}Vn z_h3djk1&j~z{xbOSvmoWZSJ$V64lXKfn7m@-4)O+mDS@P2VU72E6(lX1VS?$ccxfm zyQN#u3Xm*hO>blFi z&({!2Jxw`!08Pz#LMW)zF_+pn!r@-K-4_0x=6)F=ZV~4&TRFFj49MH^^YJC54q(Gd zGP5d>z=zwV{Mc#D&2-av>9@#LSVI?cgIR;V_P6OcQFv9cHt5pB)Eve0JK`kYe-F4o zTks`pLUa6cT)Yc^F#xbNJcT@>VZo{V?dBAKhe>gER!j88aookC?^!*h`NerOIuLL2 zd1ziFr~r|gmOK{q7XlSy0WE7wlfQKv#^!50xEFnGrc{vr0S7c*n_Diyv0+y3tWZ;{ zMVHSYf5bJ{^_Y~84OE^;PWGVtA=@(o|32u7^JO8IGZh(?3Q zBrFsFcAS-4;~o@I3UF*l8SFGBS?XU3fYcpK+jm^YoqDJ`!4+@@i6Hkw>XkljY!*f+ zPfA~_uvTUGdbTjNDF%(Mk2!4m2(lL%0(nQSgLqoAe_Nh@jmM(vF`;D!6tYy)LZG4I zQf-hXB>A3RL}dE5)b^*ZyR6osLTmr~jw+Go@J2q27N6&ZzR1q^8MI+8Z;ol*Scp>r;kVmLdY#p0z zYHa-3a->3I1N8L-oWQm&lj*T@xRAF5_EKTNTM)4H%g!Zsf?9Nja>@zMoN2uSagIUdCog}4+fD#fK1UiQDbUJ#0nCDEoq z{alU?9jt1FvP6KbkUo`||6iZumplRy6Ttzv=6`-4gnMoP^8sPb$p#^;|NN)Vzd!Qt zbp2Bx{*|tO_sM@~2*?EZSFQZ3h5zH+`2RgmMi0*v;62-YS=r`y3j9fl%Zuea)bss6 D__yV@ literal 0 HcmV?d00001 diff --git a/spring-state-machine/bpmn/img/simple.png b/spring-state-machine/bpmn/img/simple.png new file mode 100644 index 0000000000000000000000000000000000000000..7a79bf1d895a401b2b59d4b093e770f3546bc3d3 GIT binary patch literal 22706 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 AC;$Ke literal 0 HcmV?d00001 diff --git a/spring-state-machine/bpmn/simple.bpmn b/spring-state-machine/bpmn/simple.bpmn new file mode 100644 index 0000000000..8ed463e9f9 --- /dev/null +++ b/spring-state-machine/bpmn/simple.bpmn @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-state-machine/pom.xml b/spring-state-machine/pom.xml new file mode 100644 index 0000000000..5393626083 --- /dev/null +++ b/spring-state-machine/pom.xml @@ -0,0 +1,31 @@ + + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + 4.0.0 + + baeldung-spring-state-machine + + 1.8 + 1.8 + + + + + org.springframework.statemachine + spring-statemachine-core + 1.2.3.RELEASE + + + junit + junit + 4.11 + test + + + \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java new file mode 100644 index 0000000000..971fc5dde7 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java @@ -0,0 +1,5 @@ +package com.baeldung.spring.stateMachine.applicationReview; + +public enum ApplicationReviewEvents { + APPROVE, REJECT +} diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java new file mode 100644 index 0000000000..1df2db1f86 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java @@ -0,0 +1,5 @@ +package com.baeldung.spring.stateMachine.applicationReview; + +public enum ApplicationReviewStates { + PEER_REVIEW, PRINCIPAL_REVIEW, APPROVED, REJECTED +} diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java new file mode 100644 index 0000000000..c55104a627 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java @@ -0,0 +1,74 @@ +package com.baeldung.spring.stateMachine.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +@Configuration +@EnableStateMachine +public class ForkJoinStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .fork("SFork") + .join("SJoin") + .end("SF") + .and() + .withStates() + .parent("SFork") + .initial("Sub1-1") + .end("Sub1-2") + .and() + .withStates() + .parent("SFork") + .initial("Sub2-1") + .end("Sub2-2"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("SFork").event("E1") + .and().withExternal() + .source("Sub1-1").target("Sub1-2").event("sub1") + .and().withExternal() + .source("Sub2-1").target("Sub2-2").event("sub2") + .and() + .withFork() + .source("SFork") + .target("Sub1-1") + .target("Sub2-1") + .and() + .withJoin() + .source("Sub1-2") + .source("Sub2-2") + .target("SJoin"); + } + + @Bean + public Guard mediumGuard() { + return (ctx) -> false; + } + + @Bean + public Guard highGuard() { + return (ctx) -> false; + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java new file mode 100644 index 0000000000..708dbd3077 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.stateMachine.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; + +@Configuration +@EnableStateMachine +public class HierarchicalStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .state("SI") + .end("SF") + .and() + .withStates() + .parent("SI") + .initial("SUB1") + .state("SUB2") + .end("SUBEND"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("SF").event("end") + .and().withExternal() + .source("SUB1").target("SUB2").event("se1") + .and().withExternal() + .source("SUB2").target("SUBEND").event("s-end"); + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java new file mode 100644 index 0000000000..e1bae10fb7 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java @@ -0,0 +1,60 @@ +package com.baeldung.spring.stateMachine.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +@Configuration +@EnableStateMachine +public class JunctionStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .junction("SJ") + .state("high") + .state("medium") + .state("low") + .end("SF"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("SJ").event("E1") + .and() + .withJunction() + .source("SJ") + .first("high", highGuard()) + .then("medium", mediumGuard()) + .last("low") + .and().withExternal() + .source("low").target("SF").event("end"); + } + + @Bean + public Guard mediumGuard() { + return (ctx) -> false; + } + + @Bean + public Guard highGuard() { + return (ctx) -> false; + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java new file mode 100644 index 0000000000..4e11851644 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java @@ -0,0 +1,53 @@ +package com.baeldung.spring.stateMachine.config; + +import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewEvents; +import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewStates; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +import java.util.Arrays; +import java.util.HashSet; + +@Configuration +@EnableStateMachine +public class SimpleEnumStateMachineConfiguration extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial(ApplicationReviewStates.PEER_REVIEW) + .state(ApplicationReviewStates.PRINCIPAL_REVIEW) + .end(ApplicationReviewStates.APPROVED) + .end(ApplicationReviewStates.REJECTED); + + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source(ApplicationReviewStates.PEER_REVIEW).target(ApplicationReviewStates.PRINCIPAL_REVIEW).event(ApplicationReviewEvents.APPROVE) + .and().withExternal() + .source(ApplicationReviewStates.PRINCIPAL_REVIEW).target(ApplicationReviewStates.APPROVED).event(ApplicationReviewEvents.APPROVE) + .and().withExternal() + .source(ApplicationReviewStates.PEER_REVIEW).target(ApplicationReviewStates.REJECTED).event(ApplicationReviewEvents.REJECT) + .and().withExternal() + .source(ApplicationReviewStates.PRINCIPAL_REVIEW).target(ApplicationReviewStates.REJECTED).event(ApplicationReviewEvents.REJECT); + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java new file mode 100644 index 0000000000..bb7556f97f --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java @@ -0,0 +1,105 @@ +package com.baeldung.spring.stateMachine.config; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.logging.Logger; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; +import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; +import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; +import org.springframework.statemachine.guard.Guard; + +@Configuration +@EnableStateMachine +public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapter { + + public static final Logger LOGGER = Logger.getLogger(SimpleStateMachineConfiguration.class.getName()); + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true) + .listener(new StateMachineListener()); + } + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .end("SF") + .states(new HashSet<>(Arrays.asList("S1", "S2"))) + .state("S4", executeAction(), errorAction()) + .stateEntry("S3", entryAction()) + .stateDo("S3", executeAction()) + .stateExit("S3", exitAction()); + + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions.withExternal() + .source("SI").target("S1").event("E1").action(initAction()) + .and().withExternal() + .source("S1").target("S2").event("E2") + .and().withExternal() + .source("SI").target("S3").event("E3") + .and().withExternal() + .source("S3").target("S4").event("E4").and().withExternal().source("S4").target("SF").event("end").guard(simpleGuard()) + .and().withExternal() + .source("S2").target("SF").event("end"); + } + + @Bean + public Guard simpleGuard() { + return (ctx) -> { + int approvalCount = (int) ctx.getExtendedState().getVariables().getOrDefault("approvalCount", 0); + return approvalCount > 0; + }; + } + + @Bean + public Action entryAction() { + return (ctx) -> { + LOGGER.info("Entry " + ctx.getTarget().getId()); + }; + } + + @Bean + public Action executeAction() { + return (ctx) -> { + LOGGER.info("Do " + ctx.getTarget().getId()); + int approvals = (int) ctx.getExtendedState().getVariables().getOrDefault("approvalCount", 0); + approvals++; + ctx.getExtendedState().getVariables().put("approvalCount", approvals); + }; + } + + @Bean + public Action exitAction() { + return (ctx) -> { + LOGGER.info("Exit " + ctx.getSource().getId() + " -> " + ctx.getTarget().getId()); + }; + } + + @Bean + public Action errorAction() { + return (ctx) -> { + LOGGER.info("Error " + ctx.getSource().getId() + ctx.getException()); + }; + } + + @Bean + public Action initAction() { + return (ctx) -> { + LOGGER.info(ctx.getTarget().getId()); + }; + } +} \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java new file mode 100644 index 0000000000..bb7859c683 --- /dev/null +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.stateMachine.config; + +import org.springframework.statemachine.listener.StateMachineListenerAdapter; +import org.springframework.statemachine.state.State; + +import java.util.logging.Logger; + +public class StateMachineListener extends StateMachineListenerAdapter { + + public static final Logger LOGGER = Logger.getLogger(StateMachineListener.class.getName()); + + @Override + public void stateChanged(State from, State to) { + LOGGER.info(String.format("Transitioned from %s to %s%n", from == null ? "none" : from.getId(), to.getId())); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java new file mode 100644 index 0000000000..416da5f0fe --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java @@ -0,0 +1,45 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.config.ForkJoinStateMachineConfiguration; + +public class ForkJoinStateMachineTest { + + @Test + public void whenForkStateEntered_thenMultipleSubStatesEntered() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ForkJoinStateMachineConfiguration.class); + StateMachine stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + + boolean success = stateMachine.sendEvent("E1"); + + assertTrue(success); + + assertTrue(Arrays.asList("SFork", "Sub1-1", "Sub2-1").containsAll(stateMachine.getState().getIds())); + } + + @Test + public void whenAllConfiguredJoinEntryStatesAreEntered_thenTransitionToJoinState() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ForkJoinStateMachineConfiguration.class); + StateMachine stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + + boolean success = stateMachine.sendEvent("E1"); + + assertTrue(success); + + assertTrue(Arrays.asList("SFork", "Sub1-1", "Sub2-1").containsAll(stateMachine.getState().getIds())); + + assertTrue(stateMachine.sendEvent("sub1")); + assertTrue(stateMachine.sendEvent("sub2")); + assertEquals("SJoin", stateMachine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java new file mode 100644 index 0000000000..3557a63211 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java @@ -0,0 +1,37 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.config.HierarchicalStateMachineConfiguration; + +public class HierarchicalStateMachineTest { + + @Test + public void whenTransitionToSubMachine_thenSubStateIsEntered() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(HierarchicalStateMachineConfiguration.class); + StateMachine stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + + + assertEquals(Arrays.asList("SI", "SUB1"), stateMachine.getState().getIds()); + + stateMachine.sendEvent("se1"); + + assertEquals(Arrays.asList("SI", "SUB2"), stateMachine.getState().getIds()); + + stateMachine.sendEvent("s-end"); + + assertEquals(Arrays.asList("SI", "SUBEND"), stateMachine.getState().getIds()); + + stateMachine.sendEvent("end"); + + assertEquals(1, stateMachine.getState().getIds().size()); + assertEquals("SF", stateMachine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java new file mode 100644 index 0000000000..d0c1225c9b --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java @@ -0,0 +1,24 @@ +package com.baeldung.spring.stateMachine; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.config.JunctionStateMachineConfiguration; + +public class JunctionStateMachineTest { + + @Test + public void whenTransitioningToJunction_thenArriveAtSubJunctionNode() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JunctionStateMachineConfiguration.class); + StateMachine stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + + stateMachine.sendEvent("E1"); + Assert.assertEquals("low", stateMachine.getState().getId()); + + stateMachine.sendEvent("end"); + Assert.assertEquals("SF", stateMachine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java new file mode 100644 index 0000000000..1fd7bd85f0 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java @@ -0,0 +1,33 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewEvents; +import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewStates; +import com.baeldung.spring.stateMachine.config.SimpleEnumStateMachineConfiguration; + +public class StateEnumMachineTest { + + private StateMachine stateMachine; + + @Before + public void setUp() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SimpleEnumStateMachineConfiguration.class); + stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + } + + @Test + public void whenStateMachineConfiguredWithEnums_thenStateMachineAcceptsEnumEvents() { + assertTrue(stateMachine.sendEvent(ApplicationReviewEvents.APPROVE)); + assertEquals(ApplicationReviewStates.PRINCIPAL_REVIEW, stateMachine.getState().getId()); + assertTrue(stateMachine.sendEvent(ApplicationReviewEvents.REJECT)); + assertEquals(ApplicationReviewStates.REJECTED, stateMachine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java new file mode 100644 index 0000000000..cdd1e951e0 --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java @@ -0,0 +1,35 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.springframework.statemachine.StateMachine; +import org.springframework.statemachine.config.StateMachineBuilder; + +public class StateMachineBuilderTest { + + @Test + public void whenUseStateMachineBuilder_thenBuildSuccessAndMachineWorks() throws Exception { + StateMachineBuilder.Builder builder = StateMachineBuilder.builder(); + builder.configureStates().withStates() + .initial("SI") + .state("S1") + .end("SF"); + + builder.configureTransitions() + .withExternal() + .source("SI").target("S1").event("E1") + .and().withExternal() + .source("S1").target("SF").event("E2"); + + StateMachine machine = builder.build(); + + machine.start(); + + machine.sendEvent("E1"); + assertEquals("S1", machine.getState().getId()); + + machine.sendEvent("E2"); + assertEquals("SF", machine.getState().getId()); + } +} diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java new file mode 100644 index 0000000000..9409f66f4f --- /dev/null +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java @@ -0,0 +1,51 @@ +package com.baeldung.spring.stateMachine; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.stateMachine.config.SimpleStateMachineConfiguration; + +public class StateMachineTest { + + private AnnotationConfigApplicationContext ctx; + private StateMachine stateMachine; + + @Before + public void setUp() { + ctx = new AnnotationConfigApplicationContext(SimpleStateMachineConfiguration.class); + stateMachine = ctx.getBean(StateMachine.class); + stateMachine.start(); + } + + @Test + public void whenSimpleStringStateMachineEvents_thenEndState() { + assertEquals("SI", stateMachine.getState().getId()); + + stateMachine.sendEvent("E1"); + assertEquals("S1", stateMachine.getState().getId()); + + stateMachine.sendEvent("E2"); + assertEquals("S2", stateMachine.getState().getId()); + } + + @Test + public void whenSimpleStringMachineActionState_thenActionExecuted() { + + stateMachine.sendEvent("E3"); + assertEquals("S3", stateMachine.getState().getId()); + + boolean acceptedE4 = stateMachine.sendEvent("E4"); + + assertTrue(acceptedE4); + assertEquals("S4", stateMachine.getState().getId()); + assertEquals(2, stateMachine.getExtendedState().getVariables().get("approvalCount")); + + stateMachine.sendEvent("end"); + assertEquals("SF", stateMachine.getState().getId()); + } +} From 6a0e1420649662bad2569bbc6186bf266fc32d1a Mon Sep 17 00:00:00 2001 From: Tomasz Sobala Date: Mon, 27 Mar 2017 21:28:15 +0200 Subject: [PATCH 062/102] BAEL-431 Exploring the Spring Boot TestRestTemplate (#1444) * injecting beans * XML-based configuration replaced with Java Config. * [BAEL-431] Exploring TestRestTemplate. * Revert of evaluation task "XML-based configuration replaced with Java Config." This reverts commit 66471cf0574c85f8ff514ec4caf5ba44ebba1a74. * Revert of evaluation task "injecting beans" This reverts commit d2ac20185e636245bc0ae0b4ccb952965de88e28. * [BAEL-431] fix to the tests in TestRestTemplateBasicLiveTest. --- spring-rest/pom.xml | 4 + .../client/TestRestTemplateBasicLiveTest.java | 118 ++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java diff --git a/spring-rest/pom.xml b/spring-rest/pom.xml index da26d8abe9..a9b208bcd2 100644 --- a/spring-rest/pom.xml +++ b/spring-rest/pom.xml @@ -29,6 +29,10 @@ org.springframework.boot spring-boot-devtools + + org.springframework.boot + spring-boot-test + diff --git a/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java b/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java new file mode 100644 index 0000000000..9f4319d857 --- /dev/null +++ b/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java @@ -0,0 +1,118 @@ +package org.baeldung.client; + +import okhttp3.Request; +import okhttp3.RequestBody; +import org.junit.Before; +import org.junit.Test; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import static org.baeldung.client.Consts.APPLICATION_PORT; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; + +public class TestRestTemplateBasicLiveTest { + + private RestTemplate restTemplate; + private static final String FOO_RESOURCE_URL = "http://localhost:" + APPLICATION_PORT + "/spring-rest/myfoos"; + private static final String URL_SECURED_BY_AUTHENTICATION = "http://browserspy.dk/password-ok.php"; + private static final String BASE_URL = "http://localhost:" + APPLICATION_PORT + "/spring-rest"; + + @Before + public void beforeTest() { + restTemplate = new RestTemplate(); + } + + // GET + @Test + public void givenTestRestTemplate_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate(); + ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenRestTemplateWrapper_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplate); + ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenRestTemplateBuilderWrapper_whenSendGetForEntity_thenStatusOk() { + RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); + restTemplateBuilder.build(); + TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplateBuilder); + ResponseEntity response = testRestTemplate.getForEntity(FOO_RESOURCE_URL + "/1", String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenRestTemplateWrapperWithCredentials_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplate, "test", "test"); + ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION + "/1", + String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenTestRestTemplateWithCredentials_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate("test", "test"); + ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION + "/1", + String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenTestRestTemplateWithBasicAuth_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate(); + ResponseEntity response = testRestTemplate.withBasicAuth("test", "test"). + getForEntity(URL_SECURED_BY_AUTHENTICATION + "/1", String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + @Test + public void givenTestRestTemplateWithCredentialsAndEnabledCookies_whenSendGetForEntity_thenStatusOk() { + TestRestTemplate testRestTemplate = new TestRestTemplate("test", "test", TestRestTemplate. + HttpClientOption.ENABLE_COOKIES); + ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION + "/1", + String.class); + assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); + } + + // HEAD + @Test + public void givenFooService_whenCallHeadForHeaders_thenReceiveAllHeaders() { + TestRestTemplate testRestTemplate = new TestRestTemplate(); + final HttpHeaders httpHeaders = testRestTemplate.headForHeaders(FOO_RESOURCE_URL); + assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON)); + } + + // POST + @Test + public void givenService_whenPostForObject_thenCreatedObjectIsReturned() { + TestRestTemplate testRestTemplate = new TestRestTemplate("test", "test"); + final RequestBody body = RequestBody.create(okhttp3.MediaType.parse("text/html; charset=utf-8"), + "{\"id\":1,\"name\":\"Jim\"}"); + final Request request = new Request.Builder().url(BASE_URL + "/users/detail").post(body).build(); + Object response = testRestTemplate.postForObject(URL_SECURED_BY_AUTHENTICATION, request, String.class); + assertTrue(response.toString().contains("Success")); + } + + // PUT + @Test + public void givenService_whenPutForObject_thenCreatedObjectIsReturned() { + TestRestTemplate testRestTemplate = new TestRestTemplate("test", "test"); + final RequestBody body = RequestBody.create(okhttp3.MediaType.parse("text/html; charset=utf-8"), + "{\"id\":1,\"name\":\"Jim\"}"); + final Request request = new Request.Builder().url(BASE_URL + "/users/detail").post(body).build(); + testRestTemplate.put(URL_SECURED_BY_AUTHENTICATION, request, String.class); + } + +} From 0c8aa7e46deb161c6fbe863d9e5acb2f77685370 Mon Sep 17 00:00:00 2001 From: azrairshad Date: Mon, 27 Mar 2017 14:17:58 -0700 Subject: [PATCH 063/102] Added an example for array copy via stream (#1510) --- .../baeldung/arraycopy/ArrayCopyUtilTest.java | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java b/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java index 2c9a97c496..060f3c3109 100644 --- a/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java +++ b/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java @@ -144,7 +144,31 @@ public class ArrayCopyUtilTest { //change in employees' element didn't change in the copied array Assert.assertFalse(copiedArray[0].getName().equals(employees[0].getName())); } - + + @Test + public void givenArraysOfNonPrimitiveType_whenCopiedViaStream_thenDoShallowCopy(){ + Employee[] copiedArray = Arrays.stream(employees).toArray(Employee[]::new); + + Assert.assertNotNull(copiedArray); + Assert.assertTrue(copiedArray.length == employees.length); + employees[0].setName(employees[0].getName()+"_Changed"); + //change in employees' element didn't change in the copied array + Assert.assertTrue(copiedArray[0].getName().equals(employees[0].getName())); + } + + @Test + public void givenArraysOfPrimitiveType_whenCopiedViaStream_thenSuccessful(){ + String[] strArray = {"orange", "red", "green'"}; + + String[] copiedArray = Arrays.stream(strArray).toArray(String[]::new); + + Assert.assertNotNull(copiedArray); + Assert.assertTrue(copiedArray.length == strArray.length); + Assert.assertTrue(copiedArray[0] == strArray[0]); + Assert.assertTrue(copiedArray[1] == strArray[1]); + Assert.assertTrue(copiedArray[2] == strArray[2]); + } + private Address[] createAddressArray(){ Address[] addresses = new Address[1]; addresses[0] = createAddress(); From 85969c69d22e0d9313648d417bbdf6e51915fe9d Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Tue, 28 Mar 2017 08:17:00 +0200 Subject: [PATCH 064/102] State machine refactor (#1520) * State machine refactor * Add surefire plugin * Refactor --- spring-state-machine/pom.xml | 29 ++++++++++++++++++- .../ApplicationReviewEvents.java | 2 +- .../ApplicationReviewStates.java | 2 +- .../ForkJoinStateMachineConfiguration.java | 2 +- ...HierarchicalStateMachineConfiguration.java | 2 +- .../JunctionStateMachineConfiguration.java | 2 +- .../SimpleEnumStateMachineConfiguration.java | 12 ++------ .../SimpleStateMachineConfiguration.java | 2 +- .../config/StateMachineListener.java | 2 +- .../ForkJoinStateMachineTest.java | 13 ++++----- .../HierarchicalStateMachineTest.java | 12 ++++---- .../JunctionStateMachineTest.java | 5 ++-- .../statemachine/StateEnumMachineTest.java | 13 ++++----- .../statemachine/StateMachineBuilderTest.java | 2 +- ....java => StateMachineIntegrationTest.java} | 13 ++++----- spring-zuul/pom.xml | 1 - 16 files changed, 65 insertions(+), 49 deletions(-) rename spring-state-machine/src/test/java/com/baeldung/spring/statemachine/{StateMachineTest.java => StateMachineIntegrationTest.java} (88%) diff --git a/spring-state-machine/pom.xml b/spring-state-machine/pom.xml index 5393626083..bec03c39e8 100644 --- a/spring-state-machine/pom.xml +++ b/spring-state-machine/pom.xml @@ -9,7 +9,7 @@ 4.0.0 - baeldung-spring-state-machine + spring-state-machine 1.8 1.8 @@ -27,5 +27,32 @@ 4.11 test + + + com.jayway.awaitility + awaitility + 1.7.0 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + + \ No newline at end of file diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java index 971fc5dde7..300bd6027e 100644 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewEvents.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.stateMachine.applicationReview; +package com.baeldung.spring.statemachine.applicationreview; public enum ApplicationReviewEvents { APPROVE, REJECT diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java index 1df2db1f86..3d173e7471 100644 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/applicationreview/ApplicationReviewStates.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.stateMachine.applicationReview; +package com.baeldung.spring.statemachine.applicationreview; public enum ApplicationReviewStates { PEER_REVIEW, PRINCIPAL_REVIEW, APPROVED, REJECTED diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java index c55104a627..3a3e632c51 100644 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/ForkJoinStateMachineConfiguration.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.stateMachine.config; +package com.baeldung.spring.statemachine.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java index 708dbd3077..2bf9405c39 100644 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/HierarchicalStateMachineConfiguration.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.stateMachine.config; +package com.baeldung.spring.statemachine.config; import org.springframework.context.annotation.Configuration; import org.springframework.statemachine.config.EnableStateMachine; diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java index e1bae10fb7..2f48a9dbb5 100644 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/JunctionStateMachineConfiguration.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.stateMachine.config; +package com.baeldung.spring.statemachine.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java index 4e11851644..5339dea7d0 100644 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleEnumStateMachineConfiguration.java @@ -1,19 +1,13 @@ -package com.baeldung.spring.stateMachine.config; +package com.baeldung.spring.statemachine.config; -import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewEvents; -import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewStates; -import org.springframework.context.annotation.Bean; +import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewEvents; +import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewStates; import org.springframework.context.annotation.Configuration; -import org.springframework.statemachine.action.Action; import org.springframework.statemachine.config.EnableStateMachine; import org.springframework.statemachine.config.StateMachineConfigurerAdapter; import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; -import org.springframework.statemachine.guard.Guard; - -import java.util.Arrays; -import java.util.HashSet; @Configuration @EnableStateMachine diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java index bb7556f97f..e9b448f6e7 100644 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.stateMachine.config; +package com.baeldung.spring.statemachine.config; import java.util.Arrays; import java.util.HashSet; diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java index bb7859c683..47a274404e 100644 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/StateMachineListener.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.stateMachine.config; +package com.baeldung.spring.statemachine.config; import org.springframework.statemachine.listener.StateMachineListenerAdapter; import org.springframework.statemachine.state.State; diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java index 416da5f0fe..7b4b1928ea 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java @@ -1,15 +1,14 @@ -package com.baeldung.spring.stateMachine; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; +package com.baeldung.spring.statemachine; +import com.baeldung.spring.statemachine.config.ForkJoinStateMachineConfiguration; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.statemachine.StateMachine; -import com.baeldung.spring.stateMachine.config.ForkJoinStateMachineConfiguration; +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class ForkJoinStateMachineTest { diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java index 3557a63211..d2944be173 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java @@ -1,14 +1,14 @@ -package com.baeldung.spring.stateMachine; - -import static org.junit.Assert.assertEquals; - -import java.util.Arrays; +package com.baeldung.spring.statemachine; +import com.baeldung.spring.statemachine.config.HierarchicalStateMachineConfiguration; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.statemachine.StateMachine; -import com.baeldung.spring.stateMachine.config.HierarchicalStateMachineConfiguration; +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; + public class HierarchicalStateMachineTest { diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java index d0c1225c9b..f01683638b 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java @@ -1,17 +1,16 @@ -package com.baeldung.spring.stateMachine; +package com.baeldung.spring.statemachine; import org.junit.Assert; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.statemachine.StateMachine; -import com.baeldung.spring.stateMachine.config.JunctionStateMachineConfiguration; public class JunctionStateMachineTest { @Test public void whenTransitioningToJunction_thenArriveAtSubJunctionNode() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JunctionStateMachineConfiguration.class); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration.class); StateMachine stateMachine = ctx.getBean(StateMachine.class); stateMachine.start(); diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java index 1fd7bd85f0..257ed8768c 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java @@ -1,16 +1,15 @@ -package com.baeldung.spring.stateMachine; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +package com.baeldung.spring.statemachine; +import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewEvents; +import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewStates; +import com.baeldung.spring.statemachine.config.SimpleEnumStateMachineConfiguration; import org.junit.Before; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.statemachine.StateMachine; -import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewEvents; -import com.baeldung.spring.stateMachine.applicationReview.ApplicationReviewStates; -import com.baeldung.spring.stateMachine.config.SimpleEnumStateMachineConfiguration; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class StateEnumMachineTest { diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java index cdd1e951e0..a5e4d07f18 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineBuilderTest.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.stateMachine; +package com.baeldung.spring.statemachine; import static org.junit.Assert.assertEquals; diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java similarity index 88% rename from spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java rename to spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java index 9409f66f4f..d7b26eeb97 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java @@ -1,16 +1,15 @@ -package com.baeldung.spring.stateMachine; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +package com.baeldung.spring.statemachine; +import com.baeldung.spring.statemachine.config.SimpleStateMachineConfiguration; import org.junit.Before; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.statemachine.StateMachine; -import com.baeldung.spring.stateMachine.config.SimpleStateMachineConfiguration; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; -public class StateMachineTest { +public class StateMachineIntegrationTest { private AnnotationConfigApplicationContext ctx; private StateMachine stateMachine; @@ -34,7 +33,7 @@ public class StateMachineTest { } @Test - public void whenSimpleStringMachineActionState_thenActionExecuted() { + public void whenSimpleStringMachineActionState_thenActionExecuted() throws InterruptedException { stateMachine.sendEvent("E3"); assertEquals("S3", stateMachine.getState().getId()); diff --git a/spring-zuul/pom.xml b/spring-zuul/pom.xml index 50b20b8791..02fe589a3a 100644 --- a/spring-zuul/pom.xml +++ b/spring-zuul/pom.xml @@ -45,7 +45,6 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} true From a18d779294b7c023f4167774ec811e2cf331955d Mon Sep 17 00:00:00 2001 From: Tian Baoqiang Date: Tue, 28 Mar 2017 17:56:57 +0800 Subject: [PATCH 065/102] BAEL-636: add standalone deployment (#1521) --- spring-5/pom.xml | 7 -- .../com/baeldung/functional/RootServlet.java | 87 +++++++++++++++++++ .../java/com/baeldung/web/FooController.java | 3 +- spring-5/src/main/webapp/WEB-INF/web.xml | 21 +++++ 4 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 spring-5/src/main/java/com/baeldung/functional/RootServlet.java create mode 100644 spring-5/src/main/webapp/WEB-INF/web.xml diff --git a/spring-5/pom.xml b/spring-5/pom.xml index 59bead4b73..6c52e6c8cc 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -40,13 +40,6 @@ spring-boot-starter-webflux - - - io.projectreactor - reactor-core - 3.0.6.BUILD-SNAPSHOT - - org.apache.commons diff --git a/spring-5/src/main/java/com/baeldung/functional/RootServlet.java b/spring-5/src/main/java/com/baeldung/functional/RootServlet.java new file mode 100644 index 0000000000..457206221b --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/RootServlet.java @@ -0,0 +1,87 @@ +package com.baeldung.functional; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.http.server.reactive.ServletHttpHandlerAdapter; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; + +import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers; +import static org.springframework.web.reactive.function.BodyExtractors.toFormData; +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.RequestPredicates.*; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; +import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +public class RootServlet extends ServletHttpHandlerAdapter { + + public RootServlet() { + this(WebHttpHandlerBuilder + .webHandler(toHttpHandler(routingFunction())) + .prependFilter(new IndexRewriteFilter()) + .build()); + } + + private RootServlet(HttpHandler httpHandler) { + super(httpHandler); + } + + private static final Actor BRAD_PITT = new Actor("Brad", "Pitt"); + private static final Actor TOM_HANKS = new Actor("Tom", "Hanks"); + private static final List actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS)); + + private static RouterFunction routingFunction() { + + return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))) + .andRoute(POST("/login"), serverRequest -> serverRequest + .body(toFormData()) + .map(MultiValueMap::toSingleValueMap) + .map(formData -> { + System.out.println("form data: " + formData.toString()); + if ("baeldung".equals(formData.get("user")) && "you_know_what_to_do".equals(formData.get("token"))) { + return ok() + .body(Mono.just("welcome back!"), String.class) + .block(); + } + return ServerResponse + .badRequest() + .build() + .block(); + })) + .andRoute(POST("/upload"), serverRequest -> serverRequest + .body(toDataBuffers()) + .collectList() + .map(dataBuffers -> { + AtomicLong atomicLong = new AtomicLong(0); + dataBuffers.forEach(d -> atomicLong.addAndGet(d + .asByteBuffer() + .array().length)); + System.out.println("data length:" + atomicLong.get()); + return ok() + .body(fromObject(atomicLong.toString())) + .block(); + })) + .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) + .andNest(path("/actor"), route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest + .bodyToMono(Actor.class) + .doOnNext(actors::add) + .then(ok().build()))) + .filter((request, next) -> { + System.out.println("Before handler invocation: " + request.path()); + return next.handle(request); + }); + + } + +} diff --git a/spring-5/src/main/java/com/baeldung/web/FooController.java b/spring-5/src/main/java/com/baeldung/web/FooController.java index de6928033e..d0b69e707e 100644 --- a/spring-5/src/main/java/com/baeldung/web/FooController.java +++ b/spring-5/src/main/java/com/baeldung/web/FooController.java @@ -1,6 +1,7 @@ package com.baeldung.web; import java.util.List; +import java.util.Optional; import javax.validation.constraints.Max; import javax.validation.constraints.Min; @@ -33,7 +34,7 @@ public class FooController { @ResponseBody @Validated public Foo findById(@PathVariable @Min(0) final long id) { - return repo.findOne(id); + return repo.findOne(id).orElse(null); } @RequestMapping(method = RequestMethod.GET) diff --git a/spring-5/src/main/webapp/WEB-INF/web.xml b/spring-5/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..bfcf43dad2 --- /dev/null +++ b/spring-5/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + + Spring Functional Application + + + functional + com.baeldung.functional.RootServlet + 1 + true + + + functional + / + + + + \ No newline at end of file From 0bc9bfbacbd10b0673a65ec52a0293da2db7f6ff Mon Sep 17 00:00:00 2001 From: Tian Baoqiang Date: Tue, 28 Mar 2017 19:50:03 +0800 Subject: [PATCH 066/102] BAEL-636: register servlet using java config (#1525) --- .../FunctionalSpringBootApplication.java | 87 +++++++++++++++++++ .../com/baeldung/functional/RootServlet.java | 2 +- 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 spring-5/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java diff --git a/spring-5/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java b/spring-5/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java new file mode 100644 index 0000000000..258a23a2bf --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java @@ -0,0 +1,87 @@ +package com.baeldung.functional; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import reactor.core.publisher.Flux; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.server.RequestPredicates.*; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; +import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +@SpringBootApplication +@ComponentScan(basePackages = { "com.baeldung.functional" }) +public class FunctionalSpringBootApplication { + + private static final Actor BRAD_PITT = new Actor("Brad", "Pitt"); + private static final Actor TOM_HANKS = new Actor("Tom", "Hanks"); + private static final List actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS)); + + private RouterFunction routingFunction() { + FormHandler formHandler = new FormHandler(); + + RouterFunction restfulRouter = route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest + .bodyToMono(Actor.class) + .doOnNext(actors::add) + .then(ok().build())); + + return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))) + .andRoute(POST("/login"), formHandler::handleLogin) + .andRoute(POST("/upload"), formHandler::handleUpload) + .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/"))) + .andNest(path("/actor"), restfulRouter) + .filter((request, next) -> { + System.out.println("Before handler invocation: " + request.path()); + return next.handle(request); + }); + } + + @Bean + public ServletRegistrationBean servletRegistrationBean() throws Exception { + HttpHandler httpHandler = WebHttpHandlerBuilder + .webHandler(toHttpHandler(routingFunction())) + .prependFilter(new IndexRewriteFilter()) + .build(); + ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(new RootServlet(httpHandler), "/"); + registrationBean.setLoadOnStartup(1); + registrationBean.setAsyncSupported(true); + return registrationBean; + } + + @Configuration + @EnableWebSecurity + @Profile("!https") + static class SecurityConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(final HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest() + .permitAll(); + } + } + + public static void main(String[] args) { + SpringApplication.run(FunctionalSpringBootApplication.class, args); + } + +} diff --git a/spring-5/src/main/java/com/baeldung/functional/RootServlet.java b/spring-5/src/main/java/com/baeldung/functional/RootServlet.java index 457206221b..54892c829f 100644 --- a/spring-5/src/main/java/com/baeldung/functional/RootServlet.java +++ b/spring-5/src/main/java/com/baeldung/functional/RootServlet.java @@ -33,7 +33,7 @@ public class RootServlet extends ServletHttpHandlerAdapter { .build()); } - private RootServlet(HttpHandler httpHandler) { + RootServlet(HttpHandler httpHandler) { super(httpHandler); } From 70d8fecc54c9c7dd5812a6e3c3329e81e3f15c49 Mon Sep 17 00:00:00 2001 From: Parth Joshi Date: Tue, 28 Mar 2017 18:29:33 +0530 Subject: [PATCH 067/102] Comparator comparing (#1515) * Initial commit for Comparator.comparing article. * Changes in the code as per suggestions in review. * Change in test names as per suggestions... * Changes in tests names for nullFirst and nullLast cases * clean up. --- .../java8/comparator/Java8ComparatorTest.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java b/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java index 57e3898274..ebcbb7a3fc 100644 --- a/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java +++ b/core-java/src/test/java/com/baeldung/java8/comparator/Java8ComparatorTest.java @@ -76,6 +76,15 @@ public class Java8ComparatorTest { // System.out.println(Arrays.toString(employees)); assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); } + + @Test + public void whenReversed_thenSortedByNameDesc() { + Comparator employeeNameComparator = Comparator.comparing(Employee::getName); + Comparator employeeNameComparatorReversed = employeeNameComparator.reversed(); + Arrays.sort(employees, employeeNameComparatorReversed); +// System.out.println(Arrays.toString(employees)); + assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); + } @Test public void whenComparingInt_thenSortedByAge() { @@ -153,4 +162,6 @@ public class Java8ComparatorTest { assertTrue(Arrays.equals(someMoreEmployees, sortedEmployeesByNameAge)); } -} \ No newline at end of file + +} + From ef91c379b74f6645f53c348f96234bf19df44f4f Mon Sep 17 00:00:00 2001 From: ahamedm Date: Tue, 28 Mar 2017 18:50:00 +0400 Subject: [PATCH 068/102] BAEL-696 Implement OR in the REST API Query Language (#1518) * Dependency Injection Types, XML-Config, Java-Config, Test Classes * Formatting done with Formatter Configuration in Eclipse * REST Query Lang - Adv Search Ops - Improvement - C1 * REST Query Lang - Adv Search Ops - Improvement - C2 * BAEL-696 Code formatting * REST Query Lang - Adv Search Ops - Improvement - C3 * BAEL-636: add standalone deployment (#1521) * BAEL-696 Formatting --- .gitignore | 2 + .../persistence/IEnhancedSpecification.java | 10 - .../dao/GenericSpecificationsBuilder.java | 98 +++--- .../dao/UserSpecificationsBuilder.java | 18 +- .../web/controller/UserController.java | 298 +++++++++--------- .../baeldung/web/util/SearchOperation.java | 8 +- .../baeldung/web/util/SpecSearchCriteria.java | 14 +- .../JPASpecificationIntegrationTest.java | 23 +- 8 files changed, 250 insertions(+), 221 deletions(-) delete mode 100644 spring-security-rest-full/src/main/java/org/baeldung/persistence/IEnhancedSpecification.java diff --git a/.gitignore b/.gitignore index 784627b616..f3fa30f3e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +*/bin/* + *.class # Package Files # diff --git a/spring-security-rest-full/src/main/java/org/baeldung/persistence/IEnhancedSpecification.java b/spring-security-rest-full/src/main/java/org/baeldung/persistence/IEnhancedSpecification.java deleted file mode 100644 index 58d08a161e..0000000000 --- a/spring-security-rest-full/src/main/java/org/baeldung/persistence/IEnhancedSpecification.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.baeldung.persistence; - -import org.springframework.data.jpa.domain.Specification; - -public interface IEnhancedSpecification extends Specification { - - default boolean isOfLowPrecedence() { - return false; - } -} diff --git a/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/GenericSpecificationsBuilder.java b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/GenericSpecificationsBuilder.java index 4936c2e1c1..45c015f233 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/GenericSpecificationsBuilder.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/GenericSpecificationsBuilder.java @@ -1,6 +1,7 @@ package org.baeldung.persistence.dao; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; @@ -12,53 +13,64 @@ import org.springframework.data.jpa.domain.Specifications; public class GenericSpecificationsBuilder { - private final List params; + private final List params; - public GenericSpecificationsBuilder() { - this.params = new ArrayList<>(); - } + public GenericSpecificationsBuilder() { + this.params = new ArrayList<>(); + } - public final GenericSpecificationsBuilder with(final String key, final String operation, final Object value, - final String prefix, final String suffix) { - return with(null, key, operation, value, prefix, suffix); - } + public final GenericSpecificationsBuilder with(final String key, final String operation, final Object value, final String prefix, final String suffix) { + return with(null, key, operation, value, prefix, suffix); + } - public final GenericSpecificationsBuilder with(final String precedenceIndicator, final String key, - final String operation, final Object value, final String prefix, final String suffix) { - SearchOperation op = SearchOperation.getSimpleOperation(operation.charAt(0)); - if (op != null) { - if (op == SearchOperation.EQUALITY) // the operation may be complex operation - { - final boolean startWithAsterisk = prefix != null && prefix.contains(SearchOperation.ZERO_OR_MORE_REGEX); - final boolean endWithAsterisk = suffix != null && suffix.contains(SearchOperation.ZERO_OR_MORE_REGEX); + public final GenericSpecificationsBuilder with(final String precedenceIndicator, final String key, final String operation, final Object value, final String prefix, final String suffix) { + SearchOperation op = SearchOperation.getSimpleOperation(operation.charAt(0)); + if (op != null) { + if (op == SearchOperation.EQUALITY) // the operation may be complex operation + { + final boolean startWithAsterisk = prefix != null && prefix.contains(SearchOperation.ZERO_OR_MORE_REGEX); + final boolean endWithAsterisk = suffix != null && suffix.contains(SearchOperation.ZERO_OR_MORE_REGEX); - if (startWithAsterisk && endWithAsterisk) { - op = SearchOperation.CONTAINS; - } else if (startWithAsterisk) { - op = SearchOperation.ENDS_WITH; - } else if (endWithAsterisk) { - op = SearchOperation.STARTS_WITH; - } - } - params.add(new SpecSearchCriteria(precedenceIndicator, key, op, value)); - } - return this; - } + if (startWithAsterisk && endWithAsterisk) { + op = SearchOperation.CONTAINS; + } else if (startWithAsterisk) { + op = SearchOperation.ENDS_WITH; + } else if (endWithAsterisk) { + op = SearchOperation.STARTS_WITH; + } + } + params.add(new SpecSearchCriteria(precedenceIndicator, key, op, value)); + } + return this; + } - public Specification build(Function> converter) { + public Specification build(Function> converter) { + + if (params.size() == 0) { + return null; + } + + params.sort(Comparator.comparing(SpecSearchCriteria::isOrPredicate)); + + final List> specs = params + .stream() + .map(converter) + .collect(Collectors.toCollection(ArrayList::new)); + + Specification result = specs.get(0); + + for (int idx = 1; idx < specs.size(); idx++) { + result = params + .get(idx) + .isOrPredicate() + ? Specifications + .where(result) + .or(specs.get(idx)) + : Specifications + .where(result) + .and(specs.get(idx)); + } + return result; + } - if (params.size() == 0) - return null; - - params.sort((spec0, spec1) -> Boolean.compare(spec0.isLowPrecedence(), spec1.isLowPrecedence())); - - final List> specs = params.stream().map(converter).collect(Collectors.toCollection(ArrayList::new)); - - Specification result = specs.get(0); - - for (int idx = 1; idx < specs.size(); idx++) { - result=params.get(idx).isLowPrecedence()? Specifications.where(result).or(specs.get(idx)): Specifications.where(result).and(specs.get(idx)); - } - return result; - } } diff --git a/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecificationsBuilder.java b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecificationsBuilder.java index 8a10163f51..bbcb521241 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecificationsBuilder.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/persistence/dao/UserSpecificationsBuilder.java @@ -1,14 +1,15 @@ package org.baeldung.persistence.dao; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + import org.baeldung.persistence.model.User; import org.baeldung.web.util.SearchOperation; import org.baeldung.web.util.SpecSearchCriteria; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specifications; -import java.util.ArrayList; -import java.util.List; - public final class UserSpecificationsBuilder { private final List params; @@ -48,14 +49,17 @@ public final class UserSpecificationsBuilder { if (params.size() == 0) return null; - params.sort((spec0, spec1) -> { - return Boolean.compare(spec0.isLowPrecedence(), spec1.isLowPrecedence()); - }); + params.sort(Comparator.comparing(SpecSearchCriteria::isOrPredicate)); Specification result = new UserSpecification(params.get(0)); for (int i = 1; i < params.size(); i++) { - result = params.get(i).isLowPrecedence() ? Specifications.where(result).or(new UserSpecification(params.get(i))) : Specifications.where(result).and(new UserSpecification(params.get(i))); + result = params.get(i) + .isOrPredicate() + ? Specifications.where(result) + .or(new UserSpecification(params.get(i))) + : Specifications.where(result) + .and(new UserSpecification(params.get(i))); } diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/controller/UserController.java b/spring-security-rest-full/src/main/java/org/baeldung/web/controller/UserController.java index fff089a62b..4c21d9836d 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/web/controller/UserController.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/web/controller/UserController.java @@ -1,141 +1,157 @@ -package org.baeldung.web.controller; - -import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; -import com.querydsl.core.types.Predicate; -import com.querydsl.core.types.dsl.BooleanExpression; -import cz.jirutka.rsql.parser.RSQLParser; -import cz.jirutka.rsql.parser.ast.Node; -import org.baeldung.persistence.dao.*; -import org.baeldung.persistence.dao.rsql.CustomRsqlVisitor; -import org.baeldung.persistence.model.MyUser; -import org.baeldung.persistence.model.User; -import org.baeldung.web.util.SearchCriteria; -import org.baeldung.web.util.SearchOperation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.querydsl.binding.QuerydslPredicate; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -//@EnableSpringDataWebSupport -@Controller -@RequestMapping(value = "/auth/") -public class UserController { - - @Autowired - private IUserDAO service; - - @Autowired - private UserRepository dao; - - @Autowired - private MyUserRepository myUserRepository; - - public UserController() { - super(); - } - - // API - READ - - @RequestMapping(method = RequestMethod.GET, value = "/users") - @ResponseBody - public List findAll(@RequestParam(value = "search", required = false) final String search) { - final List params = new ArrayList(); - if (search != null) { - final Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),"); - final Matcher matcher = pattern.matcher(search + ","); - while (matcher.find()) { - params.add(new SearchCriteria(matcher.group(1), matcher.group(2), matcher.group(3))); - } - } - return service.searchUser(params); - } - - @RequestMapping(method = RequestMethod.GET, value = "/users/spec") - @ResponseBody - public List findAllBySpecification(@RequestParam(value = "search") final String search) { - final UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); - final String operationSetExper = Joiner.on("|").join(SearchOperation.SIMPLE_OPERATION_SET); - final Pattern pattern = Pattern.compile("(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),"); - final Matcher matcher = pattern.matcher(search + ","); - while (matcher.find()) { - builder.with(matcher.group(1), matcher.group(2), matcher.group(4), matcher.group(3), matcher.group(5)); - } - - final Specification spec = builder.build(); - return dao.findAll(spec); - } - - @RequestMapping(method = RequestMethod.GET, value = "/users/espec") - @ResponseBody - public List findAllByOptionalSpecification(@RequestParam(value = "search") final String search) { - final Specification spec = resolveSpecification(search); - return dao.findAll(spec); - } - - protected Specification resolveSpecification(String searchParameters) { - - final UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); - final String operationSetExper = Joiner.on("|").join(SearchOperation.SIMPLE_OPERATION_SET); - final Pattern pattern = Pattern.compile("(\\p{Punct}?)(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),"); - final Matcher matcher = pattern.matcher(searchParameters + ","); - while (matcher.find()) { - builder.with(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(5), matcher.group(4), matcher.group(6)); - } - return builder.build(); - } - - @RequestMapping(method = RequestMethod.GET, value = "/myusers") - @ResponseBody - public Iterable findAllByQuerydsl(@RequestParam(value = "search") final String search) { - final MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder(); - if (search != null) { - final Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),"); - final Matcher matcher = pattern.matcher(search + ","); - while (matcher.find()) { - builder.with(matcher.group(1), matcher.group(2), matcher.group(3)); - } - } - final BooleanExpression exp = builder.build(); - return myUserRepository.findAll(exp); - } - - @RequestMapping(method = RequestMethod.GET, value = "/users/rsql") - @ResponseBody - public List findAllByRsql(@RequestParam(value = "search") final String search) { - final Node rootNode = new RSQLParser().parse(search); - final Specification spec = rootNode.accept(new CustomRsqlVisitor()); - return dao.findAll(spec); - } - - @RequestMapping(method = RequestMethod.GET, value = "/api/myusers") - @ResponseBody - public Iterable findAllByWebQuerydsl(@QuerydslPredicate(root = MyUser.class) final Predicate predicate) { - return myUserRepository.findAll(predicate); - } - - // API - WRITE - - @RequestMapping(method = RequestMethod.POST, value = "/users") - @ResponseStatus(HttpStatus.CREATED) - public void create(@RequestBody final User resource) { - Preconditions.checkNotNull(resource); - dao.save(resource); - } - - @RequestMapping(method = RequestMethod.POST, value = "/myusers") - @ResponseStatus(HttpStatus.CREATED) - public void addMyUser(@RequestBody final MyUser resource) { - Preconditions.checkNotNull(resource); - myUserRepository.save(resource); - - } - -} +package org.baeldung.web.controller; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.baeldung.persistence.dao.IUserDAO; +import org.baeldung.persistence.dao.MyUserPredicatesBuilder; +import org.baeldung.persistence.dao.MyUserRepository; +import org.baeldung.persistence.dao.UserRepository; +import org.baeldung.persistence.dao.UserSpecificationsBuilder; +import org.baeldung.persistence.dao.rsql.CustomRsqlVisitor; +import org.baeldung.persistence.model.MyUser; +import org.baeldung.persistence.model.User; +import org.baeldung.web.util.SearchCriteria; +import org.baeldung.web.util.SearchOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.querydsl.binding.QuerydslPredicate; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.querydsl.core.types.Predicate; +import com.querydsl.core.types.dsl.BooleanExpression; + +import cz.jirutka.rsql.parser.RSQLParser; +import cz.jirutka.rsql.parser.ast.Node; + +//@EnableSpringDataWebSupport +@Controller +@RequestMapping(value = "/auth/") +public class UserController { + + @Autowired + private IUserDAO service; + + @Autowired + private UserRepository dao; + + @Autowired + private MyUserRepository myUserRepository; + + public UserController() { + super(); + } + + // API - READ + + @RequestMapping(method = RequestMethod.GET, value = "/users") + @ResponseBody + public List findAll(@RequestParam(value = "search", required = false) String search) { + List params = new ArrayList(); + if (search != null) { + Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),"); + Matcher matcher = pattern.matcher(search + ","); + while (matcher.find()) { + params.add(new SearchCriteria(matcher.group(1), matcher.group(2), matcher.group(3))); + } + } + return service.searchUser(params); + } + + @RequestMapping(method = RequestMethod.GET, value = "/users/spec") + @ResponseBody + public List findAllBySpecification(@RequestParam(value = "search") String search) { + UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); + String operationSetExper = Joiner + .on("|") + .join(SearchOperation.SIMPLE_OPERATION_SET); + Pattern pattern = Pattern.compile("(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),"); + Matcher matcher = pattern.matcher(search + ","); + while (matcher.find()) { + builder.with(matcher.group(1), matcher.group(2), matcher.group(4), matcher.group(3), matcher.group(5)); + } + + Specification spec = builder.build(); + return dao.findAll(spec); + } + + @GetMapping(value = "/users/espec") + @ResponseBody + public List findAllByOrPredicate(@RequestParam(value = "search") String search) { + Specification spec = resolveSpecification(search); + return dao.findAll(spec); + } + + protected Specification resolveSpecification(String searchParameters) { + + UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); + String operationSetExper = Joiner + .on("|") + .join(SearchOperation.SIMPLE_OPERATION_SET); + Pattern pattern = Pattern.compile("(\\p{Punct}?)(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),"); + Matcher matcher = pattern.matcher(searchParameters + ","); + while (matcher.find()) { + builder.with(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(5), matcher.group(4), matcher.group(6)); + } + return builder.build(); + } + + @RequestMapping(method = RequestMethod.GET, value = "/myusers") + @ResponseBody + public Iterable findAllByQuerydsl(@RequestParam(value = "search") String search) { + MyUserPredicatesBuilder builder = new MyUserPredicatesBuilder(); + if (search != null) { + Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),"); + Matcher matcher = pattern.matcher(search + ","); + while (matcher.find()) { + builder.with(matcher.group(1), matcher.group(2), matcher.group(3)); + } + } + BooleanExpression exp = builder.build(); + return myUserRepository.findAll(exp); + } + + @RequestMapping(method = RequestMethod.GET, value = "/users/rsql") + @ResponseBody + public List findAllByRsql(@RequestParam(value = "search") String search) { + Node rootNode = new RSQLParser().parse(search); + Specification spec = rootNode.accept(new CustomRsqlVisitor()); + return dao.findAll(spec); + } + + @RequestMapping(method = RequestMethod.GET, value = "/api/myusers") + @ResponseBody + public Iterable findAllByWebQuerydsl(@QuerydslPredicate(root = MyUser.class) Predicate predicate) { + return myUserRepository.findAll(predicate); + } + + // API - WRITE + + @RequestMapping(method = RequestMethod.POST, value = "/users") + @ResponseStatus(HttpStatus.CREATED) + public void create(@RequestBody User resource) { + Preconditions.checkNotNull(resource); + dao.save(resource); + } + + @RequestMapping(method = RequestMethod.POST, value = "/myusers") + @ResponseStatus(HttpStatus.CREATED) + public void addMyUser(@RequestBody MyUser resource) { + Preconditions.checkNotNull(resource); + myUserRepository.save(resource); + + } + +} diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/util/SearchOperation.java b/spring-security-rest-full/src/main/java/org/baeldung/web/util/SearchOperation.java index 41a556c18a..fa09662201 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/web/util/SearchOperation.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/web/util/SearchOperation.java @@ -4,10 +4,10 @@ public enum SearchOperation { EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, LIKE, STARTS_WITH, ENDS_WITH, CONTAINS; public static final String[] SIMPLE_OPERATION_SET = { ":", "!", ">", "<", "~" }; - - public static final String LOW_PRECEDENCE_INDICATOR="'"; - - public static final String ZERO_OR_MORE_REGEX="*"; + + public static final String OR_PREDICATE_FLAG = "'"; + + public static final String ZERO_OR_MORE_REGEX = "*"; public static SearchOperation getSimpleOperation(final char input) { switch (input) { diff --git a/spring-security-rest-full/src/main/java/org/baeldung/web/util/SpecSearchCriteria.java b/spring-security-rest-full/src/main/java/org/baeldung/web/util/SpecSearchCriteria.java index 7dbb66edea..6b37fb579c 100644 --- a/spring-security-rest-full/src/main/java/org/baeldung/web/util/SpecSearchCriteria.java +++ b/spring-security-rest-full/src/main/java/org/baeldung/web/util/SpecSearchCriteria.java @@ -5,7 +5,7 @@ public class SpecSearchCriteria { private String key; private SearchOperation operation; private Object value; - private boolean lowPrecedence; + private boolean orPredicate; public SpecSearchCriteria() { @@ -18,9 +18,9 @@ public class SpecSearchCriteria { this.value = value; } - public SpecSearchCriteria(final String lowPrecedenceIndicator, final String key, final SearchOperation operation, final Object value) { + public SpecSearchCriteria(final String orPredicate, final String key, final SearchOperation operation, final Object value) { super(); - this.lowPrecedence = lowPrecedenceIndicator != null && lowPrecedenceIndicator.equals(SearchOperation.LOW_PRECEDENCE_INDICATOR); + this.orPredicate = orPredicate != null && orPredicate.equals(SearchOperation.OR_PREDICATE_FLAG); this.key = key; this.operation = operation; this.value = value; @@ -50,12 +50,12 @@ public class SpecSearchCriteria { this.value = value; } - public boolean isLowPrecedence() { - return lowPrecedence; + public boolean isOrPredicate() { + return orPredicate; } - public void setLowPrecedence(boolean lowPrecedence) { - this.lowPrecedence = lowPrecedence; + public void setOrPredicate(boolean orPredicate) { + this.orPredicate = orPredicate; } } diff --git a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationIntegrationTest.java b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationIntegrationTest.java index e5c408bfdb..244e19db90 100644 --- a/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationIntegrationTest.java +++ b/spring-security-rest-full/src/test/java/org/baeldung/persistence/query/JPASpecificationIntegrationTest.java @@ -70,7 +70,9 @@ public class JPASpecificationIntegrationTest { public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() { final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.EQUALITY, "john")); final UserSpecification spec1 = new UserSpecification(new SpecSearchCriteria("lastName", SearchOperation.EQUALITY, "doe")); - final List results = repository.findAll(Specifications.where(spec).and(spec1)); + final List results = repository.findAll(Specifications + .where(spec) + .and(spec1)); assertThat(userJohn, isIn(results)); assertThat(userTom, not(isIn(results))); @@ -80,10 +82,13 @@ public class JPASpecificationIntegrationTest { public void givenFirstOrLastName_whenGettingListOfUsers_thenCorrect() { UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); - final SpecSearchCriteria spec = new SpecSearchCriteria("'", "firstName", SearchOperation.EQUALITY, "john"); - final SpecSearchCriteria spec1 = new SpecSearchCriteria("lastName", SearchOperation.EQUALITY, "doe"); + SpecSearchCriteria spec = new SpecSearchCriteria("'", "firstName", SearchOperation.EQUALITY, "john"); + SpecSearchCriteria spec1 = new SpecSearchCriteria("lastName", SearchOperation.EQUALITY, "doe"); - final List results = repository.findAll(builder.with(spec1).with(spec).build()); + List results = repository.findAll(builder + .with(spec1) + .with(spec) + .build()); assertThat(results, hasSize(2)); assertThat(userJohn, isIn(results)); @@ -97,7 +102,8 @@ public class JPASpecificationIntegrationTest { builder.with("'", "firstName", ":", "john", null, null); builder.with(null, "lastName", ":", "doe", null, null); - final List results = repository.findAll(builder.build(converter)); + List results = repository.findAll(builder.build(converter)); + assertThat(results, hasSize(2)); assertThat(userJohn, isIn(results)); assertThat(userTom, isIn(results)); @@ -116,7 +122,6 @@ public class JPASpecificationIntegrationTest { public void givenMinAge_whenGettingListOfUsers_thenCorrect() { final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.GREATER_THAN, "25")); final List results = repository.findAll(Specifications.where(spec)); - assertThat(userTom, isIn(results)); assertThat(userJohn, not(isIn(results))); } @@ -125,7 +130,6 @@ public class JPASpecificationIntegrationTest { public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() { final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.STARTS_WITH, "jo")); final List results = repository.findAll(spec); - assertThat(userJohn, isIn(results)); assertThat(userTom, not(isIn(results))); } @@ -134,7 +138,6 @@ public class JPASpecificationIntegrationTest { public void givenFirstNameSuffix_whenGettingListOfUsers_thenCorrect() { final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("firstName", SearchOperation.ENDS_WITH, "n")); final List results = repository.findAll(spec); - assertThat(userJohn, isIn(results)); assertThat(userTom, not(isIn(results))); } @@ -152,7 +155,9 @@ public class JPASpecificationIntegrationTest { public void givenAgeRange_whenGettingListOfUsers_thenCorrect() { final UserSpecification spec = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.GREATER_THAN, "20")); final UserSpecification spec1 = new UserSpecification(new SpecSearchCriteria("age", SearchOperation.LESS_THAN, "25")); - final List results = repository.findAll(Specifications.where(spec).and(spec1)); + final List results = repository.findAll(Specifications + .where(spec) + .and(spec1)); assertThat(userJohn, isIn(results)); assertThat(userTom, not(isIn(results))); From 17042f0420772d036b8d24dab1c6644ec918a8a7 Mon Sep 17 00:00:00 2001 From: Chandravadan S Date: Tue, 28 Mar 2017 21:01:25 +0530 Subject: [PATCH 069/102] BAEL-732: String to enum (#1526) --- .../com/baeldung/enums/PizzaUnitTest.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java b/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java index 6cf6ad3551..db7aa6d920 100644 --- a/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java +++ b/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java @@ -1,12 +1,14 @@ package com.baeldung.enums; -import org.junit.Test; +import static junit.framework.TestCase.assertTrue; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; -import static junit.framework.TestCase.assertTrue; +import org.junit.Test; + +import com.baeldung.enums.Pizza.PizzaStatusEnum; public class PizzaUnitTest { @@ -75,5 +77,25 @@ public class PizzaUnitTest { pz.deliver(); assertTrue(pz.getStatus() == Pizza.PizzaStatusEnum.DELIVERED); } + + @Test + public void givenValidEnumValueAsString_whenConvertedIntoEnum_thenGetsConvertedCorrectly() { + String pizzaEnumValue = "READY"; + PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue); + assertTrue(pizzaStatusEnum == PizzaStatusEnum.READY); + } + + @Test(expected = IllegalArgumentException.class) + public void givenInvalidEnumValueCaseWiseAsString_whenConvertedIntoEnum_thenThrowsException() { + String pizzaEnumValue = "rEAdY"; + PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue); + } + + @Test(expected = IllegalArgumentException.class) + public void givenInvalidEnumValueContentWiseAsString_whenConvertedIntoEnum_thenThrowsException() { + String pizzaEnumValue = "invalid"; + PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue); + } + } From 6e2dcfcf5903d2e273ef48507e8c3daf3f0d8f3b Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Tue, 28 Mar 2017 22:45:46 +0200 Subject: [PATCH 070/102] Refactor PizzaUnitTest (#1527) --- .../test/java/com/baeldung/enums/PizzaUnitTest.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java b/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java index db7aa6d920..35aa07821c 100644 --- a/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java +++ b/core-java/src/test/java/com/baeldung/enums/PizzaUnitTest.java @@ -1,14 +1,13 @@ package com.baeldung.enums; -import static junit.framework.TestCase.assertTrue; +import com.baeldung.enums.Pizza.PizzaStatusEnum; +import org.junit.Test; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; -import org.junit.Test; - -import com.baeldung.enums.Pizza.PizzaStatusEnum; +import static junit.framework.TestCase.assertTrue; public class PizzaUnitTest { @@ -71,7 +70,7 @@ public class PizzaUnitTest { } @Test - public void givenPizaOrder_whenDelivered_thenPizzaGetsDeliveredAndStatusChanges() { + public void whenDelivered_thenPizzaGetsDeliveredAndStatusChanges() { Pizza pz = new Pizza(); pz.setStatus(Pizza.PizzaStatusEnum.READY); pz.deliver(); @@ -79,14 +78,14 @@ public class PizzaUnitTest { } @Test - public void givenValidEnumValueAsString_whenConvertedIntoEnum_thenGetsConvertedCorrectly() { + public void whenConvertedIntoEnum_thenGetsConvertedCorrectly() { String pizzaEnumValue = "READY"; PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue); assertTrue(pizzaStatusEnum == PizzaStatusEnum.READY); } @Test(expected = IllegalArgumentException.class) - public void givenInvalidEnumValueCaseWiseAsString_whenConvertedIntoEnum_thenThrowsException() { + public void whenConvertedIntoEnum_thenThrowsException() { String pizzaEnumValue = "rEAdY"; PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue); } From 6fe778979a3b7fce3ff5a483a1544e8eba360deb Mon Sep 17 00:00:00 2001 From: Devendra Tiwari Date: Wed, 29 Mar 2017 02:18:47 +0530 Subject: [PATCH 071/102] Guide to Guava | common.util.concurrent (#1528) Code and Tests for common.util.concurrent package --- .../tutorial/AtomicLongMapTutorials.java | 28 ++++++++ .../guava/tutorial/MonitorExample.java | 29 +++++++++ guava21/src/test/java/AtomicLongMapTests.java | 64 +++++++++++++++++++ guava21/src/test/java/MonitorUnitTests.java | 61 ++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java create mode 100644 guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java create mode 100644 guava21/src/test/java/AtomicLongMapTests.java create mode 100644 guava21/src/test/java/MonitorUnitTests.java diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java b/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java new file mode 100644 index 0000000000..69ad04ad9c --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java @@ -0,0 +1,28 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.util.concurrent.AtomicLongMap; + +public class AtomicLongMapTutorials { + + private AtomicLongMap atomicLongMap; + + public AtomicLongMapTutorials(){ + atomicLongMap = AtomicLongMap.create(); + } + + + public void addKeys(){ + atomicLongMap.addAndGet("apple",250); + atomicLongMap.addAndGet("bat",350); + atomicLongMap.addAndGet("cat",450); + atomicLongMap.addAndGet("dog",550); + } + + public static void main(String[] args){ + AtomicLongMapTutorials atomicLongMapTutorials = new AtomicLongMapTutorials(); + atomicLongMapTutorials.addKeys(); + + System.out.println(atomicLongMapTutorials.atomicLongMap.get("2")); + } + +} diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java b/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java new file mode 100644 index 0000000000..2f316c293e --- /dev/null +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java @@ -0,0 +1,29 @@ +package com.baeldung.guava.tutorial; + +import com.google.common.util.concurrent.Monitor; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BooleanSupplier; + +public class MonitorExample { + private List students = new ArrayList(); + private static final int MAX_SIZE = 100; + + private Monitor monitor = new Monitor(); + + + public void addToCourse(String item) throws InterruptedException { + Monitor.Guard studentsBelowCapacity = monitor.newGuard(this::isStudentsCapacityUptoLimit); + monitor.enterWhen(studentsBelowCapacity); + try { + students.add(item); + } finally { + monitor.leave(); + } + } + + public Boolean isStudentsCapacityUptoLimit(){ + return students.size() > MAX_SIZE; + } +} diff --git a/guava21/src/test/java/AtomicLongMapTests.java b/guava21/src/test/java/AtomicLongMapTests.java new file mode 100644 index 0000000000..aad72907de --- /dev/null +++ b/guava21/src/test/java/AtomicLongMapTests.java @@ -0,0 +1,64 @@ +import com.google.common.util.concurrent.AtomicLongMap; +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class AtomicLongMapTests { + + private static final String SPRING_COURSE_KEY = "Spring"; + private static final String HIBERNATE_COURSE_KEY = "hibernate"; + private static final String GUAVA_COURSE_KEY = "Guava"; + + AtomicLongMap courses = AtomicLongMap.create(); + + public void setUp(){ + courses.put(SPRING_COURSE_KEY, 1056); + courses.put(HIBERNATE_COURSE_KEY, 259); + courses.put(GUAVA_COURSE_KEY, 78); + } + + +@Test +public void accumulateAndGet_withLongBinaryOperator_thenSuccessful(){ + long noOfStudents = 56; + long oldValue = courses.get(SPRING_COURSE_KEY); + + long totalNotesRequired = courses.accumulateAndGet( + "Guava", + noOfStudents, + (x,y) -> (x * y)); + + assertEquals(totalNotesRequired, oldValue * noOfStudents ); +} + + @Test + public void getAndAccumulate_withLongBinaryOperator_thenSuccessful(){ + long noOfStudents = 56; + long beforeUpdate = courses.get(SPRING_COURSE_KEY); + + long onUpdate = courses.accumulateAndGet("Guava", + noOfStudents, + (x,y) -> (x * y) + ); + + long afterUpdate = courses.get(SPRING_COURSE_KEY); + + assertEquals(onUpdate, afterUpdate); + assertEquals(afterUpdate, beforeUpdate * noOfStudents); + } + +@Test +public void updateAndGet_withLongUnaryOperator_thenSuccessful(){ + long beforeUpdate = courses.get(SPRING_COURSE_KEY); + + long onUpdate = courses.updateAndGet( + "Guava", + (x) -> (x/2)); + + long afterUpdate = courses.get(SPRING_COURSE_KEY); + + assertEquals(onUpdate, afterUpdate); + assertEquals(afterUpdate, beforeUpdate/2); +} +} diff --git a/guava21/src/test/java/MonitorUnitTests.java b/guava21/src/test/java/MonitorUnitTests.java new file mode 100644 index 0000000000..6427072db9 --- /dev/null +++ b/guava21/src/test/java/MonitorUnitTests.java @@ -0,0 +1,61 @@ +import com.google.common.util.concurrent.Monitor; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static com.google.common.util.concurrent.Uninterruptibles.joinUninterruptibly; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class MonitorUnitTests { + + @Test + public void whenGaurdConditionIsTrue_IsSuccessful() { + Monitor monitor = new Monitor(); + boolean enteredInCriticalSection = false; + + Monitor.Guard gaurdCondition = monitor.newGuard(this::returnTrue); + + if(monitor.enterIf(gaurdCondition)){ + try{ + System.out.println("Entered in critical section"); + enteredInCriticalSection = true; + }finally { + monitor.leave(); + } + } + + Assert.assertTrue(enteredInCriticalSection); + + } + + @Test + public void whenGaurdConditionIsFalse_IsSuccessful() { + Monitor monitor = new Monitor(); + boolean enteredInCriticalSection = false; + + Monitor.Guard gaurdCondition = monitor.newGuard(this::returnFalse); + + if(monitor.enterIf(gaurdCondition)){ + try{ + System.out.println("Entered in critical section"); + enteredInCriticalSection = true; + }finally { + monitor.leave(); + } + } + + Assert.assertFalse(enteredInCriticalSection); + } + + private boolean returnTrue(){ + return true; + } + + private boolean returnFalse(){ + return false; + } +} From 361df0769448c4c761a1fef9b5b088ed8de29ec9 Mon Sep 17 00:00:00 2001 From: Nancy Bosecker Date: Tue, 28 Mar 2017 18:18:00 -0700 Subject: [PATCH 072/102] Added KeyDeserializer class and test code (#1523) * Solr w Apache SolrJ * Solr w Apache SolrJ * updated test names and moved add to @before method * create apache-solrj module, moved code from spring-data-solr * More examples for indexing,delete,and query for solrj * More examples for indexing,delete,and query for solrj * Jackson Map Serialize/Deserialize * Jackson Map Serialize/Deserialize * Jackson version update * keydeserializer code added * keydeserializer code added --- .../jackson/entities/ClassWithAMap.java | 24 +++++++++++++++++++ .../serialization/MyPairDeserializer.java | 18 ++++++++++++++ .../JacksonMapDeserializeTest.java | 10 +++++++- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 jackson/src/main/java/com/baeldung/jackson/entities/ClassWithAMap.java create mode 100644 jackson/src/main/java/com/baeldung/jackson/serialization/MyPairDeserializer.java diff --git a/jackson/src/main/java/com/baeldung/jackson/entities/ClassWithAMap.java b/jackson/src/main/java/com/baeldung/jackson/entities/ClassWithAMap.java new file mode 100644 index 0000000000..54ebff8a56 --- /dev/null +++ b/jackson/src/main/java/com/baeldung/jackson/entities/ClassWithAMap.java @@ -0,0 +1,24 @@ +package com.baeldung.jackson.entities; + +import java.util.Map; + +import com.baeldung.jackson.serialization.MyPairDeserializer; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +public class ClassWithAMap { + + @JsonProperty("map") + @JsonDeserialize(keyUsing = MyPairDeserializer.class) + private final Map map; + + @JsonCreator + public ClassWithAMap(Map map) { + this.map = map; + } + + public Map getMap() { + return map; + } +} \ No newline at end of file diff --git a/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairDeserializer.java b/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairDeserializer.java new file mode 100644 index 0000000000..0aa6db98d0 --- /dev/null +++ b/jackson/src/main/java/com/baeldung/jackson/serialization/MyPairDeserializer.java @@ -0,0 +1,18 @@ +package com.baeldung.jackson.serialization; + +import java.io.IOException; + +import com.baeldung.jackson.entities.MyPair; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.KeyDeserializer; + +public class MyPairDeserializer extends KeyDeserializer { + + @Override + public MyPair deserializeKey(String key, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + + return new MyPair(key); + } +} \ No newline at end of file diff --git a/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java b/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java index 3be3981c9b..04eb89306b 100644 --- a/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java +++ b/jackson/src/test/java/com/baeldung/jackson/deserialization/JacksonMapDeserializeTest.java @@ -7,6 +7,7 @@ import java.util.Map; import org.junit.Assert; import org.junit.Test; +import com.baeldung.jackson.entities.ClassWithAMap; import com.baeldung.jackson.entities.MyPair; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.type.TypeReference; @@ -36,14 +37,21 @@ public class JacksonMapDeserializeTest { public void whenObjectStringMapDeserialize_thenCorrect() throws JsonParseException, JsonMappingException, IOException { - final String jsonInput = "{\"Abbott and Costello\" : \"Comedy\"}"; + final String jsonInput = "{\"Abbott and Costello\":\"Comedy\"}"; final ObjectMapper mapper = new ObjectMapper(); TypeReference> typeRef = new TypeReference>() { }; + map = mapper.readValue(jsonInput, typeRef); Assert.assertEquals("Comedy", map.get(new MyPair("Abbott", "Costello"))); + + ClassWithAMap classWithMap = mapper.readValue(jsonInput, + ClassWithAMap.class); + + Assert.assertEquals("Comedy", + classWithMap.getMap().get(new MyPair("Abbott", "Costello"))); } @Test From 1a7ccb824882d3df8c6353a4f4fc59ea791b7ff8 Mon Sep 17 00:00:00 2001 From: Tomasz Lelek Date: Wed, 29 Mar 2017 04:14:32 +0200 Subject: [PATCH 073/102] Bael 756 (#1513) * BAEL-756 code for kotlin null-safety article * BAEL-756 fix typo * BAEL-756 increment version of Kotlin * BAEL-756 remove duplicate form pom --- kotlin/pom.xml | 7 +- .../com/baeldung/kotlin/NullSafetyTest.kt | 140 ++++++++++++++++++ 2 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt diff --git a/kotlin/pom.xml b/kotlin/pom.xml index f928cc4037..07fb6863d4 100644 --- a/kotlin/pom.xml +++ b/kotlin/pom.xml @@ -90,10 +90,9 @@ 4.12 - 1.0.6 - 1.0.6 - 1.0.6 - 1.0.6 + 1.1.1 + 1.1.1 + 1.1.1 \ No newline at end of file diff --git a/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt b/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt new file mode 100644 index 0000000000..2adc1032bc --- /dev/null +++ b/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt @@ -0,0 +1,140 @@ +package com.baeldung.kotlin + +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull +import kotlin.test.assertTrue + + +class NullSafetyTest { + @Test + fun givenNonNullableField_whenAssignValueToIt_thenNotNeedToCheckAgainstNull() { + //given + var a: String = "value" + //a = null compilation error + + //then + assertEquals(a.length, 5) + } + + @Test + fun givenNullableField_whenReadValue_thenNeedToCheckAgainstNull() { + //given + var b: String? = "value" + b = null + + //when + if (b != null) { + + } else { + assertNull(b) + } + } + + @Test + fun givenComplexObject_whenUseSafeCall_thenShouldChainCallsResultingWithValue() { + //given + val p: Person? = Person(Country("ENG")) + + //when + val res = p?.country?.code + + //then + assertEquals(res, "ENG") + } + + @Test + fun givenComplexObject_whenUseSafeCall_thenShouldChainCallsResultingWithNull() { + //given + val p: Person? = Person(Country(null)) + + //when + val res = p?.country?.code + + //then + assertNull(res) + } + + @Test + fun givenCollectionOfObjects_whenUseLetOperator_thenShouldApplyActionOnlyOnNonNullValue() { + //given + val firstName = "Tom" + val secondName = "Michael" + val names: List = listOf(firstName, null, secondName) + + //when + var res = listOf() + for (item in names) { + item?.let { res = res.plus(it) } + } + + //then + assertEquals(2, res.size) + assertTrue { res.contains(firstName) } + assertTrue { res.contains(secondName) } + } + + @Test + fun givenNullableReference_whenUseElvisOperator_thenShouldReturnValueIfReferenceIsNotNull() { + //given + val value: String? = "name" + + //when + val res = value?.length ?: -1 + + //then + assertEquals(res, 4) + } + + @Test + fun givenNullableReference_whenUseElvisOperator_thenShouldReturnDefaultValueIfReferenceIsNull() { + //given + val value: String? = null + + //when + val res = value?.length ?: -1 + + //then + assertEquals(res, -1) + } + + @Test + fun givenNullableField_whenUsingDoubleExclamationMarkOperatorOnNull_thenThrowNPE() { + //given + var b: String? = "value" + b = null + + //when + assertFailsWith { + b!!.length + } + } + + @Test + fun givenNullableField_whenUsingDoubleExclamationMarkOperatorOnNotNull_thenReturnValue() { + //given + val b: String? = "value" + + //then + assertEquals(b!!.length, 5) + } + + @Test + fun givenNullableList_whenUseFilterNotNullMethod_thenRemoveALlNullValues() { + //given + val list: List = listOf("a", null, "b") + + //when + val res = list.filterNotNull() + + //then + assertEquals(res.size, 2) + assertTrue { res.contains("a") } + assertTrue { res.contains("b") } + } +} + +data class Person(val country: Country?) + +data class Country(val code: String?) \ No newline at end of file From e6d9dde9317f0429cd2f39f4f930b7496dede2a9 Mon Sep 17 00:00:00 2001 From: Tian Baoqiang Date: Wed, 29 Mar 2017 17:43:21 +0800 Subject: [PATCH 074/102] add a main-class variable on maven building and exclude IntegrationTests (#1530) * add a main-class variable on maven building and exclude IntegrationTests * hardcode spring-boot main class to Spring5Application --- spring-5/pom.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spring-5/pom.xml b/spring-5/pom.xml index 6c52e6c8cc..f116ed73c0 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -70,6 +70,21 @@ org.springframework.boot spring-boot-maven-plugin + + com.baeldung.Spring5Application + JAR + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LiveTest.java + + From 6f75ad86a652d485776c8baae28c811f0481e4be Mon Sep 17 00:00:00 2001 From: Tomasz Lelek Date: Wed, 29 Mar 2017 13:41:37 +0200 Subject: [PATCH 075/102] Bael 756 (#1531) * BAEL-756 code for kotlin null-safety article * BAEL-756 fix typo * BAEL-756 increment version of Kotlin * BAEL-756 remove duplicate form pom * BAEL-756 add also and run example --- .../com/baeldung/kotlin/NullSafetyTest.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt b/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt index 2adc1032bc..0ecc74b6fb 100644 --- a/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt +++ b/kotlin/src/test/kotlin/com/baeldung/kotlin/NullSafetyTest.kt @@ -66,7 +66,27 @@ class NullSafetyTest { //when var res = listOf() for (item in names) { - item?.let { res = res.plus(it) } + item?.let { res = res.plus(it); it } + ?.also{it -> println("non nullable value: $it")} + } + + //then + assertEquals(2, res.size) + assertTrue { res.contains(firstName) } + assertTrue { res.contains(secondName) } + } + + @Test + fun fivenCollectionOfObject_whenUseRunOperator_thenExecuteActionOnNonNullValue(){ + //given + val firstName = "Tom" + val secondName = "Michael" + val names: List = listOf(firstName, null, secondName) + + //when + var res = listOf() + for (item in names) { + item?.run{res = res.plus(this)} } //then From b63ef448db503c66b11f99f65d6c6b9bc73a7454 Mon Sep 17 00:00:00 2001 From: Nancy Bosecker Date: Wed, 29 Mar 2017 08:42:38 -0700 Subject: [PATCH 076/102] removed explicit types from map instantiation (#1532) * Solr w Apache SolrJ * Solr w Apache SolrJ * updated test names and moved add to @before method * create apache-solrj module, moved code from spring-data-solr * More examples for indexing,delete,and query for solrj * More examples for indexing,delete,and query for solrj * Jackson Map Serialize/Deserialize * Jackson Map Serialize/Deserialize * Jackson version update * keydeserializer code added * keydeserializer code added * remove explicit types from map instantion --- .../jackson/serialization/JacksonMapSerializeTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java b/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java index 71ba698e8e..841b12ca03 100644 --- a/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java +++ b/jackson/src/test/java/com/baeldung/jackson/serialization/JacksonMapSerializeTest.java @@ -30,7 +30,7 @@ public class JacksonMapSerializeTest { public void whenSimpleMapSerialize_thenCorrect() throws JsonProcessingException { - Map map = new HashMap(); + Map map = new HashMap<>(); map.put("key", "value"); final ObjectMapper mapper = new ObjectMapper(); @@ -43,7 +43,7 @@ public class JacksonMapSerializeTest { public void whenCustomObjectStringMapSerialize_thenCorrect() throws JsonProcessingException { - map = new HashMap(); + map = new HashMap<>(); MyPair key = new MyPair("Abbott", "Costello"); map.put(key, "Comedy"); @@ -57,7 +57,7 @@ public class JacksonMapSerializeTest { public void whenCustomObjectObjectMapSerialize_thenCorrect() throws JsonProcessingException { - cmap = new HashMap(); + cmap = new HashMap<>(); mapKey = new MyPair("Abbott", "Costello"); mapValue = new MyPair("Comedy", "1940's"); cmap.put(mapKey, mapValue); From ea345fa246b290b7ee98fcc5e5016877b2bde7d2 Mon Sep 17 00:00:00 2001 From: gitterjim-I Date: Wed, 29 Mar 2017 22:18:20 +0100 Subject: [PATCH 077/102] Integrate forEach and stream changes to test, removing non-test class. (#1529) * article Bael-667 initial commit. * Switch to use logging framework for output. --- core-java/0.8260098203820962 | 0 .../FlattenNestedListTest.java | 82 +++++++++++-------- 2 files changed, 48 insertions(+), 34 deletions(-) create mode 100644 core-java/0.8260098203820962 diff --git a/core-java/0.8260098203820962 b/core-java/0.8260098203820962 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java b/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java index cf9334954b..fdf4934cb7 100644 --- a/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java +++ b/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java @@ -1,6 +1,8 @@ package com.baeldung.list.flattennestedlist; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; @@ -8,45 +10,60 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; public class FlattenNestedListTest { + private List> lol = new ArrayList<>(); + List ls1 = Arrays.asList("one:one", "one:two", "one:three"); + List ls2 = Arrays.asList("two:one", "two:two", "two:three"); + List ls3 = Arrays.asList("three:one", "three:two", "three:three"); - @Test - public void givenListOfListOfString_flattenNestedList1() { - // given - List ls1 = Arrays.asList("one:one", "one:two", "one:three"); - List ls2 = Arrays.asList("two:one", "two:two", "two:three"); - List ls3 = Arrays.asList("three:one", "three:two", "three:three"); + @Before + public void setup() { + lol.addAll(Arrays.asList(ls1, ls2, ls3)); + } - List> list = Arrays.asList(ls1, ls2, ls3); - - // when - List ls = flattenListOfListsImperatively(list); - - // then - assertNotNull(ls); - assertTrue(ls.size() == 9); - //TODO content assertion + @After + public void tearDown() { + lol = null; } @Test - public void givenListOfListOfString_flattenNestedList2() { - // given - List ls1 = Arrays.asList("one:one", "one:two", "one:three"); - List ls2 = Arrays.asList("two:one", "two:two", "two:three"); - List ls3 = Arrays.asList("three:one", "three:two", "three:three"); + public void givenString_flattenNestedList1() { + List ls = flattenListOfListsImperatively(lol); - List> list = Arrays.asList(ls1, ls2, ls3); - - // when - List ls = flattenListOfListsStream(list); - - // then assertNotNull(ls); assertTrue(ls.size() == 9); - //TODO content assertion + // assert content + assertEquals(ls.get(0), "one:one"); + assertEquals(ls.get(1), "one:two"); + assertEquals(ls.get(2), "one:three"); + assertEquals(ls.get(3), "two:one"); + assertEquals(ls.get(4), "two:two"); + assertEquals(ls.get(5), "two:three"); + assertEquals(ls.get(6), "three:one"); + assertEquals(ls.get(7), "three:two"); + assertEquals(ls.get(8), "three:three"); + } + + @Test + public void givenString_flattenNestedList2() { + List ls = flattenListOfListsStream(lol); + + assertNotNull(ls); + assertTrue(ls.size() == 9); + // assert content + assertEquals(ls.get(0), "one:one"); + assertEquals(ls.get(1), "one:two"); + assertEquals(ls.get(2), "one:three"); + assertEquals(ls.get(3), "two:one"); + assertEquals(ls.get(4), "two:two"); + assertEquals(ls.get(5), "two:three"); + assertEquals(ls.get(6), "three:one"); + assertEquals(ls.get(7), "three:two"); + assertEquals(ls.get(8), "three:three"); } public List flattenListOfListsImperatively(List> list) { @@ -56,9 +73,6 @@ public class FlattenNestedListTest { } public List flattenListOfListsStream(List> list) { - return list.stream() - .flatMap(Collection::stream) - .collect(Collectors.toList()); + return list.stream().flatMap(Collection::stream).collect(Collectors.toList()); } - } From 47dfe6b641733689028ce727837ebf834d8aacce Mon Sep 17 00:00:00 2001 From: "Eunice A. Obugyei" Date: Thu, 30 Mar 2017 07:28:32 +0000 Subject: [PATCH 078/102] Introduction to JAX-WS[http://jira.baeldung.com/browse/BAEL-611] (#1224) * Introduction to JAX-WS[http://jira.baeldung.com/browse/BAEL-611] * Introduction to JAX-WS[http://jira.baeldung.com/browse/BAEL-611] * Removed unnecessary comment * Added mockito-core dependency * Introduction to JAX-WS[http://jira.baeldung.com/browse/BAEL-611] Added Exception test cases * Applied baeldung formatter in Eclipse --- jee7/pom.xml | 1 + .../com/baeldung/jaxws/EmployeeService.java | 31 +++++ .../baeldung/jaxws/EmployeeServiceImpl.java | 48 ++++++++ .../config/EmployeeServicePublisher.java | 12 ++ .../exception/EmployeeAlreadyExists.java | 16 +++ .../jaxws/exception/EmployeeNotFound.java | 17 +++ .../com/baeldung/jaxws/model/Employee.java | 33 ++++++ .../jaxws/repository/EmployeeRepository.java | 22 ++++ .../repository/EmployeeRepositoryImpl.java | 68 +++++++++++ .../jaxws/EmployeeServiceLiveTest.java | 108 ++++++++++++++++++ 10 files changed, 356 insertions(+) create mode 100644 jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java create mode 100644 jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java create mode 100644 jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java create mode 100644 jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java create mode 100644 jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeNotFound.java create mode 100644 jee7/src/main/java/com/baeldung/jaxws/model/Employee.java create mode 100644 jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java create mode 100644 jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java create mode 100644 jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java diff --git a/jee7/pom.xml b/jee7/pom.xml index f275f56d58..3d5d7ccdc4 100644 --- a/jee7/pom.xml +++ b/jee7/pom.xml @@ -28,6 +28,7 @@ 3.6.0 2.6 + 1.10.19 diff --git a/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java b/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java new file mode 100644 index 0000000000..7544369992 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java @@ -0,0 +1,31 @@ +package com.baeldung.jaxws; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; + +import javax.jws.WebMethod; +import javax.jws.WebService; +import java.util.List; + +@WebService +public interface EmployeeService { + + @WebMethod + Employee getEmployee(int id) throws EmployeeNotFound; + + @WebMethod + Employee updateEmployee(int id, String name) throws EmployeeNotFound; + + @WebMethod + boolean deleteEmployee(int id) throws EmployeeNotFound; + + @WebMethod + Employee addEmployee(int id, String name) throws EmployeeAlreadyExists; + + @WebMethod + int countEmployees(); + + @WebMethod + List getAllEmployees(); +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java b/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java new file mode 100644 index 0000000000..35b84fe620 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java @@ -0,0 +1,48 @@ +package com.baeldung.jaxws; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; +import com.baeldung.jaxws.repository.EmployeeRepository; + +import javax.inject.Inject; +import javax.jws.WebMethod; +import javax.jws.WebService; +import java.util.List; + +@WebService(serviceName = "EmployeeService", endpointInterface = "com.baeldung.jaxws.EmployeeService") +public class EmployeeServiceImpl implements EmployeeService { + + @Inject + private EmployeeRepository employeeRepositoryImpl; + + @WebMethod + public Employee getEmployee(int id) throws EmployeeNotFound { + return employeeRepositoryImpl.getEmployee(id); + } + + @WebMethod + public Employee updateEmployee(int id, String name) throws EmployeeNotFound { + return employeeRepositoryImpl.updateEmployee(id, name); + } + + @WebMethod + public boolean deleteEmployee(int id) throws EmployeeNotFound { + return employeeRepositoryImpl.deleteEmployee(id); + } + + @WebMethod + public Employee addEmployee(int id, String name) throws EmployeeAlreadyExists { + return employeeRepositoryImpl.addEmployee(id, name); + } + + @WebMethod + public int countEmployees() { + return employeeRepositoryImpl.count(); + } + + @WebMethod + public List getAllEmployees() { + return employeeRepositoryImpl.getAllEmployees(); + } +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java b/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java new file mode 100644 index 0000000000..6d7acc88e0 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java @@ -0,0 +1,12 @@ +package com.baeldung.jaxws.config; + +import com.baeldung.jaxws.EmployeeServiceImpl; + +import javax.xml.ws.Endpoint; + +public class EmployeeServicePublisher { + + public static void main(String[] args) { + Endpoint.publish("http://localhost:8080/employeeservice", new EmployeeServiceImpl()); + } +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java new file mode 100644 index 0000000000..d7c9d0f2cc --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java @@ -0,0 +1,16 @@ +package com.baeldung.jaxws.exception; + +import javax.xml.ws.WebFault; +import java.io.Serializable; + +@WebFault +public class EmployeeAlreadyExists extends Exception implements Serializable { + + public EmployeeAlreadyExists() { + super("This employee already exist"); + } + + public EmployeeAlreadyExists(String str) { + super(str); + } +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeNotFound.java b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeNotFound.java new file mode 100644 index 0000000000..667e3e0c72 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeNotFound.java @@ -0,0 +1,17 @@ +package com.baeldung.jaxws.exception; + +import javax.xml.ws.WebFault; +import java.io.Serializable; + +@WebFault +public class EmployeeNotFound extends Exception implements Serializable { + + public EmployeeNotFound() { + super("The specified employee does not exist"); + } + + public EmployeeNotFound(String str) { + super(str); + } + +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java b/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java new file mode 100644 index 0000000000..27d02354c0 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java @@ -0,0 +1,33 @@ +package com.baeldung.jaxws.model; + +import java.io.Serializable; + +public class Employee implements Serializable { + private int id; + private String firstName; + + public Employee() { + + } + + public Employee(int id, String firstName) { + this.id = id; + this.firstName = firstName; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java new file mode 100644 index 0000000000..3a8930ac04 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java @@ -0,0 +1,22 @@ +package com.baeldung.jaxws.repository; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; + +import java.util.List; + +public interface EmployeeRepository { + + List getAllEmployees(); + + Employee getEmployee(int id) throws EmployeeNotFound; + + Employee updateEmployee(int id, String name) throws EmployeeNotFound; + + boolean deleteEmployee(int id) throws EmployeeNotFound; + + Employee addEmployee(int id, String name) throws EmployeeAlreadyExists; + + int count(); +} diff --git a/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java new file mode 100644 index 0000000000..0e728ae253 --- /dev/null +++ b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java @@ -0,0 +1,68 @@ +package com.baeldung.jaxws.repository; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; + +import java.util.ArrayList; +import java.util.List; + +public class EmployeeRepositoryImpl implements EmployeeRepository { + private List employeeList; + + public EmployeeRepositoryImpl() { + employeeList = new ArrayList<>(); + employeeList.add(new Employee(1, "Jane")); + employeeList.add(new Employee(2, "Jack")); + employeeList.add(new Employee(3, "George")); + } + + public List getAllEmployees() { + return employeeList; + } + + public Employee getEmployee(int id) throws EmployeeNotFound { + for (Employee emp : employeeList) { + if (emp.getId() == id) { + return emp; + } + } + throw new EmployeeNotFound(); + } + + public Employee updateEmployee(int id, String name) throws EmployeeNotFound { + for (Employee employee1 : employeeList) { + if (employee1.getId() == id) { + employee1.setId(id); + employee1.setFirstName(name); + return employee1; + } + } + throw new EmployeeNotFound(); + } + + public boolean deleteEmployee(int id) throws EmployeeNotFound { + for (Employee emp : employeeList) { + if (emp.getId() == id) { + employeeList.remove(emp); + return true; + } + } + throw new EmployeeNotFound(); + } + + public Employee addEmployee(int id, String name) throws EmployeeAlreadyExists { + for (Employee emp : employeeList) { + if (emp.getId() == id) { + throw new EmployeeAlreadyExists(); + } + } + Employee employee = new Employee(id, name); + employeeList.add(employee); + return employee; + } + + public int count() { + return employeeList.size(); + } +} diff --git a/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java b/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java new file mode 100644 index 0000000000..8907179b14 --- /dev/null +++ b/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java @@ -0,0 +1,108 @@ +package com.baeldung.jaxws; + +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; +import com.baeldung.jaxws.repository.EmployeeRepository; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.xml.namespace.QName; +import javax.xml.ws.Service; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@RunWith(Arquillian.class) +public class EmployeeServiceLiveTest { + + private static final String APP_NAME = "jee7"; + private static final String WSDL_PATH = "EmployeeService?wsdl"; + private static QName SERVICE_NAME = new QName("http://jaxws.baeldung.com/", "EmployeeService"); + private static URL wsdlUrl; + + @ArquillianResource + private URL deploymentUrl; + + private EmployeeService employeeServiceProxy; + + @Deployment(testable = false) + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class, APP_NAME + ".war").addPackage(EmployeeService.class.getPackage()).addPackage(Employee.class.getPackage()).addPackage(EmployeeNotFound.class.getPackage()).addPackage(EmployeeRepository.class.getPackage()) + .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Before + public void setUp() { + try { + wsdlUrl = new URL(deploymentUrl, WSDL_PATH); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + + Service service = Service.create(wsdlUrl, SERVICE_NAME); + employeeServiceProxy = service.getPort(EmployeeService.class); + } + + @Test + public void givenGetAllEmployees_thenCorrectNumberOfEmployeesReturned() { + int employeeCount = employeeServiceProxy.countEmployees(); + List employeeList = employeeServiceProxy.getAllEmployees(); + assertEquals(employeeList.size(), employeeCount); + } + + @Test + public void givenEmployeeId_whenEmployeeExists_thenCorrectEmployeeReturned() throws EmployeeNotFound { + Employee employee = employeeServiceProxy.getEmployee(2); + assertEquals(employee.getFirstName(), "Jack"); + } + + @Test(expected = EmployeeNotFound.class) + public void givenEmployeeId_whenEmployeeNotExists_thenEmployeeNotFoundExceptionReturned() throws EmployeeNotFound { + employeeServiceProxy.getEmployee(20); + } + + @Test + public void givenAddEmployee_whenEmployeeDoesntAlreadyExist_thenEmployeeCountIncreased() throws EmployeeAlreadyExists { + int employeeCount = employeeServiceProxy.countEmployees(); + employeeServiceProxy.addEmployee(4, "Anna"); + assertEquals(employeeServiceProxy.countEmployees(), employeeCount + 1); + } + + @Test(expected = EmployeeAlreadyExists.class) + public void givenAddEmployee_whenEmployeeAlreadyExist_thenEmployeeAlreadyExistsExceptionReturned() throws EmployeeAlreadyExists { + employeeServiceProxy.addEmployee(1, "Anna"); + } + + @Test + public void givenUpdateEmployee_whenEmployeeExists_thenUpdatedEmployeeReturned() throws EmployeeNotFound { + Employee updated = employeeServiceProxy.updateEmployee(1, "Joan"); + assertEquals(updated.getFirstName(), "Joan"); + } + + @Test(expected = EmployeeNotFound.class) + public void givenUpdateEmployee_whenEmployeeNotExists_thenUpdatedEmployeeReturned() throws EmployeeNotFound { + employeeServiceProxy.updateEmployee(20, "Joan"); + } + + @Test + public void givenDeleteEmployee_whenEmployeeExists_thenCorrectStatusReturned() throws EmployeeNotFound { + boolean deleteEmployee = employeeServiceProxy.deleteEmployee(3); + assertEquals(deleteEmployee, true); + } + + @Test(expected = EmployeeNotFound.class) + public void givenDeleteEmployee_whenEmployeeNotExists_thenEmployeeNotFoundExceptionReturned() throws EmployeeNotFound { + employeeServiceProxy.deleteEmployee(20); + } + +} From ec089be605ffcc7e699ef9d35630a824fd436c1e Mon Sep 17 00:00:00 2001 From: Sunil Mogadati Date: Thu, 30 Mar 2017 02:54:26 -0600 Subject: [PATCH 079/102] BAEL-611: Minor formatting changes (#1534) * Add NDC and JBoss Logging to the demo application * NDC for Log4j, Log4j2 and JBoss Logging * Simplify NDC example by making it a single operation instead of two * Make NDC example as RestController, Use JBoss Logging only as a logging bridge * Fix merge conflicts in pull request - log-mdc pom.xml updated * BAEL-445 Update to Spring security SpEL example * BAEL-445: Change tabs to spaces in the updated code * BAEL-245: Add Enum Serialization exmaple * BAEL-245: Remove the folder jackson/src/test/java/com/baeldung/jackson/dtos/withEnum as the example is not used anymore * Add more enum serialization examples to align with previous example and prevent build fail * BAEL-611: Minor formatting changes * BAEL-611: Update Test case method names --- jee7/pom.xml | 1 - .../com/baeldung/jaxws/EmployeeService.java | 9 ++-- .../baeldung/jaxws/EmployeeServiceImpl.java | 11 ++--- .../config/EmployeeServicePublisher.java | 4 +- .../exception/EmployeeAlreadyExists.java | 7 +++- .../com/baeldung/jaxws/model/Employee.java | 1 + .../jaxws/repository/EmployeeRepository.java | 4 +- .../repository/EmployeeRepositoryImpl.java | 6 +-- .../jaxws/EmployeeServiceLiveTest.java | 42 ++++++++++--------- 9 files changed, 46 insertions(+), 39 deletions(-) diff --git a/jee7/pom.xml b/jee7/pom.xml index 3d5d7ccdc4..f275f56d58 100644 --- a/jee7/pom.xml +++ b/jee7/pom.xml @@ -28,7 +28,6 @@ 3.6.0 2.6 - 1.10.19 diff --git a/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java b/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java index 7544369992..9735607da6 100644 --- a/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java +++ b/jee7/src/main/java/com/baeldung/jaxws/EmployeeService.java @@ -1,13 +1,14 @@ package com.baeldung.jaxws; +import java.util.List; + +import javax.jws.WebMethod; +import javax.jws.WebService; + import com.baeldung.jaxws.exception.EmployeeAlreadyExists; import com.baeldung.jaxws.exception.EmployeeNotFound; import com.baeldung.jaxws.model.Employee; -import javax.jws.WebMethod; -import javax.jws.WebService; -import java.util.List; - @WebService public interface EmployeeService { diff --git a/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java b/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java index 35b84fe620..c1c9cd4385 100644 --- a/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java +++ b/jee7/src/main/java/com/baeldung/jaxws/EmployeeServiceImpl.java @@ -1,15 +1,16 @@ package com.baeldung.jaxws; +import java.util.List; + +import javax.inject.Inject; +import javax.jws.WebMethod; +import javax.jws.WebService; + import com.baeldung.jaxws.exception.EmployeeAlreadyExists; import com.baeldung.jaxws.exception.EmployeeNotFound; import com.baeldung.jaxws.model.Employee; import com.baeldung.jaxws.repository.EmployeeRepository; -import javax.inject.Inject; -import javax.jws.WebMethod; -import javax.jws.WebService; -import java.util.List; - @WebService(serviceName = "EmployeeService", endpointInterface = "com.baeldung.jaxws.EmployeeService") public class EmployeeServiceImpl implements EmployeeService { diff --git a/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java b/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java index 6d7acc88e0..ac3b049320 100644 --- a/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java +++ b/jee7/src/main/java/com/baeldung/jaxws/config/EmployeeServicePublisher.java @@ -1,9 +1,9 @@ package com.baeldung.jaxws.config; -import com.baeldung.jaxws.EmployeeServiceImpl; - import javax.xml.ws.Endpoint; +import com.baeldung.jaxws.EmployeeServiceImpl; + public class EmployeeServicePublisher { public static void main(String[] args) { diff --git a/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java index d7c9d0f2cc..4842fbf84c 100644 --- a/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java +++ b/jee7/src/main/java/com/baeldung/jaxws/exception/EmployeeAlreadyExists.java @@ -1,13 +1,16 @@ package com.baeldung.jaxws.exception; -import javax.xml.ws.WebFault; import java.io.Serializable; +import javax.xml.ws.WebFault; + @WebFault public class EmployeeAlreadyExists extends Exception implements Serializable { + private static final long serialVersionUID = 1L; + public EmployeeAlreadyExists() { - super("This employee already exist"); + super("This employee already exists"); } public EmployeeAlreadyExists(String str) { diff --git a/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java b/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java index 27d02354c0..5b1673c1e4 100644 --- a/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java +++ b/jee7/src/main/java/com/baeldung/jaxws/model/Employee.java @@ -3,6 +3,7 @@ package com.baeldung.jaxws.model; import java.io.Serializable; public class Employee implements Serializable { + private static final long serialVersionUID = 1L; private int id; private String firstName; diff --git a/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java index 3a8930ac04..0d5dec0462 100644 --- a/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java +++ b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepository.java @@ -1,11 +1,11 @@ package com.baeldung.jaxws.repository; +import java.util.List; + import com.baeldung.jaxws.exception.EmployeeAlreadyExists; import com.baeldung.jaxws.exception.EmployeeNotFound; import com.baeldung.jaxws.model.Employee; -import java.util.List; - public interface EmployeeRepository { List getAllEmployees(); diff --git a/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java index 0e728ae253..f67509fff5 100644 --- a/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java +++ b/jee7/src/main/java/com/baeldung/jaxws/repository/EmployeeRepositoryImpl.java @@ -1,12 +1,12 @@ package com.baeldung.jaxws.repository; +import java.util.ArrayList; +import java.util.List; + import com.baeldung.jaxws.exception.EmployeeAlreadyExists; import com.baeldung.jaxws.exception.EmployeeNotFound; import com.baeldung.jaxws.model.Employee; -import java.util.ArrayList; -import java.util.List; - public class EmployeeRepositoryImpl implements EmployeeRepository { private List employeeList; diff --git a/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java b/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java index 8907179b14..5311b3c5fe 100644 --- a/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java +++ b/jee7/src/test/java/com/baeldung/jaxws/EmployeeServiceLiveTest.java @@ -1,9 +1,14 @@ package com.baeldung.jaxws; -import com.baeldung.jaxws.exception.EmployeeAlreadyExists; -import com.baeldung.jaxws.exception.EmployeeNotFound; -import com.baeldung.jaxws.model.Employee; -import com.baeldung.jaxws.repository.EmployeeRepository; +import static org.junit.Assert.assertEquals; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import javax.xml.namespace.QName; +import javax.xml.ws.Service; + import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; @@ -14,13 +19,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import javax.xml.namespace.QName; -import javax.xml.ws.Service; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.List; - -import static org.junit.Assert.assertEquals; +import com.baeldung.jaxws.exception.EmployeeAlreadyExists; +import com.baeldung.jaxws.exception.EmployeeNotFound; +import com.baeldung.jaxws.model.Employee; +import com.baeldung.jaxws.repository.EmployeeRepository; @RunWith(Arquillian.class) public class EmployeeServiceLiveTest { @@ -54,54 +56,54 @@ public class EmployeeServiceLiveTest { } @Test - public void givenGetAllEmployees_thenCorrectNumberOfEmployeesReturned() { + public void givenEmployees_whenGetCount_thenCorrectNumberOfEmployeesReturned() { int employeeCount = employeeServiceProxy.countEmployees(); List employeeList = employeeServiceProxy.getAllEmployees(); assertEquals(employeeList.size(), employeeCount); } @Test - public void givenEmployeeId_whenEmployeeExists_thenCorrectEmployeeReturned() throws EmployeeNotFound { + public void givenEmployees_whenGetAvailableEmployee_thenCorrectEmployeeReturned() throws EmployeeNotFound { Employee employee = employeeServiceProxy.getEmployee(2); assertEquals(employee.getFirstName(), "Jack"); } @Test(expected = EmployeeNotFound.class) - public void givenEmployeeId_whenEmployeeNotExists_thenEmployeeNotFoundExceptionReturned() throws EmployeeNotFound { + public void givenEmployees_whenGetNonAvailableEmployee_thenEmployeeNotFoundException() throws EmployeeNotFound { employeeServiceProxy.getEmployee(20); } @Test - public void givenAddEmployee_whenEmployeeDoesntAlreadyExist_thenEmployeeCountIncreased() throws EmployeeAlreadyExists { + public void givenEmployees_whenAddNewEmployee_thenEmployeeCountIncreased() throws EmployeeAlreadyExists { int employeeCount = employeeServiceProxy.countEmployees(); employeeServiceProxy.addEmployee(4, "Anna"); assertEquals(employeeServiceProxy.countEmployees(), employeeCount + 1); } @Test(expected = EmployeeAlreadyExists.class) - public void givenAddEmployee_whenEmployeeAlreadyExist_thenEmployeeAlreadyExistsExceptionReturned() throws EmployeeAlreadyExists { + public void givenEmployees_whenAddAlreadyExistingEmployee_thenEmployeeAlreadyExistsException() throws EmployeeAlreadyExists { employeeServiceProxy.addEmployee(1, "Anna"); } @Test - public void givenUpdateEmployee_whenEmployeeExists_thenUpdatedEmployeeReturned() throws EmployeeNotFound { + public void givenEmployees_whenUpdateExistingEmployee_thenUpdatedEmployeeReturned() throws EmployeeNotFound { Employee updated = employeeServiceProxy.updateEmployee(1, "Joan"); assertEquals(updated.getFirstName(), "Joan"); } @Test(expected = EmployeeNotFound.class) - public void givenUpdateEmployee_whenEmployeeNotExists_thenUpdatedEmployeeReturned() throws EmployeeNotFound { + public void givenEmployees_whenUpdateNonExistingEmployee_thenEmployeeNotFoundException() throws EmployeeNotFound { employeeServiceProxy.updateEmployee(20, "Joan"); } @Test - public void givenDeleteEmployee_whenEmployeeExists_thenCorrectStatusReturned() throws EmployeeNotFound { + public void givenEmployees_whenDeleteExistingEmployee_thenSuccessReturned() throws EmployeeNotFound { boolean deleteEmployee = employeeServiceProxy.deleteEmployee(3); assertEquals(deleteEmployee, true); } @Test(expected = EmployeeNotFound.class) - public void givenDeleteEmployee_whenEmployeeNotExists_thenEmployeeNotFoundExceptionReturned() throws EmployeeNotFound { + public void givenEmployee_whenDeleteNonExistingEmployee_thenEmployeeNotFoundException() throws EmployeeNotFound { employeeServiceProxy.deleteEmployee(20); } From e28dfe6a4a7fa86535ec9af97bd4d6d17c4e9d7f Mon Sep 17 00:00:00 2001 From: Doha2012 Date: Thu, 30 Mar 2017 13:52:40 +0200 Subject: [PATCH 080/102] modify ratings controller (#1536) * upgrade to spring boot 1.5.2 * add full update to REST API * modify ratings controller --- .../bootstrap/svcrating/rating/RatingController.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingController.java b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingController.java index cbfeda49c0..91966034f6 100644 --- a/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingController.java +++ b/spring-cloud/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingController.java @@ -2,6 +2,7 @@ package com.baeldung.spring.cloud.bootstrap.svcrating.rating; import java.util.List; import java.util.Map; +import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; @@ -23,11 +24,9 @@ public class RatingController { private RatingService ratingService; @GetMapping - public List findRatingsByBookId(@RequestParam(required = false, defaultValue = "0") Long bookId) { - if (bookId.equals(0L)) { - return ratingService.findAllRatings(); - } - return ratingService.findRatingsByBookId(bookId); + public List findRatingsByBookId(@RequestParam(required = false) Optional bookId) { + return bookId.map(ratingService::findRatingsByBookId) + .orElseGet(ratingService::findAllRatings); } @PostMapping From ce589cc7b5b78edd80ff3b818ed53dd2af5bb98c Mon Sep 17 00:00:00 2001 From: Devendra Tiwari Date: Thu, 30 Mar 2017 23:51:17 +0530 Subject: [PATCH 081/102] Corrected Indentation using formatter. (#1541) --- .../tutorial/AtomicLongMapTutorials.java | 15 ++-- .../guava/tutorial/ComparatorsExamples.java | 6 +- .../guava/tutorial/ConcatStreams.java | 4 +- .../tutorial/InternerBuilderExample.java | 12 +-- .../guava/tutorial/MonitorExample.java | 4 +- .../guava/tutorial/MoreCollectorsExample.java | 7 +- .../guava/tutorial/StreamsUtility.java | 13 ++-- guava21/src/test/java/AtomicLongMapTests.java | 46 +++++------- .../src/test/java/ComparatorsUnitTests.java | 15 ++-- guava21/src/test/java/GauavaStreamsTests.java | 75 ++++++++++--------- .../src/test/java/InternBuilderUnitTests.java | 9 +-- guava21/src/test/java/MonitorUnitTests.java | 24 ++---- .../test/java/MoreCollectorsUnitTests.java | 24 +++--- guava21/src/test/java/StreamUtility.java | 10 +-- 14 files changed, 119 insertions(+), 145 deletions(-) diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java b/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java index 69ad04ad9c..79ce02b7f0 100644 --- a/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/AtomicLongMapTutorials.java @@ -6,19 +6,18 @@ public class AtomicLongMapTutorials { private AtomicLongMap atomicLongMap; - public AtomicLongMapTutorials(){ + public AtomicLongMapTutorials() { atomicLongMap = AtomicLongMap.create(); } - - public void addKeys(){ - atomicLongMap.addAndGet("apple",250); - atomicLongMap.addAndGet("bat",350); - atomicLongMap.addAndGet("cat",450); - atomicLongMap.addAndGet("dog",550); + public void addKeys() { + atomicLongMap.addAndGet("apple", 250); + atomicLongMap.addAndGet("bat", 350); + atomicLongMap.addAndGet("cat", 450); + atomicLongMap.addAndGet("dog", 550); } - public static void main(String[] args){ + public static void main(String[] args) { AtomicLongMapTutorials atomicLongMapTutorials = new AtomicLongMapTutorials(); atomicLongMapTutorials.addKeys(); diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java b/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java index 6eb5c7f5ba..abb4c51e8f 100644 --- a/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/ComparatorsExamples.java @@ -8,12 +8,10 @@ import java.util.List; public class ComparatorsExamples { - public static void main(String[] args){ + public static void main(String[] args) { - List integers = Arrays.asList(1,2,3,4,4,6,7,8,9,10); - //This will return true + List integers = Arrays.asList(1, 2, 3, 4, 4, 6, 7, 8, 9, 10); boolean isInAscendingOrder = Comparators.isInOrder(integers, new AscedingOrderComparator()); - System.out.println(isInAscendingOrder); } diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java b/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java index ab95b85751..0304d48fbc 100644 --- a/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/ConcatStreams.java @@ -5,7 +5,7 @@ import com.google.common.collect.Streams; import java.util.stream.Stream; public class ConcatStreams { - public static Stream concatStreams(Stream stream1, Stream stream2, Stream stream3){ - return Streams.concat(stream1,stream2,stream3); + public static Stream concatStreams(Stream stream1, Stream stream2, Stream stream3) { + return Streams.concat(stream1, stream2, stream3); } } diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java b/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java index 6b935ba2a8..5c85e684d5 100644 --- a/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/InternerBuilderExample.java @@ -3,16 +3,12 @@ package com.baeldung.guava.tutorial; import com.google.common.collect.Interner; import com.google.common.collect.Interners; -import static com.google.common.collect.Interners.newBuilder; - public class InternerBuilderExample { - public static void main(String[] args){ - Interner interners = Interners.newBuilder() - .concurrencyLevel(2) - .strong() - .build(); - + public static void main(String[] args) { + Interner interners = Interners. newBuilder() + .concurrencyLevel(2) + .strong(). build(); } diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java b/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java index 2f316c293e..78bcbe3d49 100644 --- a/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/MonitorExample.java @@ -4,7 +4,6 @@ import com.google.common.util.concurrent.Monitor; import java.util.ArrayList; import java.util.List; -import java.util.function.BooleanSupplier; public class MonitorExample { private List students = new ArrayList(); @@ -12,7 +11,6 @@ public class MonitorExample { private Monitor monitor = new Monitor(); - public void addToCourse(String item) throws InterruptedException { Monitor.Guard studentsBelowCapacity = monitor.newGuard(this::isStudentsCapacityUptoLimit); monitor.enterWhen(studentsBelowCapacity); @@ -23,7 +21,7 @@ public class MonitorExample { } } - public Boolean isStudentsCapacityUptoLimit(){ + public Boolean isStudentsCapacityUptoLimit() { return students.size() > MAX_SIZE; } } diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java b/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java index 6cf4b6b0ac..2b3bd7cdc4 100644 --- a/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/MoreCollectorsExample.java @@ -10,8 +10,9 @@ public class MoreCollectorsExample { public static void main(String[] args) { List numbers = Arrays.asList(1); - Optional number = numbers.stream() - .map(e -> e * 2) - .collect(MoreCollectors.toOptional()); + Optional number = numbers + .stream() + .map(e -> e * 2) + .collect(MoreCollectors.toOptional()); } } diff --git a/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java b/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java index 4ec3b44ef4..b15f61afd5 100644 --- a/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java +++ b/guava21/src/main/java/com/baeldung/guava/tutorial/StreamsUtility.java @@ -10,9 +10,9 @@ import java.util.stream.Stream; public class StreamsUtility { - public static void main(String[] args){ + public static void main(String[] args) { - List numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,11,12,13,14,15,16,17,18,19,20); + List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); //Using Collection Stream streamFromCollection = Streams.stream(numbers); //Using Iterator @@ -28,15 +28,12 @@ public class StreamsUtility { //Using OptionalDouble to DoubleStream DoubleStream streamFromOptionalDouble = Streams.stream(OptionalDouble.of(1.0)); - Stream concatenatedStreams = Streams.concat(streamFromCollection,streamFromIterable,streamFromIterator); + Stream concatenatedStreams = Streams.concat(streamFromCollection, streamFromIterable, streamFromIterator); - List integers = Arrays.asList(1,2,3,4,5,6,7,8,9,10); + List integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); //This will return 10 Optional lastItem = Streams.findLast(integers.stream()); - Streams.zip( - Stream.of("candy", "chocolate", "bar"), - Stream.of("$1", "$2","$3"), - (arg1, arg2) -> arg1 + ":" + arg2); + Streams.zip(Stream.of("candy", "chocolate", "bar"), Stream.of("$1", "$2", "$3"), (arg1, arg2) -> arg1 + ":" + arg2); } } diff --git a/guava21/src/test/java/AtomicLongMapTests.java b/guava21/src/test/java/AtomicLongMapTests.java index aad72907de..6bde997e8d 100644 --- a/guava21/src/test/java/AtomicLongMapTests.java +++ b/guava21/src/test/java/AtomicLongMapTests.java @@ -1,5 +1,4 @@ import com.google.common.util.concurrent.AtomicLongMap; -import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -12,35 +11,28 @@ public class AtomicLongMapTests { AtomicLongMap courses = AtomicLongMap.create(); - public void setUp(){ + public void setUp() { courses.put(SPRING_COURSE_KEY, 1056); courses.put(HIBERNATE_COURSE_KEY, 259); courses.put(GUAVA_COURSE_KEY, 78); } + @Test + public void accumulateAndGet_withLongBinaryOperator_thenSuccessful() { + long noOfStudents = 56; + long oldValue = courses.get(SPRING_COURSE_KEY); -@Test -public void accumulateAndGet_withLongBinaryOperator_thenSuccessful(){ - long noOfStudents = 56; - long oldValue = courses.get(SPRING_COURSE_KEY); + long totalNotesRequired = courses.accumulateAndGet("Guava", noOfStudents, (x, y) -> (x * y)); - long totalNotesRequired = courses.accumulateAndGet( - "Guava", - noOfStudents, - (x,y) -> (x * y)); - - assertEquals(totalNotesRequired, oldValue * noOfStudents ); -} + assertEquals(totalNotesRequired, oldValue * noOfStudents); + } @Test - public void getAndAccumulate_withLongBinaryOperator_thenSuccessful(){ + public void getAndAccumulate_withLongBinaryOperator_thenSuccessful() { long noOfStudents = 56; long beforeUpdate = courses.get(SPRING_COURSE_KEY); - long onUpdate = courses.accumulateAndGet("Guava", - noOfStudents, - (x,y) -> (x * y) - ); + long onUpdate = courses.accumulateAndGet("Guava", noOfStudents, (x, y) -> (x * y)); long afterUpdate = courses.get(SPRING_COURSE_KEY); @@ -48,17 +40,15 @@ public void accumulateAndGet_withLongBinaryOperator_thenSuccessful(){ assertEquals(afterUpdate, beforeUpdate * noOfStudents); } -@Test -public void updateAndGet_withLongUnaryOperator_thenSuccessful(){ - long beforeUpdate = courses.get(SPRING_COURSE_KEY); + @Test + public void updateAndGet_withLongUnaryOperator_thenSuccessful() { + long beforeUpdate = courses.get(SPRING_COURSE_KEY); - long onUpdate = courses.updateAndGet( - "Guava", - (x) -> (x/2)); + long onUpdate = courses.updateAndGet("Guava", (x) -> (x / 2)); - long afterUpdate = courses.get(SPRING_COURSE_KEY); + long afterUpdate = courses.get(SPRING_COURSE_KEY); - assertEquals(onUpdate, afterUpdate); - assertEquals(afterUpdate, beforeUpdate/2); -} + assertEquals(onUpdate, afterUpdate); + assertEquals(afterUpdate, beforeUpdate / 2); + } } diff --git a/guava21/src/test/java/ComparatorsUnitTests.java b/guava21/src/test/java/ComparatorsUnitTests.java index f196c41a1b..8aaae1e14e 100644 --- a/guava21/src/test/java/ComparatorsUnitTests.java +++ b/guava21/src/test/java/ComparatorsUnitTests.java @@ -2,7 +2,9 @@ import com.google.common.collect.Comparators; import org.junit.Assert; import org.junit.Test; -import java.util.*; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; import java.util.function.Function; import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; @@ -11,9 +13,9 @@ import java.util.function.ToLongFunction; public class ComparatorsUnitTests { @Test - public void isInOrderTest(){ + public void isInOrderTest() { - List numbers = Arrays.asList(1,2,3,4,4,6,7,8,9,10); + List numbers = Arrays.asList(1, 2, 3, 4, 4, 6, 7, 8, 9, 10); boolean isInAscendingOrder = Comparators.isInOrder(numbers, new AscendingOrderComparator()); @@ -21,17 +23,16 @@ public class ComparatorsUnitTests { } @Test - public void isInStrictOrderTest(){ + public void isInStrictOrderTest() { - List numbers = Arrays.asList(1,2,3,4,3,6,7,8,9,10); + List numbers = Arrays.asList(1, 2, 3, 4, 3, 6, 7, 8, 9, 10); boolean isInAscendingOrder = Comparators.isInOrder(numbers, new AscendingOrderComparator()); Assert.assertFalse(isInAscendingOrder); } - - private class AscendingOrderComparator implements Comparator{ + private class AscendingOrderComparator implements Comparator { @Override public int compare(Integer o1, Integer o2) { diff --git a/guava21/src/test/java/GauavaStreamsTests.java b/guava21/src/test/java/GauavaStreamsTests.java index 09e3e29b47..1482d685cf 100644 --- a/guava21/src/test/java/GauavaStreamsTests.java +++ b/guava21/src/test/java/GauavaStreamsTests.java @@ -14,13 +14,12 @@ public class GauavaStreamsTests { List numbers; @Before - public void setUp(){ - numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,11,12,13,14,15,16,17,18,19,20); + public void setUp() { + numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); } - @Test - public void createStreamsWithCollection(){ + public void createStreamsWithCollection() { //Deprecated API to create stream from collection Stream streamFromCollection = Streams.stream(numbers); @@ -29,7 +28,7 @@ public class GauavaStreamsTests { } @Test - public void createStreamsWithIterable(){ + public void createStreamsWithIterable() { Iterable numbersIterable = (Iterable) numbers; Stream streamFromIterable = Streams.stream(numbersIterable); @@ -39,7 +38,7 @@ public class GauavaStreamsTests { } @Test - public void createStreamsWithIterator(){ + public void createStreamsWithIterator() { Iterator numbersIterator = numbers.iterator(); Stream streamFromIterator = Streams.stream(numbersIterator); @@ -49,7 +48,7 @@ public class GauavaStreamsTests { } @Test - public void createStreamsWithOptional(){ + public void createStreamsWithOptional() { Stream streamFromOptional = Streams.stream(Optional.of(1)); @@ -58,7 +57,7 @@ public class GauavaStreamsTests { } @Test - public void createStreamsWithOptionalLong(){ + public void createStreamsWithOptionalLong() { LongStream streamFromOptionalLong = Streams.stream(OptionalLong.of(1)); @@ -67,7 +66,7 @@ public class GauavaStreamsTests { } @Test - public void createStreamsWithOptionalInt(){ + public void createStreamsWithOptionalInt() { IntStream streamFromOptionalInt = Streams.stream(OptionalInt.of(1)); @@ -76,7 +75,7 @@ public class GauavaStreamsTests { } @Test - public void createStreamsWithOptionalDouble(){ + public void createStreamsWithOptionalDouble() { DoubleStream streamFromOptionalDouble = Streams.stream(OptionalDouble.of(1.0)); @@ -86,41 +85,44 @@ public class GauavaStreamsTests { } @Test - public void concatStreamsOfSameType(){ - Stream oddNumbers = Arrays.asList(1,3,5,7,9,11,13,15,17,19).stream(); - Stream evenNumbers = Arrays.asList(2,4,6,8,10,12,14,16,18,20).stream(); + public void concatStreamsOfSameType() { + Stream oddNumbers = Arrays + .asList(1, 3, 5, 7, 9, 11, 13, 15, 17, 19) + .stream(); + Stream evenNumbers = Arrays + .asList(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) + .stream(); - Stream combinedStreams = Streams.concat(oddNumbers,evenNumbers); + Stream combinedStreams = Streams.concat(oddNumbers, evenNumbers); //Assert.assertNotNull(combinedStreams); StreamUtility.assertStreamEquals(combinedStreams, Stream.concat(oddNumbers, evenNumbers)); } @Test - public void concatStreamsOfTypeLongStream(){ - LongStream firstTwenty = LongStream.range(1,20); - LongStream nextTwenty = LongStream.range(21,40); + public void concatStreamsOfTypeLongStream() { + LongStream firstTwenty = LongStream.range(1, 20); + LongStream nextTwenty = LongStream.range(21, 40); - LongStream combinedStreams = Streams.concat(firstTwenty,nextTwenty); + LongStream combinedStreams = Streams.concat(firstTwenty, nextTwenty); Assert.assertNotNull(combinedStreams); StreamUtility.assertStreamEquals(combinedStreams, LongStream.concat(firstTwenty, nextTwenty)); } @Test - public void concatStreamsOfTypeIntStream(){ - IntStream firstTwenty = IntStream.range(1,20); - IntStream nextTwenty = IntStream.range(21,40); + public void concatStreamsOfTypeIntStream() { + IntStream firstTwenty = IntStream.range(1, 20); + IntStream nextTwenty = IntStream.range(21, 40); - IntStream combinedStreams = Streams.concat(firstTwenty,nextTwenty); + IntStream combinedStreams = Streams.concat(firstTwenty, nextTwenty); Assert.assertNotNull(combinedStreams); StreamUtility.assertStreamEquals(combinedStreams, IntStream.concat(firstTwenty, nextTwenty)); } - @Test - public void findLastOfStream(){ + public void findLastOfStream() { Optional lastElement = Streams.findLast(numbers.stream()); Assert.assertNotNull(lastElement.get()); @@ -128,28 +130,29 @@ public class GauavaStreamsTests { } @Test - public void mapWithIndexTest(){ - Stream stringSream = Stream.of("a","b","c"); + public void mapWithIndexTest() { + Stream stringSream = Stream.of("a", "b", "c"); - Stream mappedStream = Streams.mapWithIndex(stringSream,(str,index) -> str +":"+ index); + Stream mappedStream = Streams.mapWithIndex(stringSream, (str, index) -> str + ":" + index); //Assert.assertNotNull(mappedStream); - Assert.assertEquals(mappedStream.findFirst().get(), "a:0"); + Assert.assertEquals(mappedStream + .findFirst() + .get(), "a:0"); } @Test - public void streamsZipTest(){ - Stream stringSream = Stream.of("a","b","c"); - Stream intStream = Stream.of(1,2,3); - Stream mappedStream = Streams.zip(stringSream,intStream, (str,index) -> str +":"+ index); + public void streamsZipTest() { + Stream stringSream = Stream.of("a", "b", "c"); + Stream intStream = Stream.of(1, 2, 3); + Stream mappedStream = Streams.zip(stringSream, intStream, (str, index) -> str + ":" + index); //Assert.assertNotNull(mappedStream); - Assert.assertEquals(mappedStream.findFirst().get(), "a:1"); + Assert.assertEquals(mappedStream + .findFirst() + .get(), "a:1"); } - - - } diff --git a/guava21/src/test/java/InternBuilderUnitTests.java b/guava21/src/test/java/InternBuilderUnitTests.java index 513b44f249..b569b59978 100644 --- a/guava21/src/test/java/InternBuilderUnitTests.java +++ b/guava21/src/test/java/InternBuilderUnitTests.java @@ -6,12 +6,11 @@ import org.junit.Test; public class InternBuilderUnitTests { @Test - public void interBuilderTest(){ + public void interBuilderTest() { - Interner interners = Interners.newBuilder() - .concurrencyLevel(2) - .strong() - .build(); + Interner interners = Interners. newBuilder() + .concurrencyLevel(2) + .strong(). build(); Assert.assertNotNull(interners); } diff --git a/guava21/src/test/java/MonitorUnitTests.java b/guava21/src/test/java/MonitorUnitTests.java index 6427072db9..7b52c48d8f 100644 --- a/guava21/src/test/java/MonitorUnitTests.java +++ b/guava21/src/test/java/MonitorUnitTests.java @@ -2,14 +2,6 @@ import com.google.common.util.concurrent.Monitor; import org.junit.Assert; import org.junit.Test; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import static com.google.common.util.concurrent.Uninterruptibles.joinUninterruptibly; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - public class MonitorUnitTests { @Test @@ -19,11 +11,11 @@ public class MonitorUnitTests { Monitor.Guard gaurdCondition = monitor.newGuard(this::returnTrue); - if(monitor.enterIf(gaurdCondition)){ - try{ + if (monitor.enterIf(gaurdCondition)) { + try { System.out.println("Entered in critical section"); enteredInCriticalSection = true; - }finally { + } finally { monitor.leave(); } } @@ -39,11 +31,11 @@ public class MonitorUnitTests { Monitor.Guard gaurdCondition = monitor.newGuard(this::returnFalse); - if(monitor.enterIf(gaurdCondition)){ - try{ + if (monitor.enterIf(gaurdCondition)) { + try { System.out.println("Entered in critical section"); enteredInCriticalSection = true; - }finally { + } finally { monitor.leave(); } } @@ -51,11 +43,11 @@ public class MonitorUnitTests { Assert.assertFalse(enteredInCriticalSection); } - private boolean returnTrue(){ + private boolean returnTrue() { return true; } - private boolean returnFalse(){ + private boolean returnFalse() { return false; } } diff --git a/guava21/src/test/java/MoreCollectorsUnitTests.java b/guava21/src/test/java/MoreCollectorsUnitTests.java index 956331e25f..a205337450 100644 --- a/guava21/src/test/java/MoreCollectorsUnitTests.java +++ b/guava21/src/test/java/MoreCollectorsUnitTests.java @@ -9,28 +9,28 @@ import java.util.Optional; public class MoreCollectorsUnitTests { @Test - public void toOptionalTest(){ + public void toOptionalTest() { List numbers = Arrays.asList(1); - Optional number = numbers.stream() - .map( e -> e*2) - .collect(MoreCollectors.toOptional()); + Optional number = numbers + .stream() + .map(e -> e * 2) + .collect(MoreCollectors.toOptional()); - Assert.assertEquals(number.get(),new Integer(2)); + Assert.assertEquals(number.get(), new Integer(2)); } - @Test - public void onlyElementTest(){ + public void onlyElementTest() { List numbers = Arrays.asList(1); - Integer number = numbers.stream() - .map( e -> e*2) - .collect(MoreCollectors.onlyElement()); + Integer number = numbers + .stream() + .map(e -> e * 2) + .collect(MoreCollectors.onlyElement()); - Assert.assertEquals(number,new Integer(2)); + Assert.assertEquals(number, new Integer(2)); } - } diff --git a/guava21/src/test/java/StreamUtility.java b/guava21/src/test/java/StreamUtility.java index 91a03be48d..1eb866fb88 100644 --- a/guava21/src/test/java/StreamUtility.java +++ b/guava21/src/test/java/StreamUtility.java @@ -8,12 +8,12 @@ import java.util.stream.Stream; public class StreamUtility { - public static boolean assertStreamEquals(Stream stream1, Stream stream2){ + public static boolean assertStreamEquals(Stream stream1, Stream stream2) { Iterator iterator1 = stream1.iterator(); Iterator iterator2 = stream2.iterator(); - while (iterator1.hasNext()){ + while (iterator1.hasNext()) { Assert.assertEquals(iterator1.next(), iterator2.next()); } @@ -27,7 +27,7 @@ public class StreamUtility { Iterator iterator1 = stream1.iterator(); Iterator iterator2 = stream2.iterator(); - while (iterator1.hasNext()){ + while (iterator1.hasNext()) { Assert.assertEquals(iterator1.next(), iterator2.next()); } @@ -41,7 +41,7 @@ public class StreamUtility { Iterator iterator1 = stream1.iterator(); Iterator iterator2 = stream2.iterator(); - while (iterator1.hasNext()){ + while (iterator1.hasNext()) { Assert.assertEquals(iterator1.next(), iterator2.next()); } @@ -55,7 +55,7 @@ public class StreamUtility { Iterator iterator1 = stream1.iterator(); Iterator iterator2 = stream2.iterator(); - while (iterator1.hasNext()){ + while (iterator1.hasNext()) { Assert.assertEquals(iterator1.next(), iterator2.next()); } From 99688e9b19bc1bc2f5c06996b3d988ce42d5ec34 Mon Sep 17 00:00:00 2001 From: Danil Kornishev Date: Thu, 30 Mar 2017 16:27:22 -0400 Subject: [PATCH 082/102] Spring State Machine x3 (#1538) * [Fix] Move stateDo onto a separate state * Change tests to spring runner --- spring-state-machine/pom.xml | 7 +++- .../SimpleStateMachineConfiguration.java | 21 ++++++++--- .../ForkJoinStateMachineTest.java | 29 +++++++++++---- .../HierarchicalStateMachineTest.java | 27 +++++++++++--- .../JunctionStateMachineTest.java | 26 +++++++++++-- .../statemachine/StateEnumMachineTest.java | 17 ++++++++- .../StateMachineIntegrationTest.java | 37 ++++++++++++++----- 7 files changed, 130 insertions(+), 34 deletions(-) diff --git a/spring-state-machine/pom.xml b/spring-state-machine/pom.xml index bec03c39e8..f04d706d47 100644 --- a/spring-state-machine/pom.xml +++ b/spring-state-machine/pom.xml @@ -21,10 +21,15 @@ spring-statemachine-core 1.2.3.RELEASE + + org.springframework + spring-test + 4.3.7.RELEASE + junit junit - 4.11 + 4.12 test diff --git a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java index e9b448f6e7..f6c7991cf6 100644 --- a/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java +++ b/spring-state-machine/src/main/java/com/baeldung/spring/statemachine/config/SimpleStateMachineConfiguration.java @@ -36,10 +36,10 @@ public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapt .initial("SI") .end("SF") .states(new HashSet<>(Arrays.asList("S1", "S2"))) - .state("S4", executeAction(), errorAction()) .stateEntry("S3", entryAction()) - .stateDo("S3", executeAction()) - .stateExit("S3", exitAction()); + .stateExit("S3", exitAction()) + .state("S4", executeAction(), errorAction()) + .stateDo("S5", executeAction()); } @@ -52,9 +52,11 @@ public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapt .and().withExternal() .source("SI").target("S3").event("E3") .and().withExternal() - .source("S3").target("S4").event("E4").and().withExternal().source("S4").target("SF").event("end").guard(simpleGuard()) + .source("S3").target("S4").event("E4") .and().withExternal() - .source("S2").target("SF").event("end"); + .source("S4").target("S5").event("E5") + .and().withExternal() + .source("S5").target("SF").event("end").guard(simpleGuard()); } @Bean @@ -73,9 +75,16 @@ public class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapt } @Bean - public Action executeAction() { + public Action doAction() { return (ctx) -> { LOGGER.info("Do " + ctx.getTarget().getId()); + }; + } + + @Bean + public Action executeAction() { + return (ctx) -> { + LOGGER.info("Execute " + ctx.getTarget().getId()); int approvals = (int) ctx.getExtendedState().getVariables().getOrDefault("approvalCount", 0); approvals++; ctx.getExtendedState().getVariables().put("approvalCount", approvals); diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java index 7b4b1928ea..03cb101a9d 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java @@ -1,23 +1,36 @@ package com.baeldung.spring.statemachine; import com.baeldung.spring.statemachine.config.ForkJoinStateMachineConfiguration; +import com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration; +import org.junit.After; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.statemachine.StateMachine; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import javax.annotation.Resource; import java.util.Arrays; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = ForkJoinStateMachineConfiguration.class) public class ForkJoinStateMachineTest { + @Resource + private StateMachine stateMachine; + + @Before + public void setUp() { + stateMachine.start(); + } + @Test public void whenForkStateEntered_thenMultipleSubStatesEntered() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ForkJoinStateMachineConfiguration.class); - StateMachine stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); - boolean success = stateMachine.sendEvent("E1"); assertTrue(success); @@ -27,9 +40,6 @@ public class ForkJoinStateMachineTest { @Test public void whenAllConfiguredJoinEntryStatesAreEntered_thenTransitionToJoinState() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ForkJoinStateMachineConfiguration.class); - StateMachine stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); boolean success = stateMachine.sendEvent("E1"); @@ -41,4 +51,9 @@ public class ForkJoinStateMachineTest { assertTrue(stateMachine.sendEvent("sub2")); assertEquals("SJoin", stateMachine.getState().getId()); } + + @After + public void tearDown() { + stateMachine.stop(); + } } diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java index d2944be173..950414bb0e 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java @@ -1,23 +1,35 @@ package com.baeldung.spring.statemachine; import com.baeldung.spring.statemachine.config.HierarchicalStateMachineConfiguration; +import com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration; +import org.junit.After; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.statemachine.StateMachine; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import javax.annotation.Resource; import java.util.Arrays; import static org.junit.Assert.assertEquals; - +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = HierarchicalStateMachineConfiguration.class) public class HierarchicalStateMachineTest { + @Resource + private StateMachine stateMachine; + + @Before + public void setUp() { + stateMachine.start(); + } + @Test public void whenTransitionToSubMachine_thenSubStateIsEntered() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(HierarchicalStateMachineConfiguration.class); - StateMachine stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); - assertEquals(Arrays.asList("SI", "SUB1"), stateMachine.getState().getIds()); @@ -34,4 +46,9 @@ public class HierarchicalStateMachineTest { assertEquals(1, stateMachine.getState().getIds().size()); assertEquals("SF", stateMachine.getState().getId()); } + + @After + public void tearDown() { + stateMachine.stop(); + } } diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java index f01683638b..64930162fd 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java @@ -1,18 +1,33 @@ package com.baeldung.spring.statemachine; +import com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration; +import com.baeldung.spring.statemachine.config.SimpleEnumStateMachineConfiguration; +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.statemachine.StateMachine; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import javax.annotation.Resource; +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = JunctionStateMachineConfiguration.class) public class JunctionStateMachineTest { + @Resource + private StateMachine stateMachine; + + @Before + public void setUp() { + stateMachine.start(); + } + @Test public void whenTransitioningToJunction_thenArriveAtSubJunctionNode() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(com.baeldung.spring.statemachine.config.JunctionStateMachineConfiguration.class); - StateMachine stateMachine = ctx.getBean(StateMachine.class); - stateMachine.start(); stateMachine.sendEvent("E1"); Assert.assertEquals("low", stateMachine.getState().getId()); @@ -20,4 +35,9 @@ public class JunctionStateMachineTest { stateMachine.sendEvent("end"); Assert.assertEquals("SF", stateMachine.getState().getId()); } + + @After + public void tearDown() { + stateMachine.stop(); + } } diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java index 257ed8768c..b7cbebe145 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java @@ -3,22 +3,30 @@ package com.baeldung.spring.statemachine; import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewEvents; import com.baeldung.spring.statemachine.applicationreview.ApplicationReviewStates; import com.baeldung.spring.statemachine.config.SimpleEnumStateMachineConfiguration; +import com.baeldung.spring.statemachine.config.SimpleStateMachineConfiguration; +import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.statemachine.StateMachine; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.annotation.Resource; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = SimpleEnumStateMachineConfiguration.class) public class StateEnumMachineTest { + @Resource private StateMachine stateMachine; @Before public void setUp() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SimpleEnumStateMachineConfiguration.class); - stateMachine = ctx.getBean(StateMachine.class); stateMachine.start(); } @@ -29,4 +37,9 @@ public class StateEnumMachineTest { assertTrue(stateMachine.sendEvent(ApplicationReviewEvents.REJECT)); assertEquals(ApplicationReviewStates.REJECTED, stateMachine.getState().getId()); } + + @After + public void tearDown() { + stateMachine.stop(); + } } diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java index d7b26eeb97..8f61d93105 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java @@ -1,23 +1,31 @@ package com.baeldung.spring.statemachine; -import com.baeldung.spring.statemachine.config.SimpleStateMachineConfiguration; -import org.junit.Before; -import org.junit.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.statemachine.StateMachine; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.statemachine.StateMachine; + +import com.baeldung.spring.statemachine.config.SimpleStateMachineConfiguration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.annotation.Resource; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = SimpleStateMachineConfiguration.class) public class StateMachineIntegrationTest { - private AnnotationConfigApplicationContext ctx; + @Resource private StateMachine stateMachine; @Before public void setUp() { - ctx = new AnnotationConfigApplicationContext(SimpleStateMachineConfiguration.class); - stateMachine = ctx.getBean(StateMachine.class); stateMachine.start(); } @@ -42,9 +50,18 @@ public class StateMachineIntegrationTest { assertTrue(acceptedE4); assertEquals("S4", stateMachine.getState().getId()); - assertEquals(2, stateMachine.getExtendedState().getVariables().get("approvalCount")); + + stateMachine.sendEvent("E5"); + assertEquals("S5", stateMachine.getState().getId()); stateMachine.sendEvent("end"); assertEquals("SF", stateMachine.getState().getId()); + + assertEquals(2, stateMachine.getExtendedState().getVariables().get("approvalCount")); + } + + @After + public void tearDown() { + stateMachine.stop(); } } From d3590de13cb75a3d39553f2d78ecf1afe2b65e98 Mon Sep 17 00:00:00 2001 From: Abhinab Kanrar Date: Fri, 31 Mar 2017 02:40:45 +0530 Subject: [PATCH 083/102] ratpack with google guice (#1542) * rest with spark java * 4 * Update Application.java * indentation changes * spring @requestmapping shortcuts * removing spring requestmapping and pushing spring-mvc-java * Joining/Splitting Strings with Java and Stream API * adding more join/split functionality * changing package name * testcase change * adding webutils * adding testcase for WebUtils and ServletRequestUtils * adding testcase * spring-security-stormpath * adding ratpack module * adding pom.xml * adding following modules with updated testcase : DB, Filter, Json * adding spring-boot custom banner tutorial * changing banner format in plain text * Delete banner.txt~ * Delete b.txt~ * CORS in JAX-RS * ratpack with google guice --- .../java/com/baeldung/guice/Application.java | 31 ++++ .../guice/config/DependencyModule.java | 16 ++ .../guice/service/DataPumpService.java | 11 ++ .../service/impl/DataPumpServiceImpl.java | 14 ++ .../java/com/baeldung/ApplicationTest.java | 13 +- resteasy/bin/README.md | 8 + resteasy/bin/pom.xml | 171 ++++++++++++++++++ .../main/webapp/WEB-INF/classes/logback.xml | 3 + .../WEB-INF/jboss-deployment-structure.xml | 13 ++ .../bin/src/main/webapp/WEB-INF/jboss-web.xml | 4 + resteasy/bin/src/main/webapp/WEB-INF/web.xml | 13 ++ resteasy/bin/src/main/webapp/script.js | 16 ++ .../com/baeldung/server/movies/batman.json | 4 + .../baeldung/server/movies/transformer.json | 4 + 14 files changed, 317 insertions(+), 4 deletions(-) create mode 100644 ratpack/src/main/java/com/baeldung/guice/Application.java create mode 100644 ratpack/src/main/java/com/baeldung/guice/config/DependencyModule.java create mode 100644 ratpack/src/main/java/com/baeldung/guice/service/DataPumpService.java create mode 100644 ratpack/src/main/java/com/baeldung/guice/service/impl/DataPumpServiceImpl.java create mode 100644 resteasy/bin/README.md create mode 100644 resteasy/bin/pom.xml create mode 100644 resteasy/bin/src/main/webapp/WEB-INF/classes/logback.xml create mode 100644 resteasy/bin/src/main/webapp/WEB-INF/jboss-deployment-structure.xml create mode 100644 resteasy/bin/src/main/webapp/WEB-INF/jboss-web.xml create mode 100644 resteasy/bin/src/main/webapp/WEB-INF/web.xml create mode 100644 resteasy/bin/src/main/webapp/script.js create mode 100644 resteasy/bin/src/test/resources/com/baeldung/server/movies/batman.json create mode 100644 resteasy/bin/src/test/resources/com/baeldung/server/movies/transformer.json diff --git a/ratpack/src/main/java/com/baeldung/guice/Application.java b/ratpack/src/main/java/com/baeldung/guice/Application.java new file mode 100644 index 0000000000..39d29b9b2b --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/guice/Application.java @@ -0,0 +1,31 @@ +package com.baeldung.guice; + +import com.baeldung.guice.config.DependencyModule; +import com.baeldung.guice.service.DataPumpService; +import com.baeldung.guice.service.impl.DataPumpServiceImpl; + +import ratpack.guice.Guice; +import ratpack.server.RatpackServer; + +public class Application { + + public static void main(String[] args) throws Exception { + + RatpackServer + .start(server -> server.registry(Guice.registry(bindings -> bindings.module(DependencyModule.class))) + .handlers(chain -> chain.get("randomString", ctx -> { + DataPumpService dataPumpService = ctx.get(DataPumpService.class); + ctx.render(dataPumpService.generate().length()); + }))); + +// RatpackServer.start(server -> server +// .registry(Guice +// .registry(bindings -> bindings.bindInstance(DataPumpService.class, new DataPumpServiceImpl()))) +// .handlers(chain -> chain.get("randomString", ctx -> { +// DataPumpService dataPumpService = ctx.get(DataPumpService.class); +// ctx.render(dataPumpService.generate()); +// }))); + + } + +} diff --git a/ratpack/src/main/java/com/baeldung/guice/config/DependencyModule.java b/ratpack/src/main/java/com/baeldung/guice/config/DependencyModule.java new file mode 100644 index 0000000000..1824578501 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/guice/config/DependencyModule.java @@ -0,0 +1,16 @@ +package com.baeldung.guice.config; + +import com.baeldung.guice.service.DataPumpService; +import com.baeldung.guice.service.impl.DataPumpServiceImpl; +import com.google.inject.AbstractModule; +import com.google.inject.Scopes; + +public class DependencyModule extends AbstractModule { + + @Override + protected void configure() { + bind(DataPumpService.class).to(DataPumpServiceImpl.class) + .in(Scopes.SINGLETON); + } + +} diff --git a/ratpack/src/main/java/com/baeldung/guice/service/DataPumpService.java b/ratpack/src/main/java/com/baeldung/guice/service/DataPumpService.java new file mode 100644 index 0000000000..6adfec2365 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/guice/service/DataPumpService.java @@ -0,0 +1,11 @@ +package com.baeldung.guice.service; + +import com.baeldung.guice.service.impl.DataPumpServiceImpl; +import com.google.inject.ImplementedBy; + +@ImplementedBy(DataPumpServiceImpl.class) +public interface DataPumpService { + + String generate(); + +} diff --git a/ratpack/src/main/java/com/baeldung/guice/service/impl/DataPumpServiceImpl.java b/ratpack/src/main/java/com/baeldung/guice/service/impl/DataPumpServiceImpl.java new file mode 100644 index 0000000000..88f171f8a2 --- /dev/null +++ b/ratpack/src/main/java/com/baeldung/guice/service/impl/DataPumpServiceImpl.java @@ -0,0 +1,14 @@ +package com.baeldung.guice.service.impl; + +import java.util.UUID; + +import com.baeldung.guice.service.DataPumpService; + +public class DataPumpServiceImpl implements DataPumpService { + + @Override + public String generate() { + return UUID.randomUUID().toString(); + } + +} diff --git a/ratpack/src/test/java/com/baeldung/ApplicationTest.java b/ratpack/src/test/java/com/baeldung/ApplicationTest.java index 0333441928..047575ca6e 100644 --- a/ratpack/src/test/java/com/baeldung/ApplicationTest.java +++ b/ratpack/src/test/java/com/baeldung/ApplicationTest.java @@ -24,22 +24,27 @@ public class ApplicationTest { public void givenDefaultUrl_getStaticText() { assertEquals("Welcome to baeldung ratpack!!!", appUnderTest.getHttpClient().getText("/")); } - + @Test public void givenDynamicUrl_getDynamicText() { assertEquals("Hello dummybot!!!", appUnderTest.getHttpClient().getText("/dummybot")); } - + @Test public void givenUrl_getListOfEmployee() throws JsonProcessingException { List employees = new ArrayList(); ObjectMapper mapper = new ObjectMapper(); employees.add(new Employee(1L, "Mr", "John Doe")); employees.add(new Employee(2L, "Mr", "White Snow")); - + assertEquals(mapper.writeValueAsString(employees), appUnderTest.getHttpClient().getText("/data/employees")); } - + + @Test + public void givenStaticUrl_getDynamicText() { + assertEquals(21, appUnderTest.getHttpClient().getText("/randomString").length()); + } + @After public void shutdown() { appUnderTest.close(); diff --git a/resteasy/bin/README.md b/resteasy/bin/README.md new file mode 100644 index 0000000000..722f1dfe93 --- /dev/null +++ b/resteasy/bin/README.md @@ -0,0 +1,8 @@ +========= + +## A Guide to RESTEasy + + +### Relevant Articles: +- [A Guide to RESTEasy](http://www.baeldung.com/resteasy-tutorial) +- [RESTEasy Client API](http://www.baeldung.com/resteasy-client-tutorial) diff --git a/resteasy/bin/pom.xml b/resteasy/bin/pom.xml new file mode 100644 index 0000000000..f0bd8298f5 --- /dev/null +++ b/resteasy/bin/pom.xml @@ -0,0 +1,171 @@ + + + 4.0.0 + + com.baeldung + resteasy-tutorial + 1.0 + war + + + 3.0.19.Final + 4.12 + 2.5 + 2.19.1 + 1.6.1 + + + + RestEasyTutorial + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + org.codehaus.cargo + cargo-maven2-plugin + ${cargo-maven2-plugin.version} + + true + + jetty8x + embedded + + + + 8082 + + + + + + + + + + + + + org.jboss.resteasy + resteasy-servlet-initializer + ${resteasy.version} + + + + + org.jboss.resteasy + resteasy-client + ${resteasy.version} + + + + + + org.jboss.resteasy + resteasy-jaxb-provider + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-jackson-provider + ${resteasy.version} + + + + + + junit + junit + ${junit.version} + + + + commons-io + commons-io + ${commons-io.version} + + + + + + + live + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration-test + + test + + + + **/*IntegrationTest.java + + + **/*LiveTest.java + + + + + + + json + + + + + org.codehaus.cargo + cargo-maven2-plugin + ${cargo-maven2-plugin.version} + + false + + + + start-server + pre-integration-test + + start + + + + stop-server + post-integration-test + + stop + + + + + + + + + + + \ No newline at end of file diff --git a/resteasy/bin/src/main/webapp/WEB-INF/classes/logback.xml b/resteasy/bin/src/main/webapp/WEB-INF/classes/logback.xml new file mode 100644 index 0000000000..d94e9f71ab --- /dev/null +++ b/resteasy/bin/src/main/webapp/WEB-INF/classes/logback.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/resteasy/bin/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/resteasy/bin/src/main/webapp/WEB-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000000..cb258374a1 --- /dev/null +++ b/resteasy/bin/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/resteasy/bin/src/main/webapp/WEB-INF/jboss-web.xml b/resteasy/bin/src/main/webapp/WEB-INF/jboss-web.xml new file mode 100644 index 0000000000..694bb71332 --- /dev/null +++ b/resteasy/bin/src/main/webapp/WEB-INF/jboss-web.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/resteasy/bin/src/main/webapp/WEB-INF/web.xml b/resteasy/bin/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..d5f00293f4 --- /dev/null +++ b/resteasy/bin/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,13 @@ + + + + RestEasy Example + + + resteasy.servlet.mapping.prefix + /rest + + + \ No newline at end of file diff --git a/resteasy/bin/src/main/webapp/script.js b/resteasy/bin/src/main/webapp/script.js new file mode 100644 index 0000000000..88198887b0 --- /dev/null +++ b/resteasy/bin/src/main/webapp/script.js @@ -0,0 +1,16 @@ +function call(url, type, data) { + var request = $.ajax({ + url : url, + method : "GET", + data : (data) ? JSON.stringify(data) : "", + dataType : type + }); + + request.done(function(resp) { + console.log(resp); + }); + + request.fail(function(jqXHR, textStatus) { + console.log("Request failed: " + textStatus); + }); +}; \ No newline at end of file diff --git a/resteasy/bin/src/test/resources/com/baeldung/server/movies/batman.json b/resteasy/bin/src/test/resources/com/baeldung/server/movies/batman.json new file mode 100644 index 0000000000..82aaaa8f40 --- /dev/null +++ b/resteasy/bin/src/test/resources/com/baeldung/server/movies/batman.json @@ -0,0 +1,4 @@ +{ + "title": "Batman", + "imdbId": "tt0096895" +} \ No newline at end of file diff --git a/resteasy/bin/src/test/resources/com/baeldung/server/movies/transformer.json b/resteasy/bin/src/test/resources/com/baeldung/server/movies/transformer.json new file mode 100644 index 0000000000..634cefc73c --- /dev/null +++ b/resteasy/bin/src/test/resources/com/baeldung/server/movies/transformer.json @@ -0,0 +1,4 @@ +{ + "title": "Transformers", + "imdbId": "tt0418279" +} \ No newline at end of file From 626db733b9c161e68a3fcaf0728f4b7a26cf9634 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Fri, 31 Mar 2017 12:25:37 +0200 Subject: [PATCH 084/102] Corrected Indentation using formatter. (#1541) (#1544) --- .../com/baeldung/SimpleServerVerticle.java | 6 ++-- ...> RestServiceVerticleIntegrationTest.java} | 2 +- ... SimpleServerVerticleIntegrationTest.java} | 33 +++++++++---------- 3 files changed, 18 insertions(+), 23 deletions(-) rename vertx/src/test/java/com/baeldung/{RestServiceVerticleTest.java => RestServiceVerticleIntegrationTest.java} (96%) rename vertx/src/test/java/com/baeldung/{SimpleServerVerticleTest.java => SimpleServerVerticleIntegrationTest.java} (58%) diff --git a/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java b/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java index 6b56896860..96aa058ce7 100644 --- a/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java +++ b/vertx/src/main/java/com/baeldung/SimpleServerVerticle.java @@ -8,10 +8,8 @@ public class SimpleServerVerticle extends AbstractVerticle { @Override public void start(Future future) { vertx.createHttpServer() - .requestHandler(request -> { - request.response() - .end("Welcome to Vert.x Intro"); - }) + .requestHandler( + r -> r.response().end("Welcome to Vert.x Intro")) .listen(config().getInteger("http.port", 8080), result -> { if (result.succeeded()) { future.complete(); diff --git a/vertx/src/test/java/com/baeldung/RestServiceVerticleTest.java b/vertx/src/test/java/com/baeldung/RestServiceVerticleIntegrationTest.java similarity index 96% rename from vertx/src/test/java/com/baeldung/RestServiceVerticleTest.java rename to vertx/src/test/java/com/baeldung/RestServiceVerticleIntegrationTest.java index b5be0734f4..f08d9ffde1 100644 --- a/vertx/src/test/java/com/baeldung/RestServiceVerticleTest.java +++ b/vertx/src/test/java/com/baeldung/RestServiceVerticleIntegrationTest.java @@ -13,7 +13,7 @@ import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; @RunWith(VertxUnitRunner.class) -public class RestServiceVerticleTest { +public class RestServiceVerticleIntegrationTest { private Vertx vertx; diff --git a/vertx/src/test/java/com/baeldung/SimpleServerVerticleTest.java b/vertx/src/test/java/com/baeldung/SimpleServerVerticleIntegrationTest.java similarity index 58% rename from vertx/src/test/java/com/baeldung/SimpleServerVerticleTest.java rename to vertx/src/test/java/com/baeldung/SimpleServerVerticleIntegrationTest.java index 189d2f6604..194f403e25 100644 --- a/vertx/src/test/java/com/baeldung/SimpleServerVerticleTest.java +++ b/vertx/src/test/java/com/baeldung/SimpleServerVerticleIntegrationTest.java @@ -1,27 +1,23 @@ package com.baeldung; +import io.vertx.core.Vertx; +import io.vertx.ext.unit.Async; +import io.vertx.ext.unit.TestContext; +import io.vertx.ext.unit.junit.VertxUnitRunner; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.unit.Async; -import io.vertx.ext.unit.TestContext; -import io.vertx.ext.unit.junit.VertxUnitRunner; - @RunWith(VertxUnitRunner.class) -public class SimpleServerVerticleTest { +public class SimpleServerVerticleIntegrationTest { private Vertx vertx; @Before public void setup(TestContext testContext) { vertx = Vertx.vertx(); - vertx.deployVerticle(SimpleServerVerticle.class.getName(), - testContext.asyncAssertSuccess()); + vertx.deployVerticle(SimpleServerVerticle.class.getName(), testContext.asyncAssertSuccess()); } @After @@ -33,14 +29,15 @@ public class SimpleServerVerticleTest { public void whenReceivedResponse_thenSuccess(TestContext testContext) { final Async async = testContext.async(); - vertx.createHttpClient() - .getNow(8080, "localhost", "/", response -> { - response.handler(responseBody -> { - testContext.assertTrue(responseBody.toString() - .contains("Welcome")); - async.complete(); - }); - }); + vertx + .createHttpClient() + .getNow(8080, "localhost", "/", + response -> response.handler(responseBody -> { + testContext.assertTrue(responseBody + .toString() + .contains("Welcome")); + async.complete(); + })); } } From c73c25292424574d09daef7dc588446a170aeec2 Mon Sep 17 00:00:00 2001 From: KevinGilmore Date: Fri, 31 Mar 2017 06:54:17 -0500 Subject: [PATCH 085/102] BAEL-680 and BAEL-756 README files (#1539) * Add files via upload * Update pom.xml * Update RunGuice.java * Update Communication.java * Update CommunicationMode.java * Update DefaultCommunicator.java * Update EmailCommunicationMode.java * Update IMCommunicationMode.java * Update SMSCommunicationMode.java * Update MessageLogger.java * Update MessageSentLoggable.java * Update AOPModule.java * Update BasicModule.java * Update CommunicationModel.java * Update Communicator.java * Update BasicModule.java * Update RunGuice.java * Update MessageLogger.java * Update Communicator.java * Update pom.xml * BAEL-278: Updated README.md * BAEL-554: Add and update README.md files * Update pom.xml * Update pom.xml * Update pom.xml * BAEL-345: fixed assertion * BAEL-109: Updated README.md * BAEL-345: Added README.md * Reinstating reactor-core module in root-level pom * BAEL-393: Adding guide-intro module to root pom * BAEL-9: Updated README.md * BAEL-157: README.md updated * Changed project name * Update RunGuice.java Removed references to message logging and output * Update Communication.java Removed message logging-related code * BAEL-566: Updated README.md * New project name * BAEL-393: removing guice-intro directory * BAEL-393: renamed module guice-intro to guice in root pom.xml * BAEL-393 and BAEL-541 README.md files * BAEL-731: Updated README.md * BAEL-680: renamed test methods * BAEL-714: Updated README.md * BAEL-737: Updated README.md * BAEL-680 and BAEL-756 README.md updates --- core-java/README.md | 1 + kotlin/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/core-java/README.md b/core-java/README.md index 63e3748ec6..0861ee7c5e 100644 --- a/core-java/README.md +++ b/core-java/README.md @@ -84,4 +84,5 @@ - [Guide to java.util.concurrent.Locks](http://www.baeldung.com/java-concurrent-locks) - [Java Primitive Conversions](http://www.baeldung.com/java-primitive-conversions) - [Java Money and the Currency API](http://www.baeldung.com/java-money-and-currency) +- [Guide to Java 8 Comparator.comparing()](http://www.baeldung.com/java-8-comparator-comparing) diff --git a/kotlin/README.md b/kotlin/README.md index ceebde4573..309aafa4b6 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -2,3 +2,4 @@ - [Introduction to the Kotlin Language](http://www.baeldung.com/kotlin) - [A guide to the “when{}” block in Kotlin](http://www.baeldung.com/kotlin-when) +- [Comprehensive Guide to Null Safety in Kotlin](http://www.baeldung.com/kotlin-null-safety) From 4daef51161c2903f7b826278406e6ac649e920d8 Mon Sep 17 00:00:00 2001 From: Nikhil Khatwani Date: Fri, 31 Mar 2017 22:17:58 +0530 Subject: [PATCH 086/102] BAEL-711 Guide_to_Microservices_using_lagom_framework Changes (#1495) * BAEL_711_Guide_to_Microservices_using_lagom_framework Changes * BAEL_711_Guide_to_Microservices_using_lagom_framework_v2 * BAEL_711_Guide_to_Microservices_using_lagom-Review Comments --- lagom/.gitignore | 4 ++ lagom/README | 40 ++++++++++++++++ lagom/build.sbt | 41 ++++++++++++++++ .../greeting/api/GreetingService.java | 23 +++++++++ lagom/greeting-impl/bin/application.conf | 1 + .../bin/classes/application.conf | 1 + .../greeting/impl/GreetingCommand.java | 29 +++++++++++ .../greeting/impl/GreetingEntity.java | 32 +++++++++++++ .../greeting/impl/GreetingEvent.java | 23 +++++++++ .../greeting/impl/GreetingServiceImpl.java | 48 +++++++++++++++++++ .../greeting/impl/GreetingServiceModule.java | 18 +++++++ .../greeting/impl/GreetingState.java | 24 ++++++++++ .../src/main/resources/application.conf | 1 + lagom/project/build.properties | 1 + lagom/project/plugins.sbt | 2 + .../weather/api/WeatherService.java | 28 +++++++++++ .../helloworld/weather/api/WeatherStats.java | 32 +++++++++++++ lagom/weather-impl/bin/application.conf | 1 + .../weather/impl/WeatherServiceImpl.java | 19 ++++++++ .../weather/impl/WeatherServiceModule.java | 16 +++++++ .../src/main/resources/application.conf | 1 + 21 files changed, 385 insertions(+) create mode 100644 lagom/.gitignore create mode 100644 lagom/README create mode 100644 lagom/build.sbt create mode 100644 lagom/greeting-api/src/main/java/org/baeldung/lagom/helloworld/greeting/api/GreetingService.java create mode 100644 lagom/greeting-impl/bin/application.conf create mode 100644 lagom/greeting-impl/bin/classes/application.conf create mode 100644 lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingCommand.java create mode 100644 lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEntity.java create mode 100644 lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEvent.java create mode 100644 lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceImpl.java create mode 100644 lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceModule.java create mode 100644 lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingState.java create mode 100644 lagom/greeting-impl/src/main/resources/application.conf create mode 100644 lagom/project/build.properties create mode 100644 lagom/project/plugins.sbt create mode 100644 lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherService.java create mode 100644 lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherStats.java create mode 100644 lagom/weather-impl/bin/application.conf create mode 100644 lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceImpl.java create mode 100644 lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceModule.java create mode 100644 lagom/weather-impl/src/main/resources/application.conf diff --git a/lagom/.gitignore b/lagom/.gitignore new file mode 100644 index 0000000000..5e521241aa --- /dev/null +++ b/lagom/.gitignore @@ -0,0 +1,4 @@ +greeting-impl/logs/* +lagom-hello-world/greeting-impl/bin/classes/* +lagom-hello-world/logs/application.log +lagom-hello-world/weather-impl/logs/application.log diff --git a/lagom/README b/lagom/README new file mode 100644 index 0000000000..0d81a4b3c1 --- /dev/null +++ b/lagom/README @@ -0,0 +1,40 @@ +Steps to setup from scratch + +1) Create sbt build file "build.sbt" +2) Create plugins file project/plugins.sbt +3) Create build properties file project/build.properties +4) Run sbt command from project root to generate project directories, generated projects at target/lagom-dynamic-projects + Service Locator project: lagom-internal-meta-project-service-locator + cassandra project: lagom-internal-meta-project-cassandra +5) Create folders in all projects to follow maven like directory structure layout for project source code: src/main/java and src/main/java/resources and test folders. +6) Weather microservice + a) WeatherService Interface with method: + weatherStatsForToday() + b) WeatherServiceImpl to return weatherstats from a random list of weather stats + +7) Greeting Microservice + GreetingService Interface: + handleGreetFrom(String user) + + a) Reply back to user with greeting message("Hello") along with weather stats fetched from weather microservice + b) Update the greeting message("Hello Again") for an existing user. + +8) GreetingEntity: PersistentEntity + a) handles ReceivedGreetingCommand + b) Stores ReceivedGreetingEvent events to cassandra + c) Processes ReceivedGreetingEvent to update GreetingState("Hello" to "Hello Again") if required. + +9) Register modules with lagom using application.conf files. +10) Run project: sbt lagom:runAll + + +Sample Run: +curl http://localhost:9000/api/greeting/Nikhil; +Hello Nikhil! Today's weather stats: Going to be very humid, Take Water + +curl http://localhost:9000/api/greeting/Nikhil; +Hello Again Nikhil! Today's weather stats: Going to Rain, Take Umbrella + + + + diff --git a/lagom/build.sbt b/lagom/build.sbt new file mode 100644 index 0000000000..064d67468e --- /dev/null +++ b/lagom/build.sbt @@ -0,0 +1,41 @@ +organization in ThisBuild := "org.baeldung" + +// the Scala version that will be used for cross-compiled libraries +scalaVersion in ThisBuild := "2.11.7" + +lagomKafkaEnabled in ThisBuild := false + +lazy val greetingApi = project("greeting-api") + .settings( + version := "1.0-SNAPSHOT", + libraryDependencies ++= Seq( + lagomJavadslApi + ) + ) + +lazy val greetingImpl = project("greeting-impl") + .enablePlugins(LagomJava) + .settings( + version := "1.0-SNAPSHOT", + libraryDependencies ++= Seq( + lagomJavadslPersistenceCassandra + ) + ) + .dependsOn(greetingApi, weatherApi) + +lazy val weatherApi = project("weather-api") + .settings( + version := "1.0-SNAPSHOT", + libraryDependencies ++= Seq( + lagomJavadslApi + ) + ) + +lazy val weatherImpl = project("weather-impl") + .enablePlugins(LagomJava) + .settings( + version := "1.0-SNAPSHOT" + ) + .dependsOn(weatherApi) + +def project(id: String) = Project(id, base = file(id)) \ No newline at end of file diff --git a/lagom/greeting-api/src/main/java/org/baeldung/lagom/helloworld/greeting/api/GreetingService.java b/lagom/greeting-api/src/main/java/org/baeldung/lagom/helloworld/greeting/api/GreetingService.java new file mode 100644 index 0000000000..93567f0185 --- /dev/null +++ b/lagom/greeting-api/src/main/java/org/baeldung/lagom/helloworld/greeting/api/GreetingService.java @@ -0,0 +1,23 @@ +package org.baeldung.lagom.helloworld.greeting.api; + +import static com.lightbend.lagom.javadsl.api.Service.named; +import static com.lightbend.lagom.javadsl.api.Service.restCall; + +import com.lightbend.lagom.javadsl.api.Descriptor; +import com.lightbend.lagom.javadsl.api.Service; +import com.lightbend.lagom.javadsl.api.ServiceCall; +import com.lightbend.lagom.javadsl.api.transport.Method; + +import akka.NotUsed; + +public interface GreetingService extends Service { + + public ServiceCall handleGreetFrom(String user); + + @Override + default Descriptor descriptor() { + return named("greetingservice").withCalls( + restCall(Method.GET, "/api/greeting/:fromUser", this::handleGreetFrom) + ).withAutoAcl(true); + } +} \ No newline at end of file diff --git a/lagom/greeting-impl/bin/application.conf b/lagom/greeting-impl/bin/application.conf new file mode 100644 index 0000000000..1e78ce4a79 --- /dev/null +++ b/lagom/greeting-impl/bin/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.greeting.impl.GreetingServiceModule \ No newline at end of file diff --git a/lagom/greeting-impl/bin/classes/application.conf b/lagom/greeting-impl/bin/classes/application.conf new file mode 100644 index 0000000000..1e78ce4a79 --- /dev/null +++ b/lagom/greeting-impl/bin/classes/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.greeting.impl.GreetingServiceModule \ No newline at end of file diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingCommand.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingCommand.java new file mode 100644 index 0000000000..be9e713ec9 --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingCommand.java @@ -0,0 +1,29 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.base.Preconditions; +import com.lightbend.lagom.javadsl.persistence.PersistentEntity; +import com.lightbend.lagom.serialization.CompressedJsonable; +import com.lightbend.lagom.serialization.Jsonable; + +public interface GreetingCommand extends Jsonable { + + @SuppressWarnings("serial") + @JsonDeserialize + public final class ReceivedGreetingCommand implements GreetingCommand, + CompressedJsonable, PersistentEntity.ReplyType { + private final String fromUser; + + @JsonCreator + public ReceivedGreetingCommand(String fromUser) { + this.fromUser = Preconditions.checkNotNull(fromUser, "fromUser"); + } + + public String getFromUser() { + return fromUser; + } + + } + +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEntity.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEntity.java new file mode 100644 index 0000000000..91fc74039d --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEntity.java @@ -0,0 +1,32 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import java.util.Optional; + +import org.baeldung.lagom.helloworld.greeting.impl.GreetingCommand.ReceivedGreetingCommand; +import org.baeldung.lagom.helloworld.greeting.impl.GreetingEvent.ReceivedGreetingEvent; + +import com.lightbend.lagom.javadsl.persistence.PersistentEntity; + +public class GreetingEntity extends PersistentEntity { + + @Override + public Behavior initialBehavior(Optional snapshotState) { + BehaviorBuilder b = newBehaviorBuilder(new GreetingState("Hello ")); + + b.setCommandHandler(ReceivedGreetingCommand.class, + (cmd, ctx) -> { + String fromUser = cmd.getFromUser(); + String currentGreeting = state().getMessage(); + return ctx.thenPersist( + new ReceivedGreetingEvent(fromUser), + evt -> ctx.reply(currentGreeting + fromUser + "!")); + }); + + b.setEventHandler(ReceivedGreetingEvent.class, + // We simply update the current state + evt -> state().withMessage("Hello Again ")); + + return b.build(); + } +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEvent.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEvent.java new file mode 100644 index 0000000000..e454f6cd1b --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingEvent.java @@ -0,0 +1,23 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.lightbend.lagom.serialization.Jsonable; + + +public interface GreetingEvent extends Jsonable { + + class ReceivedGreetingEvent implements GreetingEvent { + private final String fromUser; + + @JsonCreator + public ReceivedGreetingEvent(String fromUser) { + this.fromUser = fromUser; + } + + public String getFromUser() { + return fromUser; + } + + } + +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceImpl.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceImpl.java new file mode 100644 index 0000000000..c28687291e --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceImpl.java @@ -0,0 +1,48 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.baeldung.lagom.helloworld.greeting.api.GreetingService; +import org.baeldung.lagom.helloworld.greeting.impl.GreetingCommand.ReceivedGreetingCommand; +import org.baeldung.lagom.helloworld.weather.api.WeatherService; +import org.baeldung.lagom.helloworld.weather.api.WeatherStats; + +import com.google.inject.Inject; +import com.lightbend.lagom.javadsl.api.ServiceCall; +import com.lightbend.lagom.javadsl.persistence.PersistentEntityRef; +import com.lightbend.lagom.javadsl.persistence.PersistentEntityRegistry; + +import akka.NotUsed; + +public class GreetingServiceImpl implements GreetingService { + + private final PersistentEntityRegistry persistentEntityRegistry; + private final WeatherService weatherService; + + @Inject + public GreetingServiceImpl(PersistentEntityRegistry persistentEntityRegistry, WeatherService weatherService) { + this.persistentEntityRegistry = persistentEntityRegistry; + this.weatherService = weatherService; + persistentEntityRegistry.register(GreetingEntity.class); + } + + @Override + public ServiceCall handleGreetFrom(String user) { + return request -> { + // Look up the hello world entity for the given ID. + PersistentEntityRef ref = persistentEntityRegistry.refFor(GreetingEntity.class, user); + CompletableFuture greetingResponse = ref.ask(new ReceivedGreetingCommand(user)) + .toCompletableFuture(); + CompletableFuture todaysWeatherInfo = + (CompletableFuture) weatherService.weatherStatsForToday().invoke(); + try { + return CompletableFuture.completedFuture(greetingResponse.get() + + " Today's weather stats: " + todaysWeatherInfo.get().getMessage()); + } catch (InterruptedException | ExecutionException e) { + return CompletableFuture.completedFuture("Sorry Some Error at our end, working on it"); + } + }; + } + +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceModule.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceModule.java new file mode 100644 index 0000000000..4813e91a7c --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingServiceModule.java @@ -0,0 +1,18 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import org.baeldung.lagom.helloworld.greeting.api.GreetingService; +import org.baeldung.lagom.helloworld.weather.api.WeatherService; + +import com.google.inject.AbstractModule; +import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport; + +/** + * The module that binds the GreetingService so that it can be served. + */ +public class GreetingServiceModule extends AbstractModule implements ServiceGuiceSupport { + @Override + protected void configure() { + bindServices(serviceBinding(GreetingService.class, GreetingServiceImpl.class)); + bindClient(WeatherService.class); + } +} diff --git a/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingState.java b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingState.java new file mode 100644 index 0000000000..125795601e --- /dev/null +++ b/lagom/greeting-impl/src/main/java/org/baeldung/lagom/helloworld/greeting/impl/GreetingState.java @@ -0,0 +1,24 @@ +package org.baeldung.lagom.helloworld.greeting.impl; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +@JsonDeserialize +public class GreetingState { + + private final String message; + + @JsonCreator + public GreetingState(String message) { + this.message = message; + } + + GreetingState withMessage(String message) { + return new GreetingState(message); + } + + public String getMessage() { + return message; + } + +} diff --git a/lagom/greeting-impl/src/main/resources/application.conf b/lagom/greeting-impl/src/main/resources/application.conf new file mode 100644 index 0000000000..1e78ce4a79 --- /dev/null +++ b/lagom/greeting-impl/src/main/resources/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.greeting.impl.GreetingServiceModule \ No newline at end of file diff --git a/lagom/project/build.properties b/lagom/project/build.properties new file mode 100644 index 0000000000..43b8278c68 --- /dev/null +++ b/lagom/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.11 diff --git a/lagom/project/plugins.sbt b/lagom/project/plugins.sbt new file mode 100644 index 0000000000..8b8e36a743 --- /dev/null +++ b/lagom/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("com.lightbend.lagom" % "lagom-sbt-plugin" % "1.3.1") +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "3.0.0") diff --git a/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherService.java b/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherService.java new file mode 100644 index 0000000000..888109322b --- /dev/null +++ b/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherService.java @@ -0,0 +1,28 @@ +package org.baeldung.lagom.helloworld.weather.api; + +import static com.lightbend.lagom.javadsl.api.Service.named; +import static com.lightbend.lagom.javadsl.api.Service.*; + +import com.lightbend.lagom.javadsl.api.Descriptor; +import com.lightbend.lagom.javadsl.api.Service; +import com.lightbend.lagom.javadsl.api.ServiceCall; +import com.lightbend.lagom.javadsl.api.transport.Method; + +import akka.NotUsed; + +/** + * WeatherService Interface. + */ +public interface WeatherService extends Service { + + // Fetch Today's Weather Stats service call + public ServiceCall weatherStatsForToday(); + + @Override + default Descriptor descriptor() { + return named("weatherservice").withCalls( + restCall(Method.GET, "/api/weather", this::weatherStatsForToday) + ).withAutoAcl(true); + } + +} \ No newline at end of file diff --git a/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherStats.java b/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherStats.java new file mode 100644 index 0000000000..23530a297d --- /dev/null +++ b/lagom/weather-api/src/main/java/org/baeldung/lagom/helloworld/weather/api/WeatherStats.java @@ -0,0 +1,32 @@ +package org.baeldung.lagom.helloworld.weather.api; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +public enum WeatherStats { + + STATS_RAINY("Going to Rain, Take Umbrella"), STATS_HUMID("Going to be very humid, Take Water"); + + private final String message; + + private static final List VALUES = Collections.unmodifiableList(Arrays.asList(values())); + + private static final int SIZE = VALUES.size(); + + private static final Random RANDOM = new Random(); + + WeatherStats(String msg) { + this.message = msg; + } + + public static WeatherStats forToday() { + return VALUES.get(RANDOM.nextInt(SIZE)); + } + + public String getMessage() { + return message; + } + +} diff --git a/lagom/weather-impl/bin/application.conf b/lagom/weather-impl/bin/application.conf new file mode 100644 index 0000000000..cf6cec2115 --- /dev/null +++ b/lagom/weather-impl/bin/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.weather.impl.WeatherServiceModule \ No newline at end of file diff --git a/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceImpl.java b/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceImpl.java new file mode 100644 index 0000000000..d7f97f9105 --- /dev/null +++ b/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceImpl.java @@ -0,0 +1,19 @@ +package org.baeldung.lagom.helloworld.weather.impl; + +import java.util.concurrent.CompletableFuture; + +import org.baeldung.lagom.helloworld.weather.api.WeatherService; +import org.baeldung.lagom.helloworld.weather.api.WeatherStats; + +import com.lightbend.lagom.javadsl.api.ServiceCall; + +import akka.NotUsed; + +public class WeatherServiceImpl implements WeatherService { + + @Override + public ServiceCall weatherStatsForToday() { + return (req) -> CompletableFuture.completedFuture(WeatherStats.forToday()); + } + +} diff --git a/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceModule.java b/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceModule.java new file mode 100644 index 0000000000..ac2834ff5c --- /dev/null +++ b/lagom/weather-impl/src/main/java/org/baeldung/lagom/helloworld/weather/impl/WeatherServiceModule.java @@ -0,0 +1,16 @@ +package org.baeldung.lagom.helloworld.weather.impl; + +import org.baeldung.lagom.helloworld.weather.api.WeatherService; + +import com.google.inject.AbstractModule; +import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport; + +/** + * The module that binds the GreetingService so that it can be served. + */ +public class WeatherServiceModule extends AbstractModule implements ServiceGuiceSupport { + @Override + protected void configure() { + bindServices(serviceBinding(WeatherService.class, WeatherServiceImpl.class)); + } +} diff --git a/lagom/weather-impl/src/main/resources/application.conf b/lagom/weather-impl/src/main/resources/application.conf new file mode 100644 index 0000000000..cf6cec2115 --- /dev/null +++ b/lagom/weather-impl/src/main/resources/application.conf @@ -0,0 +1 @@ +play.modules.enabled += org.baeldung.lagom.helloworld.weather.impl.WeatherServiceModule \ No newline at end of file From 0975123b837608bf067466d6dfb09f0ad00bb20c Mon Sep 17 00:00:00 2001 From: Tomasz Sobala Date: Fri, 31 Mar 2017 19:33:34 +0200 Subject: [PATCH 087/102] Exploring the Spring Boot TestRestTemplate (#1550) * injecting beans * XML-based configuration replaced with Java Config. * [BAEL-431] Exploring TestRestTemplate. * Revert of evaluation task "XML-based configuration replaced with Java Config." This reverts commit 66471cf0574c85f8ff514ec4caf5ba44ebba1a74. * Revert of evaluation task "injecting beans" This reverts commit d2ac20185e636245bc0ae0b4ccb952965de88e28. * [BAEL-431] fix to the tests in TestRestTemplateBasicLiveTest. * [BAEL-431] added more meaningful user and password for auth. --- .../client/TestRestTemplateBasicLiveTest.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java b/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java index 9f4319d857..a4d8eb0908 100644 --- a/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java +++ b/spring-rest/src/test/java/org/baeldung/client/TestRestTemplateBasicLiveTest.java @@ -21,7 +21,7 @@ public class TestRestTemplateBasicLiveTest { private RestTemplate restTemplate; private static final String FOO_RESOURCE_URL = "http://localhost:" + APPLICATION_PORT + "/spring-rest/myfoos"; - private static final String URL_SECURED_BY_AUTHENTICATION = "http://browserspy.dk/password-ok.php"; + private static final String URL_SECURED_BY_AUTHENTICATION = "http://httpbin.org/basic-auth/user/passwd"; private static final String BASE_URL = "http://localhost:" + APPLICATION_PORT + "/spring-rest"; @Before @@ -55,16 +55,16 @@ public class TestRestTemplateBasicLiveTest { @Test public void givenRestTemplateWrapperWithCredentials_whenSendGetForEntity_thenStatusOk() { - TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplate, "test", "test"); - ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION + "/1", + TestRestTemplate testRestTemplate = new TestRestTemplate(restTemplate, "user", "passwd"); + ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION, String.class); assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); } @Test public void givenTestRestTemplateWithCredentials_whenSendGetForEntity_thenStatusOk() { - TestRestTemplate testRestTemplate = new TestRestTemplate("test", "test"); - ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION + "/1", + TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd"); + ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION, String.class); assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); } @@ -72,16 +72,16 @@ public class TestRestTemplateBasicLiveTest { @Test public void givenTestRestTemplateWithBasicAuth_whenSendGetForEntity_thenStatusOk() { TestRestTemplate testRestTemplate = new TestRestTemplate(); - ResponseEntity response = testRestTemplate.withBasicAuth("test", "test"). - getForEntity(URL_SECURED_BY_AUTHENTICATION + "/1", String.class); + ResponseEntity response = testRestTemplate.withBasicAuth("user", "passwd"). + getForEntity(URL_SECURED_BY_AUTHENTICATION, String.class); assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); } @Test public void givenTestRestTemplateWithCredentialsAndEnabledCookies_whenSendGetForEntity_thenStatusOk() { - TestRestTemplate testRestTemplate = new TestRestTemplate("test", "test", TestRestTemplate. + TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd", TestRestTemplate. HttpClientOption.ENABLE_COOKIES); - ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION + "/1", + ResponseEntity response = testRestTemplate.getForEntity(URL_SECURED_BY_AUTHENTICATION, String.class); assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); } @@ -97,18 +97,17 @@ public class TestRestTemplateBasicLiveTest { // POST @Test public void givenService_whenPostForObject_thenCreatedObjectIsReturned() { - TestRestTemplate testRestTemplate = new TestRestTemplate("test", "test"); + TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd"); final RequestBody body = RequestBody.create(okhttp3.MediaType.parse("text/html; charset=utf-8"), "{\"id\":1,\"name\":\"Jim\"}"); final Request request = new Request.Builder().url(BASE_URL + "/users/detail").post(body).build(); - Object response = testRestTemplate.postForObject(URL_SECURED_BY_AUTHENTICATION, request, String.class); - assertTrue(response.toString().contains("Success")); + testRestTemplate.postForObject(URL_SECURED_BY_AUTHENTICATION, request, String.class); } // PUT @Test public void givenService_whenPutForObject_thenCreatedObjectIsReturned() { - TestRestTemplate testRestTemplate = new TestRestTemplate("test", "test"); + TestRestTemplate testRestTemplate = new TestRestTemplate("user", "passwd"); final RequestBody body = RequestBody.create(okhttp3.MediaType.parse("text/html; charset=utf-8"), "{\"id\":1,\"name\":\"Jim\"}"); final Request request = new Request.Builder().url(BASE_URL + "/users/detail").post(body).build(); From 4c84fabd1af0f1d6e2ccf9cd69d3cd1fb39d16d2 Mon Sep 17 00:00:00 2001 From: azrairshad Date: Fri, 31 Mar 2017 10:54:12 -0700 Subject: [PATCH 088/102] Updated ArrayCopy assert statements. (#1540) --- .../baeldung/arraycopy/ArrayCopyUtilTest.java | 65 +++++++------------ 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java b/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java index 060f3c3109..2235e55338 100644 --- a/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java +++ b/core-java/src/test/java/com/baeldung/arraycopy/ArrayCopyUtilTest.java @@ -15,14 +15,18 @@ public class ArrayCopyUtilTest { @BeforeClass public static void setup(){ - employees = new Employee[MAX]; - Employee employee; - for(int i = 0; i < MAX; i++) { - employee = new Employee(); - employee.setName("Emp"+i); - employee.setId(i); - employees[i] = employee; - } + createEmployeesArray(); + } + + private static void createEmployeesArray() { + employees = new Employee[MAX]; + Employee employee; + for(int i = 0; i < MAX; i++) { + employee = new Employee(); + employee.setName("Emp"+i); + employee.setId(i); + employees[i] = employee; + } } @Test @@ -32,10 +36,7 @@ public class ArrayCopyUtilTest { System.arraycopy(array, 0, copiedArray, 0, 3); - Assert.assertTrue(array.length == copiedArray.length); - Assert.assertTrue(copiedArray[0] == array[0]); - Assert.assertTrue(copiedArray[1] == array[1]); - Assert.assertTrue(copiedArray[2] == array[2]); + Assert.assertArrayEquals(copiedArray, array); } @Test @@ -70,12 +71,7 @@ public class ArrayCopyUtilTest { int[] copiedArray = Arrays.copyOf(array, newLength); - Assert.assertNotNull(copiedArray); - Assert.assertTrue(copiedArray.length == array.length); - Assert.assertTrue(copiedArray[0] == array[0]); - Assert.assertTrue(copiedArray[1] == array[1]); - Assert.assertTrue(copiedArray[2] == array[2]); - Assert.assertTrue(copiedArray[3] == array[3]); + Assert.assertArrayEquals(copiedArray, array); array[0] = 9; Assert.assertTrue(copiedArray[0] != array[0]); copiedArray[1] = 12; @@ -86,8 +82,7 @@ public class ArrayCopyUtilTest { public void givenArrayOfNonPrimitiveType_whenCopiedViaArraysCopyOf_thenDoShallowCopy(){ Employee[] copiedArray = Arrays.copyOf(employees, employees.length); - Assert.assertNotNull(copiedArray); - Assert.assertTrue(copiedArray.length == employees.length); + Assert.assertArrayEquals(copiedArray, employees); employees[0].setName(employees[0].getName()+"_Changed"); //change in employees' element caused change in the copied array Assert.assertTrue(copiedArray[0].getName().equals(employees[0].getName())); @@ -99,12 +94,7 @@ public class ArrayCopyUtilTest { int[] copiedArray = array.clone(); - Assert.assertNotNull(copiedArray); - Assert.assertTrue(copiedArray.length == array.length); - Assert.assertTrue(copiedArray[0] == array[0]); - Assert.assertTrue(copiedArray[1] == array[1]); - Assert.assertTrue(copiedArray[2] == array[2]); - Assert.assertTrue(copiedArray[3] == array[3]); + Assert.assertArrayEquals(copiedArray, array); array[0] = 9; Assert.assertTrue(copiedArray[0] != array[0]); copiedArray[1] = 12; @@ -115,8 +105,7 @@ public class ArrayCopyUtilTest { public void givenArraysOfNonPrimitiveType_whenCopiedViaArrayClone_thenDoShallowCopy(){ Employee[] copiedArray = employees.clone(); - Assert.assertNotNull(copiedArray); - Assert.assertTrue(copiedArray.length == employees.length); + Assert.assertArrayEquals(copiedArray, employees);; employees[0].setName(employees[0].getName()+"_Changed"); //change in employees' element changed the copied array Assert.assertTrue(copiedArray[0].getName().equals(employees[0].getName())); @@ -128,29 +117,25 @@ public class ArrayCopyUtilTest { Address[] copiedArray = addresses.clone(); - Assert.assertNotNull(copiedArray); - Assert.assertTrue(copiedArray.length == addresses.length); addresses[0].setCity(addresses[0].getCity()+"_Changed"); - Assert.assertTrue(copiedArray[0].getCity().equals(addresses[0].getCity())); + Assert.assertArrayEquals(copiedArray, addresses); } @Test public void givenArraysOfSerializableNonPrimitiveType_whenCopiedViaSerializationUtils_thenDoDeepCopy(){ Employee[] copiedArray = SerializationUtils.clone(employees); - - Assert.assertNotNull(copiedArray); - Assert.assertTrue(copiedArray.length == employees.length); + employees[0].setName(employees[0].getName()+"_Changed"); //change in employees' element didn't change in the copied array - Assert.assertFalse(copiedArray[0].getName().equals(employees[0].getName())); + Assert.assertFalse( + copiedArray[0].getName().equals(employees[0].getName())); } @Test public void givenArraysOfNonPrimitiveType_whenCopiedViaStream_thenDoShallowCopy(){ Employee[] copiedArray = Arrays.stream(employees).toArray(Employee[]::new); - Assert.assertNotNull(copiedArray); - Assert.assertTrue(copiedArray.length == employees.length); + Assert.assertArrayEquals(copiedArray, employees); employees[0].setName(employees[0].getName()+"_Changed"); //change in employees' element didn't change in the copied array Assert.assertTrue(copiedArray[0].getName().equals(employees[0].getName())); @@ -162,11 +147,7 @@ public class ArrayCopyUtilTest { String[] copiedArray = Arrays.stream(strArray).toArray(String[]::new); - Assert.assertNotNull(copiedArray); - Assert.assertTrue(copiedArray.length == strArray.length); - Assert.assertTrue(copiedArray[0] == strArray[0]); - Assert.assertTrue(copiedArray[1] == strArray[1]); - Assert.assertTrue(copiedArray[2] == strArray[2]); + Assert.assertArrayEquals(copiedArray, strArray); } private Address[] createAddressArray(){ From 3c334e6b56168d3118d384a9b1529ea7242434b4 Mon Sep 17 00:00:00 2001 From: Danil Kornishev Date: Fri, 31 Mar 2017 16:01:27 -0400 Subject: [PATCH 089/102] Spring State Machine x4 (#1547) * [Fix] Move stateDo onto a separate state * Change tests to spring runner * Add generic params to StateMachine variables --- .../baeldung/spring/statemachine/ForkJoinStateMachineTest.java | 2 +- .../spring/statemachine/HierarchicalStateMachineTest.java | 2 +- .../baeldung/spring/statemachine/JunctionStateMachineTest.java | 2 +- .../com/baeldung/spring/statemachine/StateEnumMachineTest.java | 2 +- .../spring/statemachine/StateMachineIntegrationTest.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java index 03cb101a9d..0de61da3bd 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/ForkJoinStateMachineTest.java @@ -22,7 +22,7 @@ import static org.junit.Assert.assertTrue; public class ForkJoinStateMachineTest { @Resource - private StateMachine stateMachine; + private StateMachine stateMachine; @Before public void setUp() { diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java index 950414bb0e..76105368d8 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/HierarchicalStateMachineTest.java @@ -21,7 +21,7 @@ import static org.junit.Assert.assertEquals; public class HierarchicalStateMachineTest { @Resource - private StateMachine stateMachine; + private StateMachine stateMachine; @Before public void setUp() { diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java index 64930162fd..b91e6af5c5 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/JunctionStateMachineTest.java @@ -19,7 +19,7 @@ import javax.annotation.Resource; public class JunctionStateMachineTest { @Resource - private StateMachine stateMachine; + private StateMachine stateMachine; @Before public void setUp() { diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java index b7cbebe145..cd9e58eb8e 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateEnumMachineTest.java @@ -23,7 +23,7 @@ import static org.junit.Assert.assertTrue; public class StateEnumMachineTest { @Resource - private StateMachine stateMachine; + private StateMachine stateMachine; @Before public void setUp() { diff --git a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java index 8f61d93105..1c1ab58917 100644 --- a/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java +++ b/spring-state-machine/src/test/java/com/baeldung/spring/statemachine/StateMachineIntegrationTest.java @@ -22,7 +22,7 @@ import javax.annotation.Resource; public class StateMachineIntegrationTest { @Resource - private StateMachine stateMachine; + private StateMachine stateMachine; @Before public void setUp() { From aaf5a3e7b8cacd6ff7a6523f8a33d3aa4b4b8819 Mon Sep 17 00:00:00 2001 From: gitterjim-I Date: Fri, 31 Mar 2017 21:05:23 +0100 Subject: [PATCH 090/102] Latest review changes: more concise code and suggested refactoring. (#1549) * article Bael-667 initial commit. * Switch to use logging framework for output. * Make code more concise. Refactor as suggested. --- .../FlattenNestedListTest.java | 46 ++++--------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java b/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java index fdf4934cb7..b7939d09da 100644 --- a/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java +++ b/core-java/src/test/java/com/baeldung/list/flattennestedlist/FlattenNestedListTest.java @@ -1,51 +1,29 @@ package com.baeldung.list.flattennestedlist; -import static org.junit.Assert.assertEquals; +import static java.util.Arrays.asList; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import org.junit.After; -import org.junit.Before; +import org.hamcrest.collection.IsIterableContainingInOrder; import org.junit.Test; public class FlattenNestedListTest { - private List> lol = new ArrayList<>(); - List ls1 = Arrays.asList("one:one", "one:two", "one:three"); - List ls2 = Arrays.asList("two:one", "two:two", "two:three"); - List ls3 = Arrays.asList("three:one", "three:two", "three:three"); - - @Before - public void setup() { - lol.addAll(Arrays.asList(ls1, ls2, ls3)); - } - - @After - public void tearDown() { - lol = null; - } + List> lol = asList(asList("one:one"), asList("two:one", "two:two", "two:three"), asList("three:one", "three:two", "three:three", "three:four")); @Test public void givenString_flattenNestedList1() { List ls = flattenListOfListsImperatively(lol); assertNotNull(ls); - assertTrue(ls.size() == 9); + assertTrue(ls.size() == 8); // assert content - assertEquals(ls.get(0), "one:one"); - assertEquals(ls.get(1), "one:two"); - assertEquals(ls.get(2), "one:three"); - assertEquals(ls.get(3), "two:one"); - assertEquals(ls.get(4), "two:two"); - assertEquals(ls.get(5), "two:three"); - assertEquals(ls.get(6), "three:one"); - assertEquals(ls.get(7), "three:two"); - assertEquals(ls.get(8), "three:three"); + assertThat(ls, IsIterableContainingInOrder.contains("one:one", "two:one", "two:two", "two:three", "three:one", "three:two", "three:three", "three:four")); } @Test @@ -53,17 +31,9 @@ public class FlattenNestedListTest { List ls = flattenListOfListsStream(lol); assertNotNull(ls); - assertTrue(ls.size() == 9); + assertTrue(ls.size() == 8); // assert content - assertEquals(ls.get(0), "one:one"); - assertEquals(ls.get(1), "one:two"); - assertEquals(ls.get(2), "one:three"); - assertEquals(ls.get(3), "two:one"); - assertEquals(ls.get(4), "two:two"); - assertEquals(ls.get(5), "two:three"); - assertEquals(ls.get(6), "three:one"); - assertEquals(ls.get(7), "three:two"); - assertEquals(ls.get(8), "three:three"); + assertThat(ls, IsIterableContainingInOrder.contains("one:one", "two:one", "two:two", "two:three", "three:one", "three:two", "three:three", "three:four")); } public List flattenListOfListsImperatively(List> list) { From cc0c7d54a9b250c00d9efcfaac4119fe85f9cb72 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 1 Apr 2017 08:18:52 +0200 Subject: [PATCH 091/102] Enable hbase (#1545) --- hbase/pom.xml | 1 + pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hbase/pom.xml b/hbase/pom.xml index 2382e47af2..9d523abf1a 100644 --- a/hbase/pom.xml +++ b/hbase/pom.xml @@ -33,6 +33,7 @@ org.apache.hbase hbase ${hbase.version} + pom junit diff --git a/pom.xml b/pom.xml index 6b4511e45b..b26ada6f24 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ handling-spring-static-resources hazelcast - + hbase httpclient hystrix From 4b98413c4ad1f82bb9d95aeeec780dc2bd47bad1 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Sat, 1 Apr 2017 07:53:56 +0100 Subject: [PATCH 092/102] Moved AOP module and removed Spring Boot starter (#1506) * Moved AOP module and removed Spring Boot starter * Fixed accidentally committed git message * Fixed pom --- spring-aop/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml index 1c3b5bdccd..bd86839742 100644 --- a/spring-aop/pom.xml +++ b/spring-aop/pom.xml @@ -15,11 +15,6 @@ - - org.springframework.boot - spring-boot-starter - - org.springframework.boot spring-boot-starter-aop @@ -28,7 +23,6 @@ org.springframework.boot spring-boot-starter-test - test From 760692b8eb5e66ef20be5c183ab0e00e98c470df Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 1 Apr 2017 09:06:59 +0200 Subject: [PATCH 093/102] Build time optimization (#1553) * Add integration tests * Optimize build --- apache-poi/.gitignore | 2 ++ jee7/pom.xml | 2 +- .../ScheduleTimerBeanIntegrationTest.java | 36 +++++++++++-------- jooq/src/test/java/com/baeldung/JOOLTest.java | 3 -- pom.xml | 13 +++++++ querydsl/pom.xml | 1 - ...Test.java => QueryDSLIntegrationTest.java} | 20 +++++------ rxjava/pom.xml | 13 +++++++ ...xJavaBackpressureLongRunningUnitTest.java} | 2 +- ... => MessageControllerIntegrationTest.java} | 5 +-- spring-jooq/pom.xml | 2 +- 11 files changed, 66 insertions(+), 33 deletions(-) rename querydsl/src/test/java/org/baeldung/querydsl/intro/{QueryDSLTest.java => QueryDSLIntegrationTest.java} (99%) rename rxjava/src/test/java/com/baeldung/rxjava/{RxJavaBackpressureTest.java => RxJavaBackpressureLongRunningUnitTest.java} (98%) rename spring-amqp-simple/src/test/java/com/baeldung/springamqpsimple/{MessageControllerTest.java => MessageControllerIntegrationTest.java} (88%) diff --git a/apache-poi/.gitignore b/apache-poi/.gitignore index e05054868c..9552c1e63d 100644 --- a/apache-poi/.gitignore +++ b/apache-poi/.gitignore @@ -1 +1,3 @@ *.docx +temp.xls +temp.xlsx diff --git a/jee7/pom.xml b/jee7/pom.xml index f275f56d58..26d433df78 100644 --- a/jee7/pom.xml +++ b/jee7/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.baeldung - jee7schedule + jee7 1.0-SNAPSHOT JavaEE 7 Arquillian Archetype Sample war diff --git a/jee7/src/test/java/com/baeldung/timer/ScheduleTimerBeanIntegrationTest.java b/jee7/src/test/java/com/baeldung/timer/ScheduleTimerBeanIntegrationTest.java index c3c5ad44de..580edade77 100644 --- a/jee7/src/test/java/com/baeldung/timer/ScheduleTimerBeanIntegrationTest.java +++ b/jee7/src/test/java/com/baeldung/timer/ScheduleTimerBeanIntegrationTest.java @@ -19,25 +19,27 @@ import static com.jayway.awaitility.Awaitility.to; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; - @RunWith(Arquillian.class) public class ScheduleTimerBeanIntegrationTest { - final static long TIMEOUT = 5000l; - final static long TOLERANCE = 1000l; + private final static long TIMEOUT = 5000l; + private final static long TOLERANCE = 1000l; - @Inject - TimerEventListener timerEventListener; + @Inject TimerEventListener timerEventListener; @Deployment public static WebArchive deploy() { - File[] jars = Maven.resolver().loadPomFromFile("pom.xml") - .resolve("com.jayway.awaitility:awaitility") - .withTransitivity().asFile(); + File[] jars = Maven + .resolver() + .loadPomFromFile("pom.xml") + .resolve("com.jayway.awaitility:awaitility") + .withTransitivity() + .asFile(); - return ShrinkWrap.create(WebArchive.class) - .addAsLibraries(jars) - .addClasses(WithinWindowMatcher.class, TimerEvent.class, TimerEventListener.class, ScheduleTimerBean.class); + return ShrinkWrap + .create(WebArchive.class) + .addAsLibraries(jars) + .addClasses(WithinWindowMatcher.class, TimerEvent.class, TimerEventListener.class, ScheduleTimerBean.class); } @Test @@ -46,9 +48,15 @@ public class ScheduleTimerBeanIntegrationTest { Awaitility.setDefaultTimeout(30, TimeUnit.SECONDS); await().untilCall(to(timerEventListener.getEvents()).size(), equalTo(3)); - TimerEvent firstEvent = timerEventListener.getEvents().get(0); - TimerEvent secondEvent = timerEventListener.getEvents().get(1); - TimerEvent thirdEvent = timerEventListener.getEvents().get(2); + TimerEvent firstEvent = timerEventListener + .getEvents() + .get(0); + TimerEvent secondEvent = timerEventListener + .getEvents() + .get(1); + TimerEvent thirdEvent = timerEventListener + .getEvents() + .get(2); long delay = secondEvent.getTime() - firstEvent.getTime(); assertThat(delay, Matchers.is(WithinWindowMatcher.withinWindow(TIMEOUT, TOLERANCE))); diff --git a/jooq/src/test/java/com/baeldung/JOOLTest.java b/jooq/src/test/java/com/baeldung/JOOLTest.java index 13bf1a3ec3..a0d5f4037f 100644 --- a/jooq/src/test/java/com/baeldung/JOOLTest.java +++ b/jooq/src/test/java/com/baeldung/JOOLTest.java @@ -1,12 +1,9 @@ package com.baeldung; - -import junit.framework.Assert; import org.jooq.lambda.Seq; import org.jooq.lambda.Unchecked; import org.jooq.lambda.function.Function1; import org.jooq.lambda.function.Function2; -import org.jooq.lambda.tuple.Tuple; import org.jooq.lambda.tuple.Tuple2; import org.jooq.lambda.tuple.Tuple3; import org.jooq.lambda.tuple.Tuple4; diff --git a/pom.xml b/pom.xml index b26ada6f24..76f7247f51 100644 --- a/pom.xml +++ b/pom.xml @@ -214,6 +214,7 @@ vertx spring-data-gemfire + @@ -226,6 +227,18 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IntegrationTest.java + **/*LongRunningUnitTest.java + **/*ManualTest.java + + true + + @@ -280,6 +286,7 @@ 4.3.4.RELEASE 4.2.0.RELEASE 1.1.5.RELEASE + 1.2.0.RELEASE 5.2.5.Final diff --git a/spring-all/src/main/java/org/baeldung/shell/Main.java b/spring-all/src/main/java/org/baeldung/shell/Main.java new file mode 100644 index 0000000000..3d9f7a5860 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/Main.java @@ -0,0 +1,10 @@ +package org.baeldung.shell; + +import java.io.IOException; +import org.springframework.shell.Bootstrap; + +public class Main { + public static void main(String[] args) throws IOException { + Bootstrap.main(args); + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java new file mode 100644 index 0000000000..df7a48cd32 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java @@ -0,0 +1,34 @@ +package org.baeldung.shell.simple; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.shell.plugin.support.DefaultBannerProvider; +import org.springframework.shell.support.util.OsUtils; +import org.springframework.stereotype.Component; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimpleBannerProvider extends DefaultBannerProvider { + + public String getBanner() { + StringBuffer buf = new StringBuffer(); + buf.append("=======================================").append(OsUtils.LINE_SEPARATOR); + buf.append("* Baeldung Shell *").append(OsUtils.LINE_SEPARATOR); + buf.append("=======================================").append(OsUtils.LINE_SEPARATOR); + buf.append("Version:").append(this.getVersion()); + return buf.toString(); + } + + public String getVersion() { + return "1.0.1"; + } + + public String getWelcomeMessage() { + return "Welcome to Baeldung CLI"; + } + + @Override + public String getProviderName() { + return "Baeldung Banner"; + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java new file mode 100644 index 0000000000..0bbc62cf2c --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java @@ -0,0 +1,81 @@ +package org.baeldung.shell.simple; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.springframework.shell.Bootstrap; +import org.springframework.shell.core.CommandMarker; +import org.springframework.shell.core.annotation.CliAvailabilityIndicator; +import org.springframework.shell.core.annotation.CliCommand; +import org.springframework.shell.core.annotation.CliOption; +import org.springframework.stereotype.Component; + +@Component +public class SimpleCLI implements CommandMarker { + + private String getContentsOfUrlAsString(URL url) { + StringBuilder sb = new StringBuilder(); + try { + try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()))) { + String inputLine; + while ((inputLine = in.readLine()) != null) { + sb.append(inputLine); + } + } + } catch (IOException ex) { + sb.append("ERROR"); + } + return sb.toString(); + } + + @CliCommand( + value = {"web-get", "wg"}, + help = "Displays the contents of a URL." + ) + public String webGet( + @CliOption( + key = {"", "url"}, + help = "URL whose contents will be displayed." + ) URL url) { + return getContentsOfUrlAsString(url); + } + + @CliCommand( + value = {"web-save", "ws"}, + help = "Saves the contents of a URL.") + public String webSave( + @CliOption(key = {"", "url"}, help = "URL whose contents will be saved.") URL url, + @CliOption(key = {"out", "file"}, mandatory = true, help = "The name of the file.") String file) { + String contents = getContentsOfUrlAsString(url); + try (PrintWriter out = new PrintWriter(file)) { + out.write(contents); + } catch (FileNotFoundException ex) { + //Ignore + } + return "Done."; + } + + private boolean adminEnableExecuted = false; + + @CliAvailabilityIndicator(value = {"web-save"}) + public boolean isAdminEnabled() { + return adminEnableExecuted; + } + + @CliCommand(value = "admin-enable") + public String adminEnable() { + adminEnableExecuted = true; + return "Admin commands enabled."; + } + + @CliCommand(value = "admin-disable") + public String adminDisable() { + adminEnableExecuted = false; + return "Admin commands disabled."; + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java new file mode 100644 index 0000000000..cef53adc69 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java @@ -0,0 +1,22 @@ +package org.baeldung.shell.simple; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.shell.plugin.support.DefaultHistoryFileNameProvider; +import org.springframework.stereotype.Component; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimpleHistoryFileNameProvider extends DefaultHistoryFileNameProvider { + + @Override + public String getHistoryFileName() { + return "baeldung-shell.log"; + } + + @Override + public String getProviderName() { + return "Baeldung History"; + } + +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java new file mode 100644 index 0000000000..9a84954e05 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java @@ -0,0 +1,21 @@ +package org.baeldung.shell.simple; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.shell.plugin.support.DefaultPromptProvider; +import org.springframework.stereotype.Component; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimplePromptProvider extends DefaultPromptProvider { + + @Override + public String getPrompt() { + return "baeldung-shell>"; + } + + @Override + public String getProviderName() { + return "Baeldung Prompt"; + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java new file mode 100644 index 0000000000..66bab5c488 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java @@ -0,0 +1,35 @@ +package org.baeldung.shell.simple; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import org.springframework.shell.core.Completion; +import org.springframework.shell.core.Converter; +import org.springframework.shell.core.MethodTarget; +import org.springframework.stereotype.Component; + +@Component +public class SimpleURLConverter implements Converter { + + @Override + public URL convertFromText(String value, Class requiredType, String optionContext) { + try { + return new URL(value); + } catch (MalformedURLException ex) { + //Ignore + } + return null; + } + + @Override + public boolean getAllPossibleValues(List completions, Class requiredType, + String existingData, String optionContext, MethodTarget target) { + return false; + } + + @Override + public boolean supports(Class requiredType, String optionContext) { + return URL.class.isAssignableFrom(requiredType); + } + +} diff --git a/spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml b/spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml new file mode 100644 index 0000000000..aea1a663c1 --- /dev/null +++ b/spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java b/spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java new file mode 100644 index 0000000000..0353083943 --- /dev/null +++ b/spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java @@ -0,0 +1,86 @@ +package org.baeldung.shell.simple; + +import java.io.File; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.shell.Bootstrap; +import org.springframework.shell.core.CommandResult; +import org.springframework.shell.core.JLineShellComponent; + +public class SimpleCLIUnitTest { + + static JLineShellComponent shell; + + @BeforeClass + public static void startUp() throws InterruptedException { + Bootstrap bootstrap = new Bootstrap(); + shell = bootstrap.getJLineShellComponent(); + } + + @AfterClass + public static void shutdown() { + shell.stop(); + } + + public static JLineShellComponent getShell() { + return shell; + } + + @Test + public void givenCommandConfig_whenExecutingWebGetCommand_thenCorrectResult() { + + CommandResult resultWebSave = shell.executeCommand("web-get --url https://www.google.com"); + + Assert.assertTrue(resultWebSave.isSuccess()); + } + + @Test + public void givenCommandConfig_whenExecutingWebSaveCommand_thenCorrectResult() { + + shell.executeCommand("admin-enable"); + CommandResult result = shell.executeCommand("web-save --url https://www.google.com --out contents.txt"); + + Assert.assertArrayEquals( + new boolean[]{ + result.isSuccess(), + new File("contents.txt").exists()}, + new boolean[]{true, true}); + } + + @Test + public void givenCommandConfig_whenAdminEnableCommandExecuted_thenCorrectAvailability() { + + CommandResult resultAdminDisable = shell.executeCommand("admin-disable"); + CommandResult resultWebSaveUnavailable = shell.executeCommand("web-save --url https://www.google.com --out contents.txt"); + CommandResult resultAdminEnable = shell.executeCommand("admin-enable"); + CommandResult resultWebSaveAvailable = shell.executeCommand("web-save --url https://www.google.com --out contents.txt"); + + Assert.assertArrayEquals( + new boolean[]{ + resultAdminDisable.isSuccess(), + resultWebSaveUnavailable.isSuccess(), + resultAdminEnable.isSuccess(), + resultWebSaveAvailable.isSuccess()}, + new boolean[]{true, false, true, true}); + } + + @Test + public void givenCommandConfig_whenWebSaveCommandExecutedNoOutArgument_thenError() { + + shell.executeCommand("admin-enable"); + CommandResult resultWebSave = shell.executeCommand("web-save --url https://www.google.com"); + + Assert.assertEquals(resultWebSave.isSuccess(), false); + } + + @Test + public void givenCommandConfig_whenExecutingWebGetCommandWithDefaultArgument_thenCorrectResult() { + + CommandResult result = shell.executeCommand("web-get https://www.google.com"); + + Assert.assertEquals(result.isSuccess(), true); + } + +} From 077d745b8d2bb33a48d82d14cdf378bc7eca1eab Mon Sep 17 00:00:00 2001 From: Wim Deblauwe Date: Sat, 1 Apr 2017 23:02:19 +0200 Subject: [PATCH 100/102] BAEL-75 - Spring Boot Audit Support (#1561) Rework to avoid the separate spring-boot-auditing module by moving the code to the spring-security-core module --- spring-boot-auditing/.gitignore | 6 - spring-boot-auditing/pom.xml | 198 ------------------ .../main/java/org/baeldung/Application.java | 13 -- .../src/main/java/org/baeldung/MvcConfig.java | 18 -- .../java/org/baeldung/WebSecurityConfig.java | 34 --- .../src/main/resources/application.properties | 1 - .../src/main/resources/logback.xml | 14 -- .../src/main/resources/templates/hello.html | 13 -- .../src/main/resources/templates/home.html | 11 - .../src/main/resources/templates/login.html | 20 -- spring-security-core/pom.xml | 4 + ...temptedPathAuthorizationAuditListener.java | 0 .../auditing/LoginAttemptsLogger.java | 8 +- .../baeldung/config/WebSecurityConfig.java | 5 +- 14 files changed, 12 insertions(+), 333 deletions(-) delete mode 100644 spring-boot-auditing/.gitignore delete mode 100644 spring-boot-auditing/pom.xml delete mode 100755 spring-boot-auditing/src/main/java/org/baeldung/Application.java delete mode 100755 spring-boot-auditing/src/main/java/org/baeldung/MvcConfig.java delete mode 100755 spring-boot-auditing/src/main/java/org/baeldung/WebSecurityConfig.java delete mode 100644 spring-boot-auditing/src/main/resources/application.properties delete mode 100644 spring-boot-auditing/src/main/resources/logback.xml delete mode 100755 spring-boot-auditing/src/main/resources/templates/hello.html delete mode 100755 spring-boot-auditing/src/main/resources/templates/home.html delete mode 100755 spring-boot-auditing/src/main/resources/templates/login.html rename {spring-boot-auditing => spring-security-core}/src/main/java/org/baeldung/auditing/ExposeAttemptedPathAuthorizationAuditListener.java (100%) rename {spring-boot-auditing => spring-security-core}/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java (72%) diff --git a/spring-boot-auditing/.gitignore b/spring-boot-auditing/.gitignore deleted file mode 100644 index 31ce405488..0000000000 --- a/spring-boot-auditing/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/target/ -.settings/ -.classpath -.project -*.iml -.idea \ No newline at end of file diff --git a/spring-boot-auditing/pom.xml b/spring-boot-auditing/pom.xml deleted file mode 100644 index 0afbe0becc..0000000000 --- a/spring-boot-auditing/pom.xml +++ /dev/null @@ -1,198 +0,0 @@ - - 4.0.0 - com.baeldung - spring-boot-auditing - 0.0.1-SNAPSHOT - war - spring-boot-auditing - This is simple boot application for Spring boot auditing test - - - - org.springframework.boot - spring-boot-starter-parent - 1.5.2.RELEASE - - - - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - - org.springframework.boot - spring-boot-starter-actuator - - - - org.springframework.boot - spring-boot-starter-security - - - - io.dropwizard.metrics - metrics-core - - - - com.h2database - h2 - - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.springframework.boot - spring-boot-starter - - - com.jayway.jsonpath - json-path - test - - - org.springframework.boot - spring-boot-starter-mail - - - org.subethamail - subethasmtp - ${subethasmtp.version} - test - - - - org.webjars - bootstrap - ${bootstrap.version} - - - org.webjars - jquery - ${jquery.version} - - - - org.apache.tomcat - tomcat-servlet-api - ${tomee-servlet-api.version} - provided - - - - - - spring-boot - - - src/main/resources - true - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - - - - - org.apache.maven.plugins - maven-war-plugin - - - - pl.project13.maven - git-commit-id-plugin - ${git-commit-id-plugin.version} - - - - org.apache.maven.plugins - maven-surefire-plugin - - - **/*IntegrationTest.java - **/*LiveTest.java - - - - - - - - - - - integration - - - - org.apache.maven.plugins - maven-surefire-plugin - - - integration-test - - test - - - - **/*LiveTest.java - - - **/*IntegrationTest.java - - - - - - - json - - - - - - - - - - - UTF-8 - 1.8 - 4.3.7.RELEASE - 2.2.1 - 3.1.1 - 3.3.7-1 - 3.1.7 - 8.5.11 - - - diff --git a/spring-boot-auditing/src/main/java/org/baeldung/Application.java b/spring-boot-auditing/src/main/java/org/baeldung/Application.java deleted file mode 100755 index bf7b7bd1a6..0000000000 --- a/spring-boot-auditing/src/main/java/org/baeldung/Application.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.baeldung; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class Application { - - public static void main(String[] args) throws Throwable { - SpringApplication.run(Application.class, args); - } - -} diff --git a/spring-boot-auditing/src/main/java/org/baeldung/MvcConfig.java b/spring-boot-auditing/src/main/java/org/baeldung/MvcConfig.java deleted file mode 100755 index fecb8c5c0b..0000000000 --- a/spring-boot-auditing/src/main/java/org/baeldung/MvcConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.baeldung; - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; - -@Configuration -public class MvcConfig extends WebMvcConfigurerAdapter { - - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/home").setViewName("home"); - registry.addViewController("/").setViewName("home"); - registry.addViewController("/hello").setViewName("hello"); - registry.addViewController("/login").setViewName("login"); - } - -} diff --git a/spring-boot-auditing/src/main/java/org/baeldung/WebSecurityConfig.java b/spring-boot-auditing/src/main/java/org/baeldung/WebSecurityConfig.java deleted file mode 100755 index 9339d8e804..0000000000 --- a/spring-boot-auditing/src/main/java/org/baeldung/WebSecurityConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.baeldung; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; - -@Configuration -@EnableWebSecurity -public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .antMatchers("/", "/home").permitAll() - .anyRequest().authenticated() - .and() - .formLogin() - .loginPage("/login") - .permitAll() - .and() - .logout() - .permitAll(); - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER", "ACTUATOR"); - } -} diff --git a/spring-boot-auditing/src/main/resources/application.properties b/spring-boot-auditing/src/main/resources/application.properties deleted file mode 100644 index cf09473b60..0000000000 --- a/spring-boot-auditing/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -logging.level.org.springframework=INFO \ No newline at end of file diff --git a/spring-boot-auditing/src/main/resources/logback.xml b/spring-boot-auditing/src/main/resources/logback.xml deleted file mode 100644 index 78913ee76f..0000000000 --- a/spring-boot-auditing/src/main/resources/logback.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - web - %date [%thread] %-5level %logger{36} - %message%n - - - - - - - - - \ No newline at end of file diff --git a/spring-boot-auditing/src/main/resources/templates/hello.html b/spring-boot-auditing/src/main/resources/templates/hello.html deleted file mode 100755 index 46feef7e2c..0000000000 --- a/spring-boot-auditing/src/main/resources/templates/hello.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - Hello World! - - -

Hello [[${#httpServletRequest.remoteUser}]]!

-
- -
- - \ No newline at end of file diff --git a/spring-boot-auditing/src/main/resources/templates/home.html b/spring-boot-auditing/src/main/resources/templates/home.html deleted file mode 100755 index fe4e8b337e..0000000000 --- a/spring-boot-auditing/src/main/resources/templates/home.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - Spring Security Example - - -

Welcome!

- -

Click here to see a greeting.

- - \ No newline at end of file diff --git a/spring-boot-auditing/src/main/resources/templates/login.html b/spring-boot-auditing/src/main/resources/templates/login.html deleted file mode 100755 index a1785313f5..0000000000 --- a/spring-boot-auditing/src/main/resources/templates/login.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - Spring Security Example - - -
- Invalid username and password. -
-
- You have been logged out. -
-
-
-
-
-
- - \ No newline at end of file diff --git a/spring-security-core/pom.xml b/spring-security-core/pom.xml index a8ffce84b7..971f5a9d0f 100644 --- a/spring-security-core/pom.xml +++ b/spring-security-core/pom.xml @@ -32,6 +32,10 @@ org.springframework.boot spring-boot-starter-thymeleaf
+ + org.springframework.boot + spring-boot-starter-actuator + com.h2database h2 diff --git a/spring-boot-auditing/src/main/java/org/baeldung/auditing/ExposeAttemptedPathAuthorizationAuditListener.java b/spring-security-core/src/main/java/org/baeldung/auditing/ExposeAttemptedPathAuthorizationAuditListener.java similarity index 100% rename from spring-boot-auditing/src/main/java/org/baeldung/auditing/ExposeAttemptedPathAuthorizationAuditListener.java rename to spring-security-core/src/main/java/org/baeldung/auditing/ExposeAttemptedPathAuthorizationAuditListener.java diff --git a/spring-boot-auditing/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java b/spring-security-core/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java similarity index 72% rename from spring-boot-auditing/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java rename to spring-security-core/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java index 5be8cebfd3..bf0781bced 100644 --- a/spring-boot-auditing/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java +++ b/spring-security-core/src/main/java/org/baeldung/auditing/LoginAttemptsLogger.java @@ -15,11 +15,11 @@ public class LoginAttemptsLogger { @EventListener public void auditEventHappened(AuditApplicationEvent auditApplicationEvent) { AuditEvent auditEvent = auditApplicationEvent.getAuditEvent(); - LOGGER.debug("Principal " + auditEvent.getPrincipal() + " - " + auditEvent.getType()); + LOGGER.info("Principal " + auditEvent.getPrincipal() + " - " + auditEvent.getType()); WebAuthenticationDetails details = (WebAuthenticationDetails) auditEvent.getData().get("details"); - LOGGER.debug(" Remote IP address: " + details.getRemoteAddress()); - LOGGER.debug(" Session Id: " + details.getSessionId()); - LOGGER.debug(" Request URL: " + auditEvent.getData().get("requestUrl")); + LOGGER.info(" Remote IP address: " + details.getRemoteAddress()); + LOGGER.info(" Session Id: " + details.getSessionId()); + LOGGER.info(" Request URL: " + auditEvent.getData().get("requestUrl")); } } diff --git a/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java b/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java index 5441dac7f7..0b6cd34f3e 100644 --- a/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java +++ b/spring-security-core/src/main/java/org/baeldung/config/WebSecurityConfig.java @@ -20,6 +20,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser("jim").password("jim").roles("USER").and().withUser("pam").password("pam").roles("USER").and().withUser("michael").password("michael").roles("MANAGER"); + auth.inMemoryAuthentication() + .withUser("jim").password("jim").roles("USER", "ACTUATOR") + .and().withUser("pam").password("pam").roles("USER") + .and().withUser("michael").password("michael").roles("MANAGER"); } } From 8fc519538dff4ddb6ed83164bdd8759759435120 Mon Sep 17 00:00:00 2001 From: slavisa-baeldung Date: Sun, 2 Apr 2017 10:07:37 +0200 Subject: [PATCH 101/102] Bael 112 validator upgrade (#1567) * BAEL-112 - upgrading hibernate validator * BAEL-112 - upgrading hibernate validator --- spring-mvc-java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-mvc-java/pom.xml b/spring-mvc-java/pom.xml index 0f6dbfbd98..dcf740b22b 100644 --- a/spring-mvc-java/pom.xml +++ b/spring-mvc-java/pom.xml @@ -176,7 +176,7 @@ org.hibernate hibernate-validator - 5.1.2.Final + ${hibernate-validator.version} javax.el @@ -370,7 +370,7 @@ 1.1.7 - 5.3.3.Final + 5.4.1.Final 3.1.0 1.2 From b4b5f79ce1ca5cb0f78a2cb8ae15d4dc920f97ee Mon Sep 17 00:00:00 2001 From: KevinGilmore Date: Sun, 2 Apr 2017 03:08:13 -0500 Subject: [PATCH 102/102] BAEL-666: README (#1563) * Add files via upload * Update pom.xml * Update RunGuice.java * Update Communication.java * Update CommunicationMode.java * Update DefaultCommunicator.java * Update EmailCommunicationMode.java * Update IMCommunicationMode.java * Update SMSCommunicationMode.java * Update MessageLogger.java * Update MessageSentLoggable.java * Update AOPModule.java * Update BasicModule.java * Update CommunicationModel.java * Update Communicator.java * Update BasicModule.java * Update RunGuice.java * Update MessageLogger.java * Update Communicator.java * Update pom.xml * BAEL-278: Updated README.md * BAEL-554: Add and update README.md files * Update pom.xml * Update pom.xml * Update pom.xml * BAEL-345: fixed assertion * BAEL-109: Updated README.md * BAEL-345: Added README.md * Reinstating reactor-core module in root-level pom * BAEL-393: Adding guide-intro module to root pom * BAEL-9: Updated README.md * BAEL-157: README.md updated * Changed project name * Update RunGuice.java Removed references to message logging and output * Update Communication.java Removed message logging-related code * BAEL-566: Updated README.md * New project name * BAEL-393: removing guice-intro directory * BAEL-393: renamed module guice-intro to guice in root pom.xml * BAEL-393 and BAEL-541 README.md files * BAEL-731: Updated README.md * BAEL-680: renamed test methods * BAEL-714: Updated README.md * BAEL-737: Updated README.md * BAEL-680 and BAEL-756 README.md updates * BAEL-666: Updated README --- jackson/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/jackson/README.md b/jackson/README.md index d9faa377f1..5bc16e66b7 100644 --- a/jackson/README.md +++ b/jackson/README.md @@ -26,3 +26,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring - [Inheritance with Jackson](http://www.baeldung.com/jackson-inheritance) - [Guide to @JsonFormat in Jackson](http://www.baeldung.com/jackson-jsonformat) - [A Guide to Optional with Jackson](http://www.baeldung.com/jackson-optional) +- [Map Serialization and Deserialization with Jackson](http://www.baeldung.com/jackson-map)