Merge branch 'eugenp:master' into danielmcnally285_return_first_non_null

This commit is contained in:
danielmcnally285
2023-11-18 19:59:52 +00:00
committed by GitHub
470 changed files with 6955 additions and 1620 deletions
@@ -0,0 +1,2 @@
## Relevant Articles
- [Implement Connect 4 Game with Java](https://www.baeldung.com/java-connect-4-game)
+1 -1
View File
@@ -28,7 +28,7 @@
</modules>
<properties>
<commons-codec.version>1.11</commons-codec.version>
<commons-codec.version>1.16.0</commons-codec.version>
<commons-math3.version>3.6.1</commons-math3.version>
<cobertura.plugin.version>2.7</cobertura.plugin.version>
<tradukisto.version>1.0.1</tradukisto.version>
+1 -1
View File
@@ -234,7 +234,7 @@
<properties>
<!-- util -->
<commons-codec.version>1.10</commons-codec.version>
<commons-codec.version>1.16.0</commons-codec.version>
<httpasyncclient.version>4.1.5</httpasyncclient.version>
<!-- testing -->
<wiremock.version>2.5.1</wiremock.version>
-1
View File
@@ -1 +0,0 @@
log4j.rootLogger=INFO, stdout
+6 -5
View File
@@ -23,11 +23,6 @@
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@@ -57,6 +52,11 @@
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.databind.version} </version>
</dependency>
</dependencies>
<properties>
@@ -64,6 +64,7 @@
<kafka.version>2.8.0</kafka.version>
<testcontainers-kafka.version>1.15.3</testcontainers-kafka.version>
<testcontainers-jupiter.version>1.15.3</testcontainers-jupiter.version>
<jackson.databind.version>2.15.2</jackson.databind.version>
</properties>
</project>
@@ -0,0 +1,11 @@
package com.baeldung.kafka.message.ordering;
public class Config {
public static final String CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS = "value.deserializer.serializedClass";
public static final String MULTI_PARTITION_TOPIC = "multi_partition_topic";
public static final String SINGLE_PARTITION_TOPIC = "single_partition_topic";
public static final int MULTIPLE_PARTITIONS = 5;
public static final int SINGLE_PARTITION = 1;
public static final short REPLICATION_FACTOR = 1;
}
@@ -0,0 +1,61 @@
package com.baeldung.kafka.message.ordering.payload;
import java.util.Objects;
public class UserEvent implements Comparable<UserEvent> {
private String userEventId;
private long eventNanoTime;
private long globalSequenceNumber;
@SuppressWarnings("unused")
public UserEvent() {
// Required for Jackson Serialization and Deserialization
}
public UserEvent(String userEventId) {
this.userEventId = userEventId;
}
public String getUserEventId() {
return userEventId;
}
public long getEventNanoTime() {
return eventNanoTime;
}
public void setEventNanoTime(long eventNanoTime) {
this.eventNanoTime = eventNanoTime;
}
public long getGlobalSequenceNumber() {
return globalSequenceNumber;
}
public void setGlobalSequenceNumber(long globalSequenceNumber) {
this.globalSequenceNumber = globalSequenceNumber;
}
@Override
public int compareTo(UserEvent other) {
return Long.compare(this.globalSequenceNumber, other.globalSequenceNumber);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof UserEvent)) {
return false;
}
UserEvent userEvent = (UserEvent) obj;
return this.globalSequenceNumber == userEvent.globalSequenceNumber;
}
@Override
public int hashCode() {
return Objects.hash(globalSequenceNumber);
}
}
@@ -0,0 +1,34 @@
package com.baeldung.kafka.message.ordering.serialization;
import com.baeldung.kafka.message.ordering.Config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.kafka.common.serialization.Deserializer;
import java.util.Map;
/**
* Configured via {@link org.apache.kafka.clients.consumer.ConsumerConfig#VALUE_DESERIALIZER_CLASS_CONFIG}
*/
public class JacksonDeserializer<T> implements Deserializer<T> {
private final ObjectMapper objectMapper = new ObjectMapper();
private Class<T> type;
@Override
public void configure(Map<String, ?> configs, boolean isKey) {
this.type = (Class<T>) configs.get(Config.CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS);
}
@Override
public T deserialize(String topic, byte[] bytes) {
if (bytes == null) {
return null;
}
try {
return objectMapper.readValue(bytes, type);
} catch (Exception e) {
throw new RuntimeException("Error deserializing value", e);
}
}
}
@@ -0,0 +1,24 @@
package com.baeldung.kafka.message.ordering.serialization;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.kafka.common.serialization.Serializer;
/**
* Configured via {@link org.apache.kafka.clients.producer.ProducerConfig#VALUE_SERIALIZER_CLASS_CONFIG}
*/
public class JacksonSerializer<T> implements Serializer<T> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public byte[] serialize(String topic, T data) {
if (data == null) {
return null;
}
try {
return objectMapper.writeValueAsBytes(data);
} catch (Exception e) {
throw new RuntimeException("Error serializing value", e);
}
}
}
@@ -0,0 +1,126 @@
package com.baeldung.kafka.message.ordering;
import com.baeldung.kafka.message.ordering.payload.UserEvent;
import com.baeldung.kafka.message.ordering.serialization.JacksonDeserializer;
import com.baeldung.kafka.message.ordering.serialization.JacksonSerializer;
import org.apache.kafka.clients.admin.*;
import org.apache.kafka.clients.consumer.ConsumerConfig;
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.LongDeserializer;
import org.apache.kafka.common.serialization.LongSerializer;
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;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import com.google.common.collect.ImmutableList;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@Testcontainers
public class ExternalSequenceWithTimeWindowLiveTest {
private static Admin admin;
private static KafkaProducer<Long, UserEvent> producer;
private static KafkaConsumer<Long, UserEvent> consumer;
private static final Duration TIMEOUT_WAIT_FOR_MESSAGES = Duration.ofSeconds(5);
private static final long BUFFER_PERIOD_NS = Duration.ofSeconds(5)
.toNanos();
private static Logger logger = LoggerFactory.getLogger(ExternalSequenceWithTimeWindowLiveTest.class);
@Container
private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest"));
@BeforeAll
static void setup() throws ExecutionException, InterruptedException {
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, LongSerializer.class.getName());
producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JacksonSerializer.class.getName());
Properties consumerProperties = new Properties();
consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers());
consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, LongDeserializer.class.getName());
consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JacksonDeserializer.class.getName());
consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
consumerProperties.put(Config.CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS, UserEvent.class);
consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "test-group");
admin = Admin.create(adminProperties);
producer = new KafkaProducer<>(producerProperties);
consumer = new KafkaConsumer<>(consumerProperties);
admin.createTopics(ImmutableList.of(new NewTopic(Config.MULTI_PARTITION_TOPIC, Config.MULTIPLE_PARTITIONS, Config.REPLICATION_FACTOR)))
.all()
.get();
}
@AfterAll
static void destroy() {
KAFKA_CONTAINER.stop();
}
@Test
void givenMultiplePartitions_whenPublishedToKafkaAndConsumedWithExtSeqNumberAndTimeWindow_thenCheckForMessageOrder() throws ExecutionException, InterruptedException {
List<UserEvent> sentUserEventList = new ArrayList<>();
List<UserEvent> receivedUserEventList = new ArrayList<>();
for (long sequenceNumber = 1; sequenceNumber <= 10; sequenceNumber++) {
UserEvent userEvent = new UserEvent(UUID.randomUUID()
.toString());
userEvent.setEventNanoTime(System.nanoTime());
userEvent.setGlobalSequenceNumber(sequenceNumber);
Future<RecordMetadata> future = producer.send(new ProducerRecord<>(Config.MULTI_PARTITION_TOPIC, sequenceNumber, userEvent));
sentUserEventList.add(userEvent);
RecordMetadata metadata = future.get();
logger.info("User Event ID: " + userEvent.getUserEventId() + ", Partition : " + metadata.partition());
}
consumer.subscribe(Collections.singletonList(Config.MULTI_PARTITION_TOPIC));
List<UserEvent> buffer = new ArrayList<>();
long lastProcessedTime = System.nanoTime();
ConsumerRecords<Long, UserEvent> records = consumer.poll(TIMEOUT_WAIT_FOR_MESSAGES);
records.forEach(record -> {
buffer.add(record.value());
});
while (!buffer.isEmpty()) {
if (System.nanoTime() - lastProcessedTime > BUFFER_PERIOD_NS) {
processBuffer(buffer, receivedUserEventList);
lastProcessedTime = System.nanoTime();
}
records = consumer.poll(TIMEOUT_WAIT_FOR_MESSAGES);
records.forEach(record -> {
buffer.add(record.value());
});
}
assertThat(receivedUserEventList).isEqualTo(sentUserEventList)
.containsExactlyElementsOf(sentUserEventList);
}
private static void processBuffer(List<UserEvent> buffer, List<UserEvent> receivedUserEventList) {
Collections.sort(buffer);
buffer.forEach(userEvent -> {
receivedUserEventList.add(userEvent);
logger.info("Processing message with Global Sequence number: " + userEvent.getGlobalSequenceNumber() + ", User Event Id: " + userEvent.getUserEventId());
});
buffer.clear();
}
}
@@ -0,0 +1,105 @@
package com.baeldung.kafka.message.ordering;
import com.baeldung.kafka.message.ordering.payload.UserEvent;
import com.baeldung.kafka.message.ordering.serialization.JacksonDeserializer;
import com.baeldung.kafka.message.ordering.serialization.JacksonSerializer;
import org.apache.kafka.clients.admin.*;
import org.apache.kafka.clients.consumer.ConsumerConfig;
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.LongDeserializer;
import org.apache.kafka.common.serialization.LongSerializer;
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;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import com.google.common.collect.ImmutableList;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@Testcontainers
public class MultiplePartitionLiveTest {
private static Admin admin;
private static KafkaProducer<Long, UserEvent> producer;
private static KafkaConsumer<Long, UserEvent> consumer;
private static final Duration TIMEOUT_WAIT_FOR_MESSAGES = Duration.ofSeconds(5);
private static Logger logger = LoggerFactory.getLogger(MultiplePartitionLiveTest.class);
@Container
private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest"));
@BeforeAll
static void setup() throws ExecutionException, InterruptedException {
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, LongSerializer.class.getName());
producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JacksonSerializer.class.getName());
Properties consumerProperties = new Properties();
consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers());
consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, LongDeserializer.class.getName());
consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JacksonDeserializer.class.getName());
consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
consumerProperties.put(Config.CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS, UserEvent.class);
consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "test-group");
admin = Admin.create(adminProperties);
producer = new KafkaProducer<>(producerProperties);
consumer = new KafkaConsumer<>(consumerProperties);
admin.createTopics(ImmutableList.of(new NewTopic(Config.MULTI_PARTITION_TOPIC, Config.MULTIPLE_PARTITIONS, Config.REPLICATION_FACTOR)))
.all()
.get();
}
@AfterAll
static void destroy() {
KAFKA_CONTAINER.stop();
}
@Test
void givenMultiplePartitions_whenPublishedToKafkaAndConsumed_thenCheckForMessageOrder() throws ExecutionException, InterruptedException {
List<UserEvent> sentUserEventList = new ArrayList<>();
List<UserEvent> receivedUserEventList = new ArrayList<>();
for (long sequenceNumber = 1; sequenceNumber <= 10; sequenceNumber++) {
UserEvent userEvent = new UserEvent(UUID.randomUUID()
.toString());
userEvent.setGlobalSequenceNumber(sequenceNumber);
userEvent.setEventNanoTime(System.nanoTime());
Future<RecordMetadata> future = producer.send(new ProducerRecord<>(Config.MULTI_PARTITION_TOPIC, sequenceNumber, userEvent));
sentUserEventList.add(userEvent);
RecordMetadata metadata = future.get();
logger.info("User Event ID: " + userEvent.getUserEventId() + ", Partition : " + metadata.partition());
}
consumer.subscribe(Collections.singletonList(Config.MULTI_PARTITION_TOPIC));
ConsumerRecords<Long, UserEvent> records = consumer.poll(TIMEOUT_WAIT_FOR_MESSAGES);
records.forEach(record -> {
UserEvent userEvent = record.value();
receivedUserEventList.add(userEvent);
logger.info("User Event ID: " + userEvent.getUserEventId());
});
assertThat(receivedUserEventList).isNotEqualTo(sentUserEventList)
.containsExactlyInAnyOrderElementsOf(sentUserEventList);
}
}
@@ -0,0 +1,109 @@
package com.baeldung.kafka.message.ordering;
import com.baeldung.kafka.message.ordering.payload.UserEvent;
import com.baeldung.kafka.message.ordering.serialization.JacksonDeserializer;
import com.baeldung.kafka.message.ordering.serialization.JacksonSerializer;
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.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.LongDeserializer;
import org.apache.kafka.common.serialization.LongSerializer;
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;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import com.google.common.collect.ImmutableList;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@Testcontainers
public class SinglePartitionLiveTest {
private static Admin admin;
private static KafkaProducer<Long, UserEvent> producer;
private static KafkaConsumer<Long, UserEvent> consumer;
private static final Duration TIMEOUT_WAIT_FOR_MESSAGES = Duration.ofSeconds(5);
private static Logger logger = LoggerFactory.getLogger(SinglePartitionLiveTest.class);
@Container
private static final KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest"));
@BeforeAll
static void setup() throws ExecutionException, InterruptedException {
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, LongSerializer.class.getName());
producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JacksonSerializer.class.getName());
Properties consumerProperties = new Properties();
consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers());
consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, LongDeserializer.class.getName());
consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JacksonDeserializer.class.getName());
consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
consumerProperties.put(Config.CONSUMER_VALUE_DESERIALIZER_SERIALIZED_CLASS, UserEvent.class);
consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "test-group");
admin = Admin.create(adminProperties);
producer = new KafkaProducer<>(producerProperties);
consumer = new KafkaConsumer<>(consumerProperties);
admin.createTopics(ImmutableList.of(new NewTopic(Config.SINGLE_PARTITION_TOPIC, Config.SINGLE_PARTITION, Config.REPLICATION_FACTOR)))
.all()
.get();
}
@AfterAll
static void destroy() {
KAFKA_CONTAINER.stop();
}
@Test
void givenASinglePartition_whenPublishedToKafkaAndConsumed_thenCheckForMessageOrder() throws ExecutionException, InterruptedException {
List<UserEvent> sentUserEventList = new ArrayList<>();
List<UserEvent> receivedUserEventList = new ArrayList<>();
for (long sequenceNumber = 1; sequenceNumber <= 10; sequenceNumber++) {
UserEvent userEvent = new UserEvent(UUID.randomUUID()
.toString());
userEvent.setGlobalSequenceNumber(sequenceNumber);
userEvent.setEventNanoTime(System.nanoTime());
ProducerRecord<Long, UserEvent> producerRecord = new ProducerRecord<>(Config.SINGLE_PARTITION_TOPIC, userEvent);
Future<RecordMetadata> future = producer.send(producerRecord);
sentUserEventList.add(userEvent);
RecordMetadata metadata = future.get();
logger.info("User Event ID: " + userEvent.getUserEventId() + ", Partition : " + metadata.partition());
}
consumer.subscribe(Collections.singletonList(Config.SINGLE_PARTITION_TOPIC));
ConsumerRecords<Long, UserEvent> records = consumer.poll(TIMEOUT_WAIT_FOR_MESSAGES);
records.forEach(record -> {
UserEvent userEvent = record.value();
receivedUserEventList.add(userEvent);
logger.info("User Event ID: " + userEvent.getUserEventId());
});
assertThat(receivedUserEventList).isEqualTo(sentUserEventList)
.containsExactlyElementsOf(sentUserEventList);
}
}
@@ -0,0 +1,11 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
+1 -19
View File
@@ -82,26 +82,11 @@
<artifactId>geronimo-jta_1.0.1B_spec</artifactId>
<version>${geronimo.version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation-api.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>${javax.el-api.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>${javax.el.version}</version>
</dependency>
</dependencies>
<properties>
@@ -113,10 +98,7 @@
<derby.version>10.8.1.2</derby.version>
<jta.version>1.1</jta.version>
<geronimo.version>1.0</geronimo.version>
<validation-api.version>2.0.1.Final</validation-api.version>
<hibernate-validator.version>6.1.2.Final</hibernate-validator.version>
<javax.el-api.version>3.0.0</javax.el-api.version>
<javax.el.version>2.2.4</javax.el.version>
<hibernate-validator.version>8.0.1.Final</hibernate-validator.version>
</properties>
</project>
@@ -3,10 +3,10 @@ package com.baeldung.atomikos.spring.jpa;
import java.util.Set;
import java.util.UUID;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@@ -3,7 +3,7 @@ package com.baeldung.atomikos.spring.jpa.order;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Max;
import jakarta.validation.constraints.Max;
@Entity
@Table(name = "ORDERS")
+1 -1
View File
@@ -79,7 +79,7 @@
</build>
<properties>
<gson.version>2.8.0</gson.version>
<gson.version>2.10.1</gson.version>
<dynamodblocal.version>1.21.1</dynamodblocal.version>
<maven-plugins-version>3.1.1</maven-plugins-version>
</properties>
+2 -2
View File
@@ -73,9 +73,9 @@
</build>
<properties>
<gson.version>2.8.0</gson.version>
<gson.version>2.10.1</gson.version>
<dynamodblocal.version>1.21.1</dynamodblocal.version>
<commons-codec-version>1.10.L001</commons-codec-version>
<commons-codec-version>1.16.0</commons-codec-version>
<jets3t-version>0.9.4.0006L</jets3t-version>
<maven-plugins-version>3.1.1</maven-plugins-version>
</properties>
@@ -0,0 +1,47 @@
package com.baeldung.deserializationfilters;
import java.io.ObjectInputFilter;
import java.util.function.BinaryOperator;
import com.baeldung.deserializationfilters.service.DeserializationService;
import com.baeldung.deserializationfilters.service.LimitedArrayService;
import com.baeldung.deserializationfilters.service.LowDepthService;
import com.baeldung.deserializationfilters.service.SmallObjectService;
import com.baeldung.deserializationfilters.utils.FilterUtils;
public class ContextSpecificDeserializationFilterFactory implements BinaryOperator<ObjectInputFilter> {
@Override
public ObjectInputFilter apply(ObjectInputFilter current, ObjectInputFilter next) {
if (current == null) {
Class<?> caller = findInStack(DeserializationService.class);
if (caller == null) {
current = FilterUtils.fallbackFilter();
} else if (caller.equals(SmallObjectService.class)) {
current = FilterUtils.safeSizeFilter(190);
} else if (caller.equals(LowDepthService.class)) {
current = FilterUtils.safeDepthFilter(2);
} else if (caller.equals(LimitedArrayService.class)) {
current = FilterUtils.safeArrayFilter(3);
}
}
return ObjectInputFilter.merge(current, next);
}
private static Class<?> findInStack(Class<?> superType) {
for (StackTraceElement element : Thread.currentThread()
.getStackTrace()) {
try {
Class<?> subType = Class.forName(element.getClassName());
if (superType.isAssignableFrom(subType)) {
return subType;
}
} catch (ClassNotFoundException e) {
return null;
}
}
return null;
}
}
@@ -0,0 +1,7 @@
package com.baeldung.deserializationfilters.pojo;
import java.io.Serializable;
public interface ContextSpecific extends Serializable {
}
@@ -0,0 +1,19 @@
package com.baeldung.deserializationfilters.pojo;
public class NestedSample implements ContextSpecific {
private static final long serialVersionUID = 1L;
private Sample optional;
public NestedSample(Sample optional) {
this.optional = optional;
}
public Sample getOptional() {
return optional;
}
public void setOptional(Sample optional) {
this.optional = optional;
}
}
@@ -0,0 +1,61 @@
package com.baeldung.deserializationfilters.pojo;
public class Sample implements ContextSpecific, Comparable<Sample> {
private static final long serialVersionUID = 1L;
private int[] array;
private String name;
private NestedSample nested;
public Sample(String name) {
this.name = name;
}
public Sample(int[] array) {
this.array = array;
}
public Sample(NestedSample nested) {
this.nested = nested;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int[] getArray() {
return array;
}
public void setArray(int[] array) {
this.array = array;
}
public NestedSample getNested() {
return nested;
}
public void setNested(NestedSample nested) {
this.nested = nested;
}
@Override
public String toString() {
return name;
}
@Override
public int compareTo(Sample o) {
if (name == null)
return -1;
if (o == null || o.getName() == null)
return 1;
return getName().compareTo(o.getName());
}
}
@@ -0,0 +1,25 @@
package com.baeldung.deserializationfilters.pojo;
public class SampleExploit extends Sample {
private static final long serialVersionUID = 1L;
public SampleExploit() {
super("exploit");
}
public static void maliciousCode() {
System.out.println("exploit executed");
}
@Override
public String toString() {
maliciousCode();
return "exploit";
}
@Override
public int compareTo(Sample o) {
maliciousCode();
return super.compareTo(o);
}
}
@@ -0,0 +1,11 @@
package com.baeldung.deserializationfilters.service;
import java.io.InputStream;
import java.util.Set;
import com.baeldung.deserializationfilters.pojo.ContextSpecific;
public interface DeserializationService {
Set<ContextSpecific> process(InputStream... inputStreams);
}
@@ -0,0 +1,15 @@
package com.baeldung.deserializationfilters.service;
import java.io.InputStream;
import java.util.Set;
import com.baeldung.deserializationfilters.pojo.ContextSpecific;
import com.baeldung.deserializationfilters.utils.DeserializationUtils;
public class LimitedArrayService implements DeserializationService {
@Override
public Set<ContextSpecific> process(InputStream... inputStreams) {
return DeserializationUtils.deserializeIntoSet(inputStreams);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.deserializationfilters.service;
import java.io.InputStream;
import java.io.ObjectInputFilter;
import java.util.Set;
import com.baeldung.deserializationfilters.pojo.ContextSpecific;
import com.baeldung.deserializationfilters.utils.DeserializationUtils;
public class LowDepthService implements DeserializationService {
public Set<ContextSpecific> process(ObjectInputFilter filter, InputStream... inputStreams) {
return DeserializationUtils.deserializeIntoSet(filter, inputStreams);
}
@Override
public Set<ContextSpecific> process(InputStream... inputStreams) {
return process(null, inputStreams);
}
}
@@ -0,0 +1,15 @@
package com.baeldung.deserializationfilters.service;
import java.io.InputStream;
import java.util.Set;
import com.baeldung.deserializationfilters.pojo.ContextSpecific;
import com.baeldung.deserializationfilters.utils.DeserializationUtils;
public class SmallObjectService implements DeserializationService {
@Override
public Set<ContextSpecific> process(InputStream... inputStreams) {
return DeserializationUtils.deserializeIntoSet(inputStreams);
}
}
@@ -0,0 +1,50 @@
package com.baeldung.deserializationfilters.utils;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.util.Set;
import java.util.TreeSet;
import com.baeldung.deserializationfilters.pojo.ContextSpecific;
public class DeserializationUtils {
private DeserializationUtils() {
}
public static Object deserialize(InputStream inStream) {
return deserialize(inStream, null);
}
public static Object deserialize(InputStream inStream, ObjectInputFilter filter) {
try (ObjectInputStream in = new ObjectInputStream(inStream)) {
if (filter != null) {
in.setObjectInputFilter(filter);
}
return in.readObject();
} catch (InvalidClassException e) {
return null;
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
public static Set<ContextSpecific> deserializeIntoSet(InputStream... inputStreams) {
return deserializeIntoSet(null, inputStreams);
}
public static Set<ContextSpecific> deserializeIntoSet(ObjectInputFilter filter, InputStream... inputStreams) {
Set<ContextSpecific> set = new TreeSet<>();
for (InputStream inputStream : inputStreams) {
Object object = deserialize(inputStream, filter);
if (object != null) {
set.add((ContextSpecific) object);
}
}
return set;
}
}
@@ -0,0 +1,32 @@
package com.baeldung.deserializationfilters.utils;
import java.io.ObjectInputFilter;
public class FilterUtils {
private static final String DEFAULT_PACKAGE_PATTERN = "java.base/*;!*";
private static final String POJO_PACKAGE = "com.baeldung.deserializationfilters.pojo";
private FilterUtils() {
}
private static ObjectInputFilter baseFilter(String parameter, int max) {
return ObjectInputFilter.Config.createFilter(String.format("%s=%d;%s.**;%s", parameter, max, POJO_PACKAGE, DEFAULT_PACKAGE_PATTERN));
}
public static ObjectInputFilter fallbackFilter() {
return ObjectInputFilter.Config.createFilter(String.format("%s", DEFAULT_PACKAGE_PATTERN));
}
public static ObjectInputFilter safeSizeFilter(int max) {
return baseFilter("maxbytes", max);
}
public static ObjectInputFilter safeArrayFilter(int max) {
return baseFilter("maxarray", max);
}
public static ObjectInputFilter safeDepthFilter(int max) {
return baseFilter("maxdepth", max);
}
}
@@ -0,0 +1,17 @@
package com.baeldung.deserializationfilters.utils;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class SerializationUtils {
private SerializationUtils() {
}
public static void serialize(Object object, OutputStream outStream) throws IOException {
try (ObjectOutputStream objStream = new ObjectOutputStream(outStream)) {
objStream.writeObject(object);
}
}
}

Some files were not shown because too many files have changed in this diff Show More