Merge branch 'eugenp:master' into master
This commit is contained in:
commit
f2f8e45b96
@ -0,0 +1,2 @@
|
|||||||
|
## Relevant Articles
|
||||||
|
- [Implement Connect 4 Game with Java](https://www.baeldung.com/java-connect-4-game)
|
@ -28,7 +28,7 @@
|
|||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<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>
|
<commons-math3.version>3.6.1</commons-math3.version>
|
||||||
<cobertura.plugin.version>2.7</cobertura.plugin.version>
|
<cobertura.plugin.version>2.7</cobertura.plugin.version>
|
||||||
<tradukisto.version>1.0.1</tradukisto.version>
|
<tradukisto.version>1.0.1</tradukisto.version>
|
||||||
|
@ -58,11 +58,16 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.tomakehurst</groupId>
|
<groupId>org.wiremock</groupId>
|
||||||
<artifactId>wiremock</artifactId>
|
<artifactId>wiremock</artifactId>
|
||||||
<version>${wiremock.version}</version>
|
<version>${wiremock.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>${httpclient.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@ -77,11 +82,12 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<mockserver.version>5.6.1</mockserver.version>
|
<mockserver.version>5.6.1</mockserver.version>
|
||||||
<wiremock.version>2.5.1</wiremock.version>
|
<wiremock.version>3.3.1</wiremock.version>
|
||||||
<!-- http client & core 5 -->
|
<!-- http client & core 5 -->
|
||||||
<httpcore5.version>5.2</httpcore5.version>
|
<httpcore5.version>5.2</httpcore5.version>
|
||||||
<httpclient5.version>5.2</httpclient5.version>
|
<httpclient5.version>5.2</httpclient5.version>
|
||||||
<httpclient5-fluent.version>5.2</httpclient5-fluent.version>
|
<httpclient5-fluent.version>5.2</httpclient5-fluent.version>
|
||||||
|
<httpclient.version>4.5.14</httpclient.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -8,7 +8,7 @@ import static org.mockserver.model.HttpResponse.response;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.mockserver.client.MockServerClient;
|
import org.mockserver.client.MockServerClient;
|
||||||
|
@ -38,8 +38,6 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
|
|
||||||
class HttpAsyncClientLiveTest extends GetRequestMockServer {
|
class HttpAsyncClientLiveTest extends GetRequestMockServer {
|
||||||
|
|
||||||
private static final String HOST = "http://www.google.com";
|
|
||||||
private static final String HOST_WITH_SSL = "https://mms.nw.ru/";
|
private static final String HOST_WITH_SSL = "https://mms.nw.ru/";
|
||||||
private static final String HOST_WITH_PROXY = "http://httpbin.org/";
|
private static final String HOST_WITH_PROXY = "http://httpbin.org/";
|
||||||
private static final String URL_SECURED_BY_BASIC_AUTHENTICATION = "http://browserspy.dk/password-ok.php";// "http://localhost:8080/spring-security-rest-basic-auth/api/foos/1";
|
private static final String URL_SECURED_BY_BASIC_AUTHENTICATION = "http://browserspy.dk/password-ok.php";// "http://localhost:8080/spring-security-rest-basic-auth/api/foos/1";
|
||||||
@ -136,7 +134,7 @@ class HttpAsyncClientLiveTest extends GetRequestMockServer {
|
|||||||
|
|
||||||
client.start();
|
client.start();
|
||||||
|
|
||||||
final SimpleHttpRequest request = new SimpleHttpRequest("GET",HOST_WITH_SSL);
|
final SimpleHttpRequest request = new SimpleHttpRequest("GET", HOST_WITH_SSL);
|
||||||
final Future<SimpleHttpResponse> future = client.execute(request, null);
|
final Future<SimpleHttpResponse> future = client.execute(request, null);
|
||||||
|
|
||||||
final HttpResponse response = future.get();
|
final HttpResponse response = future.get();
|
||||||
@ -201,7 +199,7 @@ class HttpAsyncClientLiveTest extends GetRequestMockServer {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
final Future<SimpleHttpResponse> future = client.execute(SimpleHttpRequest.copy(request), context, null);
|
final Future<SimpleHttpResponse> future = client.execute(SimpleRequestBuilder.copy(request).build(), context, null);
|
||||||
final HttpResponse response = future.get();
|
final HttpResponse response = future.get();
|
||||||
assertThat(response.getCode(), equalTo(200));
|
assertThat(response.getCode(), equalTo(200));
|
||||||
} catch (final Exception ex) {
|
} catch (final Exception ex) {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package com.baeldung.httpclient;
|
package com.baeldung.httpclient;
|
||||||
|
|
||||||
import org.apache.http.HttpEntity;
|
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
|
||||||
|
import org.apache.hc.core5.http.HttpEntity;
|
||||||
|
|
||||||
public final class ResponseUtil {
|
public final class ResponseUtil {
|
||||||
private ResponseUtil() {
|
private ResponseUtil() {
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ class HttpClientAdvancedConfigurationIntegrationTest {
|
|||||||
void givenServerThatIsBehindProxy_whenClientIsConfiguredToSendRequestViaProxy_shouldReturn200() throws IOException {
|
void givenServerThatIsBehindProxy_whenClientIsConfiguredToSendRequestViaProxy_shouldReturn200() throws IOException {
|
||||||
//given
|
//given
|
||||||
proxyMock.stubFor(get(urlMatching(".*"))
|
proxyMock.stubFor(get(urlMatching(".*"))
|
||||||
.willReturn(aResponse().proxiedFrom("http://localhost:8089/")));
|
.willReturn(aResponse().proxiedFrom("http://localhost:8089")));
|
||||||
|
|
||||||
serviceMock.stubFor(get(urlEqualTo("/private"))
|
serviceMock.stubFor(get(urlEqualTo("/private"))
|
||||||
.willReturn(aResponse().withStatus(200)));
|
.willReturn(aResponse().withStatus(200)));
|
||||||
@ -129,7 +129,7 @@ class HttpClientAdvancedConfigurationIntegrationTest {
|
|||||||
public void givenServerThatIsBehindAuthorizationProxy_whenClientSendRequest_shouldAuthorizeProperly() throws IOException {
|
public void givenServerThatIsBehindAuthorizationProxy_whenClientSendRequest_shouldAuthorizeProperly() throws IOException {
|
||||||
//given
|
//given
|
||||||
proxyMock.stubFor(get(urlMatching("/private"))
|
proxyMock.stubFor(get(urlMatching("/private"))
|
||||||
.willReturn(aResponse().proxiedFrom("http://localhost:8089/")));
|
.willReturn(aResponse().proxiedFrom("http://localhost:8089")));
|
||||||
serviceMock.stubFor(get(urlEqualTo("/private"))
|
serviceMock.stubFor(get(urlEqualTo("/private"))
|
||||||
.willReturn(aResponse().withStatus(200)));
|
.willReturn(aResponse().withStatus(200)));
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@
|
|||||||
<version>${mockserver.version}</version>
|
<version>${mockserver.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.tomakehurst</groupId>
|
<groupId>org.wiremock</groupId>
|
||||||
<artifactId>wiremock</artifactId>
|
<artifactId>wiremock</artifactId>
|
||||||
<version>${wiremock.version}</version>
|
<version>${wiremock.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
@ -234,10 +234,10 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- util -->
|
<!-- 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>
|
<httpasyncclient.version>4.1.5</httpasyncclient.version>
|
||||||
<!-- testing -->
|
<!-- testing -->
|
||||||
<wiremock.version>2.5.1</wiremock.version>
|
<wiremock.version>3.3.1</wiremock.version>
|
||||||
<httpcore.version>4.4.16</httpcore.version>
|
<httpcore.version>4.4.16</httpcore.version>
|
||||||
<httpclient.version>4.5.14</httpclient.version>
|
<httpclient.version>4.5.14</httpclient.version>
|
||||||
<mockserver.version>5.11.2</mockserver.version>
|
<mockserver.version>5.11.2</mockserver.version>
|
||||||
|
@ -102,7 +102,7 @@ class HttpClientAdvancedConfigurationIntegrationTest {
|
|||||||
void givenServerThatIsBehindProxy_whenClientIsConfiguredToSendRequestViaProxy_shouldReturn200() throws IOException {
|
void givenServerThatIsBehindProxy_whenClientIsConfiguredToSendRequestViaProxy_shouldReturn200() throws IOException {
|
||||||
//given
|
//given
|
||||||
proxyMock.stubFor(get(urlMatching(".*"))
|
proxyMock.stubFor(get(urlMatching(".*"))
|
||||||
.willReturn(aResponse().proxiedFrom("http://localhost:8089/")));
|
.willReturn(aResponse().proxiedFrom("http://localhost:8089")));
|
||||||
|
|
||||||
serviceMock.stubFor(get(urlEqualTo("/private"))
|
serviceMock.stubFor(get(urlEqualTo("/private"))
|
||||||
.willReturn(aResponse().withStatus(200)));
|
.willReturn(aResponse().withStatus(200)));
|
||||||
@ -128,7 +128,7 @@ class HttpClientAdvancedConfigurationIntegrationTest {
|
|||||||
void givenServerThatIsBehindAuthorizationProxy_whenClientSendRequest_shouldAuthorizeProperly() throws IOException {
|
void givenServerThatIsBehindAuthorizationProxy_whenClientSendRequest_shouldAuthorizeProperly() throws IOException {
|
||||||
//given
|
//given
|
||||||
proxyMock.stubFor(get(urlMatching("/private"))
|
proxyMock.stubFor(get(urlMatching("/private"))
|
||||||
.willReturn(aResponse().proxiedFrom("http://localhost:8089/")));
|
.willReturn(aResponse().proxiedFrom("http://localhost:8089")));
|
||||||
serviceMock.stubFor(get(urlEqualTo("/private"))
|
serviceMock.stubFor(get(urlEqualTo("/private"))
|
||||||
.willReturn(aResponse().withStatus(200)));
|
.willReturn(aResponse().withStatus(200)));
|
||||||
|
|
||||||
|
@ -14,3 +14,4 @@ You can build the project from the command line using: *mvn clean install*, or i
|
|||||||
- [Get Partition Count for a Topic in Kafka](https://www.baeldung.com/java-kafka-partition-count-topic)
|
- [Get Partition Count for a Topic in Kafka](https://www.baeldung.com/java-kafka-partition-count-topic)
|
||||||
- [bootstrap-server in Kafka Configuration](https://www.baeldung.com/java-kafka-bootstrap-server)
|
- [bootstrap-server in Kafka Configuration](https://www.baeldung.com/java-kafka-bootstrap-server)
|
||||||
- [Introduction to Apache Kafka](https://www.baeldung.com/apache-kafka)
|
- [Introduction to Apache Kafka](https://www.baeldung.com/apache-kafka)
|
||||||
|
- [Ensuring Message Ordering in Kafka: Strategies and Configurations](https://www.baeldung.com/kafka-message-ordering)
|
||||||
|
@ -1 +0,0 @@
|
|||||||
log4j.rootLogger=INFO, stdout
|
|
@ -23,11 +23,6 @@
|
|||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<version>${org.slf4j.version}</version>
|
<version>${org.slf4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-log4j12</artifactId>
|
|
||||||
<version>${org.slf4j.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
@ -57,6 +52,11 @@
|
|||||||
<version>${lombok.version}</version>
|
<version>${lombok.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.databind.version} </version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@ -64,6 +64,7 @@
|
|||||||
<kafka.version>2.8.0</kafka.version>
|
<kafka.version>2.8.0</kafka.version>
|
||||||
<testcontainers-kafka.version>1.15.3</testcontainers-kafka.version>
|
<testcontainers-kafka.version>1.15.3</testcontainers-kafka.version>
|
||||||
<testcontainers-jupiter.version>1.15.3</testcontainers-jupiter.version>
|
<testcontainers-jupiter.version>1.15.3</testcontainers-jupiter.version>
|
||||||
|
<jackson.databind.version>2.15.2</jackson.databind.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</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);
|
||||||
|
}
|
||||||
|
}
|
11
apache-kafka-2/src/test/resources/logback.xml
Normal file
11
apache-kafka-2/src/test/resources/logback.xml
Normal file
@ -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>
|
@ -82,26 +82,11 @@
|
|||||||
<artifactId>geronimo-jta_1.0.1B_spec</artifactId>
|
<artifactId>geronimo-jta_1.0.1B_spec</artifactId>
|
||||||
<version>${geronimo.version}</version>
|
<version>${geronimo.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>javax.validation</groupId>
|
|
||||||
<artifactId>validation-api</artifactId>
|
|
||||||
<version>${validation-api.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hibernate.validator</groupId>
|
<groupId>org.hibernate.validator</groupId>
|
||||||
<artifactId>hibernate-validator</artifactId>
|
<artifactId>hibernate-validator</artifactId>
|
||||||
<version>${hibernate-validator.version}</version>
|
<version>${hibernate-validator.version}</version>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@ -113,10 +98,7 @@
|
|||||||
<derby.version>10.8.1.2</derby.version>
|
<derby.version>10.8.1.2</derby.version>
|
||||||
<jta.version>1.1</jta.version>
|
<jta.version>1.1</jta.version>
|
||||||
<geronimo.version>1.0</geronimo.version>
|
<geronimo.version>1.0</geronimo.version>
|
||||||
<validation-api.version>2.0.1.Final</validation-api.version>
|
<hibernate-validator.version>8.0.1.Final</hibernate-validator.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>
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -3,10 +3,10 @@ package com.baeldung.atomikos.spring.jpa;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.validation.ConstraintViolation;
|
import jakarta.validation.ConstraintViolation;
|
||||||
import javax.validation.Validation;
|
import jakarta.validation.Validation;
|
||||||
import javax.validation.Validator;
|
import jakarta.validation.Validator;
|
||||||
import javax.validation.ValidatorFactory;
|
import jakarta.validation.ValidatorFactory;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
@ -3,7 +3,7 @@ package com.baeldung.atomikos.spring.jpa.order;
|
|||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.validation.constraints.Max;
|
import jakarta.validation.constraints.Max;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "ORDERS")
|
@Table(name = "ORDERS")
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<gson.version>2.8.0</gson.version>
|
<gson.version>2.10.1</gson.version>
|
||||||
<dynamodblocal.version>1.21.1</dynamodblocal.version>
|
<dynamodblocal.version>1.21.1</dynamodblocal.version>
|
||||||
<maven-plugins-version>3.1.1</maven-plugins-version>
|
<maven-plugins-version>3.1.1</maven-plugins-version>
|
||||||
</properties>
|
</properties>
|
||||||
|
@ -73,9 +73,9 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<gson.version>2.8.0</gson.version>
|
<gson.version>2.10.1</gson.version>
|
||||||
<dynamodblocal.version>1.21.1</dynamodblocal.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>
|
<jets3t-version>0.9.4.0006L</jets3t-version>
|
||||||
<maven-plugins-version>3.1.1</maven-plugins-version>
|
<maven-plugins-version>3.1.1</maven-plugins-version>
|
||||||
</properties>
|
</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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
package com.baeldung.deserializationfilters;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputFilter;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import com.baeldung.deserializationfilters.pojo.ContextSpecific;
|
||||||
|
import com.baeldung.deserializationfilters.pojo.NestedSample;
|
||||||
|
import com.baeldung.deserializationfilters.pojo.Sample;
|
||||||
|
import com.baeldung.deserializationfilters.pojo.SampleExploit;
|
||||||
|
import com.baeldung.deserializationfilters.service.LimitedArrayService;
|
||||||
|
import com.baeldung.deserializationfilters.service.LowDepthService;
|
||||||
|
import com.baeldung.deserializationfilters.service.SmallObjectService;
|
||||||
|
import com.baeldung.deserializationfilters.utils.DeserializationUtils;
|
||||||
|
import com.baeldung.deserializationfilters.utils.FilterUtils;
|
||||||
|
import com.baeldung.deserializationfilters.utils.SerializationUtils;
|
||||||
|
|
||||||
|
public class ContextSpecificDeserializationFilterIntegrationTest {
|
||||||
|
|
||||||
|
private static ByteArrayOutputStream serialSampleA = new ByteArrayOutputStream();
|
||||||
|
private static ByteArrayOutputStream serialBigSampleA = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
private static ByteArrayOutputStream serialSampleB = new ByteArrayOutputStream();
|
||||||
|
private static ByteArrayOutputStream serialBigSampleB = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
private static ByteArrayOutputStream serialSampleC = new ByteArrayOutputStream();
|
||||||
|
private static ByteArrayOutputStream serialBigSampleC = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
private static ByteArrayInputStream bytes(ByteArrayOutputStream stream) {
|
||||||
|
return new ByteArrayInputStream(stream.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setup() throws IOException {
|
||||||
|
ObjectInputFilter.Config.setSerialFilterFactory(new ContextSpecificDeserializationFilterFactory());
|
||||||
|
|
||||||
|
SerializationUtils.serialize(new Sample("simple"), serialSampleA);
|
||||||
|
SerializationUtils.serialize(new SampleExploit(), serialBigSampleA);
|
||||||
|
|
||||||
|
SerializationUtils.serialize(new Sample(new int[] { 1, 2, 3 }), serialSampleB);
|
||||||
|
SerializationUtils.serialize(new Sample(new int[] { 1, 2, 3, 4, 5, 6 }), serialBigSampleB);
|
||||||
|
|
||||||
|
SerializationUtils.serialize(new Sample(new NestedSample(null)), serialSampleC);
|
||||||
|
SerializationUtils.serialize(new Sample(new NestedSample(new Sample("deep"))), serialBigSampleC);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenSmallObjectContext_thenCorrectFilterApplied() {
|
||||||
|
Set<ContextSpecific> result = new SmallObjectService().process( //
|
||||||
|
bytes(serialSampleA), //
|
||||||
|
bytes(serialBigSampleA));
|
||||||
|
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
assertEquals("simple", ((Sample) result.iterator()
|
||||||
|
.next()).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenLimitedArrayContext_thenCorrectFilterApplied() {
|
||||||
|
Set<ContextSpecific> result = new LimitedArrayService().process( //
|
||||||
|
bytes(serialSampleB), //
|
||||||
|
bytes(serialBigSampleB));
|
||||||
|
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenLowDepthContext_thenCorrectFilterApplied() {
|
||||||
|
Set<ContextSpecific> result = new LowDepthService().process( //
|
||||||
|
bytes(serialSampleC), //
|
||||||
|
bytes(serialBigSampleC));
|
||||||
|
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenExtraFilter_whenCombinedContext_thenMergedFiltersApplied() {
|
||||||
|
Set<ContextSpecific> result = new LowDepthService().process( //
|
||||||
|
FilterUtils.safeSizeFilter(190), //
|
||||||
|
bytes(serialSampleA), //
|
||||||
|
bytes(serialBigSampleA), //
|
||||||
|
bytes(serialSampleC), //
|
||||||
|
bytes(serialBigSampleC));
|
||||||
|
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
assertEquals("simple", ((Sample) result.iterator()
|
||||||
|
.next()).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenFallbackContext_whenUsingBaseClasses_thenRestrictiveFilterApplied() throws IOException {
|
||||||
|
String a = new String("a");
|
||||||
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||||
|
SerializationUtils.serialize(a, outStream);
|
||||||
|
|
||||||
|
String deserializedA = (String) DeserializationUtils.deserialize(bytes(outStream));
|
||||||
|
|
||||||
|
assertEquals(a, deserializedA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenFallbackContext_whenUsingAppClasses_thenRejected() throws IOException {
|
||||||
|
Sample a = new Sample("a");
|
||||||
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||||
|
SerializationUtils.serialize(a, outStream);
|
||||||
|
|
||||||
|
Sample deserializedA = (Sample) DeserializationUtils.deserialize(bytes(outStream));
|
||||||
|
|
||||||
|
assertNull(deserializedA);
|
||||||
|
}
|
||||||
|
}
|
@ -18,19 +18,19 @@ public class VehicleUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCar_whenUsingReflectionAPI_thenSuperClassIsSealed() {
|
public void givenCar_whenUsingReflectionAPI_thenSuperClassIsSealed() throws ClassNotFoundException {
|
||||||
Assertions.assertThat(car.getClass().isSealed()).isEqualTo(false);
|
Assertions.assertThat(car.getClass().isSealed()).isEqualTo(false);
|
||||||
Assertions.assertThat(car.getClass().getSuperclass().isSealed()).isEqualTo(true);
|
Assertions.assertThat(car.getClass().getSuperclass().isSealed()).isEqualTo(true);
|
||||||
Assertions.assertThat(car.getClass().getSuperclass().getPermittedSubclasses())
|
Assertions.assertThat(car.getClass().getSuperclass().getPermittedSubclasses())
|
||||||
.contains(ClassDesc.of(car.getClass().getCanonicalName()));
|
.contains(Class.forName(car.getClass().getCanonicalName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenTruck_whenUsingReflectionAPI_thenSuperClassIsSealed() {
|
public void givenTruck_whenUsingReflectionAPI_thenSuperClassIsSealed() throws ClassNotFoundException {
|
||||||
Assertions.assertThat(truck.getClass().isSealed()).isEqualTo(false);
|
Assertions.assertThat(truck.getClass().isSealed()).isEqualTo(false);
|
||||||
Assertions.assertThat(truck.getClass().getSuperclass().isSealed()).isEqualTo(true);
|
Assertions.assertThat(truck.getClass().getSuperclass().isSealed()).isEqualTo(true);
|
||||||
Assertions.assertThat(truck.getClass().getSuperclass().getPermittedSubclasses())
|
Assertions.assertThat(truck.getClass().getSuperclass().getPermittedSubclasses())
|
||||||
.contains(ClassDesc.of(truck.getClass().getCanonicalName()));
|
.contains(Class.forName(truck.getClass().getCanonicalName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -18,19 +18,19 @@ public class VehicleUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenCar_whenUsingReflectionAPI_thenInterfaceIsSealed() {
|
public void givenCar_whenUsingReflectionAPI_thenInterfaceIsSealed() throws ClassNotFoundException {
|
||||||
Assertions.assertThat(car.getClass().isSealed()).isEqualTo(false);
|
Assertions.assertThat(car.getClass().isSealed()).isEqualTo(false);
|
||||||
Assertions.assertThat(car.getClass().getInterfaces()[0].isSealed()).isEqualTo(true);
|
Assertions.assertThat(car.getClass().getInterfaces()[0].isSealed()).isEqualTo(true);
|
||||||
Assertions.assertThat(car.getClass().getInterfaces()[0].permittedSubclasses())
|
Assertions.assertThat(car.getClass().getInterfaces()[0].getPermittedSubclasses())
|
||||||
.contains(ClassDesc.of(car.getClass().getCanonicalName()));
|
.contains(Class.forName(car.getClass().getCanonicalName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenTruck_whenUsingReflectionAPI_thenInterfaceIsSealed() {
|
public void givenTruck_whenUsingReflectionAPI_thenInterfaceIsSealed() throws ClassNotFoundException {
|
||||||
Assertions.assertThat(truck.getClass().isSealed()).isEqualTo(false);
|
Assertions.assertThat(truck.getClass().isSealed()).isEqualTo(false);
|
||||||
Assertions.assertThat(truck.getClass().getInterfaces()[0].isSealed()).isEqualTo(true);
|
Assertions.assertThat(truck.getClass().getInterfaces()[0].isSealed()).isEqualTo(true);
|
||||||
Assertions.assertThat(truck.getClass().getInterfaces()[0].permittedSubclasses())
|
Assertions.assertThat(truck.getClass().getInterfaces()[0].getPermittedSubclasses())
|
||||||
.contains(ClassDesc.of(truck.getClass().getCanonicalName()));
|
.contains(Class.forName(truck.getClass().getCanonicalName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -6,4 +6,6 @@
|
|||||||
- [How Many Days Are There in a Particular Month of a Given Year?](https://www.baeldung.com/days-particular-month-given-year)
|
- [How Many Days Are There in a Particular Month of a Given Year?](https://www.baeldung.com/days-particular-month-given-year)
|
||||||
- [Difference Between Instant and LocalDateTime](https://www.baeldung.com/java-instant-vs-localdatetime)
|
- [Difference Between Instant and LocalDateTime](https://www.baeldung.com/java-instant-vs-localdatetime)
|
||||||
- [Add Minutes to a Time String in Java](https://www.baeldung.com/java-string-time-add-mins)
|
- [Add Minutes to a Time String in Java](https://www.baeldung.com/java-string-time-add-mins)
|
||||||
|
- [Round the Date in Java](https://www.baeldung.com/java-round-the-date)
|
||||||
|
- [Representing Furthest Possible Date in Java](https://www.baeldung.com/java-date-represent-max)
|
||||||
- [[<-- Prev]](/core-java-modules/core-java-datetime-java8-1)
|
- [[<-- Prev]](/core-java-modules/core-java-datetime-java8-1)
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
<joda-time.version>2.10</joda-time.version>
|
<joda-time.version>2.12.5</joda-time.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.baeldung.maxdate;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class DateComparison {
|
||||||
|
public int compareTodayWithMaxDate() {
|
||||||
|
Date today = new Date();
|
||||||
|
Date maxDate = new Date(Long.MAX_VALUE);
|
||||||
|
|
||||||
|
int comparisonResult = today.compareTo(maxDate);
|
||||||
|
return comparisonResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
DateComparison comparator = new DateComparison();
|
||||||
|
System.out.println(comparator.compareTodayWithMaxDate());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.baeldung.maxdate;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
|
public class MaxDateDisplay {
|
||||||
|
public String getMaxDateValue() {
|
||||||
|
Date maxDate = new Date(Long.MAX_VALUE);
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||||
|
return "The maximum date value in Java is: " + sdf.format(maxDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
MaxDateDisplay display = new MaxDateDisplay();
|
||||||
|
System.out.println(display.getMaxDateValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public class SimpleParseDate {
|
public class SimpleParseDate {
|
||||||
|
|
||||||
public Date parseDate(String dateString, List<String> formatStrings) {
|
public static Date parseDate(String dateString, List<String> formatStrings) {
|
||||||
for (String formatString : formatStrings) {
|
for (String formatString : formatStrings) {
|
||||||
try {
|
try {
|
||||||
return new SimpleDateFormat(formatString).parse(dateString);
|
return new SimpleDateFormat(formatString).parse(dateString);
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.baeldung.maxdate;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class DateComparisonUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenCompareTodayWithMaxDate_thenCorrectResult() {
|
||||||
|
DateComparison comparator = new DateComparison();
|
||||||
|
int result = comparator.compareTodayWithMaxDate();
|
||||||
|
|
||||||
|
assertTrue(result < 0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.baeldung.maxdate;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class MaxDateDisplayUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenGetMaxDate_thenCorrectResult() {
|
||||||
|
MaxDateDisplay display = new MaxDateDisplay();
|
||||||
|
String result = display.getMaxDateValue();
|
||||||
|
assertEquals(
|
||||||
|
"The maximum date value in Java is: 292278994-08-17 07:12:55.807",
|
||||||
|
result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,43 +1,41 @@
|
|||||||
package com.baeldung.parsingDates;
|
package com.baeldung.parsingDates;
|
||||||
|
|
||||||
import com.baeldung.parsingDates.SimpleDateTimeFormat;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import com.baeldung.parsingDates.SimpleDateTimeFormater;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import com.baeldung.parsingDates.SimpleDateUtils;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import com.baeldung.parsingDates.SimpleParseDate;
|
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import org.junit.*;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class SimpleParseDateUnitTest {
|
class SimpleParseDateUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenInvalidInput_thenGettingUnexpectedResult() {
|
void whenInvalidInput_thenGettingUnexpectedResult() {
|
||||||
SimpleParseDate simpleParseDate = new SimpleParseDate();
|
|
||||||
String date = "2022-40-40";
|
String date = "2022-40-40";
|
||||||
assertEquals("Sat May 10 00:00:00 UTC 2025", simpleParseDate.parseDate(date, Arrays.asList("MM/dd/yyyy", "dd.MM.yyyy", "yyyy-MM-dd")).toString());
|
assertEquals("Sat May 10 00:00:00 UTC 2025", SimpleParseDate.parseDate(date, Arrays.asList("MM/dd/yyyy", "dd.MM.yyyy", "yyyy-MM-dd"))
|
||||||
|
.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenInvalidDate_thenAssertThrows() {
|
void whenInvalidDate_thenAssertThrows() {
|
||||||
SimpleDateTimeFormater simpleDateTimeFormater = new SimpleDateTimeFormater();
|
assertEquals(java.time.LocalDate.parse("2022-12-04"), SimpleDateTimeFormater.parseDate("2022-12-04"));
|
||||||
assertEquals(java.time.LocalDate.parse("2022-12-04"), simpleDateTimeFormater.parseDate("2022-12-04"));
|
assertThrows(DateTimeParseException.class, () -> SimpleDateTimeFormater.parseDate("2022-13-04"));
|
||||||
assertThrows(DateTimeParseException.class, () -> simpleDateTimeFormater.parseDate("2022-13-04"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenDateIsCorrect_thenParseCorrect() {
|
void whenDateIsCorrect_thenParseCorrect() {
|
||||||
SimpleDateUtils simpleDateUtils = new SimpleDateUtils();
|
assertNull(SimpleDateUtils.parseDate("53/10/2014"));
|
||||||
assertNull(simpleDateUtils.parseDate("53/10/2014"));
|
assertEquals("Wed Sep 10 00:00:00 UTC 2014", SimpleDateUtils.parseDate("10/09/2014")
|
||||||
assertEquals("Wed Sep 10 00:00:00 UTC 2014", simpleDateUtils.parseDate("10/09/2014").toString());
|
.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenDateIsCorrect_thenResultCorrect() {
|
void whenDateIsCorrect_thenResultCorrect() {
|
||||||
SimpleDateTimeFormat simpleDateTimeFormat = new SimpleDateTimeFormat();
|
assertNull(SimpleDateTimeFormat.parseDate("53/10/2014"));
|
||||||
assertNull(simpleDateTimeFormat.parseDate("53/10/2014"));
|
assertEquals(LocalDate.parse("2014-10-10"), SimpleDateTimeFormat.parseDate("2014-10-10"));
|
||||||
assertEquals(LocalDate.parse("2014-10-10"), simpleDateTimeFormat.parseDate("2014-10-10"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
<joda-time.version>2.10</joda-time.version>
|
<joda-time.version>2.12.5</joda-time.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -1,62 +0,0 @@
|
|||||||
package com.baeldung.streamreduce.application;
|
|
||||||
|
|
||||||
import com.baeldung.streamreduce.entities.User;
|
|
||||||
import com.baeldung.streamreduce.utilities.NumberUtils;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Application {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
|
|
||||||
int result1 = numbers.stream().reduce(0, (a, b) -> a + b);
|
|
||||||
System.out.println(result1);
|
|
||||||
|
|
||||||
int result2 = numbers.stream().reduce(0, Integer::sum);
|
|
||||||
System.out.println(result2);
|
|
||||||
|
|
||||||
List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
|
|
||||||
String result3 = letters.stream().reduce("", (a, b) -> a + b);
|
|
||||||
System.out.println(result3);
|
|
||||||
|
|
||||||
String result4 = letters.stream().reduce("", String::concat);
|
|
||||||
System.out.println(result4);
|
|
||||||
|
|
||||||
String result5 = letters.stream().reduce("", (a, b) -> a.toUpperCase() + b.toUpperCase());
|
|
||||||
System.out.println(result5);
|
|
||||||
|
|
||||||
List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
|
|
||||||
int result6 = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(), Integer::sum);
|
|
||||||
System.out.println(result6);
|
|
||||||
|
|
||||||
String result7 = letters.parallelStream().reduce("", String::concat);
|
|
||||||
System.out.println(result7);
|
|
||||||
|
|
||||||
int result8 = users.parallelStream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(), Integer::sum);
|
|
||||||
System.out.println(result8);
|
|
||||||
|
|
||||||
List<User> userList = new ArrayList<>();
|
|
||||||
for (int i = 0; i <= 1000000; i++) {
|
|
||||||
userList.add(new User("John" + i, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
long t1 = System.currentTimeMillis();
|
|
||||||
int result9 = userList.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(), Integer::sum);
|
|
||||||
long t2 = System.currentTimeMillis();
|
|
||||||
System.out.println(result9);
|
|
||||||
System.out.println("Sequential stream time: " + (t2 - t1) + "ms");
|
|
||||||
|
|
||||||
long t3 = System.currentTimeMillis();
|
|
||||||
int result10 = userList.parallelStream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(), Integer::sum);
|
|
||||||
long t4 = System.currentTimeMillis();
|
|
||||||
System.out.println(result10);
|
|
||||||
System.out.println("Parallel stream time: " + (t4 - t3) + "ms");
|
|
||||||
|
|
||||||
int result11 = NumberUtils.divideListElements(numbers, 1);
|
|
||||||
System.out.println(result11);
|
|
||||||
|
|
||||||
int result12 = NumberUtils.divideListElementsWithExtractedTryCatchBlock(numbers, 0);
|
|
||||||
System.out.println(result12);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package com.baeldung.streamreduce.entities;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Rating {
|
|
||||||
|
|
||||||
double points;
|
|
||||||
List<Review> reviews = new ArrayList<>();
|
|
||||||
|
|
||||||
public Rating() {}
|
|
||||||
|
|
||||||
public void add(Review review) {
|
|
||||||
reviews.add(review);
|
|
||||||
computeRating();
|
|
||||||
}
|
|
||||||
|
|
||||||
private double computeRating() {
|
|
||||||
double totalPoints = reviews.stream().map(Review::getPoints).reduce(0, Integer::sum);
|
|
||||||
this.points = totalPoints / reviews.size();
|
|
||||||
return this.points;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rating average(Rating r1, Rating r2) {
|
|
||||||
Rating combined = new Rating();
|
|
||||||
combined.reviews = new ArrayList<>(r1.reviews);
|
|
||||||
combined.reviews.addAll(r2.reviews);
|
|
||||||
combined.computeRating();
|
|
||||||
return combined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getPoints() {
|
|
||||||
return points;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Review> getReviews() {
|
|
||||||
return reviews;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package com.baeldung.streamreduce.entities;
|
|
||||||
|
|
||||||
public class Review {
|
|
||||||
|
|
||||||
int points;
|
|
||||||
String review;
|
|
||||||
|
|
||||||
public Review(int points, String review) {
|
|
||||||
this.points = points;
|
|
||||||
this.review = review;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPoints() {
|
|
||||||
return points;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPoints(int points) {
|
|
||||||
this.points = points;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getReview() {
|
|
||||||
return review;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReview(String review) {
|
|
||||||
this.review = review;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package com.baeldung.streamreduce.entities;
|
|
||||||
|
|
||||||
public class User {
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final int age;
|
|
||||||
private final Rating rating = new Rating();
|
|
||||||
|
|
||||||
public User(String name, int age) {
|
|
||||||
this.name = name;
|
|
||||||
this.age = age;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAge() {
|
|
||||||
return age;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Rating getRating() {
|
|
||||||
return rating;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "User{" + "name=" + name + ", age=" + age + '}';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package com.baeldung.streamreduce.utilities;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
public abstract class NumberUtils {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(NumberUtils.class.getName());
|
|
||||||
|
|
||||||
public static int divideListElements(List<Integer> values, Integer divider) {
|
|
||||||
return values.stream()
|
|
||||||
.reduce(0, (a, b) -> {
|
|
||||||
try {
|
|
||||||
return a / divider + b / divider;
|
|
||||||
} catch (ArithmeticException e) {
|
|
||||||
LOGGER.log(Level.INFO, "Arithmetic Exception: Division by Zero");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int divideListElementsWithExtractedTryCatchBlock(List<Integer> values, int divider) {
|
|
||||||
return values.stream().reduce(0, (a, b) -> divide(a, divider) + divide(b, divider));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int divideListElementsWithApplyFunctionMethod(List<Integer> values, int divider) {
|
|
||||||
BiFunction<Integer, Integer, Integer> division = (a, b) -> a / b;
|
|
||||||
return values.stream().reduce(0, (a, b) -> applyFunction(division, a, divider) + applyFunction(division, b, divider));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int divide(int value, int factor) {
|
|
||||||
int result = 0;
|
|
||||||
try {
|
|
||||||
result = value / factor;
|
|
||||||
} catch (ArithmeticException e) {
|
|
||||||
LOGGER.log(Level.INFO, "Arithmetic Exception: Division by Zero");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int applyFunction(BiFunction<Integer, Integer, Integer> function, int a, int b) {
|
|
||||||
try {
|
|
||||||
return function.apply(a, b);
|
|
||||||
}
|
|
||||||
catch(Exception e) {
|
|
||||||
LOGGER.log(Level.INFO, "Exception occurred!");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
@ -34,7 +34,6 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<commons-lang.version>2.2</commons-lang.version>
|
<commons-lang.version>2.2</commons-lang.version>
|
||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -8,4 +8,6 @@
|
|||||||
- [Difference Between Arrays.sort() and Collections.sort()](https://www.baeldung.com/java-arrays-collections-sort-methods)
|
- [Difference Between Arrays.sort() and Collections.sort()](https://www.baeldung.com/java-arrays-collections-sort-methods)
|
||||||
- [Skipping the First Iteration in Java](https://www.baeldung.com/java-skip-first-iteration)
|
- [Skipping the First Iteration in Java](https://www.baeldung.com/java-skip-first-iteration)
|
||||||
- [Remove Elements From a Queue Using Loop](https://www.baeldung.com/java-remove-elements-queue)
|
- [Remove Elements From a Queue Using Loop](https://www.baeldung.com/java-remove-elements-queue)
|
||||||
|
- [Intro to Vector Class in Java](https://www.baeldung.com/java-vector-guide)
|
||||||
|
- [HashSet toArray() Method in Java](https://www.baeldung.com/java-hashset-toarray)
|
||||||
- More articles: [[<-- prev]](/core-java-modules/core-java-collections-4)
|
- More articles: [[<-- prev]](/core-java-modules/core-java-collections-4)
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package com.baeldung.collectionssortcomplexity;
|
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@BenchmarkMode(Mode.AverageTime)
|
||||||
|
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||||
|
@Fork(value = 1, warmups = 1)
|
||||||
|
@Warmup(iterations = 5, time = 1)
|
||||||
|
@Measurement(iterations = 5, time = 1)
|
||||||
|
public class CollectionsSortTimeComplexityJMH {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
org.openjdk.jmh.Main.main(args);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void measureCollectionsSortBestCase(BestCaseBenchmarkState state) {
|
||||||
|
List<Integer> sortedList = new ArrayList<>(state.sortedList);
|
||||||
|
Collections.sort(sortedList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void measureCollectionsSortAverageWorstCase(AverageWorstCaseBenchmarkState state) {
|
||||||
|
List<Integer> unsortedList = new ArrayList<>(state.unsortedList);
|
||||||
|
Collections.sort(unsortedList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
public static class BestCaseBenchmarkState {
|
||||||
|
List<Integer> sortedList;
|
||||||
|
|
||||||
|
@Setup(Level.Trial)
|
||||||
|
public void setUp() {
|
||||||
|
sortedList = new ArrayList<>();
|
||||||
|
for (int i = 1; i <= 1000000; i++) {
|
||||||
|
sortedList.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
public static class AverageWorstCaseBenchmarkState {
|
||||||
|
List<Integer> unsortedList;
|
||||||
|
|
||||||
|
@Setup(Level.Trial)
|
||||||
|
public void setUp() {
|
||||||
|
unsortedList = new ArrayList<>();
|
||||||
|
for (int i = 1000000; i > 0; i--) {
|
||||||
|
unsortedList.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.baeldung.collectionssortcomplexity;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CollectionsSortTimeComplexityMain {
|
||||||
|
// O(n log n) Time Complexity Example
|
||||||
|
public static void worstAndAverageCasesTimeComplexity() {
|
||||||
|
Integer[] sortedArray = {20, 21, 22, 23, 24, 25, 26, 17, 28, 29, 30, 31, 18, 19, 32, 33, 34, 27, 35};
|
||||||
|
List<Integer> list = Arrays.asList(sortedArray);
|
||||||
|
Collections.shuffle(list);
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
Collections.sort(list);
|
||||||
|
long endTime = System.nanoTime();
|
||||||
|
System.out.println("Execution Time for O(n log n): " + (endTime - startTime) + " nanoseconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
// O(n) Time Complexity Example
|
||||||
|
public static void bestCaseTimeComplexity() {
|
||||||
|
Integer[] sortedArray = {19, 22, 19, 22, 24, 25, 17, 11, 22, 23, 28, 23, 0, 1, 12, 9, 13, 27, 15};
|
||||||
|
List<Integer> list = Arrays.asList(sortedArray);
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
Collections.sort(list);
|
||||||
|
long endTime = System.nanoTime();
|
||||||
|
System.out.println("Execution Time for O(n): " + (endTime - startTime) + " nanoseconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
worstAndAverageCasesTimeComplexity();
|
||||||
|
bestCaseTimeComplexity();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
package com.baeldung.vectors;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class VectorOperationsUnitTest {
|
||||||
|
|
||||||
|
private static Vector<String> getVector() {
|
||||||
|
Vector<String> vector = new Vector<String>();
|
||||||
|
vector.add("Today");
|
||||||
|
vector.add("is");
|
||||||
|
vector.add("a");
|
||||||
|
vector.add("great");
|
||||||
|
vector.add("day!");
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenAVector_whenAddElementsUsingAddMethod_thenElementsGetAddedAtEnd() {
|
||||||
|
Vector<String> vector = getVector();
|
||||||
|
vector.add("Hello");
|
||||||
|
assertEquals(6, vector.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenAVector_whenUpdateElementAtIndex_thenElementAtIndexGetsUpdated() {
|
||||||
|
Vector<String> vector = getVector();
|
||||||
|
assertEquals(5, vector.size());
|
||||||
|
assertEquals("great", vector.get(3));
|
||||||
|
vector.set(3, "good");
|
||||||
|
assertEquals("good", vector.get(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenAVector_whenRemoveAnElement_thenElementGetsRemovedFromTheVector() {
|
||||||
|
Vector<String> vector = getVector();
|
||||||
|
assertEquals(5, vector.size());
|
||||||
|
|
||||||
|
// remove a specific element
|
||||||
|
vector.remove("a");
|
||||||
|
assertEquals(4, vector.size());
|
||||||
|
|
||||||
|
// remove at specific index
|
||||||
|
vector.remove(2);
|
||||||
|
assertEquals("day!", vector.get(2));
|
||||||
|
assertEquals(3, vector.size());
|
||||||
|
|
||||||
|
assertEquals(false, vector.remove("SomethingThatDoesn'tExist"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ArrayIndexOutOfBoundsException.class)
|
||||||
|
public void givenAVector_whenIndexIsBeyondRange_thenRemoveMethodThrowsArrayIndexOutOfBoundsException() {
|
||||||
|
Vector<String> vector = getVector();
|
||||||
|
assertEquals(5, vector.size());
|
||||||
|
vector.remove(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenAVector_whenGetElementWithinARange_thenGetMethodGetsAnElementFromTheVector() {
|
||||||
|
Vector<String> vector = getVector();
|
||||||
|
String fourthElement = vector.get(3);
|
||||||
|
assertEquals("great", fourthElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ArrayIndexOutOfBoundsException.class)
|
||||||
|
public void givenAVector_whenGetElementBeyondARange_thenGetMethodThrowsArrayIndexOutOfBoundsException() {
|
||||||
|
Vector<String> vector = getVector();
|
||||||
|
assertEquals(5, vector.size());
|
||||||
|
vector.get(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenAVector_whenAddElementFromACollection_thenAllElementsGetAdeddToTheVector() {
|
||||||
|
Vector<String> vector = getVector();
|
||||||
|
assertEquals(5, vector.size());
|
||||||
|
ArrayList<String> words = new ArrayList<>(Arrays.asList("Baeldung", "is", "cool!"));
|
||||||
|
vector.addAll(words);
|
||||||
|
assertEquals(8, vector.size());
|
||||||
|
assertEquals("cool!", vector.get(7));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.baeldung.toarraymethod;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class ConvertingHashSetToArrayUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenStringHashSet_whenConvertedToArray_thenArrayContainsStringElements() {
|
||||||
|
HashSet<String> stringSet = new HashSet<>();
|
||||||
|
stringSet.add("Apple");
|
||||||
|
stringSet.add("Banana");
|
||||||
|
stringSet.add("Cherry");
|
||||||
|
|
||||||
|
// Convert the HashSet of Strings to an array of Strings
|
||||||
|
String[] stringArray = stringSet.toArray(new String[0]);
|
||||||
|
|
||||||
|
// Test that the array is of the correct length
|
||||||
|
assertEquals(3, stringArray.length);
|
||||||
|
|
||||||
|
for (String str : stringArray) {
|
||||||
|
assertTrue(stringSet.contains(str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIntegerHashSet_whenConvertedToArray_thenArrayContainsIntegerElements() {
|
||||||
|
HashSet<Integer> integerSet = new HashSet<>();
|
||||||
|
integerSet.add(5);
|
||||||
|
integerSet.add(10);
|
||||||
|
integerSet.add(15);
|
||||||
|
|
||||||
|
// Convert the HashSet of Integers to an array of Integers
|
||||||
|
Integer[] integerArray = integerSet.toArray(new Integer[0]);
|
||||||
|
|
||||||
|
// Test that the array is of the correct length
|
||||||
|
assertEquals(3, integerArray.length);
|
||||||
|
|
||||||
|
for (Integer num : integerArray) {
|
||||||
|
assertTrue(integerSet.contains(num));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(integerSet.contains(5));
|
||||||
|
assertTrue(integerSet.contains(10));
|
||||||
|
assertTrue(integerSet.contains(15));
|
||||||
|
}
|
||||||
|
}
|
@ -44,6 +44,6 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<vavr.version>0.10.3</vavr.version>
|
<vavr.version>0.10.3</vavr.version>
|
||||||
<java.version>11</java.version>
|
<java.version>11</java.version>
|
||||||
<modelmapper.version>3.1.1</modelmapper.version>
|
<modelmapper.version>3.2.0</modelmapper.version>
|
||||||
</properties>
|
</properties>
|
||||||
</project>
|
</project>
|
@ -1,8 +1,9 @@
|
|||||||
package com.baeldung.modelmapper;
|
package com.baeldung.modelmapper;
|
||||||
|
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.modelmapper.ModelMapper;
|
import org.modelmapper.ModelMapper;
|
||||||
import org.modelmapper.TypeMap;
|
import org.modelmapper.TypeMap;
|
||||||
import org.modelmapper.TypeToken;
|
import org.modelmapper.TypeToken;
|
||||||
@ -10,11 +11,10 @@ import org.modelmapper.TypeToken;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.hasItems;
|
import static org.hamcrest.Matchers.hasItems;
|
||||||
import static org.hamcrest.Matchers.hasProperty;
|
import static org.hamcrest.Matchers.hasProperty;
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class has test methods of mapping Integer to Character list,
|
* This class has test methods of mapping Integer to Character list,
|
||||||
@ -22,12 +22,12 @@ import static org.junit.Assert.assertThat;
|
|||||||
*
|
*
|
||||||
* @author Sasa Milenkovic
|
* @author Sasa Milenkovic
|
||||||
*/
|
*/
|
||||||
public class UsersListMappingUnitTest {
|
class UsersListMappingUnitTest {
|
||||||
|
|
||||||
private ModelMapper modelMapper;
|
private ModelMapper modelMapper;
|
||||||
private List<User> users;
|
private List<User> users;
|
||||||
|
|
||||||
@Before
|
@BeforeEach
|
||||||
public void init() {
|
public void init() {
|
||||||
|
|
||||||
modelMapper = new ModelMapper();
|
modelMapper = new ModelMapper();
|
||||||
@ -35,18 +35,16 @@ public class UsersListMappingUnitTest {
|
|||||||
TypeMap<UserList, UserListDTO> typeMap = modelMapper.createTypeMap(UserList.class, UserListDTO.class);
|
TypeMap<UserList, UserListDTO> typeMap = modelMapper.createTypeMap(UserList.class, UserListDTO.class);
|
||||||
|
|
||||||
typeMap.addMappings(mapper -> mapper.using(new UsersListConverter())
|
typeMap.addMappings(mapper -> mapper.using(new UsersListConverter())
|
||||||
.map(UserList::getUsers, UserListDTO::setUsernames));
|
.map(UserList::getUsers, UserListDTO::setUsernames));
|
||||||
|
|
||||||
users = new ArrayList();
|
users = new ArrayList<>();
|
||||||
users.add(new User("b100", "user1", "user1@baeldung.com", "111-222", "USER"));
|
users.add(new User("b100", "user1", "user1@baeldung.com", "111-222", "USER"));
|
||||||
users.add(new User("b101", "user2", "user2@baeldung.com", "111-333", "USER"));
|
users.add(new User("b101", "user2", "user2@baeldung.com", "111-333", "USER"));
|
||||||
users.add(new User("b102", "user3", "user3@baeldung.com", "111-444", "ADMIN"));
|
users.add(new User("b102", "user3", "user3@baeldung.com", "111-444", "ADMIN"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenInteger_thenMapToCharacter() {
|
void whenInteger_thenMapToCharacter() {
|
||||||
|
|
||||||
List<Integer> integers = new ArrayList<Integer>();
|
List<Integer> integers = new ArrayList<Integer>();
|
||||||
|
|
||||||
integers.add(1);
|
integers.add(1);
|
||||||
@ -57,36 +55,27 @@ public class UsersListMappingUnitTest {
|
|||||||
}.getType());
|
}.getType());
|
||||||
|
|
||||||
assertThat(characters, hasItems('1', '2', '3'));
|
assertThat(characters, hasItems('1', '2', '3'));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenUsersList_whenUseGenericType_thenMapToUserDTO() {
|
void givenUsersList_whenUseGenericType_thenMapToUserDTO() {
|
||||||
|
|
||||||
// Mapping lists using custom (generic) type mapping
|
// Mapping lists using custom (generic) type mapping
|
||||||
|
|
||||||
List<UserDTO> userDtoList = MapperUtil.mapList(users, UserDTO.class);
|
List<UserDTO> userDtoList = MapperUtil.mapList(users, UserDTO.class);
|
||||||
|
|
||||||
assertThat(userDtoList, Matchers.<UserDTO>hasItem(
|
assertThat(userDtoList, Matchers.<UserDTO> hasItem(Matchers.both(hasProperty("userId", equalTo("b100")))
|
||||||
Matchers.both(hasProperty("userId", equalTo("b100")))
|
.and(hasProperty("email", equalTo("user1@baeldung.com")))
|
||||||
.and(hasProperty("email", equalTo("user1@baeldung.com")))
|
.and(hasProperty("username", equalTo("user1")))));
|
||||||
.and(hasProperty("username", equalTo("user1")))));
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenUsersList_whenUseConverter_thenMapToUsernames() {
|
void givenUsersList_whenUseConverter_thenMapToUsernames() {
|
||||||
|
|
||||||
// Mapping lists using property mapping and converter
|
// Mapping lists using property mapping and converter
|
||||||
|
|
||||||
UserList userList = new UserList();
|
UserList userList = new UserList();
|
||||||
userList.setUsers(users);
|
userList.setUsers(users);
|
||||||
UserListDTO dtos = new UserListDTO();
|
UserListDTO dtos = new UserListDTO();
|
||||||
modelMapper.map(userList, dtos);
|
modelMapper.map(userList, dtos);
|
||||||
|
|
||||||
assertThat(dtos.getUsernames(), hasItems("user1", "user2", "user3"));
|
assertThat(dtos.getUsernames(), hasItems("user1", "user2", "user3"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -66,7 +66,6 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<jmh.version>1.21</jmh.version>
|
<jmh.version>1.21</jmh.version>
|
||||||
<commons-lang.version>2.2</commons-lang.version>
|
<commons-lang.version>2.2</commons-lang.version>
|
||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
|
||||||
<gson.version>2.10.1</gson.version>
|
<gson.version>2.10.1</gson.version>
|
||||||
<jackson.version>2.15.2</jackson.version>
|
<jackson.version>2.15.2</jackson.version>
|
||||||
<org.json.version>20230618</org.json.version>
|
<org.json.version>20230618</org.json.version>
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
## Relevant Articles
|
## Relevant Articles:
|
||||||
- [Difference Between putIfAbsent() and computeIfAbsent() in Java’s Map](https://www.baeldung.com/java-map-putifabsent-computeifabsent)
|
- [Difference Between putIfAbsent() and computeIfAbsent() in Java’s Map](https://www.baeldung.com/java-map-putifabsent-computeifabsent)
|
||||||
|
- [How to Write and Read a File with a Java HashMap](https://www.baeldung.com/how-to-write-and-read-a-file-with-a-java-hashmap/)
|
||||||
- [How to Write Hashmap to CSV File](https://www.baeldung.com/java-write-hashmap-csv)
|
- [How to Write Hashmap to CSV File](https://www.baeldung.com/java-write-hashmap-csv)
|
||||||
|
- [How to Get First or Last Entry From a LinkedHashMap in Java](https://www.baeldung.com/java-linkedhashmap-first-last-key-value-pair)
|
||||||
|
- [How to Write and Read a File with a Java HashMap](https://www.baeldung.com/java-hashmap-write-read-file)
|
||||||
|
- More articles: [[<-- prev]](/core-java-modules/core-java-collections-maps-6)
|
@ -1,11 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>core-java-collections-maps-7</artifactId>
|
<artifactId>core-java-collections-maps-7</artifactId>
|
||||||
<name>core-java-collections-maps-7</name>
|
<name>core-java-collections-maps-7</name>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
<properties>
|
||||||
|
<gson.version>2.10.1</gson.version>
|
||||||
|
<csv.version>1.5</csv.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>core-java-modules</artifactId>
|
<artifactId>core-java-modules</artifactId>
|
||||||
@ -13,72 +17,29 @@
|
|||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
|
||||||
<spring.version>5.2.5.RELEASE</spring.version>
|
|
||||||
</properties>
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>2.12.4</version>
|
<version>${jackson.version}</version>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.openjdk.jmh</groupId>
|
|
||||||
<artifactId>jmh-core</artifactId>
|
|
||||||
<version>1.36</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
<version>2.8.9</version>
|
<version>${gson.version}</version>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.json</groupId>
|
|
||||||
<artifactId>json</artifactId>
|
|
||||||
<version>20230227</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.13.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter</artifactId>
|
|
||||||
<version>5.8.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter</artifactId>
|
|
||||||
<version>5.8.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.13.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter</artifactId>
|
|
||||||
<version>5.8.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.13.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-csv</artifactId>
|
<artifactId>commons-csv</artifactId>
|
||||||
<version>1.5</version>
|
<version>${csv.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>32.1.2-jre</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
@ -92,5 +53,4 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
package com.baeldung.map;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class BenchmarkMapMethods {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
BenchmarkMapMethods bmm = new BenchmarkMapMethods();
|
||||||
|
Map<String, Long> map = new HashMap<>();
|
||||||
|
map.put("Guava", bmm.benchMarkGuavaMap());
|
||||||
|
map.put("ContainsKey", bmm.benchContainsKeyMap());
|
||||||
|
map.put("MergeMethod", bmm.benchMarkMergeMethod());
|
||||||
|
map.put("ComputeMethod", bmm.benchMarComputeMethod());
|
||||||
|
map.put("GetOrDefault", bmm.benchMarkGetOrDefaultMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
private long benchMarkGuavaMap() {
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
IncrementMapValueWays im = new IncrementMapValueWays();
|
||||||
|
im.charFrequencyUsingAtomicMap(getString());
|
||||||
|
long endTime = System.nanoTime();
|
||||||
|
return endTime - startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long benchContainsKeyMap() {
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
IncrementMapValueWays im = new IncrementMapValueWays();
|
||||||
|
im.charFrequencyUsingContainsKey(getString());
|
||||||
|
long endTime = System.nanoTime();
|
||||||
|
return endTime - startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long benchMarComputeMethod() {
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
IncrementMapValueWays im = new IncrementMapValueWays();
|
||||||
|
im.charFrequencyUsingCompute(getString());
|
||||||
|
long endTime = System.nanoTime();
|
||||||
|
return endTime - startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long benchMarkMergeMethod() {
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
IncrementMapValueWays im = new IncrementMapValueWays();
|
||||||
|
im.charFrequencyUsingMerge(getString());
|
||||||
|
long endTime = System.nanoTime();
|
||||||
|
return endTime - startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long benchMarkGetOrDefaultMethod() {
|
||||||
|
long startTime = System.nanoTime();
|
||||||
|
IncrementMapValueWays im = new IncrementMapValueWays();
|
||||||
|
im.charFrequencyUsingGetOrDefault(getString());
|
||||||
|
long endTime = System.nanoTime();
|
||||||
|
return endTime - startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getString() {
|
||||||
|
return
|
||||||
|
"Once upon a time in a quaint village nestled between rolling hills and whispering forests, there lived a solitary storyteller named Elias. Elias was known for spinning tales that transported listeners to magical realms and awakened forgotten dreams.\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "His favorite spot was beneath an ancient oak tree, its sprawling branches offering shade to those who sought refuge from the bustle of daily life. Villagers of all ages would gather around Elias, their faces illuminated by the warmth of his stories.\n"
|
||||||
|
+ "\n" + "One evening, as dusk painted the sky in hues of orange and purple, a curious young girl named Lily approached Elias. Her eyes sparkled with wonder as she asked for a tale unlike any other.\n" + "\n"
|
||||||
|
+ "Elias smiled, sensing her thirst for adventure, and began a story about a forgotten kingdom veiled by mist, guarded by mystical creatures and enchanted by ancient spells. With each word, the air grew thick with anticipation, and the listeners were transported into a world where magic danced on the edges of reality.\n"
|
||||||
|
+ "\n" + "As Elias weaved the story, Lily's imagination took flight. She envisioned herself as a brave warrior, wielding a shimmering sword against dark forces, her heart fueled by courage and kindness.\n" + "\n"
|
||||||
|
+ "The night wore on, but the spell of the tale held everyone captive. The villagers laughed, gasped, and held their breaths, journeying alongside the characters through trials and triumphs.\n" + "\n"
|
||||||
|
+ "As the final words lingered in the air, a sense of enchantment settled upon the listeners. They thanked Elias for the gift of his storytelling, each carrying a piece of the magical kingdom within their hearts.\n" + "\n"
|
||||||
|
+ "Lily, inspired by the story, vowed to cherish the spirit of adventure and kindness in her own life. With a newfound spark in her eyes, she bid Elias goodnight, already dreaming of the countless adventures awaiting her.\n" + "\n"
|
||||||
|
+ "Under the star-studded sky, Elias remained beneath the ancient oak, his heart aglow with the joy of sharing tales that ignited imagination and inspired dreams. And as the night embraced the village, whispers of the enchanted kingdom lingered in the breeze, promising endless possibilities to those who dared to believe.";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.baeldung.map;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.AtomicLongMap;
|
||||||
|
|
||||||
|
public class IncrementMapValueWays {
|
||||||
|
|
||||||
|
public Map<Character, Integer> charFrequencyUsingContainsKey(String sentence) {
|
||||||
|
Map<Character, Integer> charMap = new HashMap<>();
|
||||||
|
for (int c = 0; c < sentence.length(); c++) {
|
||||||
|
int count = 0;
|
||||||
|
if (charMap.containsKey(sentence.charAt(c))) {
|
||||||
|
count = charMap.get(sentence.charAt(c));
|
||||||
|
}
|
||||||
|
charMap.put(sentence.charAt(c), count + 1);
|
||||||
|
}
|
||||||
|
return charMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Character, Integer> charFrequencyUsingGetOrDefault(String sentence) {
|
||||||
|
Map<Character, Integer> charMap = new HashMap<>();
|
||||||
|
for (int c = 0; c < sentence.length(); c++) {
|
||||||
|
charMap.put(sentence.charAt(c), charMap.getOrDefault(sentence.charAt(c), 0) + 1);
|
||||||
|
}
|
||||||
|
return charMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Character, Integer> charFrequencyUsingMerge(String sentence) {
|
||||||
|
Map<Character, Integer> charMap = new HashMap<>();
|
||||||
|
for (int c = 0; c < sentence.length(); c++) {
|
||||||
|
charMap.merge(sentence.charAt(c), 1, Integer::sum);
|
||||||
|
}
|
||||||
|
return charMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Character, Integer> charFrequencyUsingCompute(String sentence) {
|
||||||
|
Map<Character, Integer> charMap = new HashMap<>();
|
||||||
|
for (int c = 0; c < sentence.length(); c++) {
|
||||||
|
charMap.compute(sentence.charAt(c), (key, value) -> (value == null) ? 1 : value + 1);
|
||||||
|
}
|
||||||
|
return charMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Character, Long> charFrequencyUsingAtomicMap(String sentence) {
|
||||||
|
AtomicLongMap<Character> map = AtomicLongMap.create();
|
||||||
|
for (int c = 0; c < sentence.length(); c++) {
|
||||||
|
map.getAndIncrement(sentence.charAt(c));
|
||||||
|
}
|
||||||
|
return map.asMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Character, Integer> charFrequencyWithConcurrentMap(String sentence, Map<Character, Integer> charMap) {
|
||||||
|
for (int c = 0; c < sentence.length(); c++) {
|
||||||
|
charMap.compute(sentence.charAt(c), (key, value) -> (value == null) ? 1 : value + 1);
|
||||||
|
}
|
||||||
|
return charMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Character, AtomicInteger> charFrequencyWithGetAndIncrement(String sentence) {
|
||||||
|
Map<Character, AtomicInteger> charMap = new HashMap<>();
|
||||||
|
for (int c = 0; c < sentence.length(); c++) {
|
||||||
|
charMap.putIfAbsent(sentence.charAt(c), new AtomicInteger(0));
|
||||||
|
charMap.get(sentence.charAt(c))
|
||||||
|
.incrementAndGet();
|
||||||
|
}
|
||||||
|
return charMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Character, AtomicInteger> charFrequencyWithGetAndIncrementComputeIfAbsent(String sentence) {
|
||||||
|
Map<Character, AtomicInteger> charMap = new HashMap<>();
|
||||||
|
for (int c = 0; c < sentence.length(); c++) {
|
||||||
|
charMap.computeIfAbsent(sentence.charAt(c), k -> new AtomicInteger(0))
|
||||||
|
.incrementAndGet();
|
||||||
|
}
|
||||||
|
return charMap;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.baeldung.map.readandwritefile;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class Student implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
/** Default constructor for JSON serialization */
|
||||||
|
public Student() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Student(String firstName, String lastName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
|
Student student = (Student) o;
|
||||||
|
return Objects.equals(firstName, student.firstName) && Objects.equals(lastName, student.lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package com.baeldung.map;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class IncrementMapValueUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenUsingContainsKey_thenReturnFreqMap() {
|
||||||
|
String string = "the quick brown fox jumps over the lazy dog";
|
||||||
|
IncrementMapValueWays ic = new IncrementMapValueWays();
|
||||||
|
Map<Character, Integer> actualMap = ic.charFrequencyUsingContainsKey(string);
|
||||||
|
Map<Character, Integer> expectedMap = getExpectedMap();
|
||||||
|
Assert.assertEquals(expectedMap, actualMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenUsingGetOrDefault_thenReturnFreqMap() {
|
||||||
|
String string = "the quick brown fox jumps over the lazy dog";
|
||||||
|
IncrementMapValueWays ic = new IncrementMapValueWays();
|
||||||
|
Map<Character, Integer> actualMap = ic.charFrequencyUsingGetOrDefault(string);
|
||||||
|
Map<Character, Integer> expectedMap = getExpectedMap();
|
||||||
|
Assert.assertEquals(expectedMap, actualMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenUsingMapMerge_thenReturnFreqMap() {
|
||||||
|
String string = "the quick brown fox jumps over the lazy dog";
|
||||||
|
IncrementMapValueWays ic = new IncrementMapValueWays();
|
||||||
|
Map<Character, Integer> actualMap = ic.charFrequencyUsingMerge(string);
|
||||||
|
Map<Character, Integer> expectedMap = getExpectedMap();
|
||||||
|
Assert.assertEquals(expectedMap, actualMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenUsingMapCompute_thenReturnFreqMap() {
|
||||||
|
String string = "the quick brown fox jumps over the lazy dog";
|
||||||
|
IncrementMapValueWays ic = new IncrementMapValueWays();
|
||||||
|
Map<Character, Integer> actualMap = ic.charFrequencyUsingCompute(string);
|
||||||
|
Map<Character, Integer> expectedMap = getExpectedMap();
|
||||||
|
Assert.assertEquals(expectedMap, actualMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenUsingGuava_thenReturnFreqMap() {
|
||||||
|
String string = "the quick brown fox jumps over the lazy dog";
|
||||||
|
IncrementMapValueWays ic = new IncrementMapValueWays();
|
||||||
|
Map<Character, Long> actualMap = ic.charFrequencyUsingAtomicMap(string);
|
||||||
|
Map<Character, Integer> expectedMap = getExpectedMap();
|
||||||
|
Assert.assertEquals(expectedMap.keySet(), actualMap.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenUsingIncrementAndGet_thenReturnFreqMap() {
|
||||||
|
String string = "the quick brown fox jumps over the lazy dog";
|
||||||
|
IncrementMapValueWays ic = new IncrementMapValueWays();
|
||||||
|
Map<Character, AtomicInteger> actualMap = ic.charFrequencyWithGetAndIncrement(string);
|
||||||
|
Assert.assertEquals(getExpectedMap().keySet(), actualMap.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenUsingIncrementAndGetAndComputeIfAbsent_thenReturnFreqMap() {
|
||||||
|
String string = "the quick brown fox jumps over the lazy dog";
|
||||||
|
IncrementMapValueWays ic = new IncrementMapValueWays();
|
||||||
|
Map<Character, AtomicInteger> actualMap = ic.charFrequencyWithGetAndIncrementComputeIfAbsent(string);
|
||||||
|
Assert.assertEquals(getExpectedMap().keySet(), actualMap.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_whenUsingConcurrentMapCompute_thenReturnFreqMap() throws InterruptedException {
|
||||||
|
Map<Character, Integer> charMap = new ConcurrentHashMap<>();
|
||||||
|
Thread thread1 = new Thread(() -> {
|
||||||
|
IncrementMapValueWays ic = new IncrementMapValueWays();
|
||||||
|
ic.charFrequencyWithConcurrentMap("the quick brown", charMap);
|
||||||
|
});
|
||||||
|
|
||||||
|
Thread thread2 = new Thread(() -> {
|
||||||
|
IncrementMapValueWays ic = new IncrementMapValueWays();
|
||||||
|
ic.charFrequencyWithConcurrentMap(" fox jumps over the lazy dog", charMap);
|
||||||
|
});
|
||||||
|
|
||||||
|
thread1.start();
|
||||||
|
thread2.start();
|
||||||
|
thread1.join();
|
||||||
|
thread2.join();
|
||||||
|
|
||||||
|
Map<Character, Integer> expectedMap = getExpectedMap();
|
||||||
|
Assert.assertEquals(expectedMap, charMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Character, Integer> getExpectedMap() {
|
||||||
|
return Stream.of(
|
||||||
|
new Object[][] { { ' ', 8 }, { 'a', 1 }, { 'b', 1 }, { 'c', 1 }, { 'd', 1 }, { 'e', 3 }, { 'f', 1 }, { 'g', 1 }, { 'h', 2 }, { 'i', 1 }, { 'j', 1 }, { 'k', 1 }, { 'l', 1 }, { 'm', 1 }, { 'n', 1 }, { 'o', 4 }, { 'p', 1 }, { 'q', 1 }, { 'r', 2 },
|
||||||
|
{ 's', 1 }, { 't', 2 }, { 'u', 2 }, { 'v', 1 }, { 'w', 1 }, { 'x', 1 }, { 'y', 1 }, { 'z', 1 } })
|
||||||
|
.collect(Collectors.toMap(data -> (Character) data[0], data -> (Integer) data[1]));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package com.baeldung.map.readandwritefile;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
public class ReadAndWriteFileWithHashMapUnitTest {
|
||||||
|
|
||||||
|
private static final Map<Integer, Student> STUDENT_DATA = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
STUDENT_DATA.put(1234, new Student("Henry", "Winter"));
|
||||||
|
STUDENT_DATA.put(5678, new Student("Richard", "Papen"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private File file;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void createFile() throws IOException {
|
||||||
|
file = File.createTempFile("student", ".data");
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void deleteFile() {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenHashMap_whenWrittenAsPropertiesFile_thenReloadedMapIsIdentical() throws IOException {
|
||||||
|
// Given a map containing student data
|
||||||
|
Map<String, String> studentData = new HashMap<>();
|
||||||
|
studentData.put("student.firstName", "Henry");
|
||||||
|
studentData.put("student.lastName", "Winter");
|
||||||
|
|
||||||
|
// When converting to a Properties object and writing to a file
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.putAll(studentData);
|
||||||
|
try (OutputStream output = Files.newOutputStream(file.toPath())) {
|
||||||
|
props.store(output, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then the map resulting from loading the Properties file is identical
|
||||||
|
Properties propsFromFile = new Properties();
|
||||||
|
try (InputStream input = Files.newInputStream(file.toPath())) {
|
||||||
|
propsFromFile.load(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> studentDataFromProps = propsFromFile.stringPropertyNames()
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toMap(key -> key, props::getProperty));
|
||||||
|
assertThat(studentDataFromProps).isEqualTo(studentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenHashMap_whenSerializedToFile_thenDeserializedMapIsIdentical() throws IOException, ClassNotFoundException {
|
||||||
|
// Given a map containing student data (STUDENT_DATA)
|
||||||
|
|
||||||
|
// When serializing the map to a file
|
||||||
|
try (FileOutputStream fileOutput = new FileOutputStream(file); ObjectOutputStream objectStream = new ObjectOutputStream(fileOutput)) {
|
||||||
|
objectStream.writeObject(STUDENT_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then read the file back into a map and check the contents
|
||||||
|
Map<Integer, Student> studentsFromFile;
|
||||||
|
try (FileInputStream fileReader = new FileInputStream(file); ObjectInputStream objectStream = new ObjectInputStream(fileReader)) {
|
||||||
|
studentsFromFile = (HashMap<Integer, Student>) objectStream.readObject();
|
||||||
|
}
|
||||||
|
assertThat(studentsFromFile).isEqualTo(STUDENT_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenHashMap_whenSerializedToFileWithJackson_thenDeserializedMapIsIdentical() throws IOException {
|
||||||
|
// Given a map containing student data (STUDENT_DATA)
|
||||||
|
|
||||||
|
// When converting to JSON with Jackson and writing to a file
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
try (FileOutputStream fileOutput = new FileOutputStream(file)) {
|
||||||
|
mapper.writeValue(fileOutput, STUDENT_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then deserialize the file back into a map and check that it's identical
|
||||||
|
Map<Integer, Student> mapFromFile;
|
||||||
|
try (FileInputStream fileInput = new FileInputStream(file)) {
|
||||||
|
// Create a TypeReference so we can deserialize the parameterized type
|
||||||
|
TypeReference<HashMap<Integer, Student>> mapType = new TypeReference<HashMap<Integer, Student>>() {
|
||||||
|
};
|
||||||
|
mapFromFile = mapper.readValue(fileInput, mapType);
|
||||||
|
}
|
||||||
|
assertThat(mapFromFile).isEqualTo(STUDENT_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenHashMap_whenSerializedToFileWithGson_thenDeserializedMapIsIdentical() throws IOException {
|
||||||
|
// Given a map containing student data (STUDENT_DATA)
|
||||||
|
|
||||||
|
// When converting to JSON using Gson and writing to a file
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try (FileWriter writer = new FileWriter(file)) {
|
||||||
|
gson.toJson(STUDENT_DATA, writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then deserialize the file back into a map and check that it's identical
|
||||||
|
Map<Integer, Student> studentsFromFile;
|
||||||
|
try (FileReader reader = new FileReader(file)) {
|
||||||
|
Type mapType = new TypeToken<HashMap<Integer, Student>>() {
|
||||||
|
}.getType();
|
||||||
|
studentsFromFile = gson.fromJson(reader, mapType);
|
||||||
|
}
|
||||||
|
assertThat(studentsFromFile).isEqualTo(STUDENT_DATA);
|
||||||
|
}
|
||||||
|
}
|
@ -85,12 +85,12 @@
|
|||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
<jcabi-aspects.version>0.22.6</jcabi-aspects.version>
|
<jcabi-aspects.version>0.22.6</jcabi-aspects.version>
|
||||||
<aspectjrt.version>1.9.5</aspectjrt.version>
|
<aspectjrt.version>1.9.20.1</aspectjrt.version>
|
||||||
<cactoos.version>0.43</cactoos.version>
|
<cactoos.version>0.43</cactoos.version>
|
||||||
<ea-async.version>1.2.3</ea-async.version>
|
<ea-async.version>1.2.3</ea-async.version>
|
||||||
<jcabi-maven-plugin.version>0.14.1</jcabi-maven-plugin.version>
|
<jcabi-maven-plugin.version>0.14.1</jcabi-maven-plugin.version>
|
||||||
<aspectjtools.version>1.9.1</aspectjtools.version>
|
<aspectjtools.version>1.9.20.1</aspectjtools.version>
|
||||||
<aspectjweaver.version>1.9.1</aspectjweaver.version>
|
<aspectjweaver.version>1.9.20.1</aspectjweaver.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -0,0 +1,126 @@
|
|||||||
|
package com.baeldung.exceptions_completablefuture;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
public class CompletableFutureExceptionsHandlingUnitTest {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("parametersSource_handle")
|
||||||
|
void whenCompletableFutureIsScheduled_thenHandleStageIsAlwaysInvoked(int radius, long expected)
|
||||||
|
throws ExecutionException, InterruptedException {
|
||||||
|
long actual = CompletableFuture
|
||||||
|
.supplyAsync(() -> {
|
||||||
|
if (radius <= 0) {
|
||||||
|
throw new IllegalArgumentException("Supplied with non-positive radius '%d'");
|
||||||
|
}
|
||||||
|
return Math.round(Math.pow(radius, 2) * Math.PI);
|
||||||
|
})
|
||||||
|
.handle((result, ex) -> {
|
||||||
|
if (ex == null) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
|
Assertions.assertThat(actual).isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("parametersSource_exceptionally")
|
||||||
|
void whenCompletableFutureIsScheduled_thenExceptionallyExecutedOnlyOnFailure(int a, int b, int c, long expected)
|
||||||
|
throws ExecutionException, InterruptedException {
|
||||||
|
long actual = CompletableFuture
|
||||||
|
.supplyAsync(() -> {
|
||||||
|
if (a <= 0 || b <= 0 || c <= 0) {
|
||||||
|
throw new IllegalArgumentException(String.format("Supplied with incorrect edge length [%s]", List.of(a, b, c)));
|
||||||
|
}
|
||||||
|
return a * b * c;
|
||||||
|
})
|
||||||
|
.exceptionally((ex) -> -1)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
Assertions.assertThat(actual).isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("parametersSource_exceptionally")
|
||||||
|
void givenCompletableFutureIsScheduled_whenHandleIsAlreadyPresent_thenExceptionallyIsNotExecuted(int a, int b, int c, long expected)
|
||||||
|
throws ExecutionException, InterruptedException {
|
||||||
|
long actual = CompletableFuture
|
||||||
|
.supplyAsync(() -> {
|
||||||
|
if (a <= 0 || b <= 0 || c <= 0) {
|
||||||
|
throw new IllegalArgumentException(String.format("Supplied with incorrect edge length [%s]", List.of(a, b, c)));
|
||||||
|
}
|
||||||
|
return a * b * c;
|
||||||
|
})
|
||||||
|
.handle((result, throwable) -> {
|
||||||
|
if (throwable != null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
System.exit(1);
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
|
Assertions.assertThat(actual).isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("parametersSource_whenComplete")
|
||||||
|
void whenCompletableFutureIsScheduled_thenWhenCompletedExecutedAlways(Double a, long expected, Class<Exception> ifAny) {
|
||||||
|
try {
|
||||||
|
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
long actual = CompletableFuture
|
||||||
|
.supplyAsync(() -> {
|
||||||
|
if (a.isNaN()) {
|
||||||
|
throw new IllegalArgumentException("Supplied value is NaN");
|
||||||
|
}
|
||||||
|
return Math.round(Math.pow(a, 2));
|
||||||
|
})
|
||||||
|
.whenComplete((result, exception) -> countDownLatch.countDown())
|
||||||
|
.get();
|
||||||
|
|
||||||
|
Assertions.assertThat(countDownLatch.await(20L, java.util.concurrent.TimeUnit.SECONDS));
|
||||||
|
Assertions.assertThat(actual).isEqualTo(expected);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Assertions.assertThat(e.getClass()).isSameAs(ExecutionException.class);
|
||||||
|
Assertions.assertThat(e.getCause().getClass()).isSameAs(ifAny);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Arguments> parametersSource_handle() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(1, 3),
|
||||||
|
Arguments.of(-1, -1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Arguments> parametersSource_exceptionally() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(1, 5, 5, 25),
|
||||||
|
Arguments.of(-1, 10, 15, -1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Arguments> parametersSource_whenComplete() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(2d, 4, null),
|
||||||
|
Arguments.of(Double.NaN, 1, IllegalArgumentException.class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -11,4 +11,6 @@ This module contains articles about basic Java concurrency.
|
|||||||
- [CompletableFuture and ThreadPool in Java](https://www.baeldung.com/java-completablefuture-threadpool)
|
- [CompletableFuture and ThreadPool in Java](https://www.baeldung.com/java-completablefuture-threadpool)
|
||||||
- [CompletableFuture allOf().join() vs. CompletableFuture.join()](https://www.baeldung.com/java-completablefuture-allof-join)
|
- [CompletableFuture allOf().join() vs. CompletableFuture.join()](https://www.baeldung.com/java-completablefuture-allof-join)
|
||||||
- [Retry Logic with CompletableFuture](https://www.baeldung.com/java-completablefuture-retry-logic)
|
- [Retry Logic with CompletableFuture](https://www.baeldung.com/java-completablefuture-retry-logic)
|
||||||
|
- [Convert From List of CompletableFuture to CompletableFuture List](https://www.baeldung.com/java-completablefuture-list-convert)
|
||||||
|
- [Synchronize a Static Variable Among Different Threads](https://www.baeldung.com/java-synchronize-static-variable-different-threads)
|
||||||
- [[<-- Prev]](../core-java-concurrency-basic-2)
|
- [[<-- Prev]](../core-java-concurrency-basic-2)
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.baeldung.concurrent.synchronizestatic.atomicinteger;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizing static variable with AtomicInteger.
|
||||||
|
*/
|
||||||
|
public class Employee {
|
||||||
|
|
||||||
|
private static final AtomicInteger count = new AtomicInteger(0);
|
||||||
|
|
||||||
|
int id;
|
||||||
|
String name;
|
||||||
|
String title;
|
||||||
|
|
||||||
|
public Employee(int id, String name, String title) {
|
||||||
|
incrementCount();
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void incrementCount() {
|
||||||
|
count.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCount() {
|
||||||
|
return count.get();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.baeldung.concurrent.synchronizestatic.none;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No synchronization.
|
||||||
|
*/
|
||||||
|
public class Employee {
|
||||||
|
|
||||||
|
static int count;
|
||||||
|
int id;
|
||||||
|
String name;
|
||||||
|
String title;
|
||||||
|
|
||||||
|
public Employee(int id, String name, String title) {
|
||||||
|
incrementCount();
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void incrementCount() {
|
||||||
|
System.out.println("Count = " + ++count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.baeldung.concurrent.synchronizestatic.reentrantlock;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizing static variable with a Reenatrant Lock.
|
||||||
|
*/
|
||||||
|
public class Employee {
|
||||||
|
|
||||||
|
private static final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
static int count;
|
||||||
|
int id;
|
||||||
|
String name;
|
||||||
|
String title;
|
||||||
|
|
||||||
|
public Employee(int id, String name, String title) {
|
||||||
|
incrementCount();
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void incrementCount() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
System.out.println("Count = " + ++count);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCount() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.baeldung.concurrent.synchronizestatic.synchronizedblock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizing static variable with a synchronized block.
|
||||||
|
*/
|
||||||
|
public class Employee {
|
||||||
|
|
||||||
|
private static final Object lock = new Object();
|
||||||
|
|
||||||
|
static int count;
|
||||||
|
int id;
|
||||||
|
String name;
|
||||||
|
String title;
|
||||||
|
|
||||||
|
public Employee(int id, String name, String title) {
|
||||||
|
incrementCount();
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void incrementCount() {
|
||||||
|
synchronized(lock) {
|
||||||
|
System.out.println("Count = " + ++count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCount() {
|
||||||
|
synchronized(lock) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.baeldung.concurrent.synchronizestatic.synchronizedclass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizing static variable with a synchronized block.
|
||||||
|
*/
|
||||||
|
public class Employee
|
||||||
|
{
|
||||||
|
static int count;
|
||||||
|
int id;
|
||||||
|
String name;
|
||||||
|
String title;
|
||||||
|
|
||||||
|
public Employee(int id, String name, String title) {
|
||||||
|
incrementCount();
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void incrementCount() {
|
||||||
|
synchronized(Employee.class) {
|
||||||
|
System.out.println("Count = " + ++count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCount() {
|
||||||
|
synchronized(Employee.class) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.baeldung.concurrent.synchronizestatic.synchronizedmethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizing static variable with a synchronized method.
|
||||||
|
*/
|
||||||
|
public class Employee {
|
||||||
|
static int count;
|
||||||
|
int id;
|
||||||
|
String name;
|
||||||
|
String title;
|
||||||
|
|
||||||
|
public Employee(int id, String name, String title)
|
||||||
|
{
|
||||||
|
incrementCount();
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void incrementCount() {
|
||||||
|
System.out.println("Count = " + ++count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
package com.baeldung.concurrent.synchronizestatic;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tests in this class show the output of creating multiple
|
||||||
|
* types of Employee classes in the <code>synchronizedstatic</code>
|
||||||
|
* package. When not synchronized the out will not be sequential;
|
||||||
|
* when it is synchronized the output will be in sequential.
|
||||||
|
*/
|
||||||
|
public class SychronizeStaticDataUnitTest {
|
||||||
|
private final Executor pool = Executors.newFixedThreadPool(4);
|
||||||
|
|
||||||
|
private final int numberToTest = 100;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenNotSynchronized_thenDataOutOfOrder() {
|
||||||
|
|
||||||
|
System.out.println("No synchronization");
|
||||||
|
|
||||||
|
for(int i = 0; i < numberToTest; i++) {
|
||||||
|
int finalI = i;
|
||||||
|
pool.execute(() -> {
|
||||||
|
new com.baeldung.concurrent.synchronizestatic.none.Employee(finalI, "John", "Smith");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenSynchronizedMethod_thenDataInOrder() {
|
||||||
|
|
||||||
|
System.out.println("Synchronization with synchronized method");
|
||||||
|
|
||||||
|
for(int i = 0; i < numberToTest; i++) {
|
||||||
|
int finalI = i;
|
||||||
|
pool.execute(() -> {
|
||||||
|
new com.baeldung.concurrent.synchronizestatic.synchronizedmethod.Employee(finalI, "John", "Smith");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenSynchronizedClass_thenDataInOrder() {
|
||||||
|
|
||||||
|
System.out.println("Synchronization with synchronized block on class");
|
||||||
|
|
||||||
|
for(int i = 0; i < numberToTest; i++) {
|
||||||
|
int finalI = i;
|
||||||
|
pool.execute(() -> {
|
||||||
|
new com.baeldung.concurrent.synchronizestatic.synchronizedclass.Employee(finalI, "John", "Smith");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenSynchronizedBlock_thenDataInOrder() {
|
||||||
|
|
||||||
|
System.out.println("Synchronization with synchronized block on a private object");
|
||||||
|
|
||||||
|
for(int i = 0; i < numberToTest; i++) {
|
||||||
|
int finalI = i;
|
||||||
|
pool.execute(() -> {
|
||||||
|
new com.baeldung.concurrent.synchronizestatic.synchronizedblock.Employee(finalI, "John", "Smith");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenAtomicInteger_thenDataInOrder() {
|
||||||
|
// Not straight forward to test this because we cannot log/print
|
||||||
|
// and increment values in a synchronized fashion like other
|
||||||
|
// tests
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenReentrantLock_thenDataInOrder() {
|
||||||
|
|
||||||
|
System.out.println("Synchronization with ReentrantLock");
|
||||||
|
|
||||||
|
for(int i = 0; i < numberToTest; i++) {
|
||||||
|
int finalI = i;
|
||||||
|
pool.execute(() -> {
|
||||||
|
new com.baeldung.concurrent.synchronizestatic.reentrantlock.Employee(finalI, "John", "Smith");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>core-java-conditionals</artifactId>
|
<artifactId>core-java-conditionals</artifactId>
|
||||||
<version>0.1.0-SNAPSHOT</version>
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
<name>core-java-compiler</name>
|
<name>core-java-conditionals</name>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
|
@ -7,3 +7,4 @@
|
|||||||
- [ASCII Art in Java](http://www.baeldung.com/ascii-art-in-java)
|
- [ASCII Art in Java](http://www.baeldung.com/ascii-art-in-java)
|
||||||
- [System.console() vs. System.out](https://www.baeldung.com/java-system-console-vs-system-out)
|
- [System.console() vs. System.out](https://www.baeldung.com/java-system-console-vs-system-out)
|
||||||
- [How to Log to the Console in Color](https://www.baeldung.com/java-log-console-in-color)
|
- [How to Log to the Console in Color](https://www.baeldung.com/java-log-console-in-color)
|
||||||
|
- [Create Table Using ASCII in a Console in Java](https://www.baeldung.com/java-console-ascii-make-table)
|
||||||
|
@ -29,6 +29,11 @@
|
|||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.vandermeer</groupId>
|
||||||
|
<artifactId>asciitable</artifactId>
|
||||||
|
<version>${ascii.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@ -157,6 +162,7 @@
|
|||||||
<maven-javadoc-plugin.version>3.0.0-M1</maven-javadoc-plugin.version>
|
<maven-javadoc-plugin.version>3.0.0-M1</maven-javadoc-plugin.version>
|
||||||
<source.version>1.8</source.version>
|
<source.version>1.8</source.version>
|
||||||
<target.version>1.8</target.version>
|
<target.version>1.8</target.version>
|
||||||
|
<ascii.version>0.3.2</ascii.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.baeldung.consoletableoutput;
|
||||||
|
|
||||||
|
public class BodyMassIndex {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private double height;
|
||||||
|
private double weight;
|
||||||
|
|
||||||
|
public BodyMassIndex(String name, double height, double weight) {
|
||||||
|
this.name = name;
|
||||||
|
this.height = height;
|
||||||
|
this.weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeight(double height) {
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getWeight() {
|
||||||
|
return weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWeight(double weight) {
|
||||||
|
this.weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double calculate() {
|
||||||
|
double bmi = weight / (height * height);
|
||||||
|
String formattedBmi = String.format("%.2f", bmi);
|
||||||
|
return Double.parseDouble(formattedBmi);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package com.baeldung.consoletableoutput;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.vandermeer.asciitable.AsciiTable;
|
||||||
|
import de.vandermeer.skb.interfaces.transformers.textformat.TextAlignment;
|
||||||
|
|
||||||
|
public class BodyMassIndexApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
stringFormat();
|
||||||
|
asciiTable();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stringFormat() {
|
||||||
|
List<BodyMassIndex> bodyMassIndices = new ArrayList<>();
|
||||||
|
bodyMassIndices.add(new BodyMassIndex("Tom", 1.8, 80));
|
||||||
|
bodyMassIndices.add(new BodyMassIndex("Elton", 1.9, 90));
|
||||||
|
bodyMassIndices.add(new BodyMassIndex("Harry", 1.9, 90));
|
||||||
|
bodyMassIndices.add(new BodyMassIndex("Hannah", 1.9, 90));
|
||||||
|
|
||||||
|
String leftAlignment = "| %-7s | %-7.2f | %-7.2f | %-5.2f |%n";
|
||||||
|
|
||||||
|
System.out.format("+---------+---------+---------+-------+%n");
|
||||||
|
System.out.format("| Name | Height | Weight | BMI |%n");
|
||||||
|
System.out.format("+---------+---------+---------+-------+%n");
|
||||||
|
|
||||||
|
for (BodyMassIndex bodyMassIndex : bodyMassIndices) {
|
||||||
|
System.out.format(leftAlignment, bodyMassIndex.getName(), bodyMassIndex.getHeight(), bodyMassIndex.getWeight(), bodyMassIndex.calculate());
|
||||||
|
System.out.format("+---------+---------+---------+-------+%n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void asciiTable() {
|
||||||
|
List<BodyMassIndex> bodyMassIndices = new ArrayList<>();
|
||||||
|
bodyMassIndices.add(new BodyMassIndex("Tom", 1.8, 80));
|
||||||
|
bodyMassIndices.add(new BodyMassIndex("Elton", 1.9, 90));
|
||||||
|
bodyMassIndices.add(new BodyMassIndex("Harry", 1.9, 90));
|
||||||
|
bodyMassIndices.add(new BodyMassIndex("Hannah", 1.9, 90));
|
||||||
|
|
||||||
|
AsciiTable asciiTable = new AsciiTable();
|
||||||
|
asciiTable.addRule();
|
||||||
|
asciiTable.addRow("Name", "Height", "Weight", "BMI");
|
||||||
|
asciiTable.addRule();
|
||||||
|
|
||||||
|
for (BodyMassIndex bodyMassIndex : bodyMassIndices) {
|
||||||
|
|
||||||
|
asciiTable.addRow(bodyMassIndex.getName(), bodyMassIndex.getHeight(), bodyMassIndex.getWeight(), bodyMassIndex.calculate());
|
||||||
|
asciiTable.addRule();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
asciiTable.setTextAlignment(TextAlignment.CENTER);
|
||||||
|
String render = asciiTable.render();
|
||||||
|
System.out.println(render);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -54,7 +54,7 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<joda-time.version>2.10</joda-time.version>
|
<joda-time.version>2.12.5</joda-time.version>
|
||||||
<hirondelle-date4j.version>RELEASE</hirondelle-date4j.version>
|
<hirondelle-date4j.version>RELEASE</hirondelle-date4j.version>
|
||||||
<maven.compiler.source>1.9</maven.compiler.source>
|
<maven.compiler.source>1.9</maven.compiler.source>
|
||||||
<maven.compiler.target>1.9</maven.compiler.target>
|
<maven.compiler.target>1.9</maven.compiler.target>
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<joda-time.version>2.10</joda-time.version>
|
<joda-time.version>2.12.5</joda-time.version>
|
||||||
<hirondelle-date4j.version>1.5.1</hirondelle-date4j.version>
|
<hirondelle-date4j.version>1.5.1</hirondelle-date4j.version>
|
||||||
<prettytime.version>3.2.7.Final</prettytime.version>
|
<prettytime.version>3.2.7.Final</prettytime.version>
|
||||||
<time4j-base.version>5.9</time4j-base.version>
|
<time4j-base.version>5.9</time4j-base.version>
|
||||||
|
@ -11,4 +11,6 @@ This module contains articles about date operations in Java.
|
|||||||
- [Getting Yesterday’s Date in Java](https://www.baeldung.com/java-find-yesterdays-date)
|
- [Getting Yesterday’s Date in Java](https://www.baeldung.com/java-find-yesterdays-date)
|
||||||
- [How to Get the Start and End Dates of a Year Using Java](https://www.baeldung.com/java-date-year-start-end)
|
- [How to Get the Start and End Dates of a Year Using Java](https://www.baeldung.com/java-date-year-start-end)
|
||||||
- [Convert Between Java LocalDate and Epoch](https://www.baeldung.com/java-localdate-epoch)
|
- [Convert Between Java LocalDate and Epoch](https://www.baeldung.com/java-localdate-epoch)
|
||||||
|
- [Get First Date of Current Month in Java](https://www.baeldung.com/java-current-month-start-date)
|
||||||
|
- [Time Conversions Using TimeUnit](https://www.baeldung.com/java-timeunit-conversion)
|
||||||
- [[<-- Prev]](/core-java-modules/core-java-date-operations-2)
|
- [[<-- Prev]](/core-java-modules/core-java-date-operations-2)
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<joda-time.version>2.12.5</joda-time.version>
|
<joda-time.version>2.12.5</joda-time.version>
|
||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -0,0 +1,105 @@
|
|||||||
|
package com.baeldung.timeunitconversions;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class TimeUnitConversionsUnitTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenSeconds_whenConvertToMinutes_thenCorrect() {
|
||||||
|
int input = 60;
|
||||||
|
|
||||||
|
long minutes = TimeUnit.MINUTES.convert(input, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
assertThat(minutes).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenMinutes_whenConvertToSeconds_thenCorrect() {
|
||||||
|
int input = 1;
|
||||||
|
|
||||||
|
long seconds = TimeUnit.SECONDS.convert(input, TimeUnit.MINUTES);
|
||||||
|
|
||||||
|
assertThat(seconds).isEqualTo(60);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenSeconds_whenToMinutes_thenCorrect() {
|
||||||
|
int input = 60;
|
||||||
|
|
||||||
|
long minutes = TimeUnit.SECONDS.toMinutes(input);
|
||||||
|
|
||||||
|
assertThat(minutes).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenMinutes_whenToSeconds_thenCorrect() {
|
||||||
|
int input = 1;
|
||||||
|
|
||||||
|
long seconds = TimeUnit.MINUTES.toSeconds(input);
|
||||||
|
|
||||||
|
assertThat(seconds).isEqualTo(60);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenNegativeInput_whenToMinutes_thenCorrect() {
|
||||||
|
int input = -60;
|
||||||
|
|
||||||
|
long minutes = TimeUnit.SECONDS.toMinutes(input);
|
||||||
|
assertThat(minutes).isEqualTo(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenNonTotalInput_whenToMinutes_thenCorrectTotalResultWithDecimalTruncate() {
|
||||||
|
long positiveUnder = TimeUnit.SECONDS.toMinutes(59);
|
||||||
|
long positiveAbove = TimeUnit.SECONDS.toMinutes(61);
|
||||||
|
|
||||||
|
assertThat(positiveUnder).isEqualTo(0);
|
||||||
|
assertThat(positiveAbove).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenNonTotalNegativeInput_whenToMinutes_thenCorrectTotalResultWithDecimalTruncate() {
|
||||||
|
long negativeUnder = TimeUnit.SECONDS.toMinutes(-59);
|
||||||
|
long negativeAbove = TimeUnit.SECONDS.toMinutes(-61);
|
||||||
|
|
||||||
|
assertThat(negativeUnder).isEqualTo(0);
|
||||||
|
assertThat(negativeAbove).isEqualTo(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenOverflowInput_whenToMillis_thenTruncatedToLimit() {
|
||||||
|
long maxMillis = TimeUnit.DAYS.toMillis(Long.MAX_VALUE);
|
||||||
|
long minMillis = TimeUnit.DAYS.toMillis(Long.MIN_VALUE);
|
||||||
|
|
||||||
|
assertThat(maxMillis).isEqualTo(Long.MAX_VALUE);
|
||||||
|
assertThat(minMillis).isEqualTo(Long.MIN_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void givenInput_whenExtractFineTimeUnits_thenCorrect() {
|
||||||
|
long inputSeconds = 3672;
|
||||||
|
|
||||||
|
long hours = TimeUnit.SECONDS.toHours(inputSeconds);
|
||||||
|
|
||||||
|
long secondsRemainingAfterHours = inputSeconds - TimeUnit.HOURS.toSeconds(hours);
|
||||||
|
long minutes = TimeUnit.SECONDS.toMinutes(secondsRemainingAfterHours);
|
||||||
|
|
||||||
|
long seconds = secondsRemainingAfterHours - TimeUnit.MINUTES.toSeconds(minutes);
|
||||||
|
|
||||||
|
|
||||||
|
assertThat(hours).isEqualTo(1);
|
||||||
|
assertThat(minutes).isEqualTo(1);
|
||||||
|
assertThat(seconds).isEqualTo(12);
|
||||||
|
|
||||||
|
assertThat(inputSeconds).isEqualTo(
|
||||||
|
(hours * 60 * 60) +
|
||||||
|
(minutes * 60) +
|
||||||
|
(seconds)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -54,7 +54,7 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<joda-time.version>2.10</joda-time.version>
|
<joda-time.version>2.12.5</joda-time.version>
|
||||||
<maven.compiler.source>1.9</maven.compiler.source>
|
<maven.compiler.source>1.9</maven.compiler.source>
|
||||||
<maven.compiler.target>1.9</maven.compiler.target>
|
<maven.compiler.target>1.9</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.baeldung.timestamptolong;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class TimestampToLong {
|
||||||
|
|
||||||
|
public long usingSimpleDateFormat(String timestampString) throws ParseException {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
Date date = sdf.parse(timestampString);
|
||||||
|
String currentDateString = sdf.format(date);
|
||||||
|
return sdf.parse(currentDateString).getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long usingInstantClass(String timestampString) {
|
||||||
|
Instant instant = LocalDateTime.parse(timestampString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||||
|
.atZone(ZoneId.systemDefault())
|
||||||
|
.toInstant();
|
||||||
|
return instant.toEpochMilli();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long usingJava8DateTime(String timestampString) {
|
||||||
|
LocalDateTime localDateTime = LocalDateTime.parse(timestampString.replace(" ", "T"));
|
||||||
|
return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.baeldung.timestamptolocaldatetime;
|
||||||
|
|
||||||
|
import org.joda.time.DateTimeZone;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.format.DateTimeFormat;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class TimeToLocalDateTimeUnitTest {
|
||||||
|
private static final long timestampInMillis = 1700010123000L;
|
||||||
|
private static final String expectedTimestampString = "2023-11-15 01:02:03";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenTimestamp_whenConvertingToLocalDateTime_thenConvertSuccessfully() {
|
||||||
|
Instant instant = Instant.ofEpochMilli(timestampInMillis);
|
||||||
|
LocalDateTime localDateTime =
|
||||||
|
LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
|
||||||
|
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
String formattedDateTime = localDateTime.format(formatter);
|
||||||
|
|
||||||
|
assertEquals(expectedTimestampString, formattedDateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenJodaTime_whenGettingTimestamp_thenConvertToLong() {
|
||||||
|
DateTime dateTime = new DateTime(timestampInMillis, DateTimeZone.UTC);
|
||||||
|
org.joda.time.LocalDateTime localDateTime = dateTime.toLocalDateTime();
|
||||||
|
|
||||||
|
org.joda.time.format.DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
String actualTimestamp = formatter.print(localDateTime);
|
||||||
|
|
||||||
|
assertEquals(expectedTimestampString, actualTimestamp);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.baeldung.timestamptolong;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class TimestampToLongUnitTest {
|
||||||
|
private static final String timestampString = "2023-11-15 01:02:03";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenSimpleDateFormat_whenFormattingDate_thenConvertToLong() throws ParseException {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
Date date = sdf.parse(timestampString);
|
||||||
|
|
||||||
|
String currentDateString = sdf.format(date);
|
||||||
|
long actualTimestamp = sdf.parse(currentDateString).getTime();
|
||||||
|
assertEquals(1700010123000L, actualTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInstantClass_whenGettingTimestamp_thenConvertToLong() {
|
||||||
|
Instant instant = LocalDateTime.parse(timestampString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||||
|
.atZone(ZoneId.systemDefault())
|
||||||
|
.toInstant();
|
||||||
|
long actualTimestamp = instant.toEpochMilli();
|
||||||
|
assertEquals(1700010123000L, actualTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenJava8DateTime_whenGettingTimestamp_thenConvertToLong() {
|
||||||
|
LocalDateTime localDateTime = LocalDateTime.parse(timestampString.replace(" ", "T"));
|
||||||
|
long actualTimestamp = localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||||
|
assertEquals(1700010123000L, actualTimestamp);
|
||||||
|
}
|
||||||
|
}
|
@ -64,7 +64,7 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<commons-validator.version>1.6</commons-validator.version>
|
<commons-validator.version>1.7</commons-validator.version>
|
||||||
<joda-time.version>2.12.5</joda-time.version>
|
<joda-time.version>2.12.5</joda-time.version>
|
||||||
<hirondelle-date4j.version>RELEASE</hirondelle-date4j.version>
|
<hirondelle-date4j.version>RELEASE</hirondelle-date4j.version>
|
||||||
<maven.compiler.source>1.9</maven.compiler.source>
|
<maven.compiler.source>1.9</maven.compiler.source>
|
||||||
|
@ -6,6 +6,7 @@ import java.time.Instant;
|
|||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.temporal.UnsupportedTemporalTypeException;
|
import java.time.temporal.UnsupportedTemporalTypeException;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.joda.time.format.DateTimeFormat;
|
import org.joda.time.format.DateTimeFormat;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -17,7 +18,7 @@ public class FormatInstantUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void givenInstant_whenUsingDateTimeFormatter_thenFormat() {
|
public void givenInstant_whenUsingDateTimeFormatter_thenFormat() {
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN_FORMAT)
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN_FORMAT)
|
||||||
.withZone(ZoneId.systemDefault());
|
.withZone(TimeZone.getTimeZone("UTC").toZoneId());
|
||||||
|
|
||||||
Instant instant = Instant.parse("2022-02-15T18:35:24.00Z");
|
Instant instant = Instant.parse("2022-02-15T18:35:24.00Z");
|
||||||
String formattedInstant = formatter.format(instant);
|
String formattedInstant = formatter.format(instant);
|
||||||
|
@ -4,3 +4,4 @@
|
|||||||
|
|
||||||
- [Introduction to Javadoc](http://www.baeldung.com/javadoc)
|
- [Introduction to Javadoc](http://www.baeldung.com/javadoc)
|
||||||
- [Code Snippets in Java API Documentation](https://www.baeldung.com/java-doc-code-snippets)
|
- [Code Snippets in Java API Documentation](https://www.baeldung.com/java-doc-code-snippets)
|
||||||
|
- [How to Document Generic Type Parameters in Javadoc](https://www.baeldung.com/java-javadoc-generic-type-parameters)
|
||||||
|
@ -51,7 +51,6 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<mockito-inline.version>3.8.0</mockito-inline.version>
|
<mockito-inline.version>3.8.0</mockito-inline.version>
|
||||||
<assertj.version>3.22.0</assertj.version>
|
<assertj.version>3.22.0</assertj.version>
|
||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
|
||||||
<vavr.version>0.10.4</vavr.version>
|
<vavr.version>0.10.4</vavr.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<mockito-inline.version>3.8.0</mockito-inline.version>
|
<mockito-inline.version>3.8.0</mockito-inline.version>
|
||||||
<assertj.version>3.22.0</assertj.version>
|
<assertj.version>3.22.0</assertj.version>
|
||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
|
||||||
<vavr.version>0.10.4</vavr.version>
|
<vavr.version>0.10.4</vavr.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
## Relevant Articles
|
## Relevant Articles
|
||||||
- [Convert Hex to RGB Using Java](https://www.baeldung.com/java-convert-hex-to-rgb)
|
- [Convert Hex to RGB Using Java](https://www.baeldung.com/java-convert-hex-to-rgb)
|
||||||
|
- [Convert a Hex String to an Integer in Java](https://www.baeldung.com/java-convert-hex-string-to-integer)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user