Merge branch 'master' into master
This commit is contained in:
commit
2c0482c409
|
@ -90,6 +90,12 @@
|
|||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-netty</artifactId>
|
||||
<version>${mockserver.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock</artifactId>
|
||||
|
@ -112,6 +118,7 @@
|
|||
<!-- util -->
|
||||
<httpasyncclient.version>4.1.4</httpasyncclient.version>
|
||||
<!-- testing -->
|
||||
<mockserver.version>5.6.1</mockserver.version>
|
||||
<wiremock.version>2.5.1</wiremock.version>
|
||||
<httpclient.version>4.5.8</httpclient.version> <!-- 4.3.6 --> <!-- 4.4-beta1 -->
|
||||
<!-- 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;
|
||||
|
||||
|
||||
class HttpAsyncClientLiveTest {
|
||||
class HttpAsyncClientLiveTest extends GetRequestMockServer {
|
||||
|
||||
private static final String HOST = "http://www.google.com";
|
||||
private static final String HOST_WITH_SSL = "https://mms.nw.ru/";
|
||||
|
|
|
@ -9,3 +9,5 @@ You can build the project from the command line using: *mvn clean install*, or i
|
|||
- [Guide to Check if Apache Kafka Server Is Running](https://www.baeldung.com/apache-kafka-check-server-is-running)
|
||||
- [Add Custom Headers to a Kafka Message](https://www.baeldung.com/java-kafka-custom-headers)
|
||||
- [Get Last N Messages in Apache Kafka Topic](https://www.baeldung.com/java-apache-kafka-get-last-n-messages)
|
||||
- [Is a Key Required as Part of Sending Messages to Kafka?](https://www.baeldung.com/java-kafka-message-key)
|
||||
- [Read Data From the Beginning Using Kafka Consumer API](https://www.baeldung.com/java-kafka-consumer-api-read)
|
||||
|
|
|
@ -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,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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">
|
||||
<project 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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>apache-poi-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
@ -19,10 +19,15 @@
|
|||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>${poi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-scratchpad</artifactId>
|
||||
<version>${poi.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<poi.version>5.2.0</poi.version>
|
||||
<poi.version>5.2.3</poi.version>
|
||||
</properties>
|
||||
|
||||
</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)
|
||||
- [Moving Items Around in an Arraylist](https://www.baeldung.com/java-arraylist-move-items)
|
||||
- [Check if a List Contains an Element From Another List in Java](https://www.baeldung.com/java-check-elements-between-lists)
|
||||
- [Array vs. List Performance in Java](https://www.baeldung.com/java-array-vs-list-performance)
|
||||
- [Set Default Value for Elements in List](https://www.baeldung.com/java-list-set-default-values)
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
## Relevant Articles
|
||||
- [Copying All Keys and Values From One Hashmap Onto Another Without Replacing Existing Keys and Values](https://www.baeldung.com/java-copy-hashmap-no-changes)
|
||||
- [Convert Hashmap to JSON Object in Java](https://www.baeldung.com/java-convert-hashmap-to-json-object)
|
||||
|
|
|
@ -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)
|
||||
- [How to Take Input as String With Spaces in Java Using Scanner?](https://www.baeldung.com/java-scanner-input-with-spaces)
|
||||
- [Write Console Output to Text File in Java](https://www.baeldung.com/java-write-console-output-file)
|
||||
- [What’s the difference between Scanner next() and nextLine() methods?](https://www.baeldung.com/java-scanner-next-vs-nextline)
|
||||
- [Handle NoSuchElementException When Reading a File Through Scanner](https://www.baeldung.com/java-scanner-nosuchelementexception-reading-file)
|
||||
|
|
|
@ -3,33 +3,81 @@ package com.baeldung.regex.z_regexp;
|
|||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
public class ZRegularExpressionUnitTest {
|
||||
@Test
|
||||
public void givenCreditCardNumber_thenReturnIfMatched() {
|
||||
String creditCardNumber = "1234567890123456";
|
||||
String creditCardNumber2 = "1234567890123456\n";
|
||||
String pattern = "\\d{16}\\z";
|
||||
Assertions.assertTrue(creditCardNumber.matches(pattern));
|
||||
Assertions.assertTrue(Pattern.compile(pattern).matcher(creditCardNumber).find());
|
||||
Assertions.assertFalse(Pattern.compile(pattern).matcher(creditCardNumber2).find());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenCreditCardNumber_thenReturnIfNotMatched() {
|
||||
String creditCardNumber = "1234567890123456\n";
|
||||
String pattern = "\\d{16}\\z";
|
||||
Assertions.assertFalse(Pattern.compile(pattern).matcher(creditCardNumber).find());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLogOutput_thenReturnIfMatched() {
|
||||
String logLine = "2022-05-01 14:30:00,123 INFO Some log message";
|
||||
String pattern = ".*message\\z";
|
||||
Assertions.assertTrue(logLine.matches(pattern));
|
||||
Assertions.assertTrue(Pattern.compile(pattern).matcher(logLine).find());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLogOutput_thenReturnIfNotMatched() {
|
||||
String logLine = "2022-05-01 14:30:00,123 INFO Some log message\n";
|
||||
String pattern = ".*message\\z";
|
||||
Assertions.assertFalse(Pattern.compile(pattern).matcher(logLine).find());
|
||||
}
|
||||
@Test
|
||||
public void givenEmailMessage_thenReturnIfMatched() {
|
||||
String myMessage = "Hello HR, I hope i can write to Baeldung\n";
|
||||
String pattern = ".*Baeldung\\s*\\Z";
|
||||
Assertions.assertTrue(myMessage.matches(pattern));
|
||||
String myMessage2 = "Hello HR, I hope\n i can write to Baeldung";
|
||||
String pattern = ".*Baeldung\\Z";
|
||||
String pattern2 = ".*hope\\Z";
|
||||
Assertions.assertTrue(Pattern.compile(pattern).matcher(myMessage).find());
|
||||
Assertions.assertFalse(Pattern.compile(pattern2).matcher(myMessage2).find());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenEmailMessage_thenReturnIfNotMatched() {
|
||||
String myMessage = "Hello HR, I hope\n i can write to Baeldung";
|
||||
String pattern = ".*hope\\Z";
|
||||
Assertions.assertFalse(Pattern.compile(pattern).matcher(myMessage).find());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenFileExtension_thenReturnIfMatched() {
|
||||
String fileName = "image.jpeg";
|
||||
String fileName = "image.jpeg\n";
|
||||
String fileName2 = "image2.jpeg\n.png";
|
||||
String pattern = ".*\\.jpeg\\Z";
|
||||
Assertions.assertTrue(fileName.matches(pattern));
|
||||
Assertions.assertTrue(Pattern.compile(pattern).matcher(fileName).find());
|
||||
Assertions.assertFalse(Pattern.compile(pattern).matcher(fileName2).find());
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
public void givenFileExtension_thenReturnIfNotMatched() {
|
||||
String fileName = "image2.jpeg\n.png";
|
||||
String pattern = ".*\\.jpeg\\Z";
|
||||
Assertions.assertFalse(Pattern.compile(pattern).matcher(fileName).find());
|
||||
}
|
||||
@Test
|
||||
public void givenURL_thenReturnIfMatched() {
|
||||
String url = "https://www.example.com/api/endpoint\n";
|
||||
String pattern = ".*/endpoint$";
|
||||
Assertions.assertTrue(Pattern.compile(pattern).matcher(url).find());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSentence_thenReturnIfMatched() {
|
||||
String sentence = "Hello, how are you?";
|
||||
String pattern = ".*[.?!]$";
|
||||
Assertions.assertTrue(Pattern.compile(pattern).matcher(sentence).find());
|
||||
}
|
||||
|
||||
}
|
|
@ -7,3 +7,4 @@
|
|||
- [Deserialization Vulnerabilities in Java](https://www.baeldung.com/java-deserialization-vulnerabilities)
|
||||
- [Serialization Validation in Java](https://www.baeldung.com/java-validate-serializable)
|
||||
- [What Is the serialVersionUID?](https://www.baeldung.com/java-serial-version-uid)
|
||||
- [Java Serialization: readObject() vs. readResolve()](https://www.baeldung.com/java-serialization-readobject-vs-readresolve)
|
||||
|
|
|
@ -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
|
||||
|
||||
### Relevant Articles:
|
||||
|
||||
- [Solving Gson Parsing Errors](https://www.baeldung.com/gson-parsing-errors)
|
||||
|
||||
|
|
|
@ -129,13 +129,6 @@
|
|||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<finalName>libraries-3</finalName>
|
||||
<plugins>
|
||||
|
|
|
@ -49,10 +49,4 @@
|
|||
<annotations.version>23.0.0</annotations.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>projectlombok.org</id>
|
||||
<url>https://projectlombok.org/edge-releases</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
</project>
|
|
@ -27,7 +27,7 @@
|
|||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<optaplanner-core.version>8.24.0.Final</optaplanner-core.version>
|
||||
<optaplanner-core.version>9.38.0.Final</optaplanner-core.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -67,10 +67,10 @@
|
|||
<hibernate-core.version>5.2.16.Final</hibernate-core.version>
|
||||
<mysql-connector.version>6.0.6</mysql-connector.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-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>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -17,8 +17,13 @@
|
|||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
<artifactId>resilience4j-retry</artifactId>
|
||||
<version>1.7.1</version>
|
||||
<version>${resilience4j-retry.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<resilience4j-retry.version>2.0.2</resilience4j-retry.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -1,6 +1,6 @@
|
|||
package com.baeldung.backoff.jitter;
|
||||
|
||||
import io.github.resilience4j.retry.IntervalFunction;
|
||||
import io.github.resilience4j.core.IntervalFunction;
|
||||
import io.github.resilience4j.retry.Retry;
|
||||
import io.github.resilience4j.retry.RetryConfig;
|
||||
import org.junit.Before;
|
||||
|
@ -15,8 +15,8 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.function.Function;
|
||||
|
||||
import static com.baeldung.backoff.jitter.BackoffWithJitterUnitTest.RetryProperties.*;
|
||||
import static io.github.resilience4j.retry.IntervalFunction.ofExponentialBackoff;
|
||||
import static io.github.resilience4j.retry.IntervalFunction.ofExponentialRandomBackoff;
|
||||
import static io.github.resilience4j.core.IntervalFunction.ofExponentialBackoff;
|
||||
import static io.github.resilience4j.core.IntervalFunction.ofExponentialRandomBackoff;
|
||||
import static java.util.Collections.nCopies;
|
||||
import static java.util.concurrent.Executors.newFixedThreadPool;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
|
|
|
@ -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>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>dynamodb-local</id>
|
||||
<name>DynamoDB Local Release Repository</name>
|
||||
<url>${dynamodblocal.repository.url}</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<properties>
|
||||
<!-- The main class to start by executing java -jar -->
|
||||
<start-class>com.baeldung.Application</start-class>
|
||||
|
@ -186,9 +178,7 @@
|
|||
<aws-java-sdk-dynamodb.version>1.11.64</aws-java-sdk-dynamodb.version>
|
||||
<bootstrap.version>3.3.7-1</bootstrap.version>
|
||||
<sqlite4java.version>1.0.392</sqlite4java.version>
|
||||
<dynamodb.version>1.11.106</dynamodb.version>
|
||||
<dynamodblocal.version>1.11.86</dynamodblocal.version>
|
||||
<dynamodblocal.repository.url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</dynamodblocal.repository.url>
|
||||
<dynamodblocal.version>1.21.1</dynamodblocal.version>
|
||||
<maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version>
|
||||
<spring-boot.version>2.4.7</spring-boot.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)
|
||||
- [How to Persist a List of String in JPA?](https://www.baeldung.com/java-jpa-persist-string-list)
|
||||
- [Hibernate Natural IDs in Spring Boot](https://www.baeldung.com/spring-boot-hibernate-natural-ids)
|
||||
- [Correct Use of flush() in JPA](https://www.baeldung.com/spring-jpa-flush)
|
||||
- More articles: [[<-- prev]](../spring-data-jpa-repo-2)
|
||||
|
|
13
pom.xml
13
pom.xml
|
@ -366,6 +366,7 @@
|
|||
<module>muleesb</module>
|
||||
<module>web-modules/java-lite</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/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 -->
|
||||
|
@ -425,11 +426,10 @@
|
|||
|
||||
<!-- <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-static-resources</module>
|
||||
<module>spring-swagger-codegen</module>
|
||||
<module>spring-web-modules</module>
|
||||
<module>testing-modules</module>
|
||||
<module>video-tutorials</module>
|
||||
</modules>
|
||||
|
@ -547,6 +547,7 @@
|
|||
<module>muleesb</module>
|
||||
<module>web-modules/java-lite</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/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 -->
|
||||
|
@ -598,11 +599,10 @@
|
|||
|
||||
<!-- <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-static-resources</module>
|
||||
<module>spring-swagger-codegen</module>
|
||||
<module>spring-web-modules</module>
|
||||
<module>testing-modules</module>
|
||||
<module>video-tutorials</module>
|
||||
</modules>
|
||||
|
@ -913,7 +913,7 @@
|
|||
<module>spring-kafka</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-quartz</module>
|
||||
|
||||
|
@ -925,6 +925,7 @@
|
|||
<module>spring-threads</module>
|
||||
<module>spring-vault</module>
|
||||
<module>spring-websockets</module>
|
||||
<module>spring-web-modules</module>
|
||||
<module>static-analysis</module>
|
||||
<module>tensorflow-java</module>
|
||||
<module>vertx-modules</module>
|
||||
|
@ -1175,6 +1176,7 @@
|
|||
<module>spring-kafka</module>
|
||||
|
||||
<module>spring-native</module>
|
||||
<module>spring-security-modules</module>
|
||||
<module>spring-protobuf</module>
|
||||
<module>spring-quartz</module>
|
||||
|
||||
|
@ -1186,6 +1188,7 @@
|
|||
<module>spring-threads</module>
|
||||
<module>spring-vault</module>
|
||||
<module>spring-websockets</module>
|
||||
<module>spring-web-modules</module>
|
||||
<module>static-analysis</module>
|
||||
<module>tensorflow-java</module>
|
||||
<module>vertx-modules</module>
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${compiler.plugin.version}</version>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
|
@ -55,8 +54,4 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<compiler.plugin.version>3.8.1</compiler.plugin.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -52,7 +52,6 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${compiler.plugin.version}</version>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
|
@ -68,7 +67,6 @@
|
|||
|
||||
<properties>
|
||||
<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>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -6,17 +6,7 @@
|
|||
<groupId>com.baeldung.quarkus</groupId>
|
||||
<artifactId>quarkus-funqy</artifactId>
|
||||
<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>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>quarkus-modules</artifactId>
|
||||
|
@ -131,4 +121,16 @@
|
|||
</properties>
|
||||
</profile>
|
||||
</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>
|
||||
|
|
|
@ -76,7 +76,6 @@
|
|||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<parameters>${maven.compiler.parameters}</parameters>
|
||||
</configuration>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<!-- https://github.com/wildfly/jandex-maven-plugin -->
|
||||
<groupId>org.jboss.jandex</groupId>
|
||||
<artifactId>jandex-maven-plugin</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<version>${jandex-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-index</id>
|
||||
|
@ -43,4 +43,8 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<jandex-maven-plugin.version>1.2.1</jandex-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -38,7 +38,6 @@
|
|||
</dependencyManagement>
|
||||
|
||||
<properties>
|
||||
<compiler-plugin.version>3.8.1</compiler-plugin.version>
|
||||
<maven.compiler.parameters>true</maven.compiler.parameters>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>spring-project</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
|
||||
|
@ -19,7 +18,7 @@
|
|||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers-bom</artifactId>
|
||||
<version>1.17.2</version>
|
||||
<version>${testcontainers-bom.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
@ -43,7 +42,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.jasync-sql</groupId>
|
||||
<artifactId>jasync-r2dbc-mysql</artifactId>
|
||||
<version>2.0.8</version>
|
||||
<version>${jasync-r2dbc-mysql.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -104,7 +103,6 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source.version}</source>
|
||||
<target>${maven.compiler.target.version}</target>
|
||||
|
@ -182,7 +180,7 @@
|
|||
<id>local-native</id>
|
||||
<properties>
|
||||
<repackage.classifier>exec</repackage.classifier>
|
||||
<native-buildtools.version>0.9.11</native-buildtools.version>
|
||||
<native-buildtools.version>${native-buildtools.version}</native-buildtools.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
@ -241,7 +239,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M6</version>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<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
|
||||
|
@ -254,11 +252,15 @@
|
|||
</profiles>
|
||||
|
||||
<properties>
|
||||
<testcontainers-bom.version>1.17.2</testcontainers-bom.version>
|
||||
<java.version>11</java.version>
|
||||
<spring-native.version>0.12.1</spring-native.version>
|
||||
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
|
||||
<maven.compiler.source.version>11</maven.compiler.source.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>
|
||||
|
||||
</project>
|
|
@ -3,20 +3,15 @@
|
|||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>sentry-servlet</artifactId>
|
||||
<name>sentry-servlet</name>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>saas-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</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>
|
||||
<dependency>
|
||||
|
@ -37,7 +32,7 @@
|
|||
<plugin>
|
||||
<groupId>org.codehaus.cargo</groupId>
|
||||
<artifactId>cargo-maven3-plugin</artifactId>
|
||||
<version>${cargo.version}</version>
|
||||
<version>${cargo-maven3-plugin.version}</version>
|
||||
<configuration>
|
||||
<container>
|
||||
<containerId>tomcat9x</containerId>
|
||||
|
@ -47,4 +42,10 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</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>
|
|
@ -34,7 +34,6 @@
|
|||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>${maven-war-plugin.version}</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
<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)
|
||||
- [Loading Multiple YAML Configuration Files in Spring Boot](https://www.baeldung.com/spring-boot-load-multiple-yaml-configuration-files)
|
||||
- [Using Environment Variables in Spring Boot’s Properties Files](https://www.baeldung.com/spring-boot-properties-env-variables)
|
||||
- [Spring Boot Properties Prefix Must Be in Canonical Form](https://www.baeldung.com/spring-boot-properties-canonical-form)
|
||||
- More articles: [[<-- prev]](../spring-boot-properties-2)
|
||||
|
|
|
@ -18,3 +18,5 @@ spring.redis.port=6379
|
|||
|
||||
spring.sleuth.sampler.percentage=1.0
|
||||
spring.sleuth.web.skipPattern=(^cleanup.*)
|
||||
|
||||
spring.zipkin.baseUrl=http://localhost:9411
|
||||
|
|
|
@ -6,25 +6,13 @@ eureka.client.registryFetchIntervalSeconds = 5
|
|||
|
||||
management.security.sessions=always
|
||||
|
||||
zuul.routes.book-service.path=/book-service/**
|
||||
zuul.routes.book-service.sensitive-headers=Set-Cookie,Authorization
|
||||
hystrix.command.book-service.execution.isolation.thread.timeoutInMilliseconds=600000
|
||||
|
||||
zuul.routes.rating-service.path=/rating-service/**
|
||||
zuul.routes.rating-service.sensitive-headers=Set-Cookie,Authorization
|
||||
hystrix.command.rating-service.execution.isolation.thread.timeoutInMilliseconds=600000
|
||||
|
||||
zuul.routes.discovery.path=/discovery/**
|
||||
zuul.routes.discovery.sensitive-headers=Set-Cookie,Authorization
|
||||
zuul.routes.discovery.url=http://localhost:8082
|
||||
hystrix.command.discovery.execution.isolation.thread.timeoutInMilliseconds=600000
|
||||
|
||||
logging.level.org.springframework.web.=debug
|
||||
logging.level.org.springframework.security=debug
|
||||
logging.level.org.springframework.cloud.netflix.zuul=debug
|
||||
|
||||
spring.redis.host=localhost
|
||||
spring.redis.port=6379
|
||||
|
||||
spring.sleuth.sampler.percentage=1.0
|
||||
spring.sleuth.web.skipPattern=(^cleanup.*|.+favicon.*)
|
||||
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.web.skipPattern=(^cleanup.*)
|
||||
|
||||
spring.zipkin.baseUrl=http://localhost:9411
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-1</artifactId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../../parent-boot-1</relativePath>
|
||||
<relativePath>../../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -33,7 +33,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-eureka</artifactId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -42,7 +42,7 @@
|
|||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<spring-cloud-dependencies.version>Brixton.SR7</spring-cloud-dependencies.version>
|
||||
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -2,12 +2,12 @@ package com.baeldung.spring.cloud.bootstrap.config;
|
|||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.config.server.EnableConfigServer;
|
||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableConfigServer
|
||||
@EnableEurekaClient
|
||||
@EnableDiscoveryClient
|
||||
public class ConfigApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConfigApplication.class, args);
|
||||
|
|
|
@ -1,23 +1,41 @@
|
|||
package com.baeldung.spring.cloud.bootstrap.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
public class SecurityConfig {
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication().withUser("configUser").password("configPassword").roles("SYSTEM");
|
||||
@Bean
|
||||
public InMemoryUserDetailsManager userDetailsService(BCryptPasswordEncoder bCryptPasswordEncoder) {
|
||||
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
|
||||
manager.createUser(User.withUsername("configUser")
|
||||
.password(bCryptPasswordEncoder.encode("configPassword"))
|
||||
.roles("SYSTEM")
|
||||
.build());
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests().anyRequest().hasRole("SYSTEM").and().httpBasic().and().csrf().disable();
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests()
|
||||
.anyRequest()
|
||||
.hasRole("SYSTEM")
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.csrf()
|
||||
.disable();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder encoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-1</artifactId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../../parent-boot-1</relativePath>
|
||||
<relativePath>../../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -33,7 +33,11 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -41,7 +45,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session</artifactId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -50,7 +54,7 @@
|
|||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<spring-cloud-dependencies.version>Edgware.SR5</spring-cloud-dependencies.version>
|
||||
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -17,7 +17,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication().withUser("discUser").password("discPassword").roles("SYSTEM");
|
||||
auth.inMemoryAuthentication().withUser("discUser").password("{noop}discPassword").roles("SYSTEM");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-1</artifactId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../../parent-boot-1</relativePath>
|
||||
<relativePath>../../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -33,11 +33,15 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<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-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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -45,7 +49,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session</artifactId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -53,11 +57,15 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<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-starter-feign</artifactId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -97,7 +105,7 @@
|
|||
</build>
|
||||
|
||||
<properties>
|
||||
<spring-cloud-dependencies.version>Dalston.RELEASE</spring-cloud-dependencies.version>
|
||||
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -1,8 +1,8 @@
|
|||
package com.baeldung.spring.cloud.bootstrap.gateway;
|
||||
|
||||
import org.springframework.boot.web.servlet.ErrorPage;
|
||||
import org.springframework.boot.web.servlet.ErrorPageRegistrar;
|
||||
import org.springframework.boot.web.servlet.ErrorPageRegistry;
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.ErrorPageRegistrar;
|
||||
import org.springframework.boot.web.server.ErrorPageRegistry;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
|
@ -1,76 +1,15 @@
|
|||
package com.baeldung.spring.cloud.bootstrap.gateway;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClientSpecification;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
|
||||
import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter;
|
||||
import org.springframework.cloud.sleuth.zipkin.ZipkinProperties;
|
||||
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import zipkin.Span;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableZuulProxy
|
||||
@EnableEurekaClient
|
||||
@EnableFeignClients
|
||||
@EnableDiscoveryClient
|
||||
public class GatewayApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GatewayApplication.class, args);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<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;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import static org.springframework.security.config.Customizer.withDefaults;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableWebFluxSecurity
|
||||
@Configuration
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
public class SecurityConfig {
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
.and()
|
||||
.withUser("admin").password("admin").roles("ADMIN");
|
||||
@Bean
|
||||
public MapReactiveUserDetailsService userDetailsService() {
|
||||
UserDetails user = User.withUsername("user")
|
||||
.password(passwordEncoder().encode("password"))
|
||||
.roles("USER")
|
||||
.build();
|
||||
UserDetails adminUser = User.withUsername("admin")
|
||||
.password(passwordEncoder().encode("admin"))
|
||||
.roles("ADMIN")
|
||||
.build();
|
||||
return new MapReactiveUserDetailsService(user, adminUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.formLogin()
|
||||
.defaultSuccessUrl("/home/index.html", true)
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http.formLogin()
|
||||
.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/home/index.html"))
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/book-service/**", "/rating-service/**", "/login*", "/").permitAll()
|
||||
.antMatchers("/eureka/**").hasRole("ADMIN")
|
||||
.anyRequest().authenticated()
|
||||
.authorizeExchange()
|
||||
.pathMatchers("/book-service/**", "/rating-service/**", "/login*", "/")
|
||||
.permitAll()
|
||||
.pathMatchers("/eureka/**")
|
||||
.hasRole("ADMIN")
|
||||
.anyExchange()
|
||||
.authenticated()
|
||||
.and()
|
||||
.logout()
|
||||
.logout()
|
||||
.and()
|
||||
.csrf().disable();
|
||||
.csrf()
|
||||
.disable()
|
||||
.httpBasic(withDefaults());
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package com.baeldung.spring.cloud.bootstrap.gateway;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
||||
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
|
||||
|
||||
@Configuration
|
||||
@EnableRedisHttpSession(redisFlushMode = RedisFlushMode.IMMEDIATE)
|
||||
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
|
||||
@EnableRedisWebSession
|
||||
public class SessionConfig {
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.baeldung.spring.cloud.bootstrap.gateway.client.book;
|
||||
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
@ -9,6 +8,6 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
|||
@FeignClient(value = "book-service")
|
||||
public interface BooksClient {
|
||||
|
||||
@RequestMapping(value = "/books/{bookId}", method = {RequestMethod.GET})
|
||||
@RequestMapping(value = "/books/{bookId}", method = { RequestMethod.GET })
|
||||
Book getBookById(@PathVariable("bookId") Long bookId);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
package com.baeldung.spring.cloud.bootstrap.gateway.client.rating;
|
||||
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient(value = "rating-service")
|
||||
public interface RatingsClient {
|
||||
|
||||
@RequestMapping(value = "/ratings", method = {RequestMethod.GET})
|
||||
@RequestMapping(value = "/ratings", method = { RequestMethod.GET })
|
||||
List<Rating> getRatingsByBookId(@RequestParam("bookId") Long bookId, @RequestHeader("Cookie") String session);
|
||||
}
|
||||
|
|
|
@ -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.username=configUser
|
||||
spring.cloud.config.password=configPassword
|
||||
spring.cloud.gateway.discovery.locator.enabled=true
|
||||
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
|
||||
|
||||
eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/
|
|
@ -20,7 +20,6 @@
|
|||
<module>gateway</module>
|
||||
<module>svc-book</module>
|
||||
<module>svc-rating</module>
|
||||
<module>zipkin</module>
|
||||
<module>customer-service</module>
|
||||
<module>order-service</module>
|
||||
</modules>
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-1</artifactId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../../parent-boot-1</relativePath>
|
||||
<relativePath>../../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -34,7 +34,11 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -46,7 +50,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session</artifactId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -63,12 +67,16 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<spring-cloud-dependencies.version>Dalston.RELEASE</spring-cloud-dependencies.version>
|
||||
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -1,53 +1,14 @@
|
|||
package com.baeldung.spring.cloud.bootstrap.svcbook;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
||||
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
|
||||
import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter;
|
||||
import org.springframework.cloud.sleuth.zipkin.ZipkinProperties;
|
||||
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import zipkin.Span;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableEurekaClient
|
||||
@EnableDiscoveryClient
|
||||
public class BookServiceApplication {
|
||||
|
||||
@Autowired
|
||||
private EurekaClient eurekaClient;
|
||||
@Autowired
|
||||
private SpanMetricReporter spanMetricReporter;
|
||||
@Autowired
|
||||
private ZipkinProperties zipkinProperties;
|
||||
@Value("${spring.sleuth.web.skipPattern}")
|
||||
private String skipPattern;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BookServiceApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZipkinSpanReporter makeZipkinSpanReporter() {
|
||||
return new ZipkinSpanReporter() {
|
||||
private HttpZipkinSpanReporter delegate;
|
||||
private String baseUrl;
|
||||
|
||||
@Override
|
||||
public void report(Span span) {
|
||||
InstanceInfo instance = eurekaClient.getNextServerFromEureka("zipkin", false);
|
||||
if (baseUrl == null || !instance.getHomePageUrl().equals(baseUrl)) {
|
||||
baseUrl = instance.getHomePageUrl();
|
||||
}
|
||||
delegate = new HttpZipkinSpanReporter(new RestTemplate(), baseUrl, zipkinProperties.getFlushInterval(), spanMetricReporter);
|
||||
if (!span.name.matches(skipPattern)) delegate.report(span);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
public class SecurityConfig {
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal1(AuthenticationManagerBuilder auth) throws Exception {
|
||||
//try in memory auth with no users to support the case that this will allow for users that are logged in to go anywhere
|
||||
public void registerAuthProvider(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.httpBasic()
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
return http.authorizeHttpRequests((auth) -> auth.antMatchers(HttpMethod.GET, "/books")
|
||||
.permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/books/*")
|
||||
.permitAll()
|
||||
.antMatchers(HttpMethod.POST, "/books")
|
||||
.hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.PATCH, "/books/*")
|
||||
.hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.DELETE, "/books/*")
|
||||
.hasRole("ADMIN"))
|
||||
.csrf()
|
||||
.disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers(HttpMethod.GET, "/books").permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/books/*").permitAll()
|
||||
.antMatchers(HttpMethod.POST, "/books").hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.PATCH, "/books/*").hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.DELETE, "/books/*").hasRole("ADMIN")
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.csrf()
|
||||
.disable();
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.baeldung.spring.cloud.bootstrap.svcbook.book;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -23,8 +22,8 @@ public class BookService {
|
|||
}
|
||||
|
||||
public Book findBookById(Long bookId) {
|
||||
return Optional.ofNullable(bookRepository.findOne(bookId))
|
||||
.orElseThrow(() -> new BookNotFoundException("Book not found. ID: " + bookId));
|
||||
return bookRepository.findById(bookId)
|
||||
.orElseThrow(() -> new BookNotFoundException(String.format("Book not found. ID: %s", bookId)));
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
|
@ -37,7 +36,7 @@ public class BookService {
|
|||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public void deleteBook(Long bookId) {
|
||||
bookRepository.delete(bookId);
|
||||
bookRepository.deleteById(bookId);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
|
@ -60,7 +59,8 @@ public class BookService {
|
|||
public Book updateBook(Book book, Long bookId) {
|
||||
Preconditions.checkNotNull(book);
|
||||
Preconditions.checkState(book.getId() == bookId);
|
||||
Preconditions.checkNotNull(bookRepository.findOne(bookId));
|
||||
Preconditions.checkArgument(bookRepository.findById(bookId)
|
||||
.isPresent());
|
||||
return bookRepository.save(book);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,5 +3,7 @@ spring.cloud.config.discovery.service-id=config
|
|||
spring.cloud.config.discovery.enabled=true
|
||||
spring.cloud.config.username=configUser
|
||||
spring.cloud.config.password=configPassword
|
||||
spring.cloud.gateway.discovery.locator.enabled=true
|
||||
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
|
||||
|
||||
eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/
|
|
@ -10,9 +10,9 @@
|
|||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-1</artifactId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../../parent-boot-1</relativePath>
|
||||
<relativePath>../../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -34,7 +34,11 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -46,7 +50,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session</artifactId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -58,25 +62,29 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-hystrix</artifactId>
|
||||
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</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>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<spring-cloud-dependencies.version>Dalston.RELEASE</spring-cloud-dependencies.version>
|
||||
<spring-cloud-dependencies.version>2021.0.7</spring-cloud-dependencies.version>
|
||||
</properties>
|
||||
|
||||
</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;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
||||
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
|
||||
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
|
||||
import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter;
|
||||
import org.springframework.cloud.sleuth.zipkin.ZipkinProperties;
|
||||
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
|
||||
|
||||
import zipkin.Span;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableEurekaClient
|
||||
@EnableHystrix
|
||||
@EnableTransactionManagement(order=Ordered.LOWEST_PRECEDENCE, mode=AdviceMode.ASPECTJ)
|
||||
@EnableDiscoveryClient
|
||||
@EnableTransactionManagement(order = Ordered.LOWEST_PRECEDENCE, mode = AdviceMode.ASPECTJ)
|
||||
public class RatingServiceApplication {
|
||||
@Autowired
|
||||
private EurekaClient eurekaClient;
|
||||
@Autowired
|
||||
private SpanMetricReporter spanMetricReporter;
|
||||
@Autowired
|
||||
private ZipkinProperties zipkinProperties;
|
||||
@Value("${spring.sleuth.web.skipPattern}")
|
||||
private String skipPattern;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(RatingServiceApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZipkinSpanReporter makeZipkinSpanReporter() {
|
||||
return new ZipkinSpanReporter() {
|
||||
private HttpZipkinSpanReporter delegate;
|
||||
private String baseUrl;
|
||||
|
||||
@Override
|
||||
public void report(Span span) {
|
||||
InstanceInfo instance = eurekaClient.getNextServerFromEureka("zipkin", false);
|
||||
if (baseUrl == null || !instance.getHomePageUrl().equals(baseUrl)) {
|
||||
baseUrl = instance.getHomePageUrl();
|
||||
}
|
||||
delegate = new HttpZipkinSpanReporter(new RestTemplate(), baseUrl, zipkinProperties.getFlushInterval(), spanMetricReporter);
|
||||
if (!span.name.matches(skipPattern)) delegate.report(span);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@Order(value=Ordered.HIGHEST_PRECEDENCE)
|
||||
public HystrixCommandAspect hystrixAspect() {
|
||||
return new HystrixCommandAspect();
|
||||
}
|
||||
}
|
|
@ -1,39 +1,41 @@
|
|||
package com.baeldung.spring.cloud.bootstrap.svcrating;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
public class SecurityConfig {
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal1(AuthenticationManagerBuilder auth) throws Exception {
|
||||
//try in memory auth with no users to support the case that this will allow for users that are logged in to go anywhere
|
||||
auth.inMemoryAuthentication();
|
||||
@Bean
|
||||
public UserDetailsService users() {
|
||||
return new InMemoryUserDetailsManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.regexMatchers("^/ratings\\?bookId.*$").authenticated()
|
||||
.antMatchers(HttpMethod.POST,"/ratings").authenticated()
|
||||
.antMatchers(HttpMethod.PATCH,"/ratings/*").hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.DELETE,"/ratings/*").hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.GET,"/ratings").hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.GET,"/hystrix").authenticated()
|
||||
.anyRequest().authenticated()
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
return httpSecurity.authorizeHttpRequests((auth) -> auth.regexMatchers("^/ratings\\?bookId.*$")
|
||||
.authenticated()
|
||||
.antMatchers(HttpMethod.POST, "/ratings")
|
||||
.authenticated()
|
||||
.antMatchers(HttpMethod.PATCH, "/ratings/*")
|
||||
.hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.DELETE, "/ratings/*")
|
||||
.hasRole("ADMIN")
|
||||
.antMatchers(HttpMethod.GET, "/ratings")
|
||||
.hasRole("ADMIN")
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.httpBasic()
|
||||
.and()
|
||||
.httpBasic().and()
|
||||
.csrf()
|
||||
.disable();
|
||||
|
||||
|
||||
.csrf()
|
||||
.disable()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,24 +5,24 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
||||
|
||||
@Configuration
|
||||
@EnableRedisHttpSession
|
||||
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
|
||||
|
||||
@Autowired
|
||||
Environment properties;
|
||||
|
||||
private Environment properties;
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public JedisConnectionFactory connectionFactory() {
|
||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
||||
factory.setHostName(properties.getProperty("spring.redis.host","localhost"));
|
||||
factory.setPort(properties.getProperty("spring.redis.port", Integer.TYPE,6379));
|
||||
factory.afterPropertiesSet();
|
||||
factory.setUsePool(true);
|
||||
return factory;
|
||||
public LettuceConnectionFactory redisConnectionFactory() {
|
||||
RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration();
|
||||
redisConfiguration.setHostName(properties.getProperty("spring.redis.host", "localhost"));
|
||||
redisConfiguration.setPort(properties.getProperty("spring.redis.port", Integer.TYPE, 6379));
|
||||
return new LettuceConnectionFactory(redisConfiguration);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,21 +6,21 @@ import java.util.stream.Collectors;
|
|||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.SetOperations;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public class RatingCacheRepository implements InitializingBean {
|
||||
|
||||
@Autowired
|
||||
private JedisConnectionFactory cacheConnectionFactory;
|
||||
private LettuceConnectionFactory cacheConnectionFactory;
|
||||
|
||||
private StringRedisTemplate redisTemplate;
|
||||
private ValueOperations<String, String> valueOps;
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -10,7 +9,8 @@ import org.springframework.transaction.annotation.Propagation;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
|
||||
|
||||
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
|
@ -22,31 +22,31 @@ public class RatingService {
|
|||
@Autowired
|
||||
private RatingCacheRepository cacheRepository;
|
||||
|
||||
@HystrixCommand(commandKey = "ratingsByBookIdFromDB", fallbackMethod = "findCachedRatingsByBookId")
|
||||
@CircuitBreaker(name = "ratingsByBookIdFromDB", fallbackMethod = "findCachedRatingsByBookId")
|
||||
public List<Rating> findRatingsByBookId(Long bookId) {
|
||||
return ratingRepository.findRatingsByBookId(bookId);
|
||||
}
|
||||
|
||||
public List<Rating> findCachedRatingsByBookId(Long bookId) {
|
||||
public List<Rating> findCachedRatingsByBookId(Long bookId, Exception exception) {
|
||||
return cacheRepository.findCachedRatingsByBookId(bookId);
|
||||
}
|
||||
|
||||
@HystrixCommand(commandKey = "ratingsFromDB", fallbackMethod = "findAllCachedRatings")
|
||||
@CircuitBreaker(name = "ratingsFromDB", fallbackMethod = "findAllCachedRatings")
|
||||
public List<Rating> findAllRatings() {
|
||||
return ratingRepository.findAll();
|
||||
}
|
||||
|
||||
public List<Rating> findAllCachedRatings() {
|
||||
public List<Rating> findAllCachedRatings(Exception exception) {
|
||||
return cacheRepository.findAllCachedRatings();
|
||||
}
|
||||
|
||||
@HystrixCommand(commandKey = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById", ignoreExceptions = { RatingNotFoundException.class })
|
||||
@CircuitBreaker(name = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById")
|
||||
public Rating findRatingById(Long ratingId) {
|
||||
return Optional.ofNullable(ratingRepository.findOne(ratingId))
|
||||
return ratingRepository.findById(ratingId)
|
||||
.orElseThrow(() -> new RatingNotFoundException("Rating not found. ID: " + ratingId));
|
||||
}
|
||||
|
||||
public Rating findCachedRatingById(Long ratingId) {
|
||||
public Rating findCachedRatingById(Long ratingId, Exception exception) {
|
||||
return cacheRepository.findCachedRatingById(ratingId);
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ public class RatingService {
|
|||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public void deleteRating(Long ratingId) {
|
||||
ratingRepository.delete(ratingId);
|
||||
ratingRepository.deleteById(ratingId);
|
||||
cacheRepository.deleteRating(ratingId);
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ public class RatingService {
|
|||
public Rating updateRating(Rating rating, Long ratingId) {
|
||||
Preconditions.checkNotNull(rating);
|
||||
Preconditions.checkState(rating.getId() == ratingId);
|
||||
Preconditions.checkNotNull(ratingRepository.findOne(ratingId));
|
||||
Preconditions.checkNotNull(ratingRepository.findById(ratingId));
|
||||
return ratingRepository.save(rating);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,5 +3,7 @@ spring.cloud.config.discovery.service-id=config
|
|||
spring.cloud.config.discovery.enabled=true
|
||||
spring.cloud.config.username=configUser
|
||||
spring.cloud.config.password=configPassword
|
||||
spring.cloud.gateway.discovery.locator.enabled=true
|
||||
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
|
||||
|
||||
eureka.client.serviceUrl.defaultZone=http://discUser:discPassword@localhost:8082/eureka/
|
||||
|
|
|
@ -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