From 7e7ccc7eb391bf9ac2b99f904a5c90a4a15abff4 Mon Sep 17 00:00:00 2001 From: aietcn Date: Sat, 20 Jan 2018 17:09:55 +0800 Subject: [PATCH 1/9] BAEL-1416 quick guide to kong (#3448) * BAEL-1416 quick guide to kong * refactor kong samples --- .../com/baeldung/kong/QueryController.java | 32 ++++ .../main/java/com/baeldung/kong/StockApp.java | 13 ++ .../src/main/resources/application.properties | 2 +- .../baeldung/kong/KongAdminAPILiveTest.java | 165 ++++++++++++++++++ .../kong/KongLoadBalanceLiveTest.java | 68 ++++++++ .../com/baeldung/kong/domain/APIObject.java | 54 ++++++ .../baeldung/kong/domain/ConsumerObject.java | 35 ++++ .../baeldung/kong/domain/KeyAuthObject.java | 21 +++ .../baeldung/kong/domain/PluginObject.java | 30 ++++ .../baeldung/kong/domain/TargetObject.java | 31 ++++ .../baeldung/kong/domain/UpstreamObject.java | 21 +++ 11 files changed, 471 insertions(+), 1 deletion(-) create mode 100644 spring-boot/src/main/java/com/baeldung/kong/QueryController.java create mode 100644 spring-boot/src/main/java/com/baeldung/kong/StockApp.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/KongAdminAPILiveTest.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/KongLoadBalanceLiveTest.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/APIObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/ConsumerObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/KeyAuthObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/PluginObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/TargetObject.java create mode 100644 spring-boot/src/test/java/com/baeldung/kong/domain/UpstreamObject.java diff --git a/spring-boot/src/main/java/com/baeldung/kong/QueryController.java b/spring-boot/src/main/java/com/baeldung/kong/QueryController.java new file mode 100644 index 0000000000..af63a7e8a1 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/kong/QueryController.java @@ -0,0 +1,32 @@ +package com.baeldung.kong; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author aiet + */ +@RestController +@RequestMapping("/stock") +public class QueryController { + + private static int REQUEST_COUNTER = 0; + + @GetMapping("/reqcount") + public int getReqCount(){ + return REQUEST_COUNTER; + } + + @GetMapping("/{code}") + public String getStockPrice(@PathVariable String code){ + REQUEST_COUNTER++; + if("BTC".equalsIgnoreCase(code)) + return "10000"; + else return "N/A"; + } + + + +} diff --git a/spring-boot/src/main/java/com/baeldung/kong/StockApp.java b/spring-boot/src/main/java/com/baeldung/kong/StockApp.java new file mode 100644 index 0000000000..f901592938 --- /dev/null +++ b/spring-boot/src/main/java/com/baeldung/kong/StockApp.java @@ -0,0 +1,13 @@ +package com.baeldung.kong; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class StockApp { + + public static void main(String[] args) { + SpringApplication.run(StockApp.class, args); + } + +} diff --git a/spring-boot/src/main/resources/application.properties b/spring-boot/src/main/resources/application.properties index 458b4e0d46..059a6c96be 100644 --- a/spring-boot/src/main/resources/application.properties +++ b/spring-boot/src/main/resources/application.properties @@ -1,4 +1,4 @@ -server.port=8080 +server.port=9090 server.contextPath=/springbootapp management.port=8081 management.address=127.0.0.1 diff --git a/spring-boot/src/test/java/com/baeldung/kong/KongAdminAPILiveTest.java b/spring-boot/src/test/java/com/baeldung/kong/KongAdminAPILiveTest.java new file mode 100644 index 0000000000..f399806a65 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/KongAdminAPILiveTest.java @@ -0,0 +1,165 @@ +package com.baeldung.kong; + +import com.baeldung.kong.domain.APIObject; +import com.baeldung.kong.domain.ConsumerObject; +import com.baeldung.kong.domain.KeyAuthObject; +import com.baeldung.kong.domain.PluginObject; +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.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.test.context.junit4.SpringRunner; + +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; + +/** + * @author aiet + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = DEFINED_PORT, classes = StockApp.class) +public class KongAdminAPILiveTest { + + private String getStockPrice(String code) { + try { + return restTemplate.getForObject(new URI("http://localhost:8080/stock/" + code), String.class); + } catch (Exception ignored) { + } + return null; + } + + @Before + public void init() { + System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); + } + + @Autowired TestRestTemplate restTemplate; + + @Test + public void givenEndpoint_whenQueryStockPrice_thenPriceCorrect() { + String response = getStockPrice("btc"); + assertEquals("10000", response); + + response = getStockPrice("eth"); + assertEquals("N/A", response); + } + + @Test + public void givenKongAdminAPI_whenAddAPI_thenAPIAccessibleViaKong() throws Exception { + restTemplate.delete("http://localhost:8001/apis/stock-api"); + + APIObject stockAPI = new APIObject("stock-api", "stock.api", "http://localhost:8080", "/"); + HttpEntity apiEntity = new HttpEntity<>(stockAPI); + ResponseEntity addAPIResp = restTemplate.postForEntity("http://localhost:8001/apis", apiEntity, String.class); + + assertEquals(HttpStatus.CREATED, addAPIResp.getStatusCode()); + + addAPIResp = restTemplate.postForEntity("http://localhost:8001/apis", apiEntity, String.class); + assertEquals(HttpStatus.CONFLICT, addAPIResp.getStatusCode()); + String apiListResp = restTemplate.getForObject("http://localhost:8001/apis/", String.class); + + assertTrue(apiListResp.contains("stock-api")); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "stock.api"); + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); + + assertEquals("10000", stockPriceResp.getBody()); + } + + @Test + public void givenKongAdminAPI_whenAddAPIConsumer_thenAdded() { + restTemplate.delete("http://localhost:8001/consumers/eugenp"); + + ConsumerObject consumer = new ConsumerObject("eugenp"); + HttpEntity addConsumerEntity = new HttpEntity<>(consumer); + ResponseEntity addConsumerResp = restTemplate.postForEntity("http://localhost:8001/consumers/", addConsumerEntity, String.class); + + assertEquals(HttpStatus.CREATED, addConsumerResp.getStatusCode()); + + addConsumerResp = restTemplate.postForEntity("http://localhost:8001/consumers", addConsumerEntity, String.class); + assertEquals(HttpStatus.CONFLICT, addConsumerResp.getStatusCode()); + + String consumerListResp = restTemplate.getForObject("http://localhost:8001/consumers/", String.class); + assertTrue(consumerListResp.contains("eugenp")); + } + + @Test + public void givenAPI_whenEnableAuth_thenAnonymousDenied() throws Exception { + String apiListResp = restTemplate.getForObject("http://localhost:8001/apis/", String.class); + if (!apiListResp.contains("stock-api")) { + givenKongAdminAPI_whenAddAPI_thenAPIAccessibleViaKong(); + } + + PluginObject authPlugin = new PluginObject("key-auth"); + ResponseEntity enableAuthResp = restTemplate.postForEntity("http://localhost:8001/apis/stock-api/plugins", new HttpEntity<>(authPlugin), String.class); + + assertTrue(HttpStatus.CREATED == enableAuthResp.getStatusCode() || HttpStatus.CONFLICT == enableAuthResp.getStatusCode()); + + String pluginsResp = restTemplate.getForObject("http://localhost:8001/apis/stock-api/plugins", String.class); + assertTrue(pluginsResp.contains("key-auth")); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "stock.api"); + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); + assertEquals(HttpStatus.UNAUTHORIZED, stockPriceResp.getStatusCode()); + } + + @Test + public void givenAPIAuthEnabled_whenAddKey_thenAccessAllowed() throws Exception { + String apiListResp = restTemplate.getForObject("http://localhost:8001/apis/", String.class); + if (!apiListResp.contains("stock-api")) { + givenKongAdminAPI_whenAddAPI_thenAPIAccessibleViaKong(); + } + + String consumerListResp = restTemplate.getForObject("http://localhost:8001/consumers/", String.class); + if (!consumerListResp.contains("eugenp")) { + givenKongAdminAPI_whenAddAPIConsumer_thenAdded(); + } + + final String consumerKey = "eugenp.pass"; + KeyAuthObject keyAuth = new KeyAuthObject(consumerKey); + ResponseEntity keyAuthResp = restTemplate.postForEntity("http://localhost:8001/consumers/eugenp/key-auth", new HttpEntity<>(keyAuth), String.class); + + assertTrue(HttpStatus.CREATED == keyAuthResp.getStatusCode() || HttpStatus.CONFLICT == keyAuthResp.getStatusCode()); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "stock.api"); + headers.set("apikey", consumerKey); + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); + + assertEquals("10000", stockPriceResp.getBody()); + + headers.set("apikey", "wrongpass"); + requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + stockPriceResp = restTemplate.exchange(requestEntity, String.class); + assertEquals(HttpStatus.FORBIDDEN, stockPriceResp.getStatusCode()); + } + + @Test + public void givenAdminAPIProxy_whenAddAPIViaProxy_thenAPIAdded() throws Exception { + APIObject adminAPI = new APIObject("admin-api", "admin.api", "http://localhost:8001", "/admin-api"); + HttpEntity apiEntity = new HttpEntity<>(adminAPI); + ResponseEntity addAPIResp = restTemplate.postForEntity("http://localhost:8001/apis", apiEntity, String.class); + + assertTrue(HttpStatus.CREATED == addAPIResp.getStatusCode() || HttpStatus.CONFLICT == addAPIResp.getStatusCode()); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "admin.api"); + APIObject baeldungAPI = new APIObject("baeldung-api", "baeldung.com", "http://ww.baeldung.com", "/"); + RequestEntity requestEntity = new RequestEntity<>(baeldungAPI, headers, HttpMethod.POST, new URI("http://localhost:8000/admin-api/apis")); + addAPIResp = restTemplate.exchange(requestEntity, String.class); + + assertTrue(HttpStatus.CREATED == addAPIResp.getStatusCode() || HttpStatus.CONFLICT == addAPIResp.getStatusCode()); + } + +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/KongLoadBalanceLiveTest.java b/spring-boot/src/test/java/com/baeldung/kong/KongLoadBalanceLiveTest.java new file mode 100644 index 0000000000..f8090e4c86 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/KongLoadBalanceLiveTest.java @@ -0,0 +1,68 @@ +package com.baeldung.kong; + +import com.baeldung.kong.domain.APIObject; +import com.baeldung.kong.domain.TargetObject; +import com.baeldung.kong.domain.UpstreamObject; +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.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.test.context.junit4.SpringRunner; + +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; + +/** + * @author aiet + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = DEFINED_PORT, classes = StockApp.class) +public class KongLoadBalanceLiveTest { + + @Before + public void init() { + System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); + } + + @Autowired TestRestTemplate restTemplate; + + @Test + public void givenKongAdminAPI_whenAddAPI_thenAPIAccessibleViaKong() throws Exception { + UpstreamObject upstream = new UpstreamObject("stock.api.service"); + ResponseEntity addUpstreamResp = restTemplate.postForEntity("http://localhost:8001/upstreams", new HttpEntity<>(upstream), String.class); + assertTrue(HttpStatus.CREATED == addUpstreamResp.getStatusCode() || HttpStatus.CONFLICT == addUpstreamResp.getStatusCode()); + + TargetObject testTarget = new TargetObject("localhost:8080", 10); + ResponseEntity addTargetResp = restTemplate.postForEntity("http://localhost:8001/upstreams/stock.api.service/targets", new HttpEntity<>(testTarget), String.class); + assertTrue(HttpStatus.CREATED == addTargetResp.getStatusCode() || HttpStatus.CONFLICT == addTargetResp.getStatusCode()); + + TargetObject releaseTarget = new TargetObject("localhost:9090", 40); + addTargetResp = restTemplate.postForEntity("http://localhost:8001/upstreams/stock.api.service/targets", new HttpEntity<>(releaseTarget), String.class); + assertTrue(HttpStatus.CREATED == addTargetResp.getStatusCode() || HttpStatus.CONFLICT == addTargetResp.getStatusCode()); + + APIObject stockAPI = new APIObject("balanced-stock-api", "balanced.stock.api", "http://stock.api.service", "/"); + HttpEntity apiEntity = new HttpEntity<>(stockAPI); + ResponseEntity addAPIResp = restTemplate.postForEntity("http://localhost:8001/apis", apiEntity, String.class); + assertTrue(HttpStatus.CREATED == addAPIResp.getStatusCode() || HttpStatus.CONFLICT == addAPIResp.getStatusCode()); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Host", "balanced.stock.api"); + for (int i = 0; i < 1000; i++) { + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, new URI("http://localhost:8000/stock/btc")); + ResponseEntity stockPriceResp = restTemplate.exchange(requestEntity, String.class); + assertEquals("10000", stockPriceResp.getBody()); + } + + int releaseCount = restTemplate.getForObject("http://localhost:9090/stock/reqcount", Integer.class); + int testCount = restTemplate.getForObject("http://localhost:8080/stock/reqcount", Integer.class); + + assertTrue(Math.round(releaseCount * 1.0 / testCount) == 4); + } + +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/APIObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/APIObject.java new file mode 100644 index 0000000000..f386712444 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/APIObject.java @@ -0,0 +1,54 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class APIObject { + + public APIObject() { + } + + public APIObject(String name, String hosts, String upstream_url, String uris) { + this.name = name; + this.hosts = hosts; + this.upstream_url = upstream_url; + this.uris = uris; + } + + private String name; + private String hosts; + private String upstream_url; + private String uris; + + public String getUris() { + return uris; + } + + public void setUris(String uris) { + this.uris = uris; + } + + public String getUpstream_url() { + return upstream_url; + } + + public void setUpstream_url(String upstream_url) { + this.upstream_url = upstream_url; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHosts() { + return hosts; + } + + public void setHosts(String hosts) { + this.hosts = hosts; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/ConsumerObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/ConsumerObject.java new file mode 100644 index 0000000000..74bef8f2d1 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/ConsumerObject.java @@ -0,0 +1,35 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class ConsumerObject { + + private String username; + private String custom_id; + + public ConsumerObject(String username) { + this.username = username; + } + + public ConsumerObject(String username, String custom_id) { + this.username = username; + this.custom_id = custom_id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getCustom_id() { + return custom_id; + } + + public void setCustom_id(String custom_id) { + this.custom_id = custom_id; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/KeyAuthObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/KeyAuthObject.java new file mode 100644 index 0000000000..80de6bfcd9 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/KeyAuthObject.java @@ -0,0 +1,21 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class KeyAuthObject { + + public KeyAuthObject(String key) { + this.key = key; + } + + private String key; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/PluginObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/PluginObject.java new file mode 100644 index 0000000000..c161fc9b54 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/PluginObject.java @@ -0,0 +1,30 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class PluginObject { + + private String name; + private String consumer_id; + + public PluginObject(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getConsumer_id() { + return consumer_id; + } + + public void setConsumer_id(String consumer_id) { + this.consumer_id = consumer_id; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/TargetObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/TargetObject.java new file mode 100644 index 0000000000..79653e2846 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/TargetObject.java @@ -0,0 +1,31 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class TargetObject { + + public TargetObject(String target, int weight) { + this.target = target; + this.weight = weight; + } + + private String target; + private int weight; + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } +} diff --git a/spring-boot/src/test/java/com/baeldung/kong/domain/UpstreamObject.java b/spring-boot/src/test/java/com/baeldung/kong/domain/UpstreamObject.java new file mode 100644 index 0000000000..6461381ac5 --- /dev/null +++ b/spring-boot/src/test/java/com/baeldung/kong/domain/UpstreamObject.java @@ -0,0 +1,21 @@ +package com.baeldung.kong.domain; + +/** + * @author aiet + */ +public class UpstreamObject { + + public UpstreamObject(String name) { + this.name = name; + } + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} From 0293c65f7744e95b4fb9937be61fd8c35293960e Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 20 Jan 2018 11:54:24 +0100 Subject: [PATCH 2/9] Trie Examples (#3466) * Implement Trie * Implement TrieTest * Delete testDelete method * Refactorings * Refactor Trie * Refactor TrieNode * Refactor Trie --- .../src/main/java/com/baeldung/trie/Trie.java | 76 +++++++++++++++++++ .../main/java/com/baeldung/trie/TrieNode.java | 31 ++++++++ .../test/java/com/baeldung/trie/TrieTest.java | 68 +++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/trie/Trie.java create mode 100644 core-java/src/main/java/com/baeldung/trie/TrieNode.java create mode 100644 core-java/src/test/java/com/baeldung/trie/TrieTest.java diff --git a/core-java/src/main/java/com/baeldung/trie/Trie.java b/core-java/src/main/java/com/baeldung/trie/Trie.java new file mode 100644 index 0000000000..2c4119df71 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/trie/Trie.java @@ -0,0 +1,76 @@ +package com.baeldung.trie; + +public class Trie { + private TrieNode root; + + Trie() { + root = new TrieNode(); + } + + public void insert(String word) { + TrieNode current = root; + for (int i = 0; i < word.length(); i++) { + char ch = word.charAt(i); + TrieNode node = current.getChildren() + .get(ch); + if (node == null) { + node = new TrieNode(); + current.getChildren() + .put(ch, node); + } + current = node; + } + current.setEndOfWord(true); + } + + public boolean find(String word) { + TrieNode current = root; + for (int i = 0; i < word.length(); i++) { + char ch = word.charAt(i); + TrieNode node = current.getChildren() + .get(ch); + if (node == null) { + return false; + } + current = node; + } + return current.isEndOfWord(); + } + + public void delete(String word) { + delete(root, word, 0); + } + + private boolean delete(TrieNode current, String word, int index) { + if (index == word.length()) { + if (!current.isEndOfWord()) { + return false; + } + current.setEndOfWord(false); + return current.getChildren() + .size() == 0; + } + char ch = word.charAt(index); + TrieNode node = current.getChildren() + .get(ch); + if (node == null) { + return false; + } + boolean shouldDeleteCurrentNode = delete(node, word, index + 1); + + if (shouldDeleteCurrentNode) { + current.getChildren() + .remove(ch); + return current.getChildren().isEmpty(); + } + return false; + } + + public boolean containsNode(String word) { + return find(word); + } + + public boolean isEmpty() { + return root == null; + } +} \ No newline at end of file diff --git a/core-java/src/main/java/com/baeldung/trie/TrieNode.java b/core-java/src/main/java/com/baeldung/trie/TrieNode.java new file mode 100644 index 0000000000..25dc753950 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/trie/TrieNode.java @@ -0,0 +1,31 @@ +package com.baeldung.trie; + +import java.util.HashMap; +import java.util.Map; + +class TrieNode { + private Map children; + private boolean endOfWord; + + public TrieNode() { + children = new HashMap<>(); + endOfWord = false; + } + + public Map getChildren() { + return children; + } + + public void setChildren(Map children) { + this.children = children; + } + + public boolean isEndOfWord() { + return endOfWord; + } + + public void setEndOfWord(boolean endOfWord) { + this.endOfWord = endOfWord; + } + +} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/trie/TrieTest.java b/core-java/src/test/java/com/baeldung/trie/TrieTest.java new file mode 100644 index 0000000000..be7e5575d8 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/trie/TrieTest.java @@ -0,0 +1,68 @@ +package com.baeldung.trie; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TrieTest { + + @Test + public void whenEmptyTrie_thenNoElements() { + Trie trie = new Trie(); + + assertFalse(trie.isEmpty()); + } + + @Test + public void givenATrie_whenAddingElements_thenTrieNotEmpty() { + Trie trie = createExampleTrie(); + + assertFalse(trie.isEmpty()); + } + + @Test + public void givenATrie_whenAddingElements_thenTrieHasThoseElements() { + Trie trie = createExampleTrie(); + + assertFalse(trie.containsNode("3")); + assertFalse(trie.containsNode("vida")); + + assertTrue(trie.containsNode("Programming")); + assertTrue(trie.containsNode("is")); + assertTrue(trie.containsNode("a")); + assertTrue(trie.containsNode("way")); + assertTrue(trie.containsNode("of")); + assertTrue(trie.containsNode("life")); + } + + @Test + public void givenATrie_whenLookingForNonExistingElement_thenReturnsFalse() { + Trie trie = createExampleTrie(); + + assertFalse(trie.containsNode("99")); + } + + @Test + public void givenATrie_whenDeletingElements_thenTreeDoesNotContainThoseElements() { + + Trie trie = createExampleTrie(); + + assertTrue(trie.containsNode("Programming")); + trie.delete("Programming"); + assertFalse(trie.containsNode("Programming")); + } + + private Trie createExampleTrie() { + Trie trie = new Trie(); + + trie.insert("Programming"); + trie.insert("is"); + trie.insert("a"); + trie.insert("way"); + trie.insert("of"); + trie.insert("life"); + + return trie; + } +} From b1520c93fd73be57276c419a37c2713de6a1c974 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 20 Jan 2018 11:54:39 +0100 Subject: [PATCH 3/9] Update README.md (#3443) --- core-java-concurrency/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-java-concurrency/README.md b/core-java-concurrency/README.md index dc048495e4..8122c71bcb 100644 --- a/core-java-concurrency/README.md +++ b/core-java-concurrency/README.md @@ -33,4 +33,4 @@ - [Daemon Threads in Java](http://www.baeldung.com/java-daemon-thread) - [Implementing a Runnable vs Extending a Thread](http://www.baeldung.com/java-runnable-vs-extending-thread) - [How to Kill a Java Thread](http://www.baeldung.com/java-thread-stop) -- [How to Wait for Threads to Finish in the ExecutorService](http://www.baeldung.com/java-executor-wait-for-threads) +- [ExecutorService - Waiting for Threads to Finish](http://www.baeldung.com/java-executor-wait-for-threads) From d64468eddcb36a88c57fff01b2c09029ef27d762 Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 20 Jan 2018 13:33:02 +0100 Subject: [PATCH 4/9] Refactor elastic (#3467) --- .../com/baeldung/elasticsearch/Person.java | 3 +- .../ElasticSearchManualTest.java | 19 +-- .../elasticsearch/GeoQueriesTest.java | 150 +++++++++--------- .../data/es/ElasticSearchIntegrationTest.java | 42 ++--- .../es/ElasticSearchQueryIntegrationTest.java | 83 ++++++---- 5 files changed, 153 insertions(+), 144 deletions(-) diff --git a/spring-data-elasticsearch/src/main/java/com/baeldung/elasticsearch/Person.java b/spring-data-elasticsearch/src/main/java/com/baeldung/elasticsearch/Person.java index b8ad59e2e2..09b971fdc2 100644 --- a/spring-data-elasticsearch/src/main/java/com/baeldung/elasticsearch/Person.java +++ b/spring-data-elasticsearch/src/main/java/com/baeldung/elasticsearch/Person.java @@ -11,10 +11,9 @@ public class Person { private Date dateOfBirth; public Person() { - } - public Person(int age, String fullName, Date dateOfBirth) { + Person(int age, String fullName, Date dateOfBirth) { super(); this.age = age; this.fullName = fullName; diff --git a/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java index 1fb1ae76f7..285c164869 100644 --- a/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/ElasticSearchManualTest.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import static org.elasticsearch.node.NodeBuilder.nodeBuilder; import static org.junit.Assert.assertEquals; @@ -78,12 +79,9 @@ public class ElasticSearchManualTest { SearchHit[] searchHits = response .getHits() .getHits(); - List results = new ArrayList<>(); - for (SearchHit hit : searchHits) { - String sourceAsString = hit.getSourceAsString(); - Person person = JSON.parseObject(sourceAsString, Person.class); - results.add(person); - } + List results = Arrays.stream(searchHits) + .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) + .collect(Collectors.toList()); } @Test @@ -125,11 +123,10 @@ public class ElasticSearchManualTest { .actionGet(); response2.getHits(); response3.getHits(); - List searchHits = Arrays.asList(response - .getHits() - .getHits()); - final List results = new ArrayList<>(); - searchHits.forEach(hit -> results.add(JSON.parseObject(hit.getSourceAsString(), Person.class))); + + final List results = Arrays.stream(response.getHits().getHits()) + .map(hit -> JSON.parseObject(hit.getSourceAsString(), Person.class)) + .collect(Collectors.toList()); } @Test diff --git a/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java index 19514ce4c2..aa20913637 100644 --- a/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java @@ -1,11 +1,6 @@ package com.baeldung.elasticsearch; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - +import com.baeldung.spring.data.es.config.Config; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchResponse; @@ -15,6 +10,7 @@ import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -24,14 +20,19 @@ import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.data.es.config.Config; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertTrue; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) public class GeoQueriesTest { - public static final String WONDERS_OF_WORLD = "wonders-of-world"; - public static final String WONDERS = "Wonders"; + private static final String WONDERS_OF_WORLD = "wonders-of-world"; + private static final String WONDERS = "Wonders"; + @Autowired private ElasticsearchTemplate elasticsearchTemplate; @@ -44,39 +45,37 @@ public class GeoQueriesTest { CreateIndexRequest req = new CreateIndexRequest(WONDERS_OF_WORLD); req.mapping(WONDERS, jsonObject); client.admin() - .indices() - .create(req) - .actionGet(); + .indices() + .create(req) + .actionGet(); } @Test public void givenGeoShapeData_whenExecutedGeoShapeQuery_thenResultNonEmpty() { String jsonObject = "{\"name\":\"Agra\",\"region\":{\"type\":\"envelope\",\"coordinates\":[[75,25],[80.1,30.2]]}}"; IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject) - .get(); + .setSource(jsonObject) + .get(); String tajMahalId = response.getId(); client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); QueryBuilder qb = QueryBuilders.geoShapeQuery("region", ShapeBuilder.newEnvelope() - .topLeft(74.00, 24.0) - .bottomRight(81.1, 31.2)) - .relation(ShapeRelation.WITHIN); + .topLeft(74.00, 24.0) + .bottomRight(81.1, 31.2)) + .relation(ShapeRelation.WITHIN); SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(hit -> { - return hit.getId(); - }) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(tajMahalId)); } @@ -84,29 +83,27 @@ public class GeoQueriesTest { public void givenGeoPointData_whenExecutedGeoBoundingBoxQuery_thenResultNonEmpty() { String jsonObject = "{\"name\":\"Pyramids of Giza\",\"location\":[31.131302,29.976480]}"; IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject) - .get(); + .setSource(jsonObject) + .get(); String pyramidsOfGizaId = response.getId(); client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); QueryBuilder qb = QueryBuilders.geoBoundingBoxQuery("location") - .bottomLeft(28, 30) - .topRight(31, 32); + .bottomLeft(28, 30) + .topRight(31, 32); SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(hit -> { - return hit.getId(); - }) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(pyramidsOfGizaId)); } @@ -114,29 +111,27 @@ public class GeoQueriesTest { public void givenGeoPointData_whenExecutedGeoDistanceQuery_thenResultNonEmpty() { String jsonObject = "{\"name\":\"Lighthouse of alexandria\",\"location\":[31.131302,29.976480]}"; IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject) - .get(); + .setSource(jsonObject) + .get(); String lighthouseOfAlexandriaId = response.getId(); client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); QueryBuilder qb = QueryBuilders.geoDistanceQuery("location") - .point(29.976, 31.131) - .distance(10, DistanceUnit.MILES); + .point(29.976, 31.131) + .distance(10, DistanceUnit.MILES); SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(hit -> { - return hit.getId(); - }) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(lighthouseOfAlexandriaId)); } @@ -144,30 +139,28 @@ public class GeoQueriesTest { public void givenGeoPointData_whenExecutedGeoPolygonQuery_thenResultNonEmpty() { String jsonObject = "{\"name\":\"The Great Rann of Kutch\",\"location\":[69.859741,23.733732]}"; IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) - .setSource(jsonObject) - .get(); + .setSource(jsonObject) + .get(); String greatRannOfKutchid = response.getId(); client.admin() - .indices() - .prepareRefresh(WONDERS_OF_WORLD) - .get(); + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); QueryBuilder qb = QueryBuilders.geoPolygonQuery("location") - .addPoint(22.733, 68.859) - .addPoint(24.733, 68.859) - .addPoint(23, 70.859); + .addPoint(22.733, 68.859) + .addPoint(24.733, 68.859) + .addPoint(23, 70.859); SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) - .setTypes(WONDERS) - .setQuery(qb) - .execute() - .actionGet(); + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); List ids = Arrays.stream(searchResponse.getHits() - .getHits()) - .map(hit -> { - return hit.getId(); - }) - .collect(Collectors.toList()); + .getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); assertTrue(ids.contains(greatRannOfKutchid)); } @@ -175,5 +168,4 @@ public class GeoQueriesTest { public void destroy() { elasticsearchTemplate.deleteIndex(WONDERS_OF_WORLD); } - } diff --git a/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchIntegrationTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchIntegrationTest.java index 1280c8e1de..be31de724d 100644 --- a/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchIntegrationTest.java +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchIntegrationTest.java @@ -1,15 +1,9 @@ package com.baeldung.spring.data.es; -import static java.util.Arrays.asList; -import static org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND; -import static org.elasticsearch.index.query.QueryBuilders.fuzzyQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchQuery; -import static org.elasticsearch.index.query.QueryBuilders.regexpQuery; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.List; - +import com.baeldung.spring.data.es.config.Config; +import com.baeldung.spring.data.es.model.Article; +import com.baeldung.spring.data.es.model.Author; +import com.baeldung.spring.data.es.service.ArticleService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -22,10 +16,15 @@ import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.data.es.config.Config; -import com.baeldung.spring.data.es.model.Article; -import com.baeldung.spring.data.es.model.Author; -import com.baeldung.spring.data.es.service.ArticleService; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND; +import static org.elasticsearch.index.query.QueryBuilders.fuzzyQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; +import static org.elasticsearch.index.query.QueryBuilders.regexpQuery; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) @@ -72,21 +71,24 @@ public class ElasticSearchIntegrationTest { @Test public void givenPersistedArticles_whenSearchByAuthorsName_thenRightFound() { - final Page
articleByAuthorName = articleService.findByAuthorName(johnSmith.getName(), new PageRequest(0, 10)); + final Page
articleByAuthorName = articleService + .findByAuthorName(johnSmith.getName(), new PageRequest(0, 10)); assertEquals(2L, articleByAuthorName.getTotalElements()); } @Test public void givenCustomQuery_whenSearchByAuthorsName_thenArticleIsFound() { - final Page
articleByAuthorName = articleService.findByAuthorNameUsingCustomQuery("John Smith", new PageRequest(0, 10)); + final Page
articleByAuthorName = articleService + .findByAuthorNameUsingCustomQuery("John Smith", new PageRequest(0, 10)); assertEquals(3L, articleByAuthorName.getTotalElements()); } @Test public void givenPersistedArticles_whenUseRegexQuery_thenRightArticlesFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withFilter(regexpQuery("title", ".*data.*")).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder().withFilter(regexpQuery("title", ".*data.*")) + .build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); @@ -112,7 +114,8 @@ public class ElasticSearchIntegrationTest { final String articleTitle = "Spring Data Elasticsearch"; - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%")).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", articleTitle).minimumShouldMatch("75%")).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); final long count = articleService.count(); @@ -124,7 +127,8 @@ public class ElasticSearchIntegrationTest { @Test public void givenSavedDoc_whenOneTermMatches_thenFindByTitle() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(AND)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "Search engines").operator(AND)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); } diff --git a/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryIntegrationTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryIntegrationTest.java index cc4bce0c75..7a9ac2de06 100644 --- a/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryIntegrationTest.java +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/spring/data/es/ElasticSearchQueryIntegrationTest.java @@ -1,20 +1,9 @@ package com.baeldung.spring.data.es; -import static java.util.Arrays.asList; -import static java.util.stream.Collectors.toList; -import static org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND; -import static org.elasticsearch.index.query.QueryBuilders.boolQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchQuery; -import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; -import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; -import static org.elasticsearch.index.query.QueryBuilders.termQuery; -import static org.junit.Assert.assertEquals; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - +import com.baeldung.spring.data.es.config.Config; +import com.baeldung.spring.data.es.model.Article; +import com.baeldung.spring.data.es.model.Author; +import com.baeldung.spring.data.es.service.ArticleService; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.unit.Fuzziness; @@ -22,6 +11,7 @@ import org.elasticsearch.index.query.MultiMatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder; @@ -35,10 +25,20 @@ import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.baeldung.spring.data.es.config.Config; -import com.baeldung.spring.data.es.model.Article; -import com.baeldung.spring.data.es.model.Author; -import com.baeldung.spring.data.es.service.ArticleService; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toList; +import static org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; +import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; +import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; +import static org.elasticsearch.index.query.QueryBuilders.termQuery; +import static org.junit.Assert.assertEquals; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = Config.class) @@ -86,14 +86,16 @@ public class ElasticSearchQueryIntegrationTest { @Test public void givenFullTitle_whenRunMatchQuery_thenDocIsFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Search engines").operator(AND)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "Search engines").operator(AND)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); } @Test public void givenOneTermFromTitle_whenRunMatchQuery_thenDocIsFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "Engines Solutions")).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "Engines Solutions")).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); assertEquals("Search engines", articles.get(0).getTitle()); @@ -101,18 +103,21 @@ public class ElasticSearchQueryIntegrationTest { @Test public void givenPartTitle_whenRunMatchQuery_thenDocIsFound() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "elasticsearch data")).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "elasticsearch data")).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(3, articles.size()); } @Test public void givenFullTitle_whenRunMatchQueryOnVerbatimField_thenDocIsFound() { - SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch")).build(); + SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch")).build(); List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); - searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About")).build(); + searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title.verbatim", "Second Article About")) + .build(); articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(0, articles.size()); } @@ -130,38 +135,48 @@ public class ElasticSearchQueryIntegrationTest { @Test public void givenAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTokenCountsSeparately() { final TermsBuilder aggregation = AggregationBuilders.terms("top_tags").field("title"); - final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation).execute().actionGet(); + final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation) + .execute().actionGet(); final Map results = response.getAggregations().asMap(); final StringTerms topTags = (StringTerms) results.get("top_tags"); - final List keys = topTags.getBuckets().stream().map(b -> b.getKeyAsString()).collect(toList()); - Collections.sort(keys); + final List keys = topTags.getBuckets().stream() + .map(MultiBucketsAggregation.Bucket::getKeyAsString) + .sorted() + .collect(toList()); assertEquals(asList("about", "article", "data", "elasticsearch", "engines", "search", "second", "spring", "tutorial"), keys); } @Test public void givenNotAnalyzedQuery_whenMakeAggregationOnTermCount_thenEachTermCountsIndividually() { - final TermsBuilder aggregation = AggregationBuilders.terms("top_tags").field("tags").order(Terms.Order.aggregation("_count", false)); - final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation).execute().actionGet(); + final TermsBuilder aggregation = AggregationBuilders.terms("top_tags").field("tags") + .order(Terms.Order.aggregation("_count", false)); + final SearchResponse response = client.prepareSearch("blog").setTypes("article").addAggregation(aggregation) + .execute().actionGet(); final Map results = response.getAggregations().asMap(); final StringTerms topTags = (StringTerms) results.get("top_tags"); - final List keys = topTags.getBuckets().stream().map(b -> b.getKeyAsString()).collect(toList()); + final List keys = topTags.getBuckets().stream() + .map(MultiBucketsAggregation.Bucket::getKeyAsString) + .collect(toList()); assertEquals(asList("elasticsearch", "spring data", "search engines", "tutorial"), keys); } @Test public void givenNotExactPhrase_whenUseSlop_thenQueryMatches() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); } @Test public void givenPhraseWithType_whenUseFuzziness_thenQueryMatches() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", "spring date elasticserch").operator(AND).fuzziness(Fuzziness.ONE).prefixLength(3)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchQuery("title", "spring date elasticserch").operator(AND).fuzziness(Fuzziness.ONE) + .prefixLength(3)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); @@ -169,7 +184,9 @@ public class ElasticSearchQueryIntegrationTest { @Test public void givenMultimatchQuery_whenDoSearch_thenAllProvidedFieldsMatch() { - final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(multiMatchQuery("tutorial").field("title").field("tags").type(MultiMatchQueryBuilder.Type.BEST_FIELDS)).build(); + final SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(multiMatchQuery("tutorial").field("title").field("tags") + .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)).build(); final List
articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); assertEquals(2, articles.size()); From 99040c49f15daf4ee416094db71d783d47bf112a Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sat, 20 Jan 2018 15:27:28 +0200 Subject: [PATCH 5/9] remove gson --- spring-cloud/spring-cloud-security/personresource/pom.xml | 4 ---- .../java/com/baeldung/controller/PersonInfoController.java | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/spring-cloud/spring-cloud-security/personresource/pom.xml b/spring-cloud/spring-cloud-security/personresource/pom.xml index 783fd41e66..ca1ff82515 100644 --- a/spring-cloud/spring-cloud-security/personresource/pom.xml +++ b/spring-cloud/spring-cloud-security/personresource/pom.xml @@ -43,10 +43,6 @@ org.springframework.security spring-security-jwt - - com.google.code.gson - gson - diff --git a/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java index 15ffc557fc..f2beec927d 100644 --- a/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java +++ b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java @@ -14,8 +14,6 @@ public class PersonInfoController { @GetMapping("/personResource") @PreAuthorize("hasAnyRole('ADMIN', 'USER')") public @ResponseBody String personInfo() { - Gson gson = new Gson(); - String person = gson.toJson(new Person("abir", "Dhaka", "Bangladesh", 29, "Male")); - return person; + return new Person("abir", "Dhaka", "Bangladesh", 29, "Male"); } } \ No newline at end of file From 43b26b0ef93a7a91a806a6625e2c614f1e0bb870 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sat, 20 Jan 2018 15:30:28 +0200 Subject: [PATCH 6/9] remove gson --- .../java/com/baeldung/controller/PersonInfoController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java index f2beec927d..9e5420da5a 100644 --- a/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java +++ b/spring-cloud/spring-cloud-security/personresource/src/main/java/com/baeldung/controller/PersonInfoController.java @@ -6,14 +6,13 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import com.baeldung.model.Person; -import com.google.gson.Gson; @RestController public class PersonInfoController { @GetMapping("/personResource") @PreAuthorize("hasAnyRole('ADMIN', 'USER')") - public @ResponseBody String personInfo() { + public @ResponseBody Person personInfo() { return new Person("abir", "Dhaka", "Bangladesh", 29, "Male"); } } \ No newline at end of file From 7bd1fbf7349426fe730c8f483a2f996399736d7d Mon Sep 17 00:00:00 2001 From: Grzegorz Piwowarek Date: Sat, 20 Jan 2018 15:05:41 +0100 Subject: [PATCH 7/9] Refactor Trie (#3469) --- .../src/main/java/com/baeldung/trie/Trie.java | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/core-java/src/main/java/com/baeldung/trie/Trie.java b/core-java/src/main/java/com/baeldung/trie/Trie.java index 2c4119df71..dd51d97b2d 100644 --- a/core-java/src/main/java/com/baeldung/trie/Trie.java +++ b/core-java/src/main/java/com/baeldung/trie/Trie.java @@ -9,26 +9,23 @@ public class Trie { public void insert(String word) { TrieNode current = root; + for (int i = 0; i < word.length(); i++) { - char ch = word.charAt(i); - TrieNode node = current.getChildren() - .get(ch); - if (node == null) { - node = new TrieNode(); - current.getChildren() - .put(ch, node); - } - current = node; + current = current.getChildren().computeIfAbsent(word.charAt(i), c -> new TrieNode()); } current.setEndOfWord(true); } - public boolean find(String word) { + public boolean delete(String word) { + return delete(root, word, 0); + } + + public boolean containsNode(String word) { TrieNode current = root; + for (int i = 0; i < word.length(); i++) { char ch = word.charAt(i); - TrieNode node = current.getChildren() - .get(ch); + TrieNode node = current.getChildren().get(ch); if (node == null) { return false; } @@ -37,8 +34,8 @@ public class Trie { return current.isEndOfWord(); } - public void delete(String word) { - delete(root, word, 0); + public boolean isEmpty() { + return root == null; } private boolean delete(TrieNode current, String word, int index) { @@ -47,30 +44,19 @@ public class Trie { return false; } current.setEndOfWord(false); - return current.getChildren() - .size() == 0; + return current.getChildren().isEmpty(); } char ch = word.charAt(index); - TrieNode node = current.getChildren() - .get(ch); + TrieNode node = current.getChildren().get(ch); if (node == null) { return false; } boolean shouldDeleteCurrentNode = delete(node, word, index + 1); if (shouldDeleteCurrentNode) { - current.getChildren() - .remove(ch); + current.getChildren().remove(ch); return current.getChildren().isEmpty(); } return false; } - - public boolean containsNode(String word) { - return find(word); - } - - public boolean isEmpty() { - return root == null; - } } \ No newline at end of file From 740073b6ad52550ee0f799a913d46f9ba5d17d94 Mon Sep 17 00:00:00 2001 From: Miguel Rivero Date: Sat, 20 Jan 2018 16:35:48 +0100 Subject: [PATCH 8/9] BAEL-1230: Get Java Logs into the JSON Format (#3381) * Added code for BAEL-1230: Getting log in JSON format * Added code for BAEL-1230: Getting log in JSON format * Improved tests to check correct JSON format in logs * Renamed Test classes to make clear what they do * Fixed wrong indentation --- logging-modules/log4j2/pom.xml | 4 +- .../logging/log4j2/tests/JSONLayoutTest.java | 45 ++++++++++++++ .../log4j2/src/test/resources/log4j2.xml | 62 ++++++++++++------- logging-modules/logback/pom.xml | 20 +++++- .../com/baeldung/logback/JSONLayoutTest.java | 45 ++++++++++++++ .../src/test/resources/logback-test.xml | 25 ++++++-- 6 files changed, 169 insertions(+), 32 deletions(-) create mode 100644 logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/JSONLayoutTest.java create mode 100644 logging-modules/logback/src/test/java/com/baeldung/logback/JSONLayoutTest.java diff --git a/logging-modules/log4j2/pom.xml b/logging-modules/log4j2/pom.xml index 48608fbc80..46b8b80597 100644 --- a/logging-modules/log4j2/pom.xml +++ b/logging-modules/log4j2/pom.xml @@ -59,10 +59,10 @@ - 2.8.5 + 2.9.3 1.4.193 2.1.1 - 2.7 + 2.10.0 diff --git a/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/JSONLayoutTest.java b/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/JSONLayoutTest.java new file mode 100644 index 0000000000..9493c32094 --- /dev/null +++ b/logging-modules/log4j2/src/test/java/com/baeldung/logging/log4j2/tests/JSONLayoutTest.java @@ -0,0 +1,45 @@ +package com.baeldung.logging.log4j2.tests; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Before; +import org.junit.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JSONLayoutTest { + + private static Logger logger; + private ByteArrayOutputStream consoleOutput = new ByteArrayOutputStream(); + private PrintStream ps = new PrintStream(consoleOutput); + + @Before + public void setUp() { + // Redirect console output to our stream + System.setOut(ps); + logger = LogManager.getLogger("CONSOLE_JSON_APPENDER"); + } + + @Test + public void whenLogLayoutInJSON_thenOutputIsCorrectJSON() { + logger.debug("Debug message"); + String currentLog = consoleOutput.toString(); + assertTrue(!currentLog.isEmpty() && isValidJSON(currentLog)); + } + + public static boolean isValidJSON(String jsonInString) { + try { + final ObjectMapper mapper = new ObjectMapper(); + mapper.readTree(jsonInString); + return true; + } catch (IOException e) { + return false; + } + } +} \ No newline at end of file diff --git a/logging-modules/log4j2/src/test/resources/log4j2.xml b/logging-modules/log4j2/src/test/resources/log4j2.xml index 21fd1da446..4dcb7cce5a 100644 --- a/logging-modules/log4j2/src/test/resources/log4j2.xml +++ b/logging-modules/log4j2/src/test/resources/log4j2.xml @@ -1,16 +1,25 @@ - + - + - + - + + + + + + @@ -19,53 +28,58 @@ - + - + - + - + - + - + - + - + - + + + + diff --git a/logging-modules/logback/pom.xml b/logging-modules/logback/pom.xml index 8169134442..cd0d3758cc 100644 --- a/logging-modules/logback/pom.xml +++ b/logging-modules/logback/pom.xml @@ -1,6 +1,6 @@ 4.0.0 @@ -12,6 +12,8 @@ UTF-8 1.2.3 + 0.1.5 + 2.9.3 @@ -28,7 +30,21 @@ logback-classic ${logback.version} - + + ch.qos.logback.contrib + logback-json-classic + ${logback.contrib.version} + + + ch.qos.logback.contrib + logback-jackson + ${logback.contrib.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + diff --git a/logging-modules/logback/src/test/java/com/baeldung/logback/JSONLayoutTest.java b/logging-modules/logback/src/test/java/com/baeldung/logback/JSONLayoutTest.java new file mode 100644 index 0000000000..ca3c4b3457 --- /dev/null +++ b/logging-modules/logback/src/test/java/com/baeldung/logback/JSONLayoutTest.java @@ -0,0 +1,45 @@ +package com.baeldung.logback; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JSONLayoutTest { + + private static Logger logger; + private ByteArrayOutputStream consoleOutput = new ByteArrayOutputStream(); + private PrintStream ps = new PrintStream(consoleOutput); + + @Before + public void setUp() { + // Redirect console output to our stream + System.setOut(ps); + logger = LoggerFactory.getLogger("jsonLogger"); + } + + @Test + public void whenLogLayoutInJSON_thenOutputIsCorrectJSON() { + logger.debug("Debug message"); + String currentLog = consoleOutput.toString(); + assertTrue(!currentLog.isEmpty() && isValidJSON(currentLog)); + } + + public static boolean isValidJSON(String jsonInString) { + try { + final ObjectMapper mapper = new ObjectMapper(); + mapper.readTree(jsonInString); + return true; + } catch (IOException e) { + return false; + } + } +} diff --git a/logging-modules/logback/src/test/resources/logback-test.xml b/logging-modules/logback/src/test/resources/logback-test.xml index 8254e6ac80..df81f18cc2 100644 --- a/logging-modules/logback/src/test/resources/logback-test.xml +++ b/logging-modules/logback/src/test/resources/logback-test.xml @@ -1,14 +1,31 @@ - + + + test - + + + # JSON appender + + + + true + + yyyy-MM-dd' 'HH:mm:ss.SSS + + + + + + - - + + \ No newline at end of file From 6923d3accd1a9c4a235e1dc1fa73281c0227d35a Mon Sep 17 00:00:00 2001 From: Seun Matt Date: Sat, 20 Jan 2018 18:18:01 +0100 Subject: [PATCH 9/9] Example Code for Exceptions in Netty (#3406) * added updated example codes * updated example code StringToCharStream * deleted StringToCharStream.java locally * removed redundant file * added code for apache commons collection SetUtils * refactored example code * added example code for bytebuddy * added example code for PCollections * update pom * refactored tests for PCollections * spring security xml config * spring security xml config * remove redundant comment * example code for apache-shiro * updated example code for Vavr Collections * updated Vavr's Collection example * updated Vavr Collection file * updated example code for Apache Shiro * updated Vavr Collections example * added example code for N1QL * update example code for N1QL * added integration test for N1QL * update N1QL Example code * update the N1QL example Code * rename module to couchbase * rename module to couchbase * change module name in parent module and pom * added cas-server module * added cas secured app for Spring SSO with CAS * refactor cas modules into cas folder * updated files * removed redundant files * refactor the config for cas-server * added sql file to generate tables and database * added source code for VRaptor * update source code for VRaptor article * update pom and reformat code * add example code for BAEL-1439 * updated code for netty exceptions --- .../com/baeldung/netty/ChannelHandlerA.java | 22 +++++++++ .../com/baeldung/netty/ChannelHandlerB.java | 20 ++++++++ .../java/com/baeldung/netty/NettyServerB.java | 47 +++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 libraries/src/main/java/com/baeldung/netty/ChannelHandlerA.java create mode 100644 libraries/src/main/java/com/baeldung/netty/ChannelHandlerB.java create mode 100644 libraries/src/main/java/com/baeldung/netty/NettyServerB.java diff --git a/libraries/src/main/java/com/baeldung/netty/ChannelHandlerA.java b/libraries/src/main/java/com/baeldung/netty/ChannelHandlerA.java new file mode 100644 index 0000000000..c919bdb09c --- /dev/null +++ b/libraries/src/main/java/com/baeldung/netty/ChannelHandlerA.java @@ -0,0 +1,22 @@ +package com.baeldung.netty; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +import java.util.logging.Logger; + +public class ChannelHandlerA extends ChannelInboundHandlerAdapter { + + private Logger logger = Logger.getLogger(getClass().getName()); + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + throw new Exception("Ooops"); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.info("Exception Occurred in ChannelHandler A"); + ctx.fireExceptionCaught(cause); + } +} diff --git a/libraries/src/main/java/com/baeldung/netty/ChannelHandlerB.java b/libraries/src/main/java/com/baeldung/netty/ChannelHandlerB.java new file mode 100644 index 0000000000..c5bdeb1013 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/netty/ChannelHandlerB.java @@ -0,0 +1,20 @@ +package com.baeldung.netty; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +import java.util.logging.Logger; + + +public class ChannelHandlerB extends ChannelInboundHandlerAdapter { + + private Logger logger = Logger.getLogger(getClass().getName()); + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.info("Exception Handled in ChannelHandler B"); + logger.info(cause.getLocalizedMessage()); + //do more exception handling + ctx.close(); + } +} diff --git a/libraries/src/main/java/com/baeldung/netty/NettyServerB.java b/libraries/src/main/java/com/baeldung/netty/NettyServerB.java new file mode 100644 index 0000000000..c8004623c2 --- /dev/null +++ b/libraries/src/main/java/com/baeldung/netty/NettyServerB.java @@ -0,0 +1,47 @@ +package com.baeldung.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +public class NettyServerB { + + private int port; + + private NettyServerB(int port) { + this.port = port; + } + + private void run() throws Exception { + + EventLoopGroup bossGroup = new NioEventLoopGroup(); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInitializer() { + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new ChannelHandlerA(), new ChannelHandlerB()); + } + }) + .option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + ChannelFuture f = b.bind(port).sync(); // (7) + f.channel().closeFuture().sync(); + } finally { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + new NettyServerB(8080).run(); + } +}