diff --git a/apache-httpclient/pom.xml b/apache-httpclient/pom.xml index c371d1fc06..5c3ea5b3b3 100644 --- a/apache-httpclient/pom.xml +++ b/apache-httpclient/pom.xml @@ -90,6 +90,12 @@ + + org.mock-server + mockserver-netty + ${mockserver.version} + + com.github.tomakehurst wiremock @@ -112,6 +118,7 @@ 4.1.4 + 5.6.1 2.5.1 4.5.8 diff --git a/apache-httpclient/src/test/java/com/baeldung/httpclient/GetRequestMockServer.java b/apache-httpclient/src/test/java/com/baeldung/httpclient/GetRequestMockServer.java new file mode 100644 index 0000000000..f65558906f --- /dev/null +++ b/apache-httpclient/src/test/java/com/baeldung/httpclient/GetRequestMockServer.java @@ -0,0 +1,80 @@ +package com.baeldung.httpclient; + +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.matchers.Times.exactly; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URISyntaxException; + +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.mockserver.client.MockServerClient; +import org.mockserver.integration.ClientAndServer; + +public class GetRequestMockServer { + + public static ClientAndServer mockServer; + public static String serviceOneUrl; + public static String serviceTwoUrl; + + private static int serverPort; + + public static final String SERVER_ADDRESS = "127.0.0.1"; + public static final String PATH_ONE = "/test1"; + public static final String PATH_TWO = "/test2"; + public static final String METHOD = "GET"; + + @BeforeAll + static void startServer() throws IOException, URISyntaxException { + //serverPort = getFreePort(); + serverPort = 8080; + System.out.println("Free port "+serverPort); + serviceOneUrl = "http://" + SERVER_ADDRESS + ":" + serverPort + PATH_ONE; + serviceTwoUrl = "http://" + SERVER_ADDRESS + ":" + serverPort + PATH_TWO; + mockServer = startClientAndServer(serverPort); + mockGetRequest(); + } + + @AfterAll + static void stopServer() { + mockServer.stop(); + } + + private static void mockGetRequest() { + new MockServerClient(SERVER_ADDRESS, serverPort) + .when( + request() + .withPath(PATH_ONE) + .withMethod(METHOD), + exactly(5) + ) + .respond( + response() + .withStatusCode(HttpStatus.SC_OK) + .withBody("{\"status\":\"ok\"}") + ); + new MockServerClient(SERVER_ADDRESS, serverPort) + .when( + request() + .withPath(PATH_TWO) + .withMethod(METHOD), + exactly(1) + ) + .respond( + response() + .withStatusCode(HttpStatus.SC_OK) + .withBody("{\"status\":\"ok\"}") + ); + } + + private static int getFreePort () throws IOException { + try (ServerSocket serverSocket = new ServerSocket(0)) { + return serverSocket.getLocalPort(); + } + } + +} diff --git a/apache-httpclient/src/test/java/com/baeldung/httpclient/HttpAsyncClientLiveTest.java b/apache-httpclient/src/test/java/com/baeldung/httpclient/HttpAsyncClientLiveTest.java index ab0e4e6308..f4b9266d7e 100644 --- a/apache-httpclient/src/test/java/com/baeldung/httpclient/HttpAsyncClientLiveTest.java +++ b/apache-httpclient/src/test/java/com/baeldung/httpclient/HttpAsyncClientLiveTest.java @@ -38,7 +38,7 @@ import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.ssl.TrustStrategy; -class HttpAsyncClientLiveTest { +class HttpAsyncClientLiveTest extends GetRequestMockServer { private static final String HOST = "http://www.google.com"; private static final String HOST_WITH_SSL = "https://mms.nw.ru/"; diff --git a/apache-kafka-2/README.md b/apache-kafka-2/README.md index e86504d605..9a5f6e15ae 100644 --- a/apache-kafka-2/README.md +++ b/apache-kafka-2/README.md @@ -9,3 +9,5 @@ You can build the project from the command line using: *mvn clean install*, or i - [Guide to Check if Apache Kafka Server Is Running](https://www.baeldung.com/apache-kafka-check-server-is-running) - [Add Custom Headers to a Kafka Message](https://www.baeldung.com/java-kafka-custom-headers) - [Get Last N Messages in Apache Kafka Topic](https://www.baeldung.com/java-apache-kafka-get-last-n-messages) +- [Is a Key Required as Part of Sending Messages to Kafka?](https://www.baeldung.com/java-kafka-message-key) +- [Read Data From the Beginning Using Kafka Consumer API](https://www.baeldung.com/java-kafka-consumer-api-read) diff --git a/apache-kafka-2/src/main/java/com/baeldung/kafka/consumer/ConsumeFromBeginning.java b/apache-kafka-2/src/main/java/com/baeldung/kafka/consumer/ConsumeFromBeginning.java new file mode 100644 index 0000000000..569c5aa9e9 --- /dev/null +++ b/apache-kafka-2/src/main/java/com/baeldung/kafka/consumer/ConsumeFromBeginning.java @@ -0,0 +1,81 @@ +package com.baeldung.kafka.consumer; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Properties; +import java.util.UUID; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConsumeFromBeginning { + + private static Logger logger = LoggerFactory.getLogger(ConsumeFromBeginning.class); + + private static String TOPIC = "baeldung"; + private static int messagesInTopic = 10; + + private static KafkaProducer producer; + private static KafkaConsumer consumer; + + public static void main(String[] args) { + setup(); + + publishMessages(); + + consumeFromBeginning(); + } + + private static void consumeFromBeginning() { + consumer.subscribe(Arrays.asList(TOPIC)); + + ConsumerRecords records = consumer.poll(Duration.ofSeconds(10)); + + for (ConsumerRecord record : records) { + logger.info(record.value()); + } + + consumer.seekToBeginning(consumer.assignment()); + + records = consumer.poll(Duration.ofSeconds(10)); + + for (ConsumerRecord record : records) { + logger.info(record.value()); + } + } + + private static void publishMessages() { + for (int i = 1; i <= messagesInTopic; i++) { + ProducerRecord record = new ProducerRecord<>(TOPIC, String.valueOf(i)); + producer.send(record); + } + } + + private static void setup() { + Properties producerProperties = new Properties(); + producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + + Properties consumerProperties = new Properties(); + consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, UUID.randomUUID() + .toString()); + + producer = new KafkaProducer<>(producerProperties); + consumer = new KafkaConsumer<>(consumerProperties); + } + +} diff --git a/apache-kafka-2/src/main/java/com/baeldung/kafka/message/MessageWithKey.java b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/MessageWithKey.java new file mode 100644 index 0000000000..b03c1e1adc --- /dev/null +++ b/apache-kafka-2/src/main/java/com/baeldung/kafka/message/MessageWithKey.java @@ -0,0 +1,106 @@ +package com.baeldung.kafka.message; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.apache.kafka.clients.admin.Admin; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageWithKey { + + private static Logger logger = LoggerFactory.getLogger(MessageWithKey.class); + + private static String TOPIC = "baeldung"; + private static int PARTITIONS = 5; + private static short REPLICATION_FACTOR = 1; + + private static String MESSAGE_KEY = "message-key"; + + private static Admin admin; + private static KafkaProducer producer; + private static KafkaConsumer consumer; + + public static void main(String[] args) throws ExecutionException, InterruptedException { + setup(); + + publishMessagesWithoutKey(); + + consumeMessages(); + + publishMessagesWithKey(); + + consumeMessages(); + } + + private static void consumeMessages() { + consumer.subscribe(Arrays.asList(TOPIC)); + + ConsumerRecords records = consumer.poll(Duration.ofSeconds(5)); + for (ConsumerRecord record : records) { + logger.info("Key : {}, Value : {}", record.key(), record.value()); + } + } + + private static void publishMessagesWithKey() throws ExecutionException, InterruptedException { + for (int i = 1; i <= 10; i++) { + ProducerRecord record = new ProducerRecord<>(TOPIC, MESSAGE_KEY, String.valueOf(i)); + Future future = producer.send(record); + RecordMetadata metadata = future.get(); + + logger.info(String.valueOf(metadata.partition())); + } + } + + private static void publishMessagesWithoutKey() throws ExecutionException, InterruptedException { + for (int i = 1; i <= 10; i++) { + ProducerRecord record = new ProducerRecord<>(TOPIC, String.valueOf(i)); + Future future = producer.send(record); + RecordMetadata metadata = future.get(); + + logger.info(String.valueOf(metadata.partition())); + } + } + + private static void setup() { + Properties adminProperties = new Properties(); + adminProperties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + + Properties producerProperties = new Properties(); + producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + + Properties consumerProperties = new Properties(); + consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, UUID.randomUUID() + .toString()); + + admin = Admin.create(adminProperties); + producer = new KafkaProducer<>(producerProperties); + consumer = new KafkaConsumer<>(consumerProperties); + + admin.createTopics(Collections.singleton(new NewTopic(TOPIC, PARTITIONS, REPLICATION_FACTOR))); + } + +} \ No newline at end of file diff --git a/apache-kafka-2/src/test/java/com/baeldung/kafka/consumer/ConsumeFromBeginningLiveTest.java b/apache-kafka-2/src/test/java/com/baeldung/kafka/consumer/ConsumeFromBeginningLiveTest.java new file mode 100644 index 0000000000..6bfba1eca9 --- /dev/null +++ b/apache-kafka-2/src/test/java/com/baeldung/kafka/consumer/ConsumeFromBeginningLiveTest.java @@ -0,0 +1,109 @@ +package com.baeldung.kafka.consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +// This live test needs a Docker Daemon running so that a kafka container can be created + +@Testcontainers +public class ConsumeFromBeginningLiveTest { + + private static Logger logger = LoggerFactory.getLogger(ConsumeFromBeginningLiveTest.class); + + private static String TOPIC = "baeldung"; + private static int messagesInTopic = 10; + + private static KafkaProducer producer; + private static KafkaConsumer consumer; + + @Container + private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")); + + @BeforeAll + static void setup() { + KAFKA_CONTAINER.addExposedPort(9092); + + Properties producerProperties = new Properties(); + producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + + Properties consumerProperties = new Properties(); + consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, UUID.randomUUID() + .toString()); + + producer = new KafkaProducer<>(producerProperties); + consumer = new KafkaConsumer<>(consumerProperties); + } + + private static void publishMessages() throws ExecutionException, InterruptedException { + for (int i = 1; i <= messagesInTopic; i++) { + ProducerRecord record = new ProducerRecord<>(TOPIC, String.valueOf(i)); + producer.send(record) + .get(); + } + } + + @AfterAll + static void destroy() { + KAFKA_CONTAINER.stop(); + } + + @Test + void givenMessages_whenConsumedFromBeginning_thenCheckIfConsumedFromBeginning() throws ExecutionException, InterruptedException { + + publishMessages(); + + consumer.subscribe(Arrays.asList(TOPIC)); + + ConsumerRecords records = consumer.poll(Duration.ofSeconds(10)); + + int messageCount = 0; + for (ConsumerRecord record : records) { + logger.info(record.value()); + messageCount++; + } + + assertEquals(messagesInTopic, messageCount); + + consumer.seekToBeginning(consumer.assignment()); + + records = consumer.poll(Duration.ofSeconds(10)); + + messageCount = 0; + for (ConsumerRecord record : records) { + logger.info(record.value()); + messageCount++; + } + + assertEquals(messagesInTopic, messageCount); + } +} diff --git a/apache-kafka-2/src/test/java/com/baeldung/kafka/message/MessageWithKeyLiveTest.java b/apache-kafka-2/src/test/java/com/baeldung/kafka/message/MessageWithKeyLiveTest.java new file mode 100644 index 0000000000..093dc629cb --- /dev/null +++ b/apache-kafka-2/src/test/java/com/baeldung/kafka/message/MessageWithKeyLiveTest.java @@ -0,0 +1,130 @@ +package com.baeldung.kafka.message; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.apache.kafka.clients.admin.Admin; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +// This live test needs a Docker Daemon running so that a kafka container can be created + +@Testcontainers +public class MessageWithKeyLiveTest { + + private static String TOPIC = "baeldung"; + private static int PARTITIONS = 5; + private static short REPLICATION_FACTOR = 1; + + private static String MESSAGE_KEY = "message-key"; + private static String MESSAGE_VALUE = "Hello World"; + + private static Admin admin; + private static KafkaProducer producer; + private static KafkaConsumer consumer; + + @Container + private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")); + + @BeforeAll + static void setup() { + KAFKA_CONTAINER.addExposedPort(9092); + + Properties adminProperties = new Properties(); + adminProperties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + + Properties producerProperties = new Properties(); + producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + + Properties consumerProperties = new Properties(); + consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers()); + consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, UUID.randomUUID() + .toString()); + + admin = Admin.create(adminProperties); + producer = new KafkaProducer<>(producerProperties); + consumer = new KafkaConsumer<>(consumerProperties); + + admin.createTopics(Collections.singleton(new NewTopic(TOPIC, PARTITIONS, REPLICATION_FACTOR))); + } + + @AfterAll + static void destroy() { + KAFKA_CONTAINER.stop(); + } + + @Test + void givenAMessageWithKey_whenPublishedToKafkaAndConsumed_thenCheckForKey() throws ExecutionException, InterruptedException { + + ProducerRecord producerRecord = new ProducerRecord<>(TOPIC, MESSAGE_KEY, MESSAGE_VALUE); + Future future = producer.send(producerRecord); + + RecordMetadata metadata = future.get(); + + assertNotNull(metadata); + + consumer.subscribe(Arrays.asList(TOPIC)); + + ConsumerRecords records = consumer.poll(Duration.ofSeconds(5)); + for (ConsumerRecord consumerRecord : records) { + assertEquals(MESSAGE_KEY, consumerRecord.key()); + assertEquals(MESSAGE_VALUE, consumerRecord.value()); + } + } + + @Test + void givenAListOfMessageWithKeys_whenPublishedToKafka_thenCheckedIfPublishedToSamePartition() throws ExecutionException, InterruptedException { + + boolean isSamePartition = true; + int partition = 0; + + for (int i = 1; i <= 10; i++) { + ProducerRecord producerRecord = new ProducerRecord<>(TOPIC, MESSAGE_KEY, MESSAGE_VALUE); + Future future = producer.send(producerRecord); + + RecordMetadata metadata = future.get(); + + assertNotNull(metadata); + if (i == 1) { + partition = metadata.partition(); + } else { + if (partition != metadata.partition()) { + isSamePartition = false; + } + } + } + + assertTrue(isSamePartition); + } +} diff --git a/apache-poi-2/pom.xml b/apache-poi-2/pom.xml index af959292fa..9a01a76d73 100644 --- a/apache-poi-2/pom.xml +++ b/apache-poi-2/pom.xml @@ -1,7 +1,7 @@ - + 4.0.0 apache-poi-2 0.0.1-SNAPSHOT @@ -19,10 +19,15 @@ poi-ooxml ${poi.version} + + org.apache.poi + poi-scratchpad + ${poi.version} + - 5.2.0 + 5.2.3 \ No newline at end of file diff --git a/apache-poi-2/src/main/java/com/baeldung/poi/replacevariables/DocTextReplacer.java b/apache-poi-2/src/main/java/com/baeldung/poi/replacevariables/DocTextReplacer.java new file mode 100644 index 0000000000..f661551ce9 --- /dev/null +++ b/apache-poi-2/src/main/java/com/baeldung/poi/replacevariables/DocTextReplacer.java @@ -0,0 +1,38 @@ +package com.baeldung.poi.replacevariables; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.hwpf.usermodel.Range; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +public class DocTextReplacer { + + public void replaceText() throws IOException { + String filePath = getClass().getClassLoader() + .getResource("baeldung.doc") + .getPath(); + try (InputStream inputStream = new FileInputStream(filePath); POIFSFileSystem fileSystem = new POIFSFileSystem(inputStream)) { + HWPFDocument doc = new HWPFDocument(fileSystem); + doc = replaceText(doc, "Baeldung", "Hello"); + saveFile(filePath, doc); + doc.close(); + } + } + + private HWPFDocument replaceText(HWPFDocument doc, String originalText, String updatedText) { + Range range = doc.getRange(); + range.replaceText(originalText, updatedText); + return doc; + } + + private void saveFile(String filePath, HWPFDocument doc) throws IOException { + try (FileOutputStream out = new FileOutputStream(filePath)) { + doc.write(out); + } + } + +} diff --git a/apache-poi-2/src/main/java/com/baeldung/poi/replacevariables/DocxNaiveTextReplacer.java b/apache-poi-2/src/main/java/com/baeldung/poi/replacevariables/DocxNaiveTextReplacer.java new file mode 100644 index 0000000000..34c2bc43e5 --- /dev/null +++ b/apache-poi-2/src/main/java/com/baeldung/poi/replacevariables/DocxNaiveTextReplacer.java @@ -0,0 +1,63 @@ +package com.baeldung.poi.replacevariables; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; +import org.apache.poi.xwpf.usermodel.XWPFRun; +import org.apache.poi.xwpf.usermodel.XWPFTable; +import org.apache.poi.xwpf.usermodel.XWPFTableCell; +import org.apache.poi.xwpf.usermodel.XWPFTableRow; + +public class DocxNaiveTextReplacer { + + public void replaceText() throws IOException { + String filePath = getClass().getClassLoader() + .getResource("baeldung-copy.docx") + .getPath(); + try (InputStream inputStream = new FileInputStream(filePath)) { + XWPFDocument doc = new XWPFDocument(inputStream); + doc = replaceText(doc, "Baeldung", "Hello"); + saveFile(filePath, doc); + doc.close(); + } + } + + private XWPFDocument replaceText(XWPFDocument doc, String originalText, String updatedText) { + replaceTextInParagraphs(doc.getParagraphs(), originalText, updatedText); + for (XWPFTable tbl : doc.getTables()) { + for (XWPFTableRow row : tbl.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + replaceTextInParagraphs(cell.getParagraphs(), originalText, updatedText); + } + } + } + return doc; + } + + private void replaceTextInParagraphs(List paragraphs, String originalText, String updatedText) { + paragraphs.forEach(paragraph -> replaceTextInParagraph(paragraph, originalText, updatedText)); + } + + private void replaceTextInParagraph(XWPFParagraph paragraph, String originalText, String updatedText) { + List runs = paragraph.getRuns(); + for (XWPFRun run : runs) { + String text = run.getText(0); + if (text != null && text.contains(originalText)) { + String updatedRunText = text.replace(originalText, updatedText); + run.setText(updatedRunText, 0); + } + } + } + + private void saveFile(String filePath, XWPFDocument doc) throws IOException { + try (FileOutputStream out = new FileOutputStream(filePath)) { + doc.write(out); + } + } + +} diff --git a/apache-poi-2/src/main/java/com/baeldung/poi/replacevariables/DocxTextReplacer.java b/apache-poi-2/src/main/java/com/baeldung/poi/replacevariables/DocxTextReplacer.java new file mode 100644 index 0000000000..2d08d24a4e --- /dev/null +++ b/apache-poi-2/src/main/java/com/baeldung/poi/replacevariables/DocxTextReplacer.java @@ -0,0 +1,65 @@ +package com.baeldung.poi.replacevariables; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Objects; + +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; +import org.apache.poi.xwpf.usermodel.XWPFRun; +import org.apache.poi.xwpf.usermodel.XWPFTable; +import org.apache.poi.xwpf.usermodel.XWPFTableCell; +import org.apache.poi.xwpf.usermodel.XWPFTableRow; + +public class DocxTextReplacer { + + public void replaceText() throws IOException { + String filePath = getClass().getClassLoader() + .getResource("baeldung.docx") + .getPath(); + try (InputStream inputStream = new FileInputStream(filePath)) { + XWPFDocument doc = new XWPFDocument(inputStream); + doc = replaceText(doc, "Baeldung", "Hello"); + saveFile(filePath, doc); + doc.close(); + } + } + + private XWPFDocument replaceText(XWPFDocument doc, String originalText, String updatedText) { + replaceTextInParagraphs(doc.getParagraphs(), originalText, updatedText); + for (XWPFTable tbl : doc.getTables()) { + for (XWPFTableRow row : tbl.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + replaceTextInParagraphs(cell.getParagraphs(), originalText, updatedText); + } + } + } + return doc; + } + + private void replaceTextInParagraphs(List paragraphs, String originalText, String updatedText) { + paragraphs.forEach(paragraph -> replaceTextInParagraph(paragraph, originalText, updatedText)); + } + + private void replaceTextInParagraph(XWPFParagraph paragraph, String originalText, String updatedText) { + String paragraphText = paragraph.getParagraphText(); + if (paragraphText.contains(originalText)) { + String updatedParagraphText = paragraphText.replace(originalText, updatedText); + while (paragraph.getRuns().size() > 0) { + paragraph.removeRun(0); + } + XWPFRun newRun = paragraph.createRun(); + newRun.setText(updatedParagraphText); + } + } + + private void saveFile(String filePath, XWPFDocument doc) throws IOException { + try (FileOutputStream out = new FileOutputStream(filePath)) { + doc.write(out); + } + } + +} diff --git a/apache-poi-2/src/main/resources/baeldung-copy.docx b/apache-poi-2/src/main/resources/baeldung-copy.docx new file mode 100644 index 0000000000..2cb76e8ffd Binary files /dev/null and b/apache-poi-2/src/main/resources/baeldung-copy.docx differ diff --git a/apache-poi-2/src/main/resources/baeldung.doc b/apache-poi-2/src/main/resources/baeldung.doc new file mode 100644 index 0000000000..1b8474d65b Binary files /dev/null and b/apache-poi-2/src/main/resources/baeldung.doc differ diff --git a/apache-poi-2/src/main/resources/baeldung.docx b/apache-poi-2/src/main/resources/baeldung.docx new file mode 100644 index 0000000000..f0de4e057b Binary files /dev/null and b/apache-poi-2/src/main/resources/baeldung.docx differ diff --git a/apache-poi-2/src/test/java/com/baeldung/poi/replacevariables/DocTextReplacerUnitTest.java b/apache-poi-2/src/test/java/com/baeldung/poi/replacevariables/DocTextReplacerUnitTest.java new file mode 100644 index 0000000000..0c3d80a354 --- /dev/null +++ b/apache-poi-2/src/test/java/com/baeldung/poi/replacevariables/DocTextReplacerUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.poi.replacevariables; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; + +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.hwpf.extractor.WordExtractor; +import org.junit.jupiter.api.Test; + +class DocTextReplacerUnitTest { + + @Test + void whenReplaceText_ThenTextReplaced() throws IOException { + new DocTextReplacer().replaceText(); + + String filePath = getClass().getClassLoader() + .getResource("baeldung.doc") + .getPath(); + try (FileInputStream fis = new FileInputStream(filePath); HWPFDocument document = new HWPFDocument(fis); WordExtractor extractor = new WordExtractor(document)) { + long occurrencesOfHello = Arrays.stream(extractor.getText() + .split("\\s+")) + .filter(s -> s.contains("Hello")) + .count(); + assertEquals(5, occurrencesOfHello); + } + } + +} diff --git a/apache-poi-2/src/test/java/com/baeldung/poi/replacevariables/DocxNaiveTextReplacerUnitTest.java b/apache-poi-2/src/test/java/com/baeldung/poi/replacevariables/DocxNaiveTextReplacerUnitTest.java new file mode 100644 index 0000000000..324e63eb51 --- /dev/null +++ b/apache-poi-2/src/test/java/com/baeldung/poi/replacevariables/DocxNaiveTextReplacerUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.poi.replacevariables; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; + +import org.apache.poi.xwpf.extractor.XWPFWordExtractor; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.junit.jupiter.api.Test; + +class DocxNaiveTextReplacerUnitTest { + + @Test + void whenReplaceText_ThenTextReplaced() throws IOException { + new DocxNaiveTextReplacer().replaceText(); + + String filePath = getClass().getClassLoader() + .getResource("baeldung-copy.docx") + .getPath(); + try (FileInputStream fis = new FileInputStream(filePath); XWPFDocument document = new XWPFDocument(fis); XWPFWordExtractor extractor = new XWPFWordExtractor(document)) { + long occurrencesOfHello = Arrays.stream(extractor.getText() + .split("\\s+")) + .filter(s -> s.contains("Hello")) + .count(); + assertTrue(occurrencesOfHello < 5); + } + } + +} diff --git a/apache-poi-2/src/test/java/com/baeldung/poi/replacevariables/DocxTestReplacerUnitTest.java b/apache-poi-2/src/test/java/com/baeldung/poi/replacevariables/DocxTestReplacerUnitTest.java new file mode 100644 index 0000000000..d09f6b003d --- /dev/null +++ b/apache-poi-2/src/test/java/com/baeldung/poi/replacevariables/DocxTestReplacerUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.poi.replacevariables; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; + +import org.apache.poi.xwpf.extractor.XWPFWordExtractor; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.junit.jupiter.api.Test; + +class DocxTestReplacerUnitTest { + + @Test + void whenReplaceText_ThenTextReplaced() throws IOException { + new DocxTextReplacer().replaceText(); + + String filePath = getClass().getClassLoader() + .getResource("baeldung.docx") + .getPath(); + try (FileInputStream fis = new FileInputStream(filePath); XWPFDocument document = new XWPFDocument(fis); XWPFWordExtractor extractor = new XWPFWordExtractor(document)) { + long occurrencesOfHello = Arrays.stream(extractor.getText() + .split("\\s+")) + .filter(s -> s.contains("Hello")) + .count(); + assertEquals(5, occurrencesOfHello); + } + } + +} diff --git a/core-java-modules/core-java-collections-list-5/README.md b/core-java-modules/core-java-collections-list-5/README.md index 31688bc9b1..ff40ae3725 100644 --- a/core-java-modules/core-java-collections-list-5/README.md +++ b/core-java-modules/core-java-collections-list-5/README.md @@ -7,3 +7,5 @@ This module contains articles about the Java List collection - [Finding All Duplicates in a List in Java](https://www.baeldung.com/java-list-find-duplicates) - [Moving Items Around in an Arraylist](https://www.baeldung.com/java-arraylist-move-items) - [Check if a List Contains an Element From Another List in Java](https://www.baeldung.com/java-check-elements-between-lists) +- [Array vs. List Performance in Java](https://www.baeldung.com/java-array-vs-list-performance) +- [Set Default Value for Elements in List](https://www.baeldung.com/java-list-set-default-values) diff --git a/core-java-modules/core-java-collections-maps-6/README.md b/core-java-modules/core-java-collections-maps-6/README.md index fc12a1bb25..5b45752e74 100644 --- a/core-java-modules/core-java-collections-maps-6/README.md +++ b/core-java-modules/core-java-collections-maps-6/README.md @@ -1,2 +1,3 @@ ## Relevant Articles - [Copying All Keys and Values From One Hashmap Onto Another Without Replacing Existing Keys and Values](https://www.baeldung.com/java-copy-hashmap-no-changes) +- [Convert Hashmap to JSON Object in Java](https://www.baeldung.com/java-convert-hashmap-to-json-object) diff --git a/core-java-modules/core-java-collections-maps-6/src/test/com/baeldung/objecttomap/ObjectToMapUnitTest.java b/core-java-modules/core-java-collections-maps-6/src/test/com/baeldung/objecttomap/ObjectToMapUnitTest.java new file mode 100644 index 0000000000..52c2fb2bea --- /dev/null +++ b/core-java-modules/core-java-collections-maps-6/src/test/com/baeldung/objecttomap/ObjectToMapUnitTest.java @@ -0,0 +1,76 @@ +package java.com.baeldung.objecttomap; +import com.google.gson.Gson; +import org.junit.Assert; +import org.junit.Test; +import wiremock.com.fasterxml.jackson.core.type.TypeReference; +import wiremock.com.fasterxml.jackson.databind.ObjectMapper; +import wiremock.com.google.common.reflect.TypeToken; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +public class ObjectToMapUnitTest { + Employee employee = new Employee("John", 3000.0); + + @Test + public void givenJavaObject_whenUsingReflection_thenConvertToMap() throws IllegalAccessException { + Map map = convertUsingReflection(employee); + Assert.assertEquals(employee.getName(), map.get("name")); + Assert.assertEquals(employee.getSalary(), map.get("salary")); + } + + private Map convertUsingReflection(Object object) throws IllegalAccessException { + Map map = new HashMap<>(); + Field[] fields = object.getClass().getDeclaredFields(); + + for (Field field : fields) { + field.setAccessible(true); + map.put(field.getName(), field.get(object)); + } + + return map; + } + + @Test + public void givenJavaObject_whenUsingJackson_thenConvertToMap() { + ObjectMapper objectMapper = new ObjectMapper(); + Map map = objectMapper.convertValue(employee, new TypeReference>() {}); + Assert.assertEquals(employee.getName(), map.get("name")); + Assert.assertEquals(employee.getSalary(), map.get("salary")); + } + + @Test + public void givenJavaObject_whenUsingGson_thenConvertToMap() { + Gson gson = new Gson(); + String json = gson.toJson(employee); + Map map = gson.fromJson(json, new TypeToken>() {}.getType()); + Assert.assertEquals(employee.getName(), map.get("name")); + Assert.assertEquals(employee.getSalary(), map.get("salary")); + } + + private static class Employee { + private String name; + private Double salary; + + public Employee(String name, Double salary) { + this.name = name; + this.salary = salary; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getSalary() { + return salary; + } + + public void setSalary(Double age) { + this.salary = salary; + } + } +} diff --git a/core-java-modules/core-java-io-apis-2/README.md b/core-java-modules/core-java-io-apis-2/README.md index 9bd55abac4..d62fd3dbd1 100644 --- a/core-java-modules/core-java-io-apis-2/README.md +++ b/core-java-modules/core-java-io-apis-2/README.md @@ -12,3 +12,5 @@ This module contains articles about core Java input/output(IO) APIs. - [Storing Java Scanner Input in an Array](https://www.baeldung.com/java-store-scanner-input-in-array) - [How to Take Input as String With Spaces in Java Using Scanner?](https://www.baeldung.com/java-scanner-input-with-spaces) - [Write Console Output to Text File in Java](https://www.baeldung.com/java-write-console-output-file) +- [What’s the difference between Scanner next() and nextLine() methods?](https://www.baeldung.com/java-scanner-next-vs-nextline) +- [Handle NoSuchElementException When Reading a File Through Scanner](https://www.baeldung.com/java-scanner-nosuchelementexception-reading-file) diff --git a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/z_regexp/ZRegularExpressionUnitTest.java b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/z_regexp/ZRegularExpressionUnitTest.java index 90bbbb6540..a24682fc1a 100644 --- a/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/z_regexp/ZRegularExpressionUnitTest.java +++ b/core-java-modules/core-java-regex-2/src/test/java/com/baeldung/regex/z_regexp/ZRegularExpressionUnitTest.java @@ -3,33 +3,81 @@ package com.baeldung.regex.z_regexp; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.regex.Pattern; public class ZRegularExpressionUnitTest { @Test public void givenCreditCardNumber_thenReturnIfMatched() { String creditCardNumber = "1234567890123456"; + String creditCardNumber2 = "1234567890123456\n"; String pattern = "\\d{16}\\z"; - Assertions.assertTrue(creditCardNumber.matches(pattern)); + Assertions.assertTrue(Pattern.compile(pattern).matcher(creditCardNumber).find()); + Assertions.assertFalse(Pattern.compile(pattern).matcher(creditCardNumber2).find()); + } + + @Test + public void givenCreditCardNumber_thenReturnIfNotMatched() { + String creditCardNumber = "1234567890123456\n"; + String pattern = "\\d{16}\\z"; + Assertions.assertFalse(Pattern.compile(pattern).matcher(creditCardNumber).find()); } @Test public void givenLogOutput_thenReturnIfMatched() { String logLine = "2022-05-01 14:30:00,123 INFO Some log message"; String pattern = ".*message\\z"; - Assertions.assertTrue(logLine.matches(pattern)); + Assertions.assertTrue(Pattern.compile(pattern).matcher(logLine).find()); } + @Test + public void givenLogOutput_thenReturnIfNotMatched() { + String logLine = "2022-05-01 14:30:00,123 INFO Some log message\n"; + String pattern = ".*message\\z"; + Assertions.assertFalse(Pattern.compile(pattern).matcher(logLine).find()); + } @Test public void givenEmailMessage_thenReturnIfMatched() { String myMessage = "Hello HR, I hope i can write to Baeldung\n"; - String pattern = ".*Baeldung\\s*\\Z"; - Assertions.assertTrue(myMessage.matches(pattern)); + String myMessage2 = "Hello HR, I hope\n i can write to Baeldung"; + String pattern = ".*Baeldung\\Z"; + String pattern2 = ".*hope\\Z"; + Assertions.assertTrue(Pattern.compile(pattern).matcher(myMessage).find()); + Assertions.assertFalse(Pattern.compile(pattern2).matcher(myMessage2).find()); + } + + @Test + public void givenEmailMessage_thenReturnIfNotMatched() { + String myMessage = "Hello HR, I hope\n i can write to Baeldung"; + String pattern = ".*hope\\Z"; + Assertions.assertFalse(Pattern.compile(pattern).matcher(myMessage).find()); } @Test public void givenFileExtension_thenReturnIfMatched() { - String fileName = "image.jpeg"; + String fileName = "image.jpeg\n"; + String fileName2 = "image2.jpeg\n.png"; String pattern = ".*\\.jpeg\\Z"; - Assertions.assertTrue(fileName.matches(pattern)); + Assertions.assertTrue(Pattern.compile(pattern).matcher(fileName).find()); + Assertions.assertFalse(Pattern.compile(pattern).matcher(fileName2).find()); } -} + @Test + public void givenFileExtension_thenReturnIfNotMatched() { + String fileName = "image2.jpeg\n.png"; + String pattern = ".*\\.jpeg\\Z"; + Assertions.assertFalse(Pattern.compile(pattern).matcher(fileName).find()); + } + @Test + public void givenURL_thenReturnIfMatched() { + String url = "https://www.example.com/api/endpoint\n"; + String pattern = ".*/endpoint$"; + Assertions.assertTrue(Pattern.compile(pattern).matcher(url).find()); + } + + @Test + public void givenSentence_thenReturnIfMatched() { + String sentence = "Hello, how are you?"; + String pattern = ".*[.?!]$"; + Assertions.assertTrue(Pattern.compile(pattern).matcher(sentence).find()); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-serialization/README.md b/core-java-modules/core-java-serialization/README.md index ae7da3d2cf..ed8f8dc1c6 100644 --- a/core-java-modules/core-java-serialization/README.md +++ b/core-java-modules/core-java-serialization/README.md @@ -7,3 +7,4 @@ - [Deserialization Vulnerabilities in Java](https://www.baeldung.com/java-deserialization-vulnerabilities) - [Serialization Validation in Java](https://www.baeldung.com/java-validate-serializable) - [What Is the serialVersionUID?](https://www.baeldung.com/java-serial-version-uid) +- [Java Serialization: readObject() vs. readResolve()](https://www.baeldung.com/java-serialization-readobject-vs-readresolve) diff --git a/core-java-modules/core-java-serialization/src/main/java/com/baeldung/readresolvevsreadobject/Singleton.java b/core-java-modules/core-java-serialization/src/main/java/com/baeldung/readresolvevsreadobject/Singleton.java new file mode 100644 index 0000000000..91ee10dd6d --- /dev/null +++ b/core-java-modules/core-java-serialization/src/main/java/com/baeldung/readresolvevsreadobject/Singleton.java @@ -0,0 +1,21 @@ +package com.baeldung.readresolvevsreadobject; + +import java.io.ObjectStreamException; +import java.io.Serializable; + +public class Singleton implements Serializable { + + private static final long serialVersionUID = 1L; + private static Singleton INSTANCE = new Singleton(); + + private Singleton() { + } + + public static Singleton getInstance() { + return INSTANCE; + } + + private Object readResolve() throws ObjectStreamException { + return INSTANCE; + } +} diff --git a/core-java-modules/core-java-serialization/src/main/java/com/baeldung/readresolvevsreadobject/User.java b/core-java-modules/core-java-serialization/src/main/java/com/baeldung/readresolvevsreadobject/User.java new file mode 100644 index 0000000000..95aac0301e --- /dev/null +++ b/core-java-modules/core-java-serialization/src/main/java/com/baeldung/readresolvevsreadobject/User.java @@ -0,0 +1,59 @@ +package com.baeldung.readresolvevsreadobject; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +public class User implements Serializable { + + private static final long serialVersionUID = 3659932210257138726L; + 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; + } + + @Override + public String toString() { + return "User [userName=" + userName + ", password=" + password + "]"; + } + + public User() { + } + + public User(String userName, String password) { + super(); + this.userName = userName; + this.password = password; + } + + private void writeObject(ObjectOutputStream oos) throws IOException { + this.password = "xyz" + password; + oos.defaultWriteObject(); + } + + private void readObject(ObjectInputStream aInputStream) + throws ClassNotFoundException, IOException { + aInputStream.defaultReadObject(); + this.password = password.substring(3); + } + + private Object readResolve() { + return this; + } + +} diff --git a/core-java-modules/core-java-serialization/src/test/java/com/baeldung/readresolvevsreadobject/SingletonUnitTest.java b/core-java-modules/core-java-serialization/src/test/java/com/baeldung/readresolvevsreadobject/SingletonUnitTest.java new file mode 100644 index 0000000000..d5133ae976 --- /dev/null +++ b/core-java-modules/core-java-serialization/src/test/java/com/baeldung/readresolvevsreadobject/SingletonUnitTest.java @@ -0,0 +1,47 @@ +package com.baeldung.readresolvevsreadobject; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class SingletonUnitTest { + + @Test + public void testSingletonObj_withNoReadResolve() throws ClassNotFoundException, IOException { + // Serialization + FileOutputStream fos = new FileOutputStream("singleton.ser"); + ObjectOutputStream oos = new ObjectOutputStream(fos); + Singleton actualSingletonObject = Singleton.getInstance(); + oos.writeObject(actualSingletonObject); + + // Deserialization + Singleton deserializedSingletonObject = null; + FileInputStream fis = new FileInputStream("singleton.ser"); + ObjectInputStream ois = new ObjectInputStream(fis); + deserializedSingletonObject = (Singleton) ois.readObject(); + // remove readResolve() from Singleton class and uncomment this to test. + //assertNotEquals(actualSingletonObject.hashCode(), deserializedSingletonObject.hashCode()); + } + + @Test + public void testSingletonObj_withCustomReadResolve() + throws ClassNotFoundException, IOException { + // Serialization + FileOutputStream fos = new FileOutputStream("singleton.ser"); + ObjectOutputStream oos = new ObjectOutputStream(fos); + Singleton actualSingletonObject = Singleton.getInstance(); + oos.writeObject(actualSingletonObject); + + // Deserialization + Singleton deserializedSingletonObject = null; + FileInputStream fis = new FileInputStream("singleton.ser"); + ObjectInputStream ois = new ObjectInputStream(fis); + deserializedSingletonObject = (Singleton) ois.readObject(); + assertEquals(actualSingletonObject.hashCode(), deserializedSingletonObject.hashCode()); + } +} diff --git a/core-java-modules/core-java-serialization/src/test/java/com/baeldung/readresolvevsreadobject/UserUnitTest.java b/core-java-modules/core-java-serialization/src/test/java/com/baeldung/readresolvevsreadobject/UserUnitTest.java new file mode 100644 index 0000000000..ffd56d67e9 --- /dev/null +++ b/core-java-modules/core-java-serialization/src/test/java/com/baeldung/readresolvevsreadobject/UserUnitTest.java @@ -0,0 +1,52 @@ +package com.baeldung.readresolvevsreadobject; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class UserUnitTest { + + @Test + public void testDeserializeObj_withOverriddenReadObject() + throws ClassNotFoundException, IOException { + // Serialization + FileOutputStream fos = new FileOutputStream("user.ser"); + ObjectOutputStream oos = new ObjectOutputStream(fos); + User acutalObject = new User("Sachin", "Kumar"); + oos.writeObject(acutalObject); + + // Deserialization + User deserializedUser = null; + FileInputStream fis = new FileInputStream("user.ser"); + ObjectInputStream ois = new ObjectInputStream(fis); + deserializedUser = (User) ois.readObject(); + assertNotEquals(deserializedUser.hashCode(), acutalObject.hashCode()); + assertEquals(deserializedUser.getUserName(), "Sachin"); + assertEquals(deserializedUser.getPassword(), "Kumar"); + } + + @Test + public void testDeserializeObj_withDefaultReadObject() + throws ClassNotFoundException, IOException { + // Serialization + FileOutputStream fos = new FileOutputStream("user.ser"); + ObjectOutputStream oos = new ObjectOutputStream(fos); + User acutalObject = new User("Sachin", "Kumar"); + oos.writeObject(acutalObject); + + // Deserialization + User deserializedUser = null; + FileInputStream fis = new FileInputStream("user.ser"); + ObjectInputStream ois = new ObjectInputStream(fis); + deserializedUser = (User) ois.readObject(); + assertNotEquals(deserializedUser.hashCode(), acutalObject.hashCode()); + assertEquals(deserializedUser.getUserName(), "Sachin"); + // remove readObject() from User class and uncomment this to test. + //assertEquals(deserializedUser.getPassword(), "xyzKumar"); + } +} diff --git a/json-modules/gson-2/README.md b/json-modules/gson-2/README.md index 40d5515567..5580479753 100644 --- a/json-modules/gson-2/README.md +++ b/json-modules/gson-2/README.md @@ -3,5 +3,5 @@ This module contains articles about Gson ### Relevant Articles: - +- [Solving Gson Parsing Errors](https://www.baeldung.com/gson-parsing-errors) diff --git a/libraries-3/pom.xml b/libraries-3/pom.xml index 5e06a5550e..8d45b95a7b 100644 --- a/libraries-3/pom.xml +++ b/libraries-3/pom.xml @@ -129,13 +129,6 @@ - - - jitpack.io - https://jitpack.io - - - libraries-3 diff --git a/lombok-modules/lombok/pom.xml b/lombok-modules/lombok/pom.xml index 57b2a5a999..6ba90f33b5 100644 --- a/lombok-modules/lombok/pom.xml +++ b/lombok-modules/lombok/pom.xml @@ -49,10 +49,4 @@ 23.0.0 - - - projectlombok.org - https://projectlombok.org/edge-releases - - \ No newline at end of file diff --git a/optaplanner/pom.xml b/optaplanner/pom.xml index e1653fd749..033e3a3718 100644 --- a/optaplanner/pom.xml +++ b/optaplanner/pom.xml @@ -27,7 +27,7 @@ - 8.24.0.Final + 9.38.0.Final \ No newline at end of file diff --git a/patterns-modules/design-patterns-architectural/pom.xml b/patterns-modules/design-patterns-architectural/pom.xml index 2d6117a406..4efcca0c6b 100644 --- a/patterns-modules/design-patterns-architectural/pom.xml +++ b/patterns-modules/design-patterns-architectural/pom.xml @@ -67,10 +67,10 @@ 5.2.16.Final 6.0.6 2.7.5 - 3.3.0 + 5.3.0 2.7.5 5.5.14 - 3.14.0 + 3.20.4 3.14.0 diff --git a/patterns-modules/design-patterns-cloud/pom.xml b/patterns-modules/design-patterns-cloud/pom.xml index acd3e64ed5..595e0cabcd 100644 --- a/patterns-modules/design-patterns-cloud/pom.xml +++ b/patterns-modules/design-patterns-cloud/pom.xml @@ -17,8 +17,13 @@ io.github.resilience4j resilience4j-retry - 1.7.1 + ${resilience4j-retry.version} + + + 2.0.2 + + \ No newline at end of file diff --git a/patterns-modules/design-patterns-cloud/src/test/java/com/baeldung/backoff/jitter/BackoffWithJitterUnitTest.java b/patterns-modules/design-patterns-cloud/src/test/java/com/baeldung/backoff/jitter/BackoffWithJitterUnitTest.java index abfcc71e66..5bd6b0fa23 100644 --- a/patterns-modules/design-patterns-cloud/src/test/java/com/baeldung/backoff/jitter/BackoffWithJitterUnitTest.java +++ b/patterns-modules/design-patterns-cloud/src/test/java/com/baeldung/backoff/jitter/BackoffWithJitterUnitTest.java @@ -1,6 +1,6 @@ package com.baeldung.backoff.jitter; -import io.github.resilience4j.retry.IntervalFunction; +import io.github.resilience4j.core.IntervalFunction; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryConfig; import org.junit.Before; @@ -15,8 +15,8 @@ import java.util.concurrent.ExecutorService; import java.util.function.Function; import static com.baeldung.backoff.jitter.BackoffWithJitterUnitTest.RetryProperties.*; -import static io.github.resilience4j.retry.IntervalFunction.ofExponentialBackoff; -import static io.github.resilience4j.retry.IntervalFunction.ofExponentialRandomBackoff; +import static io.github.resilience4j.core.IntervalFunction.ofExponentialBackoff; +import static io.github.resilience4j.core.IntervalFunction.ofExponentialRandomBackoff; import static java.util.Collections.nCopies; import static java.util.concurrent.Executors.newFixedThreadPool; import static org.mockito.ArgumentMatchers.anyString; diff --git a/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Course.java b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Course.java new file mode 100644 index 0000000000..52d01c027c --- /dev/null +++ b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Course.java @@ -0,0 +1,42 @@ +package com.baeldung.associations.biredirectional; +import jakarta.persistence.*; +import java.util.List; + +@Entity +public class Course { + + @Id + private Long id; + + private String name; + + @ManyToMany + @JoinTable(name = "course_student", + joinColumns = @JoinColumn(name = "course_id"), + inverseJoinColumns = @JoinColumn(name = "student_id")) + private List students; + + public List getStudents() { + return students; + } + + 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; + } + + public void setStudents(List students) { + this.students = students; + } +} diff --git a/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Department.java b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Department.java new file mode 100644 index 0000000000..23f56ccd47 --- /dev/null +++ b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Department.java @@ -0,0 +1,21 @@ +package com.baeldung.associations.biredirectional; + +import java.util.List; +import jakarta.persistence.*; + + +@Entity +public class Department { + + @Id + private Long id; + + @OneToMany(mappedBy = "department") + private List employees; + + public List getEmployees() { + return employees; + } + + +} diff --git a/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Employee.java b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Employee.java new file mode 100644 index 0000000000..1e04379ae2 --- /dev/null +++ b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Employee.java @@ -0,0 +1,42 @@ +package com.baeldung.associations.biredirectional; + +import jakarta.persistence.*; + + +@Entity +public class Employee { + + @Id + private Long id; + + private String name; + + @ManyToOne + @JoinColumn(name = "department_id") + private Department department; + + + 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; + } + + public Department getDepartment() { + return department; + } + + public void setDepartment(Department department) { + this.department = department; + } +} diff --git a/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Student.java b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Student.java new file mode 100644 index 0000000000..81e608f88e --- /dev/null +++ b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/biredirectional/Student.java @@ -0,0 +1,43 @@ +package com.baeldung.associations.biredirectional; + +import java.util.List; + +import jakarta.persistence.*; + +@Entity +public class Student { + + @Id + private Long id; + + private String name; + + @ManyToMany(mappedBy = "students") + private List courses; + + + 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; + } + + public List getCourses() { + return courses; + } + + public void setCourses(List courses) { + this.courses = courses; + } +} + diff --git a/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Author.java b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Author.java new file mode 100644 index 0000000000..7e023683dc --- /dev/null +++ b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Author.java @@ -0,0 +1,40 @@ +package com.baeldung.associations.unidirectional; +import jakarta.persistence.*; +import java.util.Set; + +@Entity +public class Author { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + @ManyToMany(fetch = FetchType.LAZY, mappedBy = "authors") + private Set books; + + 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; + } + + public Set getBooks() { + return books; + } + + public void setBooks(Set books) { + this.books = books; + } +} diff --git a/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Book.java b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Book.java new file mode 100644 index 0000000000..25b192fb6b --- /dev/null +++ b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Book.java @@ -0,0 +1,44 @@ +package com.baeldung.associations.unidirectional; + +import jakarta.persistence.*; +import java.util.Set; + +@Entity +public class Book { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + @ManyToMany(fetch = FetchType.LAZY) + @JoinTable(name = "book_author", + joinColumns = @JoinColumn(name = "book_id"), + inverseJoinColumns = @JoinColumn(name = "author_id")) + private Set authors; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Set getAuthors() { + return authors; + } + + public void setAuthors(Set authors) { + this.authors = authors; + } +} diff --git a/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Department.java b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Department.java new file mode 100644 index 0000000000..3d65f2e3ef --- /dev/null +++ b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Department.java @@ -0,0 +1,33 @@ +package com.baeldung.associations.unidirectional; + +import jakarta.persistence.*; +import java.util.List; + +@Entity +public class Department { + + @Id + private Long id; + + @OneToMany(fetch = FetchType.LAZY) + @JoinColumn(name = "department_id") + private List employees; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public List getEmployees() { + return employees; + } + + public void setEmployees(List employees) { + this.employees = employees; + } +} + + diff --git a/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Employee.java b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Employee.java new file mode 100644 index 0000000000..c9fa2c7483 --- /dev/null +++ b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/Employee.java @@ -0,0 +1,42 @@ +package com.baeldung.associations.unidirectional; +import jakarta.persistence.*; + +@Entity +public class Employee { + + @Id + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parking_spot_id") + private ParkingSpot parkingSpot; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "department_id") + private Department department; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public ParkingSpot getParkingSpot() { + return parkingSpot; + } + + public void setParkingSpot(ParkingSpot parkingSpot) { + this.parkingSpot = parkingSpot; + } + + public Department getDepartment() { + return department; + } + + public void setDepartment(Department department) { + this.department = department; + } +} + diff --git a/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/ParkingSpot.java b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/ParkingSpot.java new file mode 100644 index 0000000000..6495d895eb --- /dev/null +++ b/persistence-modules/hibernate-mapping-2/src/main/java/com/baeldung/associations/unidirectional/ParkingSpot.java @@ -0,0 +1,11 @@ +package com.baeldung.associations.unidirectional; + +import jakarta.persistence.*; + +@Entity +public class ParkingSpot { + + @Id + private Long id; + +} diff --git a/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/AccountStatsApplication.java b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/AccountStatsApplication.java new file mode 100644 index 0000000000..f5a99f0ad8 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/AccountStatsApplication.java @@ -0,0 +1,11 @@ +package com.baeldung.countrows; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AccountStatsApplication { + public static void main(String[] args) { + SpringApplication.run(AccountStatsApplication.class, args); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/entity/Account.java b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/entity/Account.java new file mode 100644 index 0000000000..d422c30a0e --- /dev/null +++ b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/entity/Account.java @@ -0,0 +1,68 @@ +package com.baeldung.countrows.entity; + +import javax.persistence.*; + +import java.security.PrivateKey; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.Date; + +@Entity +@Table(name = "ACCOUNTS") +public class Account { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "accounts_seq") + @SequenceGenerator(name = "accounts_seq", sequenceName = "accounts_seq", allocationSize = 1) + @Column(name = "user_id") + private int userId; + private String username; + private String password; + private String email; + private Timestamp createdOn; + private Timestamp lastLogin; + + @OneToOne + @JoinColumn(name = "permissions_id") + private Permission permission; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setEmail(String email) { + this.email = email; + } + + public Timestamp getCreatedOn() { + return createdOn; + } + + public void setCreatedOn(Timestamp createdOn) { + this.createdOn = createdOn; + } + + public void setLastLogin(Timestamp lastLogin) { + this.lastLogin = lastLogin; + } + + public Permission getPermission() { + return permission; + } + + public void setPermission(Permission permission) { + this.permission = permission; + } + + @Override + public String toString() { + return "Account{" + "userId=" + userId + ", username='" + username + '\'' + ", password='" + password + '\'' + ", email='" + email + '\'' + ", createdOn=" + createdOn + ", lastLogin=" + lastLogin + ", permission=" + permission + '}'; + } +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/entity/Permission.java b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/entity/Permission.java new file mode 100644 index 0000000000..9acedf0558 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/entity/Permission.java @@ -0,0 +1,24 @@ +package com.baeldung.countrows.entity; + +import javax.persistence.*; + +@Entity +@Table(name = "PERMISSIONS") +public class Permission { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "permissions_id_sq") + @SequenceGenerator(name = "permissions_id_sq", sequenceName = "permissions_id_sq", allocationSize = 1) + private int id; + + private String type; + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return "Permission{" + "id=" + id + ", type='" + type + '\'' + '}'; + } +} diff --git a/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/repository/AccountRepository.java b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/repository/AccountRepository.java new file mode 100644 index 0000000000..422962ce45 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/repository/AccountRepository.java @@ -0,0 +1,23 @@ +package com.baeldung.countrows.repository; + +import com.baeldung.countrows.entity.Account; +import com.baeldung.countrows.entity.Permission; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; + +@Repository +public interface AccountRepository extends JpaRepository { + + long countByUsername(String username); + + long countByPermission(Permission permission); + + long countByPermissionAndCreatedOnGreaterThan(Permission permission, Timestamp ts); +} diff --git a/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/repository/PermissionRepository.java b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/repository/PermissionRepository.java new file mode 100644 index 0000000000..5e598b52ef --- /dev/null +++ b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/repository/PermissionRepository.java @@ -0,0 +1,12 @@ +package com.baeldung.countrows.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.baeldung.countrows.entity.Permission; + +@Repository +public interface PermissionRepository extends JpaRepository { + Permission findByType(String type); +} + diff --git a/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/service/AccountStatsLogic.java b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/service/AccountStatsLogic.java new file mode 100644 index 0000000000..e4e716b4ce --- /dev/null +++ b/persistence-modules/spring-boot-persistence-3/src/main/java/com/baeldung/countrows/service/AccountStatsLogic.java @@ -0,0 +1,116 @@ +package com.baeldung.countrows.service; + +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.persistence.criteria.*; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.baeldung.countrows.entity.Account; +import com.baeldung.countrows.entity.Permission; +import com.baeldung.countrows.repository.AccountRepository; +import com.baeldung.countrows.repository.PermissionRepository; + +@Service +public class AccountStatsLogic { + @Autowired + private AccountRepository accountRepository; + + @PersistenceContext + private EntityManager entityManager; + + @Autowired + private PermissionRepository permissionRepository; + + public long getAccountCount() { + return accountRepository.count(); + } + + public long getAccountCountByUsername(String username) { + return accountRepository.countByUsername(username); + } + + public long getAccountCountByPermission(Permission permission) { + return accountRepository.countByPermission(permission); + } + + public long getAccountCountByPermissionAndCreatedOn(Permission permission, Date date) throws ParseException { + return accountRepository.countByPermissionAndCreatedOnGreaterThan(permission, new Timestamp(date.getTime())); + } + + public long getAccountsUsingCQ() throws ParseException { + // creating criteria builder and query + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = builder.createQuery(Long.class); + Root accountRoot = criteriaQuery.from(Account.class); + + // select query + criteriaQuery.select(builder.count(accountRoot)); + + // execute and get the result + return entityManager.createQuery(criteriaQuery) + .getSingleResult(); + } + + public long getAccountsByPermissionUsingCQ(Permission permission) throws ParseException { + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = builder.createQuery(Long.class); + Root accountRoot = criteriaQuery.from(Account.class); + + List predicateList = new ArrayList<>(); // list of predicates that will go in where clause + predicateList.add(builder.equal(accountRoot.get("permission"), permission)); + + criteriaQuery.select(builder.count(accountRoot)) + .where(builder.and(predicateList.toArray(new Predicate[0]))); + + return entityManager.createQuery(criteriaQuery) + .getSingleResult(); + } + + public long getAccountsByPermissionAndCreateOnUsingCQ(Permission permission, Date date) throws ParseException { + // creating criteria builder and query + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); // create builder + CriteriaQuery criteriaQuery = builder.createQuery(Long.class);// query instance + Root accountRoot = criteriaQuery.from(Account.class); // root instance + + // list of predicates that will go in where clause + List predicateList = new ArrayList<>(); + predicateList.add(builder.equal(accountRoot.get("permission"), permission)); + predicateList.add(builder.greaterThan(accountRoot.get("createdOn"), new Timestamp(date.getTime()))); + + // select query + criteriaQuery.select(builder.count(accountRoot)) + .where(builder.and(predicateList.toArray(new Predicate[0]))); + + // execute and get the result + return entityManager.createQuery(criteriaQuery) + .getSingleResult(); + } + + public long getAccountsUsingJPQL() throws ParseException { + Query query = entityManager.createQuery("SELECT COUNT(*) FROM Account a"); + return (long) query.getSingleResult(); + } + + public long getAccountsByPermissionUsingJPQL(Permission permission) throws ParseException { + Query query = entityManager.createQuery("SELECT COUNT(*) FROM Account a WHERE a.permission = ?1"); + query.setParameter(1, permission); + return (long) query.getSingleResult(); + } + + public long getAccountsByPermissionAndCreatedOnUsingJPQL(Permission permission, Date date) throws ParseException { + Query query = entityManager.createQuery("SELECT COUNT(*) FROM Account a WHERE a.permission = ?1 and a.createdOn > ?2"); + query.setParameter(1, permission); + query.setParameter(2, new Timestamp(date.getTime())); + return (long) query.getSingleResult(); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-3/src/test/java/com/baeldung/boot/countrows/accountstatslogic/AccountStatsUnitTest.java b/persistence-modules/spring-boot-persistence-3/src/test/java/com/baeldung/boot/countrows/accountstatslogic/AccountStatsUnitTest.java new file mode 100644 index 0000000000..af825601aa --- /dev/null +++ b/persistence-modules/spring-boot-persistence-3/src/test/java/com/baeldung/boot/countrows/accountstatslogic/AccountStatsUnitTest.java @@ -0,0 +1,141 @@ +package com.baeldung.boot.countrows.accountstatslogic; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.Date; +import java.util.UUID; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import com.baeldung.countrows.AccountStatsApplication; +import com.baeldung.countrows.entity.Account; +import com.baeldung.countrows.entity.Permission; +import com.baeldung.countrows.repository.AccountRepository; +import com.baeldung.countrows.repository.PermissionRepository; +import com.baeldung.countrows.service.AccountStatsLogic; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = AccountStatsApplication.class) +class AccountStatsUnitTest { + + @Autowired + private PermissionRepository permissionRepository; + + @Autowired + private AccountRepository accountRepository; + + @Autowired + private AccountStatsLogic accountStatsLogic; + + @AfterEach + public void afterEach() { + accountRepository.deleteAll(); + permissionRepository.deleteAll(); + } + + @Test + public void givenAccountInTable_whenPerformCount_returnsAppropriateCount() { + savePermissions(); + saveAccount(); + assertThat(accountStatsLogic.getAccountCount()).isEqualTo(1); + } + + @Test + public void givenAccountInTable_whenPerformCountByUsernameOrPermission_returnsAppropriateCount() { + savePermissions(); + Account account = saveAccount(); + assertThat(accountStatsLogic.getAccountCountByUsername(account.getUsername())).isEqualTo(1); + assertThat(accountStatsLogic.getAccountCountByPermission(account.getPermission())).isEqualTo(1); + } + + @Test + public void givenAccountInTable_whenPerformCountByPermissionAndCreatedOn_returnsAppropriateCount() throws ParseException { + savePermissions(); + Account account = saveAccount(); + long count = accountStatsLogic.getAccountCountByPermissionAndCreatedOn(account.getPermission(), account.getCreatedOn()); + assertThat(count).isEqualTo(1); + } + + @Test + public void givenAccountInTable_whenPerformCountUsingCQ_returnsAppropriateCount() throws ParseException { + savePermissions(); + saveAccount(); + long count = accountStatsLogic.getAccountsUsingCQ(); + assertThat(count).isEqualTo(1); + } + + @Test + public void givenAccountInTable_whenPerformCountByPermissionUsingCQ_returnsAppropriateCount() throws ParseException { + savePermissions(); + Account account = saveAccount(); + long count = accountStatsLogic.getAccountsByPermissionUsingCQ(account.getPermission()); + assertThat(count).isEqualTo(1); + } + + @Test + public void givenAccountInTable_whenPerformCountByPermissionAndCreatedOnUsingCQ_returnsAppropriateCount() throws ParseException { + savePermissions(); + Account account = saveAccount(); + long count = accountStatsLogic.getAccountsByPermissionAndCreateOnUsingCQ(account.getPermission(), account.getCreatedOn()); + assertThat(count).isEqualTo(1); + } + + @Test + public void givenAccountInTable_whenPerformCountUsingJPQL_returnsAppropriateCount() throws ParseException { + savePermissions(); + saveAccount(); + long count = accountStatsLogic.getAccountsUsingJPQL(); + assertThat(count).isEqualTo(1); + } + + @Test + public void givenAccountInTable_whenPerformCountByPermissionUsingJPQL_returnsAppropriateCount() throws ParseException { + savePermissions(); + Account account = saveAccount(); + long count = accountStatsLogic.getAccountsByPermissionUsingJPQL(account.getPermission()); + assertThat(count).isEqualTo(1); + } + + @Test + public void givenAccountInTable_whenPerformCountByPermissionAndCreatedOnUsingJPQL_returnsAppropriateCount() throws ParseException { + savePermissions(); + Account account = saveAccount(); + long count = accountStatsLogic.getAccountsByPermissionAndCreatedOnUsingJPQL(account.getPermission(), account.getCreatedOn()); + assertThat(count).isEqualTo(1); + } + + private Account saveAccount() { + return accountRepository.save(getAccount()); + } + + private void savePermissions() { + Permission editor = new Permission(); + editor.setType("editor"); + permissionRepository.save(editor); + + Permission admin = new Permission(); + admin.setType("admin"); + permissionRepository.save(admin); + } + + private Account getAccount() { + Permission permission = permissionRepository.findByType("admin"); + Account account = new Account(); + String seed = UUID.randomUUID() + .toString(); + account.setUsername("username_" + seed); + account.setEmail("username_" + seed + "@gmail.com"); + account.setPermission(permission); + account.setPassword("password_q1234"); + account.setCreatedOn(Timestamp.from(Instant.now())); + account.setLastLogin(Timestamp.from(Instant.now())); + return account; + } +} diff --git a/persistence-modules/spring-data-dynamodb/pom.xml b/persistence-modules/spring-data-dynamodb/pom.xml index 0e990c69f3..06425cceb7 100644 --- a/persistence-modules/spring-data-dynamodb/pom.xml +++ b/persistence-modules/spring-data-dynamodb/pom.xml @@ -169,14 +169,6 @@ - - - dynamodb-local - DynamoDB Local Release Repository - ${dynamodblocal.repository.url} - - - com.baeldung.Application @@ -186,9 +178,7 @@ 1.11.64 3.3.7-1 1.0.392 - 1.11.106 - 1.11.86 - https://s3-us-west-2.amazonaws.com/dynamodb-local/release + 1.21.1 3.1.1 2.4.7 2.17.1 diff --git a/persistence-modules/spring-data-jpa-repo-3/README.md b/persistence-modules/spring-data-jpa-repo-3/README.md index 2ed2dc8896..93cc9379f7 100644 --- a/persistence-modules/spring-data-jpa-repo-3/README.md +++ b/persistence-modules/spring-data-jpa-repo-3/README.md @@ -6,4 +6,5 @@ This module contains articles about Spring Data JPA. - [New CRUD Repository Interfaces in Spring Data 3](https://www.baeldung.com/spring-data-3-crud-repository-interfaces) - [How to Persist a List of String in JPA?](https://www.baeldung.com/java-jpa-persist-string-list) - [Hibernate Natural IDs in Spring Boot](https://www.baeldung.com/spring-boot-hibernate-natural-ids) +- [Correct Use of flush() in JPA](https://www.baeldung.com/spring-jpa-flush) - More articles: [[<-- prev]](../spring-data-jpa-repo-2) diff --git a/pom.xml b/pom.xml index 9cd8f4722f..8a494719a8 100644 --- a/pom.xml +++ b/pom.xml @@ -366,6 +366,7 @@ muleesb web-modules/java-lite web-modules/restx + web-modules/jee-7 persistence-modules/deltaspike persistence-modules/hibernate-ogm persistence-modules/java-cassandra @@ -425,11 +426,10 @@ - spring-security-modules + spring-security-modules/spring-security-ldap spring-soap spring-static-resources spring-swagger-codegen - spring-web-modules testing-modules video-tutorials @@ -547,6 +547,7 @@ muleesb web-modules/java-lite web-modules/restx + web-modules/jee-7 persistence-modules/deltaspike persistence-modules/hibernate-ogm persistence-modules/java-cassandra @@ -598,11 +599,10 @@ - spring-security-modules + spring-security-modules/spring-security-ldap spring-soap spring-static-resources spring-swagger-codegen - spring-web-modules testing-modules video-tutorials @@ -913,7 +913,7 @@ spring-kafka spring-native - spring-security-modules/spring-security-oauth2-testing + spring-security-modules spring-protobuf spring-quartz @@ -925,6 +925,7 @@ spring-threads spring-vault spring-websockets + spring-web-modules static-analysis tensorflow-java vertx-modules @@ -1175,6 +1176,7 @@ spring-kafka spring-native + spring-security-modules spring-protobuf spring-quartz @@ -1186,6 +1188,7 @@ spring-threads spring-vault spring-websockets + spring-web-modules static-analysis tensorflow-java vertx-modules diff --git a/quarkus-modules/quarkus-extension/quarkus-liquibase/deployment/pom.xml b/quarkus-modules/quarkus-extension/quarkus-liquibase/deployment/pom.xml index 9a9e4485cd..823b2f674a 100644 --- a/quarkus-modules/quarkus-extension/quarkus-liquibase/deployment/pom.xml +++ b/quarkus-modules/quarkus-extension/quarkus-liquibase/deployment/pom.xml @@ -41,7 +41,6 @@ org.apache.maven.plugins maven-compiler-plugin - ${compiler.plugin.version} @@ -55,8 +54,4 @@ - - 3.8.1 - - \ No newline at end of file diff --git a/quarkus-modules/quarkus-extension/quarkus-liquibase/runtime/pom.xml b/quarkus-modules/quarkus-extension/quarkus-liquibase/runtime/pom.xml index 6656556c4b..95916932a1 100644 --- a/quarkus-modules/quarkus-extension/quarkus-liquibase/runtime/pom.xml +++ b/quarkus-modules/quarkus-extension/quarkus-liquibase/runtime/pom.xml @@ -52,7 +52,6 @@ org.apache.maven.plugins maven-compiler-plugin - ${compiler.plugin.version} @@ -68,7 +67,6 @@ 3.8.1 - 3.8.1 3.8.1 diff --git a/quarkus-modules/quarkus-funqy/pom.xml b/quarkus-modules/quarkus-funqy/pom.xml index ae9c34e7e0..39a6151c69 100644 --- a/quarkus-modules/quarkus-funqy/pom.xml +++ b/quarkus-modules/quarkus-funqy/pom.xml @@ -6,17 +6,7 @@ com.baeldung.quarkus quarkus-funqy 1.0.0-SNAPSHOT - - 3.10.1 - false - 17 - UTF-8 - UTF-8 - quarkus-bom - io.quarkus.platform - 2.16.0.Final - 3.0.0-M7 - + com.baeldung quarkus-modules @@ -131,4 +121,16 @@ + + + 3.10.1 + false + 17 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 2.16.0.Final + 3.0.0-M7 + diff --git a/quarkus-modules/quarkus-jandex/hello-app/pom.xml b/quarkus-modules/quarkus-jandex/hello-app/pom.xml index 0255a6636b..fffa05d63e 100644 --- a/quarkus-modules/quarkus-jandex/hello-app/pom.xml +++ b/quarkus-modules/quarkus-jandex/hello-app/pom.xml @@ -76,7 +76,6 @@ maven-compiler-plugin - ${compiler-plugin.version} ${maven.compiler.parameters} diff --git a/quarkus-modules/quarkus-jandex/hello-sender-maven-plugin/pom.xml b/quarkus-modules/quarkus-jandex/hello-sender-maven-plugin/pom.xml index 215a00e479..8eb1b87707 100644 --- a/quarkus-modules/quarkus-jandex/hello-sender-maven-plugin/pom.xml +++ b/quarkus-modules/quarkus-jandex/hello-sender-maven-plugin/pom.xml @@ -29,7 +29,7 @@ org.jboss.jandex jandex-maven-plugin - 1.2.1 + ${jandex-maven-plugin.version} make-index @@ -43,4 +43,8 @@ + + 1.2.1 + + \ No newline at end of file diff --git a/quarkus-modules/quarkus-jandex/pom.xml b/quarkus-modules/quarkus-jandex/pom.xml index 25aeee0a46..f207854b55 100644 --- a/quarkus-modules/quarkus-jandex/pom.xml +++ b/quarkus-modules/quarkus-jandex/pom.xml @@ -38,7 +38,6 @@ - 3.8.1 true 11 11 diff --git a/quarkus-modules/quarkus-vs-springboot/spring-project/pom.xml b/quarkus-modules/quarkus-vs-springboot/spring-project/pom.xml index 13508d7086..df3eca8a4f 100644 --- a/quarkus-modules/quarkus-vs-springboot/spring-project/pom.xml +++ b/quarkus-modules/quarkus-vs-springboot/spring-project/pom.xml @@ -3,7 +3,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.baeldung spring-project 0.1-SNAPSHOT @@ -19,7 +18,7 @@ org.testcontainers testcontainers-bom - 1.17.2 + ${testcontainers-bom.version} pom import @@ -43,7 +42,7 @@ com.github.jasync-sql jasync-r2dbc-mysql - 2.0.8 + ${jasync-r2dbc-mysql.version} org.springframework.boot @@ -104,7 +103,6 @@ org.apache.maven.plugins maven-compiler-plugin - ${maven-compiler-plugin.version} ${maven.compiler.source.version} ${maven.compiler.target.version} @@ -182,7 +180,7 @@ local-native exec - 0.9.11 + ${native-buildtools.version} @@ -241,7 +239,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M6 + ${maven-surefire-plugin.version} -DspringAot=true -agentlib:native-image-agent=access-filter-file=src/test/resources/access-filter.json,config-merge-dir=target/classes/META-INF/native-image @@ -254,11 +252,15 @@ + 1.17.2 11 0.12.1 3.10.1 11 11 + 3.1.0 + 0.9.11 + 2.0.8 \ No newline at end of file diff --git a/saas-modules/sentry-servlet/pom.xml b/saas-modules/sentry-servlet/pom.xml index 11dd2ad0ff..4f9e37ebd5 100644 --- a/saas-modules/sentry-servlet/pom.xml +++ b/saas-modules/sentry-servlet/pom.xml @@ -3,20 +3,15 @@ xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + sentry-servlet + sentry-servlet + war + com.baeldung saas-modules 1.0.0-SNAPSHOT - sentry-servlet - sentry-servlet - war - - - 6.11.0 - 1.10.4 - 3.3.2 - @@ -37,7 +32,7 @@ org.codehaus.cargo cargo-maven3-plugin - ${cargo.version} + ${cargo-maven3-plugin.version} tomcat9x @@ -47,4 +42,10 @@ + + + 6.11.0 + 1.10.4 + 3.3.2 + \ No newline at end of file diff --git a/security-modules/java-ee-8-security-api/pom.xml b/security-modules/java-ee-8-security-api/pom.xml index bcd15ed685..7d1a19cb88 100644 --- a/security-modules/java-ee-8-security-api/pom.xml +++ b/security-modules/java-ee-8-security-api/pom.xml @@ -34,7 +34,6 @@ maven-war-plugin - ${maven-war-plugin.version} false pom.xml diff --git a/spring-boot-modules/spring-boot-gradle-2/README.md b/spring-boot-modules/spring-boot-gradle-2/README.md new file mode 100644 index 0000000000..ba6f30c000 --- /dev/null +++ b/spring-boot-modules/spring-boot-gradle-2/README.md @@ -0,0 +1,2 @@ +## Relevant Articles +- [Configuring Gradle Tasks in Spring Boot 3](https://www.baeldung.com/spring-boot-3-gradle-configure-tasks) diff --git a/spring-boot-modules/spring-boot-properties-3/README.md b/spring-boot-modules/spring-boot-properties-3/README.md index cb09a0ab81..476797965a 100644 --- a/spring-boot-modules/spring-boot-properties-3/README.md +++ b/spring-boot-modules/spring-boot-properties-3/README.md @@ -13,4 +13,5 @@ - [Using Environment Variables in Spring Boot’s application.properties](https://www.baeldung.com/spring-boot-properties-env-variables) - [Loading Multiple YAML Configuration Files in Spring Boot](https://www.baeldung.com/spring-boot-load-multiple-yaml-configuration-files) - [Using Environment Variables in Spring Boot’s Properties Files](https://www.baeldung.com/spring-boot-properties-env-variables) +- [Spring Boot Properties Prefix Must Be in Canonical Form](https://www.baeldung.com/spring-boot-properties-canonical-form) - More articles: [[<-- prev]](../spring-boot-properties-2) diff --git a/spring-cloud-modules/spring-cloud-bootstrap/application-config/book-service.properties b/spring-cloud-modules/spring-cloud-bootstrap/application-config/book-service.properties index 49f7d1ed91..2ea30b9ab7 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/application-config/book-service.properties +++ b/spring-cloud-modules/spring-cloud-bootstrap/application-config/book-service.properties @@ -18,3 +18,5 @@ spring.redis.port=6379 spring.sleuth.sampler.percentage=1.0 spring.sleuth.web.skipPattern=(^cleanup.*) + +spring.zipkin.baseUrl=http://localhost:9411 diff --git a/spring-cloud-modules/spring-cloud-bootstrap/application-config/gateway.properties b/spring-cloud-modules/spring-cloud-bootstrap/application-config/gateway.properties index e9e593284c..42e114450d 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/application-config/gateway.properties +++ b/spring-cloud-modules/spring-cloud-bootstrap/application-config/gateway.properties @@ -6,25 +6,13 @@ eureka.client.registryFetchIntervalSeconds = 5 management.security.sessions=always -zuul.routes.book-service.path=/book-service/** -zuul.routes.book-service.sensitive-headers=Set-Cookie,Authorization -hystrix.command.book-service.execution.isolation.thread.timeoutInMilliseconds=600000 - -zuul.routes.rating-service.path=/rating-service/** -zuul.routes.rating-service.sensitive-headers=Set-Cookie,Authorization -hystrix.command.rating-service.execution.isolation.thread.timeoutInMilliseconds=600000 - -zuul.routes.discovery.path=/discovery/** -zuul.routes.discovery.sensitive-headers=Set-Cookie,Authorization -zuul.routes.discovery.url=http://localhost:8082 -hystrix.command.discovery.execution.isolation.thread.timeoutInMilliseconds=600000 - logging.level.org.springframework.web.=debug logging.level.org.springframework.security=debug -logging.level.org.springframework.cloud.netflix.zuul=debug spring.redis.host=localhost spring.redis.port=6379 spring.sleuth.sampler.percentage=1.0 -spring.sleuth.web.skipPattern=(^cleanup.*|.+favicon.*) \ No newline at end of file +spring.sleuth.web.skipPattern=(^cleanup.*|.+favicon.*) + +spring.zipkin.baseUrl=http://localhost:9411 diff --git a/spring-cloud-modules/spring-cloud-bootstrap/application-config/rating-service.properties b/spring-cloud-modules/spring-cloud-bootstrap/application-config/rating-service.properties index b7cbb6fbd6..059b87e4e7 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/application-config/rating-service.properties +++ b/spring-cloud-modules/spring-cloud-bootstrap/application-config/rating-service.properties @@ -18,3 +18,5 @@ spring.redis.port=6379 spring.sleuth.sampler.percentage=1.0 spring.sleuth.web.skipPattern=(^cleanup.*) + +spring.zipkin.baseUrl=http://localhost:9411 diff --git a/spring-cloud-modules/spring-cloud-bootstrap/config/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/config/pom.xml index 6c9c3c5374..c1be447822 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/config/pom.xml +++ b/spring-cloud-modules/spring-cloud-bootstrap/config/pom.xml @@ -9,9 +9,9 @@ com.baeldung - parent-boot-1 + parent-boot-2 0.0.1-SNAPSHOT - ../../../parent-boot-1 + ../../../parent-boot-2 @@ -33,7 +33,7 @@ org.springframework.cloud - spring-cloud-starter-eureka + spring-cloud-starter-netflix-eureka-client org.springframework.boot @@ -42,7 +42,7 @@ - Brixton.SR7 + 2021.0.7 \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/config/src/main/java/com/baeldung/spring/cloud/bootstrap/config/ConfigApplication.java b/spring-cloud-modules/spring-cloud-bootstrap/config/src/main/java/com/baeldung/spring/cloud/bootstrap/config/ConfigApplication.java index 847c86f881..c3e04c4b54 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/config/src/main/java/com/baeldung/spring/cloud/bootstrap/config/ConfigApplication.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/config/src/main/java/com/baeldung/spring/cloud/bootstrap/config/ConfigApplication.java @@ -2,12 +2,12 @@ package com.baeldung.spring.cloud.bootstrap.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.config.server.EnableConfigServer; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableConfigServer -@EnableEurekaClient +@EnableDiscoveryClient public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); diff --git a/spring-cloud-modules/spring-cloud-bootstrap/config/src/main/java/com/baeldung/spring/cloud/bootstrap/config/SecurityConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/config/src/main/java/com/baeldung/spring/cloud/bootstrap/config/SecurityConfig.java index ef1d7b0b78..d563052baa 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/config/src/main/java/com/baeldung/spring/cloud/bootstrap/config/SecurityConfig.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/config/src/main/java/com/baeldung/spring/cloud/bootstrap/config/SecurityConfig.java @@ -1,23 +1,41 @@ package com.baeldung.spring.cloud.bootstrap.config; -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.context.annotation.Bean; 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.security.core.userdetails.User; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; -@Configuration @EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { +public class SecurityConfig { - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser("configUser").password("configPassword").roles("SYSTEM"); + @Bean + public InMemoryUserDetailsManager userDetailsService(BCryptPasswordEncoder bCryptPasswordEncoder) { + InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); + manager.createUser(User.withUsername("configUser") + .password(bCryptPasswordEncoder.encode("configPassword")) + .roles("SYSTEM") + .build()); + return manager; } - @Override - protected void configure(HttpSecurity http) throws Exception { - http.authorizeRequests().anyRequest().hasRole("SYSTEM").and().httpBasic().and().csrf().disable(); + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeRequests() + .anyRequest() + .hasRole("SYSTEM") + .and() + .httpBasic() + .and() + .csrf() + .disable(); + return http.build(); + } + + @Bean + public BCryptPasswordEncoder encoder() { + return new BCryptPasswordEncoder(); } } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/discovery/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/discovery/pom.xml index fb06c6052b..28c1a741a6 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/discovery/pom.xml +++ b/spring-cloud-modules/spring-cloud-bootstrap/discovery/pom.xml @@ -9,9 +9,9 @@ com.baeldung - parent-boot-1 + parent-boot-2 0.0.1-SNAPSHOT - ../../../parent-boot-1 + ../../../parent-boot-2 @@ -33,7 +33,11 @@ org.springframework.cloud - spring-cloud-starter-eureka-server + spring-cloud-starter-bootstrap + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server org.springframework.boot @@ -41,7 +45,7 @@ org.springframework.session - spring-session + spring-session-data-redis org.springframework.boot @@ -50,7 +54,7 @@ - Edgware.SR5 + 2021.0.7 \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/discovery/src/main/java/com/baeldung/spring/cloud/bootstrap/discovery/SecurityConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/discovery/src/main/java/com/baeldung/spring/cloud/bootstrap/discovery/SecurityConfig.java index a89faba962..fa389ec6a3 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/discovery/src/main/java/com/baeldung/spring/cloud/bootstrap/discovery/SecurityConfig.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/discovery/src/main/java/com/baeldung/spring/cloud/bootstrap/discovery/SecurityConfig.java @@ -17,7 +17,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication().withUser("discUser").password("discPassword").roles("SYSTEM"); + auth.inMemoryAuthentication().withUser("discUser").password("{noop}discPassword").roles("SYSTEM"); } @Override diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/gateway/pom.xml index e1041516c4..fa6735199f 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/gateway/pom.xml +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway/pom.xml @@ -9,9 +9,9 @@ com.baeldung - parent-boot-1 + parent-boot-2 0.0.1-SNAPSHOT - ../../../parent-boot-1 + ../../../parent-boot-2 @@ -33,11 +33,15 @@ org.springframework.cloud - spring-cloud-starter-eureka + spring-cloud-starter-bootstrap org.springframework.cloud - spring-cloud-starter-zuul + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-gateway org.springframework.boot @@ -45,7 +49,7 @@ org.springframework.session - spring-session + spring-session-data-redis org.springframework.boot @@ -53,11 +57,15 @@ org.springframework.cloud - spring-cloud-starter-zipkin + spring-cloud-starter-sleuth org.springframework.cloud - spring-cloud-starter-feign + spring-cloud-sleuth-zipkin + + + org.springframework.cloud + spring-cloud-starter-openfeign @@ -97,7 +105,7 @@ - Dalston.RELEASE + 2021.0.7 \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/ErrorPageConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/ErrorPageConfig.java index 67d172d3cd..b1fa7ce0bb 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/ErrorPageConfig.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/ErrorPageConfig.java @@ -1,8 +1,8 @@ package com.baeldung.spring.cloud.bootstrap.gateway; -import org.springframework.boot.web.servlet.ErrorPage; -import org.springframework.boot.web.servlet.ErrorPageRegistrar; -import org.springframework.boot.web.servlet.ErrorPageRegistry; +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.ErrorPageRegistrar; +import org.springframework.boot.web.server.ErrorPageRegistry; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/GatewayApplication.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/GatewayApplication.java index 8fc75e1ff6..6adda92c25 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/GatewayApplication.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/GatewayApplication.java @@ -1,76 +1,15 @@ package com.baeldung.spring.cloud.bootstrap.gateway; -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.EurekaClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; -import org.springframework.cloud.netflix.feign.EnableFeignClients; -import org.springframework.cloud.netflix.ribbon.RibbonClientSpecification; -import org.springframework.cloud.netflix.ribbon.SpringClientFactory; -import org.springframework.cloud.netflix.zuul.EnableZuulProxy; -import org.springframework.cloud.sleuth.metric.SpanMetricReporter; -import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter; -import org.springframework.cloud.sleuth.zipkin.ZipkinProperties; -import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter; -import org.springframework.context.annotation.Bean; -import org.springframework.web.client.RestTemplate; -import zipkin.Span; - -import java.util.ArrayList; -import java.util.List; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication -@EnableZuulProxy -@EnableEurekaClient @EnableFeignClients +@EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } - - @Autowired(required = false) - private List configurations = new ArrayList<>(); - @Autowired - private EurekaClient eurekaClient; - @Autowired - private SpanMetricReporter spanMetricReporter; - @Autowired - private ZipkinProperties zipkinProperties; - @Value("${spring.sleuth.web.skipPattern}") - private String skipPattern; - - @Bean - @LoadBalanced - RestTemplate restTemplate() { - return new RestTemplate(); - } - - @Bean - public SpringClientFactory springClientFactory() { - SpringClientFactory factory = new SpringClientFactory(); - factory.setConfigurations(this.configurations); - return factory; - } - - @Bean - public ZipkinSpanReporter makeZipkinSpanReporter() { - return new ZipkinSpanReporter() { - private HttpZipkinSpanReporter delegate; - private String baseUrl; - - @Override - public void report(Span span) { - InstanceInfo instance = eurekaClient.getNextServerFromEureka("zipkin", false); - if (baseUrl == null || !instance.getHomePageUrl().equals(baseUrl)) { - baseUrl = instance.getHomePageUrl(); - } - delegate = new HttpZipkinSpanReporter(new RestTemplate(), baseUrl, zipkinProperties.getFlushInterval(), spanMetricReporter); - if (!span.name.matches(skipPattern)) delegate.report(span); - } - }; - } } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SecurityConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SecurityConfig.java index d56be699e6..088fdd01f7 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SecurityConfig.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SecurityConfig.java @@ -1,37 +1,59 @@ package com.baeldung.spring.cloud.bootstrap.gateway; -import org.springframework.beans.factory.annotation.Autowired; +import static org.springframework.security.config.Customizer.withDefaults; + +import org.springframework.context.annotation.Bean; 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.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler; -@EnableWebSecurity +@EnableWebFluxSecurity @Configuration -public class SecurityConfig extends WebSecurityConfigurerAdapter { +public class SecurityConfig { - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication() - .withUser("user").password("password").roles("USER") - .and() - .withUser("admin").password("admin").roles("ADMIN"); + @Bean + public MapReactiveUserDetailsService userDetailsService() { + UserDetails user = User.withUsername("user") + .password(passwordEncoder().encode("password")) + .roles("USER") + .build(); + UserDetails adminUser = User.withUsername("admin") + .password(passwordEncoder().encode("admin")) + .roles("ADMIN") + .build(); + return new MapReactiveUserDetailsService(user, adminUser); } - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .formLogin() - .defaultSuccessUrl("/home/index.html", true) + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http.formLogin() + .authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/home/index.html")) .and() - .authorizeRequests() - .antMatchers("/book-service/**", "/rating-service/**", "/login*", "/").permitAll() - .antMatchers("/eureka/**").hasRole("ADMIN") - .anyRequest().authenticated() + .authorizeExchange() + .pathMatchers("/book-service/**", "/rating-service/**", "/login*", "/") + .permitAll() + .pathMatchers("/eureka/**") + .hasRole("ADMIN") + .anyExchange() + .authenticated() .and() - .logout() + .logout() .and() - .csrf().disable(); + .csrf() + .disable() + .httpBasic(withDefaults()); + return http.build(); } } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SessionConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SessionConfig.java index 14f7deb770..498c780f65 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SessionConfig.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/SessionConfig.java @@ -1,11 +1,10 @@ package com.baeldung.spring.cloud.bootstrap.gateway; import org.springframework.context.annotation.Configuration; -import org.springframework.session.data.redis.RedisFlushMode; -import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; -import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer; +import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession; @Configuration -@EnableRedisHttpSession(redisFlushMode = RedisFlushMode.IMMEDIATE) -public class SessionConfig extends AbstractHttpSessionApplicationInitializer { +@EnableRedisWebSession +public class SessionConfig { + } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/client/book/BooksClient.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/client/book/BooksClient.java index f60f65d23c..8fd235b3dc 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/client/book/BooksClient.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/client/book/BooksClient.java @@ -1,7 +1,6 @@ package com.baeldung.spring.cloud.bootstrap.gateway.client.book; -import org.springframework.cloud.netflix.feign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -9,6 +8,6 @@ import org.springframework.web.bind.annotation.RequestMethod; @FeignClient(value = "book-service") public interface BooksClient { - @RequestMapping(value = "/books/{bookId}", method = {RequestMethod.GET}) + @RequestMapping(value = "/books/{bookId}", method = { RequestMethod.GET }) Book getBookById(@PathVariable("bookId") Long bookId); } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/client/rating/RatingsClient.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/client/rating/RatingsClient.java index 9728111c5e..d04ba85082 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/client/rating/RatingsClient.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/client/rating/RatingsClient.java @@ -1,17 +1,16 @@ package com.baeldung.spring.cloud.bootstrap.gateway.client.rating; -import org.springframework.cloud.netflix.feign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; +import java.util.List; + +import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; -import java.util.List; - @FeignClient(value = "rating-service") public interface RatingsClient { - @RequestMapping(value = "/ratings", method = {RequestMethod.GET}) + @RequestMapping(value = "/ratings", method = { RequestMethod.GET }) List getRatingsByBookId(@RequestParam("bookId") Long bookId, @RequestHeader("Cookie") String session); } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/filter/SessionSavingPreFilter.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/filter/SessionSavingPreFilter.java new file mode 100644 index 0000000000..bf10152318 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/filter/SessionSavingPreFilter.java @@ -0,0 +1,25 @@ +package com.baeldung.spring.cloud.bootstrap.gateway.filter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; + +import reactor.core.publisher.Mono; + +@Component +public class SessionSavingPreFilter implements GlobalFilter { + + private static final Logger logger = LoggerFactory.getLogger(SessionSavingPreFilter.class); + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + return exchange.getSession() + .flatMap(session -> { + logger.debug("SessionId: {}", session.getId()); + return chain.filter(exchange); + }); + } +} diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/filter/SessionSavingZuulPreFilter.java b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/filter/SessionSavingZuulPreFilter.java deleted file mode 100644 index 1c90ba2e12..0000000000 --- a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/java/com/baeldung/spring/cloud/bootstrap/gateway/filter/SessionSavingZuulPreFilter.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.baeldung.spring.cloud.bootstrap.gateway.filter; - -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.session.Session; -import org.springframework.session.SessionRepository; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpSession; - -@Component -public class SessionSavingZuulPreFilter extends ZuulFilter { - - private Logger log = LoggerFactory.getLogger(this.getClass()); - - @Autowired - private SessionRepository repository; - - @Override - public boolean shouldFilter() { - return true; - } - - @Override - public Object run() { - RequestContext context = RequestContext.getCurrentContext(); - HttpSession httpSession = context.getRequest().getSession(); - Session session = repository.getSession(httpSession.getId()); - - context.addZuulRequestHeader("Cookie", "SESSION=" + httpSession.getId()); - log.info("ZuulPreFilter session proxy: {}", session.getId()); - return null; - } - - @Override - public String filterType() { - return "pre"; - } - - @Override - public int filterOrder() { - return 0; - } -} diff --git a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/resources/bootstrap.properties b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/resources/bootstrap.properties index 43491ff36b..1c90ca9db0 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/resources/bootstrap.properties +++ b/spring-cloud-modules/spring-cloud-bootstrap/gateway/src/main/resources/bootstrap.properties @@ -3,5 +3,7 @@ spring.cloud.config.discovery.service-id=config spring.cloud.config.discovery.enabled=true spring.cloud.config.username=configUser spring.cloud.config.password=configPassword +spring.cloud.gateway.discovery.locator.enabled=true +spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/ \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/pom.xml index 1e97082db1..e7fe7e7485 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/pom.xml +++ b/spring-cloud-modules/spring-cloud-bootstrap/pom.xml @@ -20,7 +20,6 @@ gateway svc-book svc-rating - zipkin customer-service order-service diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/pom.xml index b1aa205af5..c973968a70 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/pom.xml +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/pom.xml @@ -10,9 +10,9 @@ com.baeldung - parent-boot-1 + parent-boot-2 0.0.1-SNAPSHOT - ../../../parent-boot-1 + ../../../parent-boot-2 @@ -34,7 +34,11 @@ org.springframework.cloud - spring-cloud-starter-eureka + spring-cloud-starter-bootstrap + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client org.springframework.boot @@ -46,7 +50,7 @@ org.springframework.session - spring-session + spring-session-data-redis org.springframework.boot @@ -63,12 +67,16 @@ org.springframework.cloud - spring-cloud-starter-zipkin + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin - Dalston.RELEASE + 2021.0.7 \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/BookServiceApplication.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/BookServiceApplication.java index d787b5e407..8b1eab7885 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/BookServiceApplication.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/BookServiceApplication.java @@ -1,53 +1,14 @@ package com.baeldung.spring.cloud.bootstrap.svcbook; -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.EurekaClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; -import org.springframework.cloud.sleuth.metric.SpanMetricReporter; -import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter; -import org.springframework.cloud.sleuth.zipkin.ZipkinProperties; -import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter; -import org.springframework.context.annotation.Bean; -import org.springframework.web.client.RestTemplate; - -import zipkin.Span; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication -@EnableEurekaClient +@EnableDiscoveryClient public class BookServiceApplication { - @Autowired - private EurekaClient eurekaClient; - @Autowired - private SpanMetricReporter spanMetricReporter; - @Autowired - private ZipkinProperties zipkinProperties; - @Value("${spring.sleuth.web.skipPattern}") - private String skipPattern; - public static void main(String[] args) { SpringApplication.run(BookServiceApplication.class, args); } - - @Bean - public ZipkinSpanReporter makeZipkinSpanReporter() { - return new ZipkinSpanReporter() { - private HttpZipkinSpanReporter delegate; - private String baseUrl; - - @Override - public void report(Span span) { - InstanceInfo instance = eurekaClient.getNextServerFromEureka("zipkin", false); - if (baseUrl == null || !instance.getHomePageUrl().equals(baseUrl)) { - baseUrl = instance.getHomePageUrl(); - } - delegate = new HttpZipkinSpanReporter(new RestTemplate(), baseUrl, zipkinProperties.getFlushInterval(), spanMetricReporter); - if (!span.name.matches(skipPattern)) delegate.report(span); - } - }; - } } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/CookieConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/CookieConfig.java new file mode 100644 index 0000000000..99696836c1 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/CookieConfig.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.cloud.bootstrap.svcbook; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.session.web.http.DefaultCookieSerializer; + +@Configuration +public class CookieConfig { + + @Bean + public DefaultCookieSerializer cookieSerializer() { + DefaultCookieSerializer serializer = new DefaultCookieSerializer(); + serializer.setUseBase64Encoding(false); + return serializer; + } +} \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/SecurityConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/SecurityConfig.java index 6aa996c575..0b9520c976 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/SecurityConfig.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/SecurityConfig.java @@ -1,36 +1,37 @@ package com.baeldung.spring.cloud.bootstrap.svcbook; import org.springframework.beans.factory.annotation.Autowired; +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.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.SecurityFilterChain; @EnableWebSecurity @Configuration -public class SecurityConfig extends WebSecurityConfigurerAdapter { +public class SecurityConfig { @Autowired - public void configureGlobal1(AuthenticationManagerBuilder auth) throws Exception { - //try in memory auth with no users to support the case that this will allow for users that are logged in to go anywhere + public void registerAuthProvider(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication(); } - @Override - protected void configure(HttpSecurity http) throws Exception { - http.httpBasic() + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http.authorizeHttpRequests((auth) -> auth.antMatchers(HttpMethod.GET, "/books") + .permitAll() + .antMatchers(HttpMethod.GET, "/books/*") + .permitAll() + .antMatchers(HttpMethod.POST, "/books") + .hasRole("ADMIN") + .antMatchers(HttpMethod.PATCH, "/books/*") + .hasRole("ADMIN") + .antMatchers(HttpMethod.DELETE, "/books/*") + .hasRole("ADMIN")) + .csrf() .disable() - .authorizeRequests() - .antMatchers(HttpMethod.GET, "/books").permitAll() - .antMatchers(HttpMethod.GET, "/books/*").permitAll() - .antMatchers(HttpMethod.POST, "/books").hasRole("ADMIN") - .antMatchers(HttpMethod.PATCH, "/books/*").hasRole("ADMIN") - .antMatchers(HttpMethod.DELETE, "/books/*").hasRole("ADMIN") - .anyRequest().authenticated() - .and() - .csrf() - .disable(); + .build(); } } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookService.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookService.java index 106fdad5d9..4ee3112049 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookService.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/java/com/baeldung/spring/cloud/bootstrap/svcbook/book/BookService.java @@ -2,7 +2,6 @@ package com.baeldung.spring.cloud.bootstrap.svcbook.book; import java.util.List; import java.util.Map; -import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -23,8 +22,8 @@ public class BookService { } public Book findBookById(Long bookId) { - return Optional.ofNullable(bookRepository.findOne(bookId)) - .orElseThrow(() -> new BookNotFoundException("Book not found. ID: " + bookId)); + return bookRepository.findById(bookId) + .orElseThrow(() -> new BookNotFoundException(String.format("Book not found. ID: %s", bookId))); } @Transactional(propagation = Propagation.REQUIRED) @@ -37,7 +36,7 @@ public class BookService { @Transactional(propagation = Propagation.REQUIRED) public void deleteBook(Long bookId) { - bookRepository.delete(bookId); + bookRepository.deleteById(bookId); } @Transactional(propagation = Propagation.REQUIRED) @@ -60,7 +59,8 @@ public class BookService { public Book updateBook(Book book, Long bookId) { Preconditions.checkNotNull(book); Preconditions.checkState(book.getId() == bookId); - Preconditions.checkNotNull(bookRepository.findOne(bookId)); + Preconditions.checkArgument(bookRepository.findById(bookId) + .isPresent()); return bookRepository.save(book); } } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/resources/bootstrap.properties b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/resources/bootstrap.properties index 481cdc182c..a50048c671 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/resources/bootstrap.properties +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-book/src/main/resources/bootstrap.properties @@ -3,5 +3,7 @@ spring.cloud.config.discovery.service-id=config spring.cloud.config.discovery.enabled=true spring.cloud.config.username=configUser spring.cloud.config.password=configPassword +spring.cloud.gateway.discovery.locator.enabled=true +spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/ \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/pom.xml index 336c1ff2c6..29ebb4c4bc 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/pom.xml +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/pom.xml @@ -10,9 +10,9 @@ com.baeldung - parent-boot-1 + parent-boot-2 0.0.1-SNAPSHOT - ../../../parent-boot-1 + ../../../parent-boot-2 @@ -34,7 +34,11 @@ org.springframework.cloud - spring-cloud-starter-eureka + spring-cloud-starter-bootstrap + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client org.springframework.boot @@ -46,7 +50,7 @@ org.springframework.session - spring-session + spring-session-data-redis org.springframework.boot @@ -58,25 +62,29 @@ org.springframework.cloud - spring-cloud-starter-hystrix + spring-cloud-starter-circuitbreaker-resilience4j org.springframework.boot spring-boot-starter-actuator + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + com.h2database h2 runtime - - org.springframework.cloud - spring-cloud-starter-zipkin - - Dalston.RELEASE + 2021.0.7 \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/CookieConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/CookieConfig.java new file mode 100644 index 0000000000..9774c18568 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/CookieConfig.java @@ -0,0 +1,16 @@ +package com.baeldung.spring.cloud.bootstrap.svcrating; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.session.web.http.DefaultCookieSerializer; + +@Configuration +public class CookieConfig { + + @Bean + public DefaultCookieSerializer cookieSerializer() { + DefaultCookieSerializer serializer = new DefaultCookieSerializer(); + serializer.setUseBase64Encoding(false); + return serializer; + } +} \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java index 5a94f19472..1774407d26 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/RatingServiceApplication.java @@ -1,69 +1,18 @@ package com.baeldung.spring.cloud.bootstrap.svcrating; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; -import org.springframework.cloud.netflix.hystrix.EnableHystrix; -import org.springframework.cloud.sleuth.metric.SpanMetricReporter; -import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter; -import org.springframework.cloud.sleuth.zipkin.ZipkinProperties; -import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.AdviceMode; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.springframework.web.client.RestTemplate; - -import com.netflix.appinfo.InstanceInfo; -import com.netflix.discovery.EurekaClient; -import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect; - -import zipkin.Span; @SpringBootApplication -@EnableEurekaClient -@EnableHystrix -@EnableTransactionManagement(order=Ordered.LOWEST_PRECEDENCE, mode=AdviceMode.ASPECTJ) +@EnableDiscoveryClient +@EnableTransactionManagement(order = Ordered.LOWEST_PRECEDENCE, mode = AdviceMode.ASPECTJ) public class RatingServiceApplication { - @Autowired - private EurekaClient eurekaClient; - @Autowired - private SpanMetricReporter spanMetricReporter; - @Autowired - private ZipkinProperties zipkinProperties; - @Value("${spring.sleuth.web.skipPattern}") - private String skipPattern; public static void main(String[] args) { SpringApplication.run(RatingServiceApplication.class, args); } - - @Bean - public ZipkinSpanReporter makeZipkinSpanReporter() { - return new ZipkinSpanReporter() { - private HttpZipkinSpanReporter delegate; - private String baseUrl; - - @Override - public void report(Span span) { - InstanceInfo instance = eurekaClient.getNextServerFromEureka("zipkin", false); - if (baseUrl == null || !instance.getHomePageUrl().equals(baseUrl)) { - baseUrl = instance.getHomePageUrl(); - } - delegate = new HttpZipkinSpanReporter(new RestTemplate(), baseUrl, zipkinProperties.getFlushInterval(), spanMetricReporter); - if (!span.name.matches(skipPattern)) delegate.report(span); - } - }; - } - - @Bean - @Primary - @Order(value=Ordered.HIGHEST_PRECEDENCE) - public HystrixCommandAspect hystrixAspect() { - return new HystrixCommandAspect(); - } } \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SecurityConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SecurityConfig.java index 9b6afc8059..f470946e1d 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SecurityConfig.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SecurityConfig.java @@ -1,39 +1,41 @@ package com.baeldung.spring.cloud.bootstrap.svcrating; -import org.springframework.beans.factory.annotation.Autowired; +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.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; @EnableWebSecurity @Configuration -public class SecurityConfig extends WebSecurityConfigurerAdapter { +public class SecurityConfig { - @Autowired - public void configureGlobal1(AuthenticationManagerBuilder auth) throws Exception { - //try in memory auth with no users to support the case that this will allow for users that are logged in to go anywhere - auth.inMemoryAuthentication(); + @Bean + public UserDetailsService users() { + return new InMemoryUserDetailsManager(); } - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .regexMatchers("^/ratings\\?bookId.*$").authenticated() - .antMatchers(HttpMethod.POST,"/ratings").authenticated() - .antMatchers(HttpMethod.PATCH,"/ratings/*").hasRole("ADMIN") - .antMatchers(HttpMethod.DELETE,"/ratings/*").hasRole("ADMIN") - .antMatchers(HttpMethod.GET,"/ratings").hasRole("ADMIN") - .antMatchers(HttpMethod.GET,"/hystrix").authenticated() - .anyRequest().authenticated() + @Bean + public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { + return httpSecurity.authorizeHttpRequests((auth) -> auth.regexMatchers("^/ratings\\?bookId.*$") + .authenticated() + .antMatchers(HttpMethod.POST, "/ratings") + .authenticated() + .antMatchers(HttpMethod.PATCH, "/ratings/*") + .hasRole("ADMIN") + .antMatchers(HttpMethod.DELETE, "/ratings/*") + .hasRole("ADMIN") + .antMatchers(HttpMethod.GET, "/ratings") + .hasRole("ADMIN") + .anyRequest() + .authenticated()) + .httpBasic() .and() - .httpBasic().and() - .csrf() - .disable(); - - + .csrf() + .disable() + .build(); } } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SessionConfig.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SessionConfig.java index 6e8fcd10d4..c0e067026d 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SessionConfig.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/SessionConfig.java @@ -5,24 +5,24 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.env.Environment; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer; @Configuration @EnableRedisHttpSession public class SessionConfig extends AbstractHttpSessionApplicationInitializer { + @Autowired - Environment properties; - + private Environment properties; + @Bean @Primary - public JedisConnectionFactory connectionFactory() { - JedisConnectionFactory factory = new JedisConnectionFactory(); - factory.setHostName(properties.getProperty("spring.redis.host","localhost")); - factory.setPort(properties.getProperty("spring.redis.port", Integer.TYPE,6379)); - factory.afterPropertiesSet(); - factory.setUsePool(true); - return factory; + public LettuceConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration(); + redisConfiguration.setHostName(properties.getProperty("spring.redis.host", "localhost")); + redisConfiguration.setPort(properties.getProperty("spring.redis.port", Integer.TYPE, 6379)); + return new LettuceConnectionFactory(redisConfiguration); } } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepository.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepository.java index d9f3a3584e..1263093b80 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepository.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingCacheRepository.java @@ -6,21 +6,21 @@ import java.util.stream.Collectors; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.SetOperations; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Repository; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Repository; @Repository public class RatingCacheRepository implements InitializingBean { @Autowired - private JedisConnectionFactory cacheConnectionFactory; + private LettuceConnectionFactory cacheConnectionFactory; private StringRedisTemplate redisTemplate; private ValueOperations valueOps; diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java index 395ff50bd7..e02803bff3 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/java/com/baeldung/spring/cloud/bootstrap/svcrating/rating/RatingService.java @@ -2,7 +2,6 @@ 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.stereotype.Service; @@ -10,7 +9,8 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.google.common.base.Preconditions; -import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; + +import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; @Service @Transactional(readOnly = true) @@ -22,31 +22,31 @@ public class RatingService { @Autowired private RatingCacheRepository cacheRepository; - @HystrixCommand(commandKey = "ratingsByBookIdFromDB", fallbackMethod = "findCachedRatingsByBookId") + @CircuitBreaker(name = "ratingsByBookIdFromDB", fallbackMethod = "findCachedRatingsByBookId") public List findRatingsByBookId(Long bookId) { return ratingRepository.findRatingsByBookId(bookId); } - public List findCachedRatingsByBookId(Long bookId) { + public List findCachedRatingsByBookId(Long bookId, Exception exception) { return cacheRepository.findCachedRatingsByBookId(bookId); } - @HystrixCommand(commandKey = "ratingsFromDB", fallbackMethod = "findAllCachedRatings") + @CircuitBreaker(name = "ratingsFromDB", fallbackMethod = "findAllCachedRatings") public List findAllRatings() { return ratingRepository.findAll(); } - public List findAllCachedRatings() { + public List findAllCachedRatings(Exception exception) { return cacheRepository.findAllCachedRatings(); } - @HystrixCommand(commandKey = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById", ignoreExceptions = { RatingNotFoundException.class }) + @CircuitBreaker(name = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById") public Rating findRatingById(Long ratingId) { - return Optional.ofNullable(ratingRepository.findOne(ratingId)) + return ratingRepository.findById(ratingId) .orElseThrow(() -> new RatingNotFoundException("Rating not found. ID: " + ratingId)); } - public Rating findCachedRatingById(Long ratingId) { + public Rating findCachedRatingById(Long ratingId, Exception exception) { return cacheRepository.findCachedRatingById(ratingId); } @@ -62,7 +62,7 @@ public class RatingService { @Transactional(propagation = Propagation.REQUIRED) public void deleteRating(Long ratingId) { - ratingRepository.delete(ratingId); + ratingRepository.deleteById(ratingId); cacheRepository.deleteRating(ratingId); } @@ -86,7 +86,7 @@ public class RatingService { public Rating updateRating(Rating rating, Long ratingId) { Preconditions.checkNotNull(rating); Preconditions.checkState(rating.getId() == ratingId); - Preconditions.checkNotNull(ratingRepository.findOne(ratingId)); + Preconditions.checkNotNull(ratingRepository.findById(ratingId)); return ratingRepository.save(rating); } diff --git a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/resources/bootstrap.properties b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/resources/bootstrap.properties index be5cf7f1e1..846bc4c7aa 100644 --- a/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/resources/bootstrap.properties +++ b/spring-cloud-modules/spring-cloud-bootstrap/svc-rating/src/main/resources/bootstrap.properties @@ -3,5 +3,7 @@ spring.cloud.config.discovery.service-id=config spring.cloud.config.discovery.enabled=true spring.cloud.config.username=configUser spring.cloud.config.password=configPassword +spring.cloud.gateway.discovery.locator.enabled=true +spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/ diff --git a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/README.md b/spring-cloud-modules/spring-cloud-bootstrap/zipkin/README.md new file mode 100644 index 0000000000..66f150ede0 --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/zipkin/README.md @@ -0,0 +1,19 @@ +# Zipkin server + +Zipkin project [deprecated custom server](https://github.com/openzipkin/zipkin/tree/master/zipkin-server). +It's no longer possible to run a custom Zipkin server compatible with Spring Cloud or even Spring Boot. + +The best approach to run a Zipkin server is to use docker. We provided a docker-compose file that you can run: + +```bash +$ docker compose up -d +``` + +After that Zipkin is accessible via [http://localhost:9411](http://localhost:9411) + +Alternatively, you can run the Zipkin Jar file, + +```bash +$ curl -sSL https://zipkin.io/quickstart.sh | bash -s +$ java -jar zipkin.jar +``` \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/docker-compose.yml b/spring-cloud-modules/spring-cloud-bootstrap/zipkin/docker-compose.yml new file mode 100644 index 0000000000..20528dca8f --- /dev/null +++ b/spring-cloud-modules/spring-cloud-bootstrap/zipkin/docker-compose.yml @@ -0,0 +1,6 @@ +version: "3.9" +services: + zipkin: + image: openzipkin/zipkin + ports: + - 9411:9411 \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/pom.xml b/spring-cloud-modules/spring-cloud-bootstrap/zipkin/pom.xml deleted file mode 100644 index b515661a00..0000000000 --- a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/pom.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - 4.0.0 - zipkin - 1.0.0-SNAPSHOT - zipkin - - - com.baeldung - parent-boot-1 - 0.0.1-SNAPSHOT - ../../../parent-boot-1 - - - - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud-dependencies.version} - pom - import - - - - - - - org.springframework.cloud - spring-cloud-starter-config - - - org.springframework.cloud - spring-cloud-starter-eureka - - - io.zipkin.java - zipkin-server - - - io.zipkin.java - zipkin-autoconfigure-ui - runtime - - - - - Brixton.SR7 - - - \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/main/java/com/baeldung/spring/cloud/bootstrap/zipkin/ZipkinApplication.java b/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/main/java/com/baeldung/spring/cloud/bootstrap/zipkin/ZipkinApplication.java deleted file mode 100644 index d757567156..0000000000 --- a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/main/java/com/baeldung/spring/cloud/bootstrap/zipkin/ZipkinApplication.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.baeldung.spring.cloud.bootstrap.zipkin; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; -import zipkin.server.EnableZipkinServer; - -@SpringBootApplication -@EnableEurekaClient -@EnableZipkinServer -public class ZipkinApplication { - public static void main(String[] args) { - SpringApplication.run(ZipkinApplication.class, args); - } -} diff --git a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/main/resources/bootstrap.properties b/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/main/resources/bootstrap.properties deleted file mode 100644 index 9569179a4f..0000000000 --- a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/main/resources/bootstrap.properties +++ /dev/null @@ -1,7 +0,0 @@ -spring.cloud.config.name=zipkin -spring.cloud.config.discovery.service-id=config -spring.cloud.config.discovery.enabled=true -spring.cloud.config.username=configUser -spring.cloud.config.password=configPassword - -eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/ \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/main/resources/logback.xml b/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/main/resources/logback.xml deleted file mode 100644 index 7d900d8ea8..0000000000 --- a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/main/resources/logback.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - \ No newline at end of file diff --git a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/test/java/com/baeldung/SpringContextTest.java b/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/test/java/com/baeldung/SpringContextTest.java deleted file mode 100644 index 71e67df191..0000000000 --- a/spring-cloud-modules/spring-cloud-bootstrap/zipkin/src/test/java/com/baeldung/SpringContextTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.baeldung; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -import com.baeldung.spring.cloud.bootstrap.zipkin.ZipkinApplication; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = ZipkinApplication.class) -public class SpringContextTest { - - @Test - public void whenSpringContextIsBootstrapped_thenNoExceptions() { - } -} diff --git a/spring-ejb-modules/pom.xml b/spring-ejb-modules/pom.xml index 146d64c298..1ebfb66c32 100755 --- a/spring-ejb-modules/pom.xml +++ b/spring-ejb-modules/pom.xml @@ -55,23 +55,6 @@ - - - jboss-public-repository-group - JBoss Public Maven Repository Group - http://repository.jboss.org/nexus/content/groups/public/ - default - - true - never - - - true - never - - - - diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml index 030f1ef876..2c9a8e3dfe 100644 --- a/spring-security-modules/pom.xml +++ b/spring-security-modules/pom.xml @@ -20,7 +20,7 @@ spring-security-cognito spring-security-core spring-security-core-2 - spring-security-ldap + spring-security-legacy-oidc spring-security-oauth2 spring-security-oauth2-sso diff --git a/spring-security-modules/spring-security-oauth2-sso/pom.xml b/spring-security-modules/spring-security-oauth2-sso/pom.xml index c9f9274c98..a87e4d7814 100644 --- a/spring-security-modules/spring-security-oauth2-sso/pom.xml +++ b/spring-security-modules/spring-security-oauth2-sso/pom.xml @@ -3,7 +3,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.baeldung spring-security-oauth2-sso 1.0.0-SNAPSHOT spring-security-oauth2-sso diff --git a/spring-security-modules/spring-security-oauth2-sso/spring-security-sso-auth-server/pom.xml b/spring-security-modules/spring-security-oauth2-sso/spring-security-sso-auth-server/pom.xml index 1a8d1b580f..9ecea81ed3 100644 --- a/spring-security-modules/spring-security-oauth2-sso/spring-security-sso-auth-server/pom.xml +++ b/spring-security-modules/spring-security-oauth2-sso/spring-security-sso-auth-server/pom.xml @@ -23,6 +23,27 @@ spring-security-oauth2 ${oauth.version} + + jakarta.annotation + jakarta.annotation-api + ${jakarta.annotation-api.version} + + + jakarta.xml.bind + jakarta.xml.bind-api + ${jakarta.xml.bind-api.version} + + + org.glassfish.jaxb + jaxb-runtime + ${jaxb-runtime.version} + + + 1.3.5 + 2.3.3 + 2.3.5 + + \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-boot-4/README.md b/spring-security-modules/spring-security-web-boot-4/README.md index af8ed4e76a..8a7dbf3029 100644 --- a/spring-security-modules/spring-security-web-boot-4/README.md +++ b/spring-security-modules/spring-security-web-boot-4/README.md @@ -9,5 +9,5 @@ The "REST With Spring" Classes: http://github.learnspringsecurity.com - [Spring Security: Upgrading the Deprecated WebSecurityConfigurerAdapter](https://www.baeldung.com/spring-deprecated-websecurityconfigureradapter) - [Spring @EnableMethodSecurity Annotation](https://www.baeldung.com/spring-enablemethodsecurity) - +- [Securing Spring Boot API With API Key and Secret](https://www.baeldung.com/spring-boot-api-key-secret) More articles: [[<-- prev]](/spring-security-modules/spring-security-web-boot-3) diff --git a/spring-security-modules/spring-security-web-mvc-custom/pom.xml b/spring-security-modules/spring-security-web-mvc-custom/pom.xml index 104b7025ad..f21c6dbe40 100644 --- a/spring-security-modules/spring-security-web-mvc-custom/pom.xml +++ b/spring-security-modules/spring-security-web-mvc-custom/pom.xml @@ -126,6 +126,11 @@ ${spring-security.version} test + + javax.annotation + javax.annotation-api + ${javax.annotation-api.version} + @@ -169,6 +174,7 @@ 3.2.2 1.6.1 + 1.3.2 \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-rest-basic-auth/pom.xml b/spring-security-modules/spring-security-web-rest-basic-auth/pom.xml index 291d732049..3c842a8a54 100644 --- a/spring-security-modules/spring-security-web-rest-basic-auth/pom.xml +++ b/spring-security-modules/spring-security-web-rest-basic-auth/pom.xml @@ -127,6 +127,11 @@ spring-test test + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + @@ -220,6 +225,7 @@ + 2.3.1 4.4.11 4.5.8 diff --git a/spring-security-modules/spring-security-web-rest-custom/pom.xml b/spring-security-modules/spring-security-web-rest-custom/pom.xml index d420c45df2..dfd2f59aaf 100644 --- a/spring-security-modules/spring-security-web-rest-custom/pom.xml +++ b/spring-security-modules/spring-security-web-rest-custom/pom.xml @@ -115,6 +115,11 @@ commons-lang3 ${commons-lang3.version} + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + @@ -165,6 +170,7 @@ 1.2 + 2.3.1 1.6.1 diff --git a/spring-security-modules/spring-security-web-sockets/pom.xml b/spring-security-modules/spring-security-web-sockets/pom.xml index 802c894612..513ee28c85 100644 --- a/spring-security-modules/spring-security-web-sockets/pom.xml +++ b/spring-security-modules/spring-security-web-sockets/pom.xml @@ -149,6 +149,12 @@ ${spring-boot-starter-test.version} test + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + @@ -168,12 +174,22 @@ org.apache.maven.plugins maven-war-plugin - 3.0.0 + ${maven-war-plugin.version} src/main/webapp false + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.lang.invoke=ALL-UNNAMED + + + @@ -182,6 +198,7 @@ 1.11.3.RELEASE 1.5.10.RELEASE 1.7.6 + 2.3.1 \ No newline at end of file diff --git a/spring-web-modules/spring-freemarker/pom.xml b/spring-web-modules/spring-freemarker/pom.xml index 07d37736b6..9ac01fd106 100644 --- a/spring-web-modules/spring-freemarker/pom.xml +++ b/spring-web-modules/spring-freemarker/pom.xml @@ -55,9 +55,23 @@ + + + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.lang=ALL-UNNAMED + + + + + + 5.0.8.RELEASE - 2.3.28 + 2.3.32 false 1.5.10.RELEASE diff --git a/spring-web-modules/spring-mvc-basics-2/pom.xml b/spring-web-modules/spring-mvc-basics-2/pom.xml index 79d1531274..28eb3a16f2 100644 --- a/spring-web-modules/spring-mvc-basics-2/pom.xml +++ b/spring-web-modules/spring-mvc-basics-2/pom.xml @@ -136,7 +136,6 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} src/main/webapp springMvcSimple @@ -149,8 +148,6 @@ - 1.8 - 1.8 6.0.10.Final enter-location-of-server 3.0.11.RELEASE diff --git a/spring-web-modules/spring-mvc-basics-5/pom.xml b/spring-web-modules/spring-mvc-basics-5/pom.xml index 0f2b0bc7bd..c957d669bd 100644 --- a/spring-web-modules/spring-mvc-basics-5/pom.xml +++ b/spring-web-modules/spring-mvc-basics-5/pom.xml @@ -41,14 +41,18 @@ org.apache.commons commons-io - 1.3.2 + ${commons-io.version} com.jayway.jsonpath json-path - 2.7.0 + ${json-path.version} + + + org.glassfish.jaxb + jaxb-runtime + ${jaxb-runtime.version} - @@ -65,4 +69,10 @@ + + 1.3.2 + 2.7.0 + 2.3.5 + + \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-basics/pom.xml b/spring-web-modules/spring-mvc-basics/pom.xml index ccd6773e6f..0f1d423ca2 100644 --- a/spring-web-modules/spring-mvc-basics/pom.xml +++ b/spring-web-modules/spring-mvc-basics/pom.xml @@ -39,6 +39,11 @@ spring-boot-starter-test test + + org.glassfish.jaxb + jaxb-runtime + ${jaxb-runtime.version} + @@ -55,4 +60,8 @@ + + 2.3.5 + + \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-crash/pom.xml b/spring-web-modules/spring-mvc-crash/pom.xml index 2b7b79666d..f3faa18816 100644 --- a/spring-web-modules/spring-mvc-crash/pom.xml +++ b/spring-web-modules/spring-mvc-crash/pom.xml @@ -124,7 +124,15 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} + + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.lang=ALL-UNNAMED + + diff --git a/spring-web-modules/spring-mvc-forms-jsp/pom.xml b/spring-web-modules/spring-mvc-forms-jsp/pom.xml index b0269e3344..3ad815e95f 100644 --- a/spring-web-modules/spring-mvc-forms-jsp/pom.xml +++ b/spring-web-modules/spring-mvc-forms-jsp/pom.xml @@ -68,6 +68,11 @@ ${spring-boot-starter-test.version} test + + javax.annotation + javax.annotation-api + ${javax.annotation-api.version} + @@ -83,7 +88,6 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} src/main/webapp spring-mvc-forms @@ -101,6 +105,7 @@ 6.0.10.Final 5.2.5.Final 6.0.6 + 1.3.2 \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-java-2/pom.xml b/spring-web-modules/spring-mvc-java-2/pom.xml index 1bbb066786..d88a9c320a 100644 --- a/spring-web-modules/spring-mvc-java-2/pom.xml +++ b/spring-web-modules/spring-mvc-java-2/pom.xml @@ -36,6 +36,11 @@ commons-io ${commons-io.version} + + org.glassfish.jaxb + jaxb-runtime + ${jaxb-runtime.version} + @@ -51,6 +56,7 @@ 4.0.1 5.2.2.RELEASE + 2.3.5 \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-java/pom.xml b/spring-web-modules/spring-mvc-java/pom.xml index 208973eccb..8098f07282 100644 --- a/spring-web-modules/spring-mvc-java/pom.xml +++ b/spring-web-modules/spring-mvc-java/pom.xml @@ -126,7 +126,6 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} false @@ -227,7 +226,6 @@ 4.5.2 2.23 - 3.2.2 2.7 1.6.1 3.1.0 diff --git a/spring-web-modules/spring-mvc-velocity/pom.xml b/spring-web-modules/spring-mvc-velocity/pom.xml index 1b1e8b1ea4..676fa09dac 100644 --- a/spring-web-modules/spring-mvc-velocity/pom.xml +++ b/spring-web-modules/spring-mvc-velocity/pom.xml @@ -88,11 +88,19 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} false + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.lang=ALL-UNNAMED + + + diff --git a/spring-web-modules/spring-mvc-views/pom.xml b/spring-web-modules/spring-mvc-views/pom.xml index 79cf82bc0b..d2b5d45366 100644 --- a/spring-web-modules/spring-mvc-views/pom.xml +++ b/spring-web-modules/spring-mvc-views/pom.xml @@ -83,16 +83,10 @@ org.apache.maven.plugins maven-compiler-plugin - 2.0.2 - - ${java.version} - ${java.version} - org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} src/main/webapp spring-mvc-views diff --git a/spring-web-modules/spring-mvc-webflow/pom.xml b/spring-web-modules/spring-mvc-webflow/pom.xml index 69985a7b9d..a128103bac 100644 --- a/spring-web-modules/spring-mvc-webflow/pom.xml +++ b/spring-web-modules/spring-mvc-webflow/pom.xml @@ -71,7 +71,7 @@ org.apache.tomee.maven tomee-maven-plugin - 8.0.1 + ${tomee-maven-plugin.version} 8080 spring-mvc-webflow @@ -91,11 +91,19 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} false + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.lang=ALL-UNNAMED + + + @@ -110,6 +118,7 @@ 2.7 1.6.1 1.5.10.RELEASE + 8.0.1 \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-xml-2/pom.xml b/spring-web-modules/spring-mvc-xml-2/pom.xml index 5910823e95..f4326ccf68 100644 --- a/spring-web-modules/spring-mvc-xml-2/pom.xml +++ b/spring-web-modules/spring-mvc-xml-2/pom.xml @@ -81,7 +81,15 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} + + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.lang=ALL-UNNAMED + + diff --git a/spring-web-modules/spring-mvc-xml/pom.xml b/spring-web-modules/spring-mvc-xml/pom.xml index d8d4082219..bf0dc52b68 100644 --- a/spring-web-modules/spring-mvc-xml/pom.xml +++ b/spring-web-modules/spring-mvc-xml/pom.xml @@ -119,7 +119,15 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} + + + org.apache.maven.plugins + maven-surefire-plugin + + + --add-opens java.base/java.lang=ALL-UNNAMED + + diff --git a/spring-web-modules/spring-thymeleaf-4/pom.xml b/spring-web-modules/spring-thymeleaf-4/pom.xml index 8063b14c39..163d590c9f 100644 --- a/spring-web-modules/spring-thymeleaf-4/pom.xml +++ b/spring-web-modules/spring-thymeleaf-4/pom.xml @@ -107,7 +107,6 @@ org.apache.maven.plugins maven-war-plugin - ${maven-war-plugin.version} false diff --git a/testing-modules/pom.xml b/testing-modules/pom.xml index a6b5fba570..9080d5a0ea 100644 --- a/testing-modules/pom.xml +++ b/testing-modules/pom.xml @@ -52,6 +52,7 @@ xmlunit-2 zerocode mockito-2 + testing-techniques gatling-java diff --git a/testing-modules/testing-libraries-2/README.md b/testing-modules/testing-libraries-2/README.md index 7cc08a8140..d075c40919 100644 --- a/testing-modules/testing-libraries-2/README.md +++ b/testing-modules/testing-libraries-2/README.md @@ -4,3 +4,4 @@ - [Guide to the System Stubs Library](https://www.baeldung.com/java-system-stubs) - [Code Coverage with SonarQube and JaCoCo](https://www.baeldung.com/sonarqube-jacoco-code-coverage) - [Exclusions from Jacoco Report](https://www.baeldung.com/jacoco-report-exclude) +- [Gray Box Testing Using the OAT Technique](https://www.baeldung.com/java-gray-box-orthogonal-array-testing) diff --git a/testing-modules/testing-techniques/pom.xml b/testing-modules/testing-techniques/pom.xml new file mode 100644 index 0000000000..5902047aeb --- /dev/null +++ b/testing-modules/testing-techniques/pom.xml @@ -0,0 +1,14 @@ + + + + testing-techniques + 4.0.0 + + + testing-modules + com.baeldung + 1.0.0-SNAPSHOT + + \ No newline at end of file diff --git a/testing-modules/testing-techniques/src/main/java/com/baeldung/greyboxtesting/SalaryCommissionPercentageCalculator.java b/testing-modules/testing-techniques/src/main/java/com/baeldung/greyboxtesting/SalaryCommissionPercentageCalculator.java new file mode 100644 index 0000000000..d97b6687b3 --- /dev/null +++ b/testing-modules/testing-techniques/src/main/java/com/baeldung/greyboxtesting/SalaryCommissionPercentageCalculator.java @@ -0,0 +1,67 @@ +package com.baeldung.greyboxtesting; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.stream.DoubleStream; + +public class SalaryCommissionPercentageCalculator { + public BigDecimal calculate(Level level, Type type, Seniority seniority, SalesImpact impact) { + return BigDecimal.valueOf(DoubleStream.of(level.getBonus(), type.getBonus(), seniority.getBonus(), impact.getBonus(), type.getBonus()) + .average() + .orElse(0)) + .setScale(2, RoundingMode.CEILING); + } + + public enum Level { + L1(0.06), L2(0.12), L3(0.2); + private double bonus; + + Level(double bonus) { + this.bonus = bonus; + } + + public double getBonus() { + return bonus; + } + } + + public enum Type { + FULL_TIME_COMMISSIONED(0.18), CONTRACTOR(0.1), FREELANCER(0.06); + + private double bonus; + + Type(double bonus) { + this.bonus = bonus; + } + + public double getBonus() { + return bonus; + } + } + + public enum Seniority { + JR(0.8), MID(0.13), SR(0.19); + private double bonus; + + Seniority(double bonus) { + this.bonus = bonus; + } + + public double getBonus() { + return bonus; + } + } + + public enum SalesImpact { + LOW(0.06), MEDIUM(0.12), HIGH(0.2); + private double bonus; + + SalesImpact(double bonus) { + this.bonus = bonus; + } + + public double getBonus() { + return bonus; + } + } +} diff --git a/testing-modules/testing-techniques/src/test/java/com/baeldung/greyboxtesting/SalaryCommissionPercentageCalculatorUnitTest.java b/testing-modules/testing-techniques/src/test/java/com/baeldung/greyboxtesting/SalaryCommissionPercentageCalculatorUnitTest.java new file mode 100644 index 0000000000..e39a1e5445 --- /dev/null +++ b/testing-modules/testing-techniques/src/test/java/com/baeldung/greyboxtesting/SalaryCommissionPercentageCalculatorUnitTest.java @@ -0,0 +1,41 @@ +package com.baeldung.greyboxtesting; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.math.BigDecimal; +import java.util.stream.Stream; + +import static com.baeldung.greyboxtesting.SalaryCommissionPercentageCalculator.*; +import static com.baeldung.greyboxtesting.SalaryCommissionPercentageCalculator.Level.*; +import static com.baeldung.greyboxtesting.SalaryCommissionPercentageCalculator.SalesImpact.*; +import static com.baeldung.greyboxtesting.SalaryCommissionPercentageCalculator.Seniority.*; +import static com.baeldung.greyboxtesting.SalaryCommissionPercentageCalculator.Type.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SalaryCommissionPercentageCalculatorUnitTest { + + private SalaryCommissionPercentageCalculator testTarget = new SalaryCommissionPercentageCalculator(); + + @ParameterizedTest + @MethodSource("provideReferenceTestScenarioTable") + void givenReferenceTable_whenCalculateAverageCommission_thenReturnExpectedResult(Level level, Type type, Seniority seniority, SalesImpact impact, double expected) { + BigDecimal got = testTarget.calculate(level, type, seniority, impact); + assertEquals(BigDecimal.valueOf(expected), got); + } + + private static Stream provideReferenceTestScenarioTable() { + return Stream.of( + Arguments.of(L1, FULL_TIME_COMMISSIONED, JR, LOW, 0.26), + Arguments.of(L1, CONTRACTOR, SR, MEDIUM, 0.12), + Arguments.of(L1, FREELANCER, MID, HIGH, 0.11), + Arguments.of(L2, FULL_TIME_COMMISSIONED, SR, HIGH, 0.18), + Arguments.of(L2, CONTRACTOR, MID, LOW, 0.11), + Arguments.of(L2, FREELANCER, JR, MEDIUM, 0.24), + Arguments.of(L3, FULL_TIME_COMMISSIONED, MID, MEDIUM, 0.17), + Arguments.of(L3, CONTRACTOR, JR, HIGH, 0.28), + Arguments.of(L3, FREELANCER, SR, LOW, 0.12) + ); + } +} \ No newline at end of file diff --git a/web-modules/pom.xml b/web-modules/pom.xml index 97134ee31c..efd87ba642 100644 --- a/web-modules/pom.xml +++ b/web-modules/pom.xml @@ -23,7 +23,7 @@ javax-servlets javax-servlets-2 - jee-7 + jooby linkrest ninja diff --git a/web-modules/ratpack/src/main/java/com/baeldung/Application.java b/web-modules/ratpack/src/main/java/com/baeldung/Application.java index 235a6f0068..8035a1d0e3 100644 --- a/web-modules/ratpack/src/main/java/com/baeldung/Application.java +++ b/web-modules/ratpack/src/main/java/com/baeldung/Application.java @@ -8,6 +8,7 @@ import com.baeldung.repository.EmployeeRepository; import com.baeldung.repository.EmployeeRepositoryImpl; import com.zaxxer.hikari.HikariConfig; import io.netty.buffer.PooledByteBufAllocator; +import ratpack.exec.internal.DefaultExecController; import ratpack.func.Action; import ratpack.func.Function; import ratpack.guice.BindingsSpec; @@ -42,7 +43,7 @@ public class Application { .maxContentLength(ServerConfig.DEFAULT_MAX_CONTENT_LENGTH) .responseMaxChunkSize(16384) .readTimeout(Duration.of(60, ChronoUnit.SECONDS)) - .byteBufAllocator(PooledByteBufAllocator.DEFAULT); + .byteBufAllocator(PooledByteBufAllocator.DEFAULT).execController(new DefaultExecController(2)); }); final Function registryFunction = Guice.registry(bindingsSpecAction); diff --git a/xml-2/README.md b/xml-2/README.md index 383d0763d4..e91078dbf0 100644 --- a/xml-2/README.md +++ b/xml-2/README.md @@ -6,3 +6,4 @@ This module contains articles about eXtensible Markup Language (XML) - [Pretty-Print XML in Java](https://www.baeldung.com/java-pretty-print-xml) - [Validate an XML File Against an XSD File](https://www.baeldung.com/java-validate-xml-xsd) +- [Converting JSON to XML in Java](https://www.baeldung.com/java-convert-json-to-xml)