Merge branch 'master' into master
This commit is contained in:
commit
2c0482c409
|
@ -90,6 +90,12 @@
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mock-server</groupId>
|
||||||
|
<artifactId>mockserver-netty</artifactId>
|
||||||
|
<version>${mockserver.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.tomakehurst</groupId>
|
<groupId>com.github.tomakehurst</groupId>
|
||||||
<artifactId>wiremock</artifactId>
|
<artifactId>wiremock</artifactId>
|
||||||
|
@ -112,6 +118,7 @@
|
||||||
<!-- util -->
|
<!-- util -->
|
||||||
<httpasyncclient.version>4.1.4</httpasyncclient.version>
|
<httpasyncclient.version>4.1.4</httpasyncclient.version>
|
||||||
<!-- testing -->
|
<!-- testing -->
|
||||||
|
<mockserver.version>5.6.1</mockserver.version>
|
||||||
<wiremock.version>2.5.1</wiremock.version>
|
<wiremock.version>2.5.1</wiremock.version>
|
||||||
<httpclient.version>4.5.8</httpclient.version> <!-- 4.3.6 --> <!-- 4.4-beta1 -->
|
<httpclient.version>4.5.8</httpclient.version> <!-- 4.3.6 --> <!-- 4.4-beta1 -->
|
||||||
<!-- http client & core 5 -->
|
<!-- http client & core 5 -->
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ import org.apache.hc.core5.ssl.SSLContexts;
|
||||||
import org.apache.hc.core5.ssl.TrustStrategy;
|
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 = "http://www.google.com";
|
||||||
private static final String HOST_WITH_SSL = "https://mms.nw.ru/";
|
private static final String HOST_WITH_SSL = "https://mms.nw.ru/";
|
||||||
|
|
|
@ -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)
|
- [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)
|
- [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)
|
- [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)
|
||||||
|
|
|
@ -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<String, String> producer;
|
||||||
|
private static KafkaConsumer<String, String> consumer;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
publishMessages();
|
||||||
|
|
||||||
|
consumeFromBeginning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void consumeFromBeginning() {
|
||||||
|
consumer.subscribe(Arrays.asList(TOPIC));
|
||||||
|
|
||||||
|
ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(10));
|
||||||
|
|
||||||
|
for (ConsumerRecord<String, String> record : records) {
|
||||||
|
logger.info(record.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
consumer.seekToBeginning(consumer.assignment());
|
||||||
|
|
||||||
|
records = consumer.poll(Duration.ofSeconds(10));
|
||||||
|
|
||||||
|
for (ConsumerRecord<String, String> record : records) {
|
||||||
|
logger.info(record.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void publishMessages() {
|
||||||
|
for (int i = 1; i <= messagesInTopic; i++) {
|
||||||
|
ProducerRecord<String, String> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<String, String> producer;
|
||||||
|
private static KafkaConsumer<String, String> 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<String, String> records = consumer.poll(Duration.ofSeconds(5));
|
||||||
|
for (ConsumerRecord<String, String> 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<String, String> record = new ProducerRecord<>(TOPIC, MESSAGE_KEY, String.valueOf(i));
|
||||||
|
Future<RecordMetadata> 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<String, String> record = new ProducerRecord<>(TOPIC, String.valueOf(i));
|
||||||
|
Future<RecordMetadata> 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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<String, String> producer;
|
||||||
|
private static KafkaConsumer<String, String> 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<String, String> 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<String, String> records = consumer.poll(Duration.ofSeconds(10));
|
||||||
|
|
||||||
|
int messageCount = 0;
|
||||||
|
for (ConsumerRecord<String, String> record : records) {
|
||||||
|
logger.info(record.value());
|
||||||
|
messageCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(messagesInTopic, messageCount);
|
||||||
|
|
||||||
|
consumer.seekToBeginning(consumer.assignment());
|
||||||
|
|
||||||
|
records = consumer.poll(Duration.ofSeconds(10));
|
||||||
|
|
||||||
|
messageCount = 0;
|
||||||
|
for (ConsumerRecord<String, String> record : records) {
|
||||||
|
logger.info(record.value());
|
||||||
|
messageCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(messagesInTopic, messageCount);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<String, String> producer;
|
||||||
|
private static KafkaConsumer<String, String> 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<String, String> producerRecord = new ProducerRecord<>(TOPIC, MESSAGE_KEY, MESSAGE_VALUE);
|
||||||
|
Future<RecordMetadata> future = producer.send(producerRecord);
|
||||||
|
|
||||||
|
RecordMetadata metadata = future.get();
|
||||||
|
|
||||||
|
assertNotNull(metadata);
|
||||||
|
|
||||||
|
consumer.subscribe(Arrays.asList(TOPIC));
|
||||||
|
|
||||||
|
ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(5));
|
||||||
|
for (ConsumerRecord<String, String> 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<String, String> producerRecord = new ProducerRecord<>(TOPIC, MESSAGE_KEY, MESSAGE_VALUE);
|
||||||
|
Future<RecordMetadata> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>apache-poi-2</artifactId>
|
<artifactId>apache-poi-2</artifactId>
|
||||||
|
@ -19,10 +19,15 @@
|
||||||
<artifactId>poi-ooxml</artifactId>
|
<artifactId>poi-ooxml</artifactId>
|
||||||
<version>${poi.version}</version>
|
<version>${poi.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-scratchpad</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<poi.version>5.2.0</poi.version>
|
<poi.version>5.2.3</poi.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<XWPFParagraph> paragraphs, String originalText, String updatedText) {
|
||||||
|
paragraphs.forEach(paragraph -> replaceTextInParagraph(paragraph, originalText, updatedText));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replaceTextInParagraph(XWPFParagraph paragraph, String originalText, String updatedText) {
|
||||||
|
List<XWPFRun> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<XWPFParagraph> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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)
|
- [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)
|
- [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)
|
- [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)
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
## Relevant Articles
|
## 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)
|
- [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)
|
||||||
|
|
|
@ -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<String, Object> map = convertUsingReflection(employee);
|
||||||
|
Assert.assertEquals(employee.getName(), map.get("name"));
|
||||||
|
Assert.assertEquals(employee.getSalary(), map.get("salary"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> convertUsingReflection(Object object) throws IllegalAccessException {
|
||||||
|
Map<String, Object> 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<String, Object> map = objectMapper.convertValue(employee, new TypeReference<Map<String, Object>>() {});
|
||||||
|
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<String, Object> map = gson.fromJson(json, new TypeToken<Map<String, Object>>() {}.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
- [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)
|
- [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)
|
- [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)
|
||||||
|
|
|
@ -3,33 +3,81 @@ package com.baeldung.regex.z_regexp;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
public class ZRegularExpressionUnitTest {
|
public class ZRegularExpressionUnitTest {
|
||||||
@Test
|
@Test
|
||||||
public void givenCreditCardNumber_thenReturnIfMatched() {
|
public void givenCreditCardNumber_thenReturnIfMatched() {
|
||||||
String creditCardNumber = "1234567890123456";
|
String creditCardNumber = "1234567890123456";
|
||||||
|
String creditCardNumber2 = "1234567890123456\n";
|
||||||
String pattern = "\\d{16}\\z";
|
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
|
@Test
|
||||||
public void givenLogOutput_thenReturnIfMatched() {
|
public void givenLogOutput_thenReturnIfMatched() {
|
||||||
String logLine = "2022-05-01 14:30:00,123 INFO Some log message";
|
String logLine = "2022-05-01 14:30:00,123 INFO Some log message";
|
||||||
String pattern = ".*message\\z";
|
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
|
@Test
|
||||||
public void givenEmailMessage_thenReturnIfMatched() {
|
public void givenEmailMessage_thenReturnIfMatched() {
|
||||||
String myMessage = "Hello HR, I hope i can write to Baeldung\n";
|
String myMessage = "Hello HR, I hope i can write to Baeldung\n";
|
||||||
String pattern = ".*Baeldung\\s*\\Z";
|
String myMessage2 = "Hello HR, I hope\n i can write to Baeldung";
|
||||||
Assertions.assertTrue(myMessage.matches(pattern));
|
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
|
@Test
|
||||||
public void givenFileExtension_thenReturnIfMatched() {
|
public void givenFileExtension_thenReturnIfMatched() {
|
||||||
String fileName = "image.jpeg";
|
String fileName = "image.jpeg\n";
|
||||||
|
String fileName2 = "image2.jpeg\n.png";
|
||||||
String pattern = ".*\\.jpeg\\Z";
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,3 +7,4 @@
|
||||||
- [Deserialization Vulnerabilities in Java](https://www.baeldung.com/java-deserialization-vulnerabilities)
|
- [Deserialization Vulnerabilities in Java](https://www.baeldung.com/java-deserialization-vulnerabilities)
|
||||||
- [Serialization Validation in Java](https://www.baeldung.com/java-validate-serializable)
|
- [Serialization Validation in Java](https://www.baeldung.com/java-validate-serializable)
|
||||||
- [What Is the serialVersionUID?](https://www.baeldung.com/java-serial-version-uid)
|
- [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)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,5 +3,5 @@
|
||||||
This module contains articles about Gson
|
This module contains articles about Gson
|
||||||
|
|
||||||
### Relevant Articles:
|
### Relevant Articles:
|
||||||
|
- [Solving Gson Parsing Errors](https://www.baeldung.com/gson-parsing-errors)
|
||||||
|
|
||||||
|
|
|
@ -129,13 +129,6 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>jitpack.io</id>
|
|
||||||
<url>https://jitpack.io</url>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>libraries-3</finalName>
|
<finalName>libraries-3</finalName>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
|
@ -49,10 +49,4 @@
|
||||||
<annotations.version>23.0.0</annotations.version>
|
<annotations.version>23.0.0</annotations.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>projectlombok.org</id>
|
|
||||||
<url>https://projectlombok.org/edge-releases</url>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
</project>
|
</project>
|
|
@ -27,7 +27,7 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<optaplanner-core.version>8.24.0.Final</optaplanner-core.version>
|
<optaplanner-core.version>9.38.0.Final</optaplanner-core.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -67,10 +67,10 @@
|
||||||
<hibernate-core.version>5.2.16.Final</hibernate-core.version>
|
<hibernate-core.version>5.2.16.Final</hibernate-core.version>
|
||||||
<mysql-connector.version>6.0.6</mysql-connector.version>
|
<mysql-connector.version>6.0.6</mysql-connector.version>
|
||||||
<spring-boot.version>2.7.5</spring-boot.version>
|
<spring-boot.version>2.7.5</spring-boot.version>
|
||||||
<rest-assured.version>3.3.0</rest-assured.version>
|
<rest-assured.version>5.3.0</rest-assured.version>
|
||||||
<spring-boot-starter-integration.version>2.7.5</spring-boot-starter-integration.version>
|
<spring-boot-starter-integration.version>2.7.5</spring-boot-starter-integration.version>
|
||||||
<spring-integration-test.version>5.5.14</spring-integration-test.version>
|
<spring-integration-test.version>5.5.14</spring-integration-test.version>
|
||||||
<camel-core.version>3.14.0</camel-core.version>
|
<camel-core.version>3.20.4</camel-core.version>
|
||||||
<camel-test-junit5.version>3.14.0</camel-test-junit5.version>
|
<camel-test-junit5.version>3.14.0</camel-test-junit5.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,13 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.resilience4j</groupId>
|
<groupId>io.github.resilience4j</groupId>
|
||||||
<artifactId>resilience4j-retry</artifactId>
|
<artifactId>resilience4j-retry</artifactId>
|
||||||
<version>1.7.1</version>
|
<version>${resilience4j-retry.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<resilience4j-retry.version>2.0.2</resilience4j-retry.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -1,6 +1,6 @@
|
||||||
package com.baeldung.backoff.jitter;
|
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.Retry;
|
||||||
import io.github.resilience4j.retry.RetryConfig;
|
import io.github.resilience4j.retry.RetryConfig;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -15,8 +15,8 @@ import java.util.concurrent.ExecutorService;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static com.baeldung.backoff.jitter.BackoffWithJitterUnitTest.RetryProperties.*;
|
import static com.baeldung.backoff.jitter.BackoffWithJitterUnitTest.RetryProperties.*;
|
||||||
import static io.github.resilience4j.retry.IntervalFunction.ofExponentialBackoff;
|
import static io.github.resilience4j.core.IntervalFunction.ofExponentialBackoff;
|
||||||
import static io.github.resilience4j.retry.IntervalFunction.ofExponentialRandomBackoff;
|
import static io.github.resilience4j.core.IntervalFunction.ofExponentialRandomBackoff;
|
||||||
import static java.util.Collections.nCopies;
|
import static java.util.Collections.nCopies;
|
||||||
import static java.util.concurrent.Executors.newFixedThreadPool;
|
import static java.util.concurrent.Executors.newFixedThreadPool;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
|
|
@ -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<Student> students;
|
||||||
|
|
||||||
|
public List<Student> 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<Student> students) {
|
||||||
|
this.students = students;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Employee> employees;
|
||||||
|
|
||||||
|
public List<Employee> getEmployees() {
|
||||||
|
return employees;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Course> 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<Course> getCourses() {
|
||||||
|
return courses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCourses(List<Course> courses) {
|
||||||
|
this.courses = courses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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<Book> 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<Book> getBooks() {
|
||||||
|
return books;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBooks(Set<Book> books) {
|
||||||
|
this.books = books;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Author> 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<Author> getAuthors() {
|
||||||
|
return authors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthors(Set<Author> authors) {
|
||||||
|
this.authors = authors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Employee> employees;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Employee> getEmployees() {
|
||||||
|
return employees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmployees(List<Employee> employees) {
|
||||||
|
this.employees = employees;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.baeldung.associations.unidirectional;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class ParkingSpot {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 + '}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 + '\'' + '}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Account, Integer> {
|
||||||
|
|
||||||
|
long countByUsername(String username);
|
||||||
|
|
||||||
|
long countByPermission(Permission permission);
|
||||||
|
|
||||||
|
long countByPermissionAndCreatedOnGreaterThan(Permission permission, Timestamp ts);
|
||||||
|
}
|
|
@ -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, Integer> {
|
||||||
|
Permission findByType(String type);
|
||||||
|
}
|
||||||
|
|
|
@ -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<Long> criteriaQuery = builder.createQuery(Long.class);
|
||||||
|
Root<Account> 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<Long> criteriaQuery = builder.createQuery(Long.class);
|
||||||
|
Root<Account> accountRoot = criteriaQuery.from(Account.class);
|
||||||
|
|
||||||
|
List<Predicate> 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<Long> criteriaQuery = builder.createQuery(Long.class);// query instance
|
||||||
|
Root<Account> accountRoot = criteriaQuery.from(Account.class); // root instance
|
||||||
|
|
||||||
|
// list of predicates that will go in where clause
|
||||||
|
List<Predicate> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -169,14 +169,6 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>dynamodb-local</id>
|
|
||||||
<name>DynamoDB Local Release Repository</name>
|
|
||||||
<url>${dynamodblocal.repository.url}</url>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- The main class to start by executing java -jar -->
|
<!-- The main class to start by executing java -jar -->
|
||||||
<start-class>com.baeldung.Application</start-class>
|
<start-class>com.baeldung.Application</start-class>
|
||||||
|
@ -186,9 +178,7 @@
|
||||||
<aws-java-sdk-dynamodb.version>1.11.64</aws-java-sdk-dynamodb.version>
|
<aws-java-sdk-dynamodb.version>1.11.64</aws-java-sdk-dynamodb.version>
|
||||||
<bootstrap.version>3.3.7-1</bootstrap.version>
|
<bootstrap.version>3.3.7-1</bootstrap.version>
|
||||||
<sqlite4java.version>1.0.392</sqlite4java.version>
|
<sqlite4java.version>1.0.392</sqlite4java.version>
|
||||||
<dynamodb.version>1.11.106</dynamodb.version>
|
<dynamodblocal.version>1.21.1</dynamodblocal.version>
|
||||||
<dynamodblocal.version>1.11.86</dynamodblocal.version>
|
|
||||||
<dynamodblocal.repository.url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</dynamodblocal.repository.url>
|
|
||||||
<maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version>
|
<maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version>
|
||||||
<spring-boot.version>2.4.7</spring-boot.version>
|
<spring-boot.version>2.4.7</spring-boot.version>
|
||||||
<log4j2.version>2.17.1</log4j2.version>
|
<log4j2.version>2.17.1</log4j2.version>
|
||||||
|
|
|
@ -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)
|
- [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)
|
- [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)
|
- [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)
|
- More articles: [[<-- prev]](../spring-data-jpa-repo-2)
|
||||||
|
|
13
pom.xml
13
pom.xml
|
@ -366,6 +366,7 @@
|
||||||
<module>muleesb</module>
|
<module>muleesb</module>
|
||||||
<module>web-modules/java-lite</module>
|
<module>web-modules/java-lite</module>
|
||||||
<module>web-modules/restx</module>
|
<module>web-modules/restx</module>
|
||||||
|
<module>web-modules/jee-7</module>
|
||||||
<module>persistence-modules/deltaspike</module> <!-- delta spike it doesn't support yet the jakarta API-->
|
<module>persistence-modules/deltaspike</module> <!-- delta spike it doesn't support yet the jakarta API-->
|
||||||
<module>persistence-modules/hibernate-ogm</module> <!-- hibernate-ogm wasn't updated because it doesn't support jakarta API -->
|
<module>persistence-modules/hibernate-ogm</module> <!-- hibernate-ogm wasn't updated because it doesn't support jakarta API -->
|
||||||
<module>persistence-modules/java-cassandra</module> <!-- JAVA-21464 cassandra-unit library doesn't support to run with jdk9 and above -->
|
<module>persistence-modules/java-cassandra</module> <!-- JAVA-21464 cassandra-unit library doesn't support to run with jdk9 and above -->
|
||||||
|
@ -425,11 +426,10 @@
|
||||||
|
|
||||||
<!-- <module>spring-roo</module> --> <!-- Not supported JAVA-17327 -->
|
<!-- <module>spring-roo</module> --> <!-- Not supported JAVA-17327 -->
|
||||||
|
|
||||||
<module>spring-security-modules</module>
|
<module>spring-security-modules/spring-security-ldap</module>
|
||||||
<module>spring-soap</module>
|
<module>spring-soap</module>
|
||||||
<module>spring-static-resources</module>
|
<module>spring-static-resources</module>
|
||||||
<module>spring-swagger-codegen</module>
|
<module>spring-swagger-codegen</module>
|
||||||
<module>spring-web-modules</module>
|
|
||||||
<module>testing-modules</module>
|
<module>testing-modules</module>
|
||||||
<module>video-tutorials</module>
|
<module>video-tutorials</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
@ -547,6 +547,7 @@
|
||||||
<module>muleesb</module>
|
<module>muleesb</module>
|
||||||
<module>web-modules/java-lite</module>
|
<module>web-modules/java-lite</module>
|
||||||
<module>web-modules/restx</module>
|
<module>web-modules/restx</module>
|
||||||
|
<module>web-modules/jee-7</module>
|
||||||
<module>persistence-modules/deltaspike</module> <!-- delta spike it doesn't support yet the jakarta API-->
|
<module>persistence-modules/deltaspike</module> <!-- delta spike it doesn't support yet the jakarta API-->
|
||||||
<module>persistence-modules/hibernate-ogm</module> <!-- hibernate-ogm wasn't updated because it doesn't support jakarta API -->
|
<module>persistence-modules/hibernate-ogm</module> <!-- hibernate-ogm wasn't updated because it doesn't support jakarta API -->
|
||||||
<module>persistence-modules/java-cassandra</module> <!-- JAVA-21464 cassandra-unit library doesn't support to run with jdk9 and above -->
|
<module>persistence-modules/java-cassandra</module> <!-- JAVA-21464 cassandra-unit library doesn't support to run with jdk9 and above -->
|
||||||
|
@ -598,11 +599,10 @@
|
||||||
|
|
||||||
<!-- <module>spring-roo</module> --> <!-- Not supported JAVA-17327 -->
|
<!-- <module>spring-roo</module> --> <!-- Not supported JAVA-17327 -->
|
||||||
|
|
||||||
<module>spring-security-modules</module>
|
<module>spring-security-modules/spring-security-ldap</module>
|
||||||
<module>spring-soap</module>
|
<module>spring-soap</module>
|
||||||
<module>spring-static-resources</module>
|
<module>spring-static-resources</module>
|
||||||
<module>spring-swagger-codegen</module>
|
<module>spring-swagger-codegen</module>
|
||||||
<module>spring-web-modules</module>
|
|
||||||
<module>testing-modules</module>
|
<module>testing-modules</module>
|
||||||
<module>video-tutorials</module>
|
<module>video-tutorials</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
@ -913,7 +913,7 @@
|
||||||
<module>spring-kafka</module>
|
<module>spring-kafka</module>
|
||||||
|
|
||||||
<module>spring-native</module>
|
<module>spring-native</module>
|
||||||
<module>spring-security-modules/spring-security-oauth2-testing</module>
|
<module>spring-security-modules</module>
|
||||||
<module>spring-protobuf</module>
|
<module>spring-protobuf</module>
|
||||||
<module>spring-quartz</module>
|
<module>spring-quartz</module>
|
||||||
|
|
||||||
|
@ -925,6 +925,7 @@
|
||||||
<module>spring-threads</module>
|
<module>spring-threads</module>
|
||||||
<module>spring-vault</module>
|
<module>spring-vault</module>
|
||||||
<module>spring-websockets</module>
|
<module>spring-websockets</module>
|
||||||
|
<module>spring-web-modules</module>
|
||||||
<module>static-analysis</module>
|
<module>static-analysis</module>
|
||||||
<module>tensorflow-java</module>
|
<module>tensorflow-java</module>
|
||||||
<module>vertx-modules</module>
|
<module>vertx-modules</module>
|
||||||
|
@ -1175,6 +1176,7 @@
|
||||||
<module>spring-kafka</module>
|
<module>spring-kafka</module>
|
||||||
|
|
||||||
<module>spring-native</module>
|
<module>spring-native</module>
|
||||||
|
<module>spring-security-modules</module>
|
||||||
<module>spring-protobuf</module>
|
<module>spring-protobuf</module>
|
||||||
<module>spring-quartz</module>
|
<module>spring-quartz</module>
|
||||||
|
|
||||||
|
@ -1186,6 +1188,7 @@
|
||||||
<module>spring-threads</module>
|
<module>spring-threads</module>
|
||||||
<module>spring-vault</module>
|
<module>spring-vault</module>
|
||||||
<module>spring-websockets</module>
|
<module>spring-websockets</module>
|
||||||
|
<module>spring-web-modules</module>
|
||||||
<module>static-analysis</module>
|
<module>static-analysis</module>
|
||||||
<module>tensorflow-java</module>
|
<module>tensorflow-java</module>
|
||||||
<module>vertx-modules</module>
|
<module>vertx-modules</module>
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>${compiler.plugin.version}</version>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<annotationProcessorPaths>
|
<annotationProcessorPaths>
|
||||||
<path>
|
<path>
|
||||||
|
@ -55,8 +54,4 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
|
||||||
<compiler.plugin.version>3.8.1</compiler.plugin.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -52,7 +52,6 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>${compiler.plugin.version}</version>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<annotationProcessorPaths>
|
<annotationProcessorPaths>
|
||||||
<path>
|
<path>
|
||||||
|
@ -68,7 +67,6 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<liquibase-core.version>3.8.1</liquibase-core.version>
|
<liquibase-core.version>3.8.1</liquibase-core.version>
|
||||||
<compiler.plugin.version>3.8.1</compiler.plugin.version>
|
|
||||||
<liquibase.version>3.8.1</liquibase.version>
|
<liquibase.version>3.8.1</liquibase.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|
|
@ -6,17 +6,7 @@
|
||||||
<groupId>com.baeldung.quarkus</groupId>
|
<groupId>com.baeldung.quarkus</groupId>
|
||||||
<artifactId>quarkus-funqy</artifactId>
|
<artifactId>quarkus-funqy</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
<properties>
|
|
||||||
<compiler-plugin.version>3.10.1</compiler-plugin.version>
|
|
||||||
<failsafe.useModulePath>false</failsafe.useModulePath>
|
|
||||||
<maven.compiler.release>17</maven.compiler.release>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
|
||||||
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
|
||||||
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
|
||||||
<quarkus.platform.version>2.16.0.Final</quarkus.platform.version>
|
|
||||||
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
|
|
||||||
</properties>
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>quarkus-modules</artifactId>
|
<artifactId>quarkus-modules</artifactId>
|
||||||
|
@ -131,4 +121,16 @@
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<compiler-plugin.version>3.10.1</compiler-plugin.version>
|
||||||
|
<failsafe.useModulePath>false</failsafe.useModulePath>
|
||||||
|
<maven.compiler.release>17</maven.compiler.release>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
||||||
|
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
||||||
|
<quarkus.platform.version>2.16.0.Final</quarkus.platform.version>
|
||||||
|
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
|
||||||
|
</properties>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -76,7 +76,6 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>${compiler-plugin.version}</version>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<parameters>${maven.compiler.parameters}</parameters>
|
<parameters>${maven.compiler.parameters}</parameters>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<!-- https://github.com/wildfly/jandex-maven-plugin -->
|
<!-- https://github.com/wildfly/jandex-maven-plugin -->
|
||||||
<groupId>org.jboss.jandex</groupId>
|
<groupId>org.jboss.jandex</groupId>
|
||||||
<artifactId>jandex-maven-plugin</artifactId>
|
<artifactId>jandex-maven-plugin</artifactId>
|
||||||
<version>1.2.1</version>
|
<version>${jandex-maven-plugin.version}</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>make-index</id>
|
<id>make-index</id>
|
||||||
|
@ -43,4 +43,8 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<jandex-maven-plugin.version>1.2.1</jandex-maven-plugin.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -38,7 +38,6 @@
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<compiler-plugin.version>3.8.1</compiler-plugin.version>
|
|
||||||
<maven.compiler.parameters>true</maven.compiler.parameters>
|
<maven.compiler.parameters>true</maven.compiler.parameters>
|
||||||
<maven.compiler.source>11</maven.compiler.source>
|
<maven.compiler.source>11</maven.compiler.source>
|
||||||
<maven.compiler.target>11</maven.compiler.target>
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.baeldung</groupId>
|
|
||||||
<artifactId>spring-project</artifactId>
|
<artifactId>spring-project</artifactId>
|
||||||
<version>0.1-SNAPSHOT</version>
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
|
@ -19,7 +18,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.testcontainers</groupId>
|
<groupId>org.testcontainers</groupId>
|
||||||
<artifactId>testcontainers-bom</artifactId>
|
<artifactId>testcontainers-bom</artifactId>
|
||||||
<version>1.17.2</version>
|
<version>${testcontainers-bom.version}</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -43,7 +42,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.jasync-sql</groupId>
|
<groupId>com.github.jasync-sql</groupId>
|
||||||
<artifactId>jasync-r2dbc-mysql</artifactId>
|
<artifactId>jasync-r2dbc-mysql</artifactId>
|
||||||
<version>2.0.8</version>
|
<version>${jasync-r2dbc-mysql.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -104,7 +103,6 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>${maven-compiler-plugin.version}</version>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>${maven.compiler.source.version}</source>
|
<source>${maven.compiler.source.version}</source>
|
||||||
<target>${maven.compiler.target.version}</target>
|
<target>${maven.compiler.target.version}</target>
|
||||||
|
@ -182,7 +180,7 @@
|
||||||
<id>local-native</id>
|
<id>local-native</id>
|
||||||
<properties>
|
<properties>
|
||||||
<repackage.classifier>exec</repackage.classifier>
|
<repackage.classifier>exec</repackage.classifier>
|
||||||
<native-buildtools.version>0.9.11</native-buildtools.version>
|
<native-buildtools.version>${native-buildtools.version}</native-buildtools.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -241,7 +239,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>3.0.0-M6</version>
|
<version>${maven-surefire-plugin.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<argLine>-DspringAot=true
|
<argLine>-DspringAot=true
|
||||||
-agentlib:native-image-agent=access-filter-file=src/test/resources/access-filter.json,config-merge-dir=target/classes/META-INF/native-image
|
-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 @@
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
<testcontainers-bom.version>1.17.2</testcontainers-bom.version>
|
||||||
<java.version>11</java.version>
|
<java.version>11</java.version>
|
||||||
<spring-native.version>0.12.1</spring-native.version>
|
<spring-native.version>0.12.1</spring-native.version>
|
||||||
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
|
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
|
||||||
<maven.compiler.source.version>11</maven.compiler.source.version>
|
<maven.compiler.source.version>11</maven.compiler.source.version>
|
||||||
<maven.compiler.target.version>11</maven.compiler.target.version>
|
<maven.compiler.target.version>11</maven.compiler.target.version>
|
||||||
|
<maven-surefire-plugin.version>3.1.0</maven-surefire-plugin.version>
|
||||||
|
<native-buildtools.version>0.9.11</native-buildtools.version>
|
||||||
|
<jasync-r2dbc-mysql.version>2.0.8</jasync-r2dbc-mysql.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -3,20 +3,15 @@
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
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">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>sentry-servlet</artifactId>
|
||||||
|
<name>sentry-servlet</name>
|
||||||
|
<packaging>war</packaging>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>saas-modules</artifactId>
|
<artifactId>saas-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>sentry-servlet</artifactId>
|
|
||||||
<name>sentry-servlet</name>
|
|
||||||
<packaging>war</packaging>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<sentry.version>6.11.0</sentry.version>
|
|
||||||
<cargo.version>1.10.4</cargo.version>
|
|
||||||
<maven-war-plugin.version>3.3.2</maven-war-plugin.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -37,7 +32,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.cargo</groupId>
|
<groupId>org.codehaus.cargo</groupId>
|
||||||
<artifactId>cargo-maven3-plugin</artifactId>
|
<artifactId>cargo-maven3-plugin</artifactId>
|
||||||
<version>${cargo.version}</version>
|
<version>${cargo-maven3-plugin.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<container>
|
<container>
|
||||||
<containerId>tomcat9x</containerId>
|
<containerId>tomcat9x</containerId>
|
||||||
|
@ -47,4 +42,10 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<sentry.version>6.11.0</sentry.version>
|
||||||
|
<cargo-maven3-plugin.version>1.10.4</cargo-maven3-plugin.version>
|
||||||
|
<maven-war-plugin.version>3.3.2</maven-war-plugin.version>
|
||||||
|
</properties>
|
||||||
</project>
|
</project>
|
|
@ -34,7 +34,6 @@
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-war-plugin</artifactId>
|
<artifactId>maven-war-plugin</artifactId>
|
||||||
<version>${maven-war-plugin.version}</version>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||||
<packagingExcludes>pom.xml</packagingExcludes>
|
<packagingExcludes>pom.xml</packagingExcludes>
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
## Relevant Articles
|
||||||
|
- [Configuring Gradle Tasks in Spring Boot 3](https://www.baeldung.com/spring-boot-3-gradle-configure-tasks)
|
|
@ -13,4 +13,5 @@
|
||||||
- [Using Environment Variables in Spring Boot’s application.properties](https://www.baeldung.com/spring-boot-properties-env-variables)
|
- [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)
|
- [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)
|
- [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)
|
- More articles: [[<-- prev]](../spring-boot-properties-2)
|
||||||
|
|
|
@ -18,3 +18,5 @@ spring.redis.port=6379
|
||||||
|
|
||||||
spring.sleuth.sampler.percentage=1.0
|
spring.sleuth.sampler.percentage=1.0
|
||||||
spring.sleuth.web.skipPattern=(^cleanup.*)
|
spring.sleuth.web.skipPattern=(^cleanup.*)
|
||||||
|
|
||||||
|
spring.zipkin.baseUrl=http://localhost:9411
|
||||||
|
|
|
@ -6,25 +6,13 @@ eureka.client.registryFetchIntervalSeconds = 5
|
||||||
|
|
||||||
management.security.sessions=always
|
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.web.=debug
|
||||||
logging.level.org.springframework.security=debug
|
logging.level.org.springframework.security=debug
|
||||||
logging.level.org.springframework.cloud.netflix.zuul=debug
|
|
||||||
|
|
||||||
spring.redis.host=localhost
|
spring.redis.host=localhost
|
||||||
spring.redis.port=6379
|
spring.redis.port=6379
|
||||||
|
|
||||||
spring.sleuth.sampler.percentage=1.0
|
spring.sleuth.sampler.percentage=1.0
|
||||||
spring.sleuth.web.skipPattern=(^cleanup.*|.+favicon.*)
|
spring.sleuth.web.skipPattern=(^cleanup.*|.+favicon.*)
|
||||||
|
|
||||||
|
spring.zipkin.baseUrl=http://localhost:9411
|
||||||
|
|
|
@ -18,3 +18,5 @@ spring.redis.port=6379
|
||||||
|
|
||||||
spring.sleuth.sampler.percentage=1.0
|
spring.sleuth.sampler.percentage=1.0
|
||||||
spring.sleuth.web.skipPattern=(^cleanup.*)
|
spring.sleuth.web.skipPattern=(^cleanup.*)
|
||||||
|
|
||||||
|
spring.zipkin.baseUrl=http://localhost:9411
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-boot-1</artifactId>
|
<artifactId>parent-boot-2</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../../../parent-boot-1</relativePath>
|
<relativePath>../../../parent-boot-2</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-eureka</artifactId>
|
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring-cloud-dependencies.version>Brixton.SR7</spring-cloud-dependencies.version>
|
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -2,12 +2,12 @@ package com.baeldung.spring.cloud.bootstrap.config;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
import org.springframework.cloud.config.server.EnableConfigServer;
|
import org.springframework.cloud.config.server.EnableConfigServer;
|
||||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableConfigServer
|
@EnableConfigServer
|
||||||
@EnableEurekaClient
|
@EnableDiscoveryClient
|
||||||
public class ConfigApplication {
|
public class ConfigApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(ConfigApplication.class, args);
|
SpringApplication.run(ConfigApplication.class, args);
|
||||||
|
|
|
@ -1,23 +1,41 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.config;
|
package com.baeldung.spring.cloud.bootstrap.config;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
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
|
@EnableWebSecurity
|
||||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
public class SecurityConfig {
|
||||||
|
|
||||||
@Autowired
|
@Bean
|
||||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
public InMemoryUserDetailsManager userDetailsService(BCryptPasswordEncoder bCryptPasswordEncoder) {
|
||||||
auth.inMemoryAuthentication().withUser("configUser").password("configPassword").roles("SYSTEM");
|
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
|
||||||
|
manager.createUser(User.withUsername("configUser")
|
||||||
|
.password(bCryptPasswordEncoder.encode("configPassword"))
|
||||||
|
.roles("SYSTEM")
|
||||||
|
.build());
|
||||||
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Bean
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http.authorizeRequests().anyRequest().hasRole("SYSTEM").and().httpBasic().and().csrf().disable();
|
http.authorizeRequests()
|
||||||
|
.anyRequest()
|
||||||
|
.hasRole("SYSTEM")
|
||||||
|
.and()
|
||||||
|
.httpBasic()
|
||||||
|
.and()
|
||||||
|
.csrf()
|
||||||
|
.disable();
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public BCryptPasswordEncoder encoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-boot-1</artifactId>
|
<artifactId>parent-boot-2</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../../../parent-boot-1</relativePath>
|
<relativePath>../../../parent-boot-2</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -33,7 +33,11 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-eureka-server</artifactId>
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -41,7 +45,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.session</groupId>
|
<groupId>org.springframework.session</groupId>
|
||||||
<artifactId>spring-session</artifactId>
|
<artifactId>spring-session-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -50,7 +54,7 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring-cloud-dependencies.version>Edgware.SR5</spring-cloud-dependencies.version>
|
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -17,7 +17,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
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
|
@Override
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-boot-1</artifactId>
|
<artifactId>parent-boot-2</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../../../parent-boot-1</relativePath>
|
<relativePath>../../../parent-boot-2</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -33,11 +33,15 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-eureka</artifactId>
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-zuul</artifactId>
|
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -45,7 +49,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.session</groupId>
|
<groupId>org.springframework.session</groupId>
|
||||||
<artifactId>spring-session</artifactId>
|
<artifactId>spring-session-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -53,11 +57,15 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-feign</artifactId>
|
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
@ -97,7 +105,7 @@
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring-cloud-dependencies.version>Dalston.RELEASE</spring-cloud-dependencies.version>
|
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -1,8 +1,8 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.gateway;
|
package com.baeldung.spring.cloud.bootstrap.gateway;
|
||||||
|
|
||||||
import org.springframework.boot.web.servlet.ErrorPage;
|
import org.springframework.boot.web.server.ErrorPage;
|
||||||
import org.springframework.boot.web.servlet.ErrorPageRegistrar;
|
import org.springframework.boot.web.server.ErrorPageRegistrar;
|
||||||
import org.springframework.boot.web.servlet.ErrorPageRegistry;
|
import org.springframework.boot.web.server.ErrorPageRegistry;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
|
@ -1,76 +1,15 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.gateway;
|
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.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
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;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableZuulProxy
|
|
||||||
@EnableEurekaClient
|
|
||||||
@EnableFeignClients
|
@EnableFeignClients
|
||||||
|
@EnableDiscoveryClient
|
||||||
public class GatewayApplication {
|
public class GatewayApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(GatewayApplication.class, args);
|
SpringApplication.run(GatewayApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
private List<RibbonClientSpecification> 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,59 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.gateway;
|
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.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
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
|
@Configuration
|
||||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
public class SecurityConfig {
|
||||||
|
|
||||||
@Autowired
|
@Bean
|
||||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
public MapReactiveUserDetailsService userDetailsService() {
|
||||||
auth.inMemoryAuthentication()
|
UserDetails user = User.withUsername("user")
|
||||||
.withUser("user").password("password").roles("USER")
|
.password(passwordEncoder().encode("password"))
|
||||||
.and()
|
.roles("USER")
|
||||||
.withUser("admin").password("admin").roles("ADMIN");
|
.build();
|
||||||
|
UserDetails adminUser = User.withUsername("admin")
|
||||||
|
.password(passwordEncoder().encode("admin"))
|
||||||
|
.roles("ADMIN")
|
||||||
|
.build();
|
||||||
|
return new MapReactiveUserDetailsService(user, adminUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Bean
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
public PasswordEncoder passwordEncoder() {
|
||||||
http
|
return new BCryptPasswordEncoder();
|
||||||
.formLogin()
|
}
|
||||||
.defaultSuccessUrl("/home/index.html", true)
|
|
||||||
|
@Bean
|
||||||
|
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||||
|
http.formLogin()
|
||||||
|
.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/home/index.html"))
|
||||||
.and()
|
.and()
|
||||||
.authorizeRequests()
|
.authorizeExchange()
|
||||||
.antMatchers("/book-service/**", "/rating-service/**", "/login*", "/").permitAll()
|
.pathMatchers("/book-service/**", "/rating-service/**", "/login*", "/")
|
||||||
.antMatchers("/eureka/**").hasRole("ADMIN")
|
.permitAll()
|
||||||
.anyRequest().authenticated()
|
.pathMatchers("/eureka/**")
|
||||||
|
.hasRole("ADMIN")
|
||||||
|
.anyExchange()
|
||||||
|
.authenticated()
|
||||||
.and()
|
.and()
|
||||||
.logout()
|
.logout()
|
||||||
.and()
|
.and()
|
||||||
.csrf().disable();
|
.csrf()
|
||||||
|
.disable()
|
||||||
|
.httpBasic(withDefaults());
|
||||||
|
return http.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.gateway;
|
package com.baeldung.spring.cloud.bootstrap.gateway;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.session.data.redis.RedisFlushMode;
|
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
|
||||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
|
||||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableRedisHttpSession(redisFlushMode = RedisFlushMode.IMMEDIATE)
|
@EnableRedisWebSession
|
||||||
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
|
public class SessionConfig {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.gateway.client.book;
|
package com.baeldung.spring.cloud.bootstrap.gateway.client.book;
|
||||||
|
|
||||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.gateway.client.rating;
|
package com.baeldung.spring.cloud.bootstrap.gateway.client.rating;
|
||||||
|
|
||||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
import java.util.List;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.web.bind.annotation.RequestHeader;
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@FeignClient(value = "rating-service")
|
@FeignClient(value = "rating-service")
|
||||||
public interface RatingsClient {
|
public interface RatingsClient {
|
||||||
|
|
||||||
|
|
|
@ -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<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
return exchange.getSession()
|
||||||
|
.flatMap(session -> {
|
||||||
|
logger.debug("SessionId: {}", session.getId());
|
||||||
|
return chain.filter(exchange);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,5 +3,7 @@ spring.cloud.config.discovery.service-id=config
|
||||||
spring.cloud.config.discovery.enabled=true
|
spring.cloud.config.discovery.enabled=true
|
||||||
spring.cloud.config.username=configUser
|
spring.cloud.config.username=configUser
|
||||||
spring.cloud.config.password=configPassword
|
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/
|
eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/
|
|
@ -20,7 +20,6 @@
|
||||||
<module>gateway</module>
|
<module>gateway</module>
|
||||||
<module>svc-book</module>
|
<module>svc-book</module>
|
||||||
<module>svc-rating</module>
|
<module>svc-rating</module>
|
||||||
<module>zipkin</module>
|
|
||||||
<module>customer-service</module>
|
<module>customer-service</module>
|
||||||
<module>order-service</module>
|
<module>order-service</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-boot-1</artifactId>
|
<artifactId>parent-boot-2</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../../../parent-boot-1</relativePath>
|
<relativePath>../../../parent-boot-2</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -34,7 +34,11 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-eureka</artifactId>
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -46,7 +50,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.session</groupId>
|
<groupId>org.springframework.session</groupId>
|
||||||
<artifactId>spring-session</artifactId>
|
<artifactId>spring-session-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -63,12 +67,16 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring-cloud-dependencies.version>Dalston.RELEASE</spring-cloud-dependencies.version>
|
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -1,53 +1,14 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.svcbook;
|
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.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
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;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableEurekaClient
|
@EnableDiscoveryClient
|
||||||
public class BookServiceApplication {
|
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) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(BookServiceApplication.class, 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,36 +1,37 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.svcbook;
|
package com.baeldung.spring.cloud.bootstrap.svcbook;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
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.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
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
|
@EnableWebSecurity
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
public class SecurityConfig {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public void configureGlobal1(AuthenticationManagerBuilder auth) throws Exception {
|
public void registerAuthProvider(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();
|
auth.inMemoryAuthentication();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Bean
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http.httpBasic()
|
return http.authorizeHttpRequests((auth) -> auth.antMatchers(HttpMethod.GET, "/books")
|
||||||
.disable()
|
.permitAll()
|
||||||
.authorizeRequests()
|
.antMatchers(HttpMethod.GET, "/books/*")
|
||||||
.antMatchers(HttpMethod.GET, "/books").permitAll()
|
.permitAll()
|
||||||
.antMatchers(HttpMethod.GET, "/books/*").permitAll()
|
.antMatchers(HttpMethod.POST, "/books")
|
||||||
.antMatchers(HttpMethod.POST, "/books").hasRole("ADMIN")
|
.hasRole("ADMIN")
|
||||||
.antMatchers(HttpMethod.PATCH, "/books/*").hasRole("ADMIN")
|
.antMatchers(HttpMethod.PATCH, "/books/*")
|
||||||
.antMatchers(HttpMethod.DELETE, "/books/*").hasRole("ADMIN")
|
.hasRole("ADMIN")
|
||||||
.anyRequest().authenticated()
|
.antMatchers(HttpMethod.DELETE, "/books/*")
|
||||||
.and()
|
.hasRole("ADMIN"))
|
||||||
.csrf()
|
.csrf()
|
||||||
.disable();
|
.disable()
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.baeldung.spring.cloud.bootstrap.svcbook.book;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -23,8 +22,8 @@ public class BookService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Book findBookById(Long bookId) {
|
public Book findBookById(Long bookId) {
|
||||||
return Optional.ofNullable(bookRepository.findOne(bookId))
|
return bookRepository.findById(bookId)
|
||||||
.orElseThrow(() -> new BookNotFoundException("Book not found. ID: " + bookId));
|
.orElseThrow(() -> new BookNotFoundException(String.format("Book not found. ID: %s", bookId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRED)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
@ -37,7 +36,7 @@ public class BookService {
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRED)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
public void deleteBook(Long bookId) {
|
public void deleteBook(Long bookId) {
|
||||||
bookRepository.delete(bookId);
|
bookRepository.deleteById(bookId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRED)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
@ -60,7 +59,8 @@ public class BookService {
|
||||||
public Book updateBook(Book book, Long bookId) {
|
public Book updateBook(Book book, Long bookId) {
|
||||||
Preconditions.checkNotNull(book);
|
Preconditions.checkNotNull(book);
|
||||||
Preconditions.checkState(book.getId() == bookId);
|
Preconditions.checkState(book.getId() == bookId);
|
||||||
Preconditions.checkNotNull(bookRepository.findOne(bookId));
|
Preconditions.checkArgument(bookRepository.findById(bookId)
|
||||||
|
.isPresent());
|
||||||
return bookRepository.save(book);
|
return bookRepository.save(book);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,7 @@ spring.cloud.config.discovery.service-id=config
|
||||||
spring.cloud.config.discovery.enabled=true
|
spring.cloud.config.discovery.enabled=true
|
||||||
spring.cloud.config.username=configUser
|
spring.cloud.config.username=configUser
|
||||||
spring.cloud.config.password=configPassword
|
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/
|
eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/
|
|
@ -10,9 +10,9 @@
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-boot-1</artifactId>
|
<artifactId>parent-boot-2</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../../../parent-boot-1</relativePath>
|
<relativePath>../../../parent-boot-2</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -34,7 +34,11 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-eureka</artifactId>
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -46,7 +50,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.session</groupId>
|
<groupId>org.springframework.session</groupId>
|
||||||
<artifactId>spring-session</artifactId>
|
<artifactId>spring-session-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -58,25 +62,29 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-hystrix</artifactId>
|
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.h2database</groupId>
|
<groupId>com.h2database</groupId>
|
||||||
<artifactId>h2</artifactId>
|
<artifactId>h2</artifactId>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring-cloud-dependencies.version>Dalston.RELEASE</spring-cloud-dependencies.version>
|
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,69 +1,18 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.svcrating;
|
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.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
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.context.annotation.AdviceMode;
|
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.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
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
|
@SpringBootApplication
|
||||||
@EnableEurekaClient
|
@EnableDiscoveryClient
|
||||||
@EnableHystrix
|
|
||||||
@EnableTransactionManagement(order = Ordered.LOWEST_PRECEDENCE, mode = AdviceMode.ASPECTJ)
|
@EnableTransactionManagement(order = Ordered.LOWEST_PRECEDENCE, mode = AdviceMode.ASPECTJ)
|
||||||
public class RatingServiceApplication {
|
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) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(RatingServiceApplication.class, 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,39 +1,41 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.svcrating;
|
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.context.annotation.Configuration;
|
||||||
import org.springframework.http.HttpMethod;
|
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.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
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
|
@EnableWebSecurity
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
public class SecurityConfig {
|
||||||
|
|
||||||
@Autowired
|
@Bean
|
||||||
public void configureGlobal1(AuthenticationManagerBuilder auth) throws Exception {
|
public UserDetailsService users() {
|
||||||
//try in memory auth with no users to support the case that this will allow for users that are logged in to go anywhere
|
return new InMemoryUserDetailsManager();
|
||||||
auth.inMemoryAuthentication();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Bean
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||||
http
|
return httpSecurity.authorizeHttpRequests((auth) -> auth.regexMatchers("^/ratings\\?bookId.*$")
|
||||||
.authorizeRequests()
|
.authenticated()
|
||||||
.regexMatchers("^/ratings\\?bookId.*$").authenticated()
|
.antMatchers(HttpMethod.POST, "/ratings")
|
||||||
.antMatchers(HttpMethod.POST,"/ratings").authenticated()
|
.authenticated()
|
||||||
.antMatchers(HttpMethod.PATCH,"/ratings/*").hasRole("ADMIN")
|
.antMatchers(HttpMethod.PATCH, "/ratings/*")
|
||||||
.antMatchers(HttpMethod.DELETE,"/ratings/*").hasRole("ADMIN")
|
.hasRole("ADMIN")
|
||||||
.antMatchers(HttpMethod.GET,"/ratings").hasRole("ADMIN")
|
.antMatchers(HttpMethod.DELETE, "/ratings/*")
|
||||||
.antMatchers(HttpMethod.GET,"/hystrix").authenticated()
|
.hasRole("ADMIN")
|
||||||
.anyRequest().authenticated()
|
.antMatchers(HttpMethod.GET, "/ratings")
|
||||||
|
.hasRole("ADMIN")
|
||||||
|
.anyRequest()
|
||||||
|
.authenticated())
|
||||||
|
.httpBasic()
|
||||||
.and()
|
.and()
|
||||||
.httpBasic().and()
|
|
||||||
.csrf()
|
.csrf()
|
||||||
.disable();
|
.disable()
|
||||||
|
.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,24 +5,24 @@ import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.core.env.Environment;
|
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.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableRedisHttpSession
|
@EnableRedisHttpSession
|
||||||
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
|
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
Environment properties;
|
private Environment properties;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Primary
|
@Primary
|
||||||
public JedisConnectionFactory connectionFactory() {
|
public LettuceConnectionFactory redisConnectionFactory() {
|
||||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration();
|
||||||
factory.setHostName(properties.getProperty("spring.redis.host","localhost"));
|
redisConfiguration.setHostName(properties.getProperty("spring.redis.host", "localhost"));
|
||||||
factory.setPort(properties.getProperty("spring.redis.port", Integer.TYPE,6379));
|
redisConfiguration.setPort(properties.getProperty("spring.redis.port", Integer.TYPE, 6379));
|
||||||
factory.afterPropertiesSet();
|
return new LettuceConnectionFactory(redisConfiguration);
|
||||||
factory.setUsePool(true);
|
|
||||||
return factory;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,21 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.SetOperations;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.data.redis.core.ValueOperations;
|
import org.springframework.data.redis.core.ValueOperations;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public class RatingCacheRepository implements InitializingBean {
|
public class RatingCacheRepository implements InitializingBean {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JedisConnectionFactory cacheConnectionFactory;
|
private LettuceConnectionFactory cacheConnectionFactory;
|
||||||
|
|
||||||
private StringRedisTemplate redisTemplate;
|
private StringRedisTemplate redisTemplate;
|
||||||
private ValueOperations<String, String> valueOps;
|
private ValueOperations<String, String> valueOps;
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -10,7 +9,8 @@ import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
|
|
||||||
|
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
|
@ -22,31 +22,31 @@ public class RatingService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private RatingCacheRepository cacheRepository;
|
private RatingCacheRepository cacheRepository;
|
||||||
|
|
||||||
@HystrixCommand(commandKey = "ratingsByBookIdFromDB", fallbackMethod = "findCachedRatingsByBookId")
|
@CircuitBreaker(name = "ratingsByBookIdFromDB", fallbackMethod = "findCachedRatingsByBookId")
|
||||||
public List<Rating> findRatingsByBookId(Long bookId) {
|
public List<Rating> findRatingsByBookId(Long bookId) {
|
||||||
return ratingRepository.findRatingsByBookId(bookId);
|
return ratingRepository.findRatingsByBookId(bookId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Rating> findCachedRatingsByBookId(Long bookId) {
|
public List<Rating> findCachedRatingsByBookId(Long bookId, Exception exception) {
|
||||||
return cacheRepository.findCachedRatingsByBookId(bookId);
|
return cacheRepository.findCachedRatingsByBookId(bookId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HystrixCommand(commandKey = "ratingsFromDB", fallbackMethod = "findAllCachedRatings")
|
@CircuitBreaker(name = "ratingsFromDB", fallbackMethod = "findAllCachedRatings")
|
||||||
public List<Rating> findAllRatings() {
|
public List<Rating> findAllRatings() {
|
||||||
return ratingRepository.findAll();
|
return ratingRepository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Rating> findAllCachedRatings() {
|
public List<Rating> findAllCachedRatings(Exception exception) {
|
||||||
return cacheRepository.findAllCachedRatings();
|
return cacheRepository.findAllCachedRatings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@HystrixCommand(commandKey = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById", ignoreExceptions = { RatingNotFoundException.class })
|
@CircuitBreaker(name = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById")
|
||||||
public Rating findRatingById(Long ratingId) {
|
public Rating findRatingById(Long ratingId) {
|
||||||
return Optional.ofNullable(ratingRepository.findOne(ratingId))
|
return ratingRepository.findById(ratingId)
|
||||||
.orElseThrow(() -> new RatingNotFoundException("Rating not found. ID: " + 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);
|
return cacheRepository.findCachedRatingById(ratingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class RatingService {
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRED)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
public void deleteRating(Long ratingId) {
|
public void deleteRating(Long ratingId) {
|
||||||
ratingRepository.delete(ratingId);
|
ratingRepository.deleteById(ratingId);
|
||||||
cacheRepository.deleteRating(ratingId);
|
cacheRepository.deleteRating(ratingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ public class RatingService {
|
||||||
public Rating updateRating(Rating rating, Long ratingId) {
|
public Rating updateRating(Rating rating, Long ratingId) {
|
||||||
Preconditions.checkNotNull(rating);
|
Preconditions.checkNotNull(rating);
|
||||||
Preconditions.checkState(rating.getId() == ratingId);
|
Preconditions.checkState(rating.getId() == ratingId);
|
||||||
Preconditions.checkNotNull(ratingRepository.findOne(ratingId));
|
Preconditions.checkNotNull(ratingRepository.findById(ratingId));
|
||||||
return ratingRepository.save(rating);
|
return ratingRepository.save(rating);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,5 +3,7 @@ spring.cloud.config.discovery.service-id=config
|
||||||
spring.cloud.config.discovery.enabled=true
|
spring.cloud.config.discovery.enabled=true
|
||||||
spring.cloud.config.username=configUser
|
spring.cloud.config.username=configUser
|
||||||
spring.cloud.config.password=configPassword
|
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/
|
eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/
|
||||||
|
|
|
@ -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
|
||||||
|
```
|
|
@ -0,0 +1,6 @@
|
||||||
|
version: "3.9"
|
||||||
|
services:
|
||||||
|
zipkin:
|
||||||
|
image: openzipkin/zipkin
|
||||||
|
ports:
|
||||||
|
- 9411:9411
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue