Merge remote-tracking branch 'upstream/master' into feature/BAEL-7062-GetIndex
This commit is contained in:
commit
e1dc061cd0
@ -4,4 +4,5 @@
|
|||||||
- [Find the N Most Frequent Elements in a Java Array](https://www.baeldung.com/java-n-most-frequent-elements-array)
|
- [Find the N Most Frequent Elements in a Java Array](https://www.baeldung.com/java-n-most-frequent-elements-array)
|
||||||
- [Getting Pixel Array From Image in Java](https://www.baeldung.com/java-getting-pixel-array-from-image)
|
- [Getting Pixel Array From Image in Java](https://www.baeldung.com/java-getting-pixel-array-from-image)
|
||||||
- [Calculate Distance Between Two Coordinates in Java](https://www.baeldung.com/java-find-distance-between-points)
|
- [Calculate Distance Between Two Coordinates in Java](https://www.baeldung.com/java-find-distance-between-points)
|
||||||
|
- [Rotate Arrays in Java](https://www.baeldung.com/java-rotate-arrays)
|
||||||
- More articles: [[<-- prev]](/algorithms-miscellaneous-6)
|
- More articles: [[<-- prev]](/algorithms-miscellaneous-6)
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
package com.baeldung.algorithms.connect4;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GameBoard {
|
||||||
|
private final List<List<Piece>> columns;
|
||||||
|
|
||||||
|
private final int rows;
|
||||||
|
|
||||||
|
public GameBoard(int columns, int rows) {
|
||||||
|
this.rows = rows;
|
||||||
|
this.columns = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < columns; ++i) {
|
||||||
|
this.columns.add(new ArrayList<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRows() {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColumns() {
|
||||||
|
return columns.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Piece getCell(int x, int y) {
|
||||||
|
assert(x >= 0 && x < getColumns());
|
||||||
|
assert(y >= 0 && y < getRows());
|
||||||
|
|
||||||
|
List<Piece> column = columns.get(x);
|
||||||
|
|
||||||
|
if (column.size() > y) {
|
||||||
|
return column.get(y);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean move(int x, Piece player) {
|
||||||
|
assert(x >= 0 && x < getColumns());
|
||||||
|
|
||||||
|
List<Piece> column = columns.get(x);
|
||||||
|
|
||||||
|
if (column.size() >= this.rows) {
|
||||||
|
throw new IllegalArgumentException("That column is full");
|
||||||
|
}
|
||||||
|
|
||||||
|
column.add(player);
|
||||||
|
|
||||||
|
return checkWin(x, column.size() - 1, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkWin(int x, int y, Piece player) {
|
||||||
|
// Vertical line
|
||||||
|
if (checkLine(x, y, 0, -1, player)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int offset = 0; offset < 4; ++offset) {
|
||||||
|
// Horizontal line
|
||||||
|
if (checkLine(x - 3 + offset, y, 1, 0, player)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leading diagonal
|
||||||
|
if (checkLine(x - 3 + offset, y + 3 - offset, 1, -1, player)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trailing diagonal
|
||||||
|
if (checkLine(x - 3 + offset, y - 3 + offset, 1, 1, player)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkLine(int x1, int y1, int xDiff, int yDiff, Piece player) {
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
int x = x1 + (xDiff * i);
|
||||||
|
int y = y1 + (yDiff * i);
|
||||||
|
|
||||||
|
if (x < 0 || x > columns.size() - 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < 0 || y > rows - 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player != getCell(x, y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
|
for (int y = getRows() - 1; y >= 0; --y) {
|
||||||
|
for (int x = 0; x < getColumns(); ++x) {
|
||||||
|
Piece piece = getCell(x, y);
|
||||||
|
|
||||||
|
result.append("|");
|
||||||
|
if (piece == null) {
|
||||||
|
result.append(" ");
|
||||||
|
} else if (piece == Piece.PLAYER_1) {
|
||||||
|
result.append("X");
|
||||||
|
} else if (piece == Piece.PLAYER_2) {
|
||||||
|
result.append("O");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append("|\n");
|
||||||
|
for (int i = 0; i < getColumns(); ++i) {
|
||||||
|
result.append("+-");
|
||||||
|
}
|
||||||
|
result.append("+\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.baeldung.algorithms.connect4;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class GameUnitTest {
|
||||||
|
@Test
|
||||||
|
public void blankGame() {
|
||||||
|
GameBoard gameBoard = new GameBoard(8, 6);
|
||||||
|
|
||||||
|
System.out.println(gameBoard);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void playedGame() {
|
||||||
|
GameBoard gameBoard = new GameBoard(8, 6);
|
||||||
|
|
||||||
|
assertFalse(gameBoard.move(3, Piece.PLAYER_1));
|
||||||
|
assertFalse(gameBoard.move(2, Piece.PLAYER_2));
|
||||||
|
|
||||||
|
assertFalse(gameBoard.move(4, Piece.PLAYER_1));
|
||||||
|
assertFalse(gameBoard.move(3, Piece.PLAYER_2));
|
||||||
|
|
||||||
|
assertFalse(gameBoard.move(5, Piece.PLAYER_1));
|
||||||
|
assertFalse(gameBoard.move(6, Piece.PLAYER_2));
|
||||||
|
|
||||||
|
assertFalse(gameBoard.move(5, Piece.PLAYER_1));
|
||||||
|
assertFalse(gameBoard.move(4, Piece.PLAYER_2));
|
||||||
|
|
||||||
|
assertFalse(gameBoard.move(5, Piece.PLAYER_1));
|
||||||
|
assertFalse(gameBoard.move(5, Piece.PLAYER_2));
|
||||||
|
|
||||||
|
assertFalse(gameBoard.move(6, Piece.PLAYER_1));
|
||||||
|
assertTrue(gameBoard.move(4, Piece.PLAYER_2));
|
||||||
|
|
||||||
|
System.out.println(gameBoard);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.baeldung.algorithms.connect4;
|
||||||
|
|
||||||
|
public enum Piece {
|
||||||
|
PLAYER_1,
|
||||||
|
PLAYER_2
|
||||||
|
}
|
@ -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,7 +58,7 @@
|
|||||||
</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>
|
||||||
@ -77,11 +77,11 @@
|
|||||||
|
|
||||||
<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.2</httpcore5.version>
|
||||||
<httpclient5.version>5.2</httpclient5.version>
|
<httpclient5.version>5.2.2</httpclient5.version>
|
||||||
<httpclient5-fluent.version>5.2</httpclient5-fluent.version>
|
<httpclient5-fluent.version>5.2.2</httpclient5-fluent.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;
|
||||||
|
@ -25,6 +25,7 @@ import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBu
|
|||||||
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
|
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
|
||||||
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
|
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
|
||||||
|
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
|
||||||
import org.apache.hc.core5.http.HttpHost;
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
|
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
|
||||||
@ -33,13 +34,10 @@ import org.apache.hc.core5.http.protocol.HttpContext;
|
|||||||
import org.apache.hc.core5.reactor.IOReactorConfig;
|
import org.apache.hc.core5.reactor.IOReactorConfig;
|
||||||
import org.apache.hc.core5.ssl.SSLContexts;
|
import org.apache.hc.core5.ssl.SSLContexts;
|
||||||
import org.apache.hc.core5.ssl.TrustStrategy;
|
import org.apache.hc.core5.ssl.TrustStrategy;
|
||||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
|
||||||
import org.junit.jupiter.api.Test;
|
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";
|
||||||
@ -122,7 +120,7 @@ class HttpAsyncClientLiveTest extends GetRequestMockServer {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
final TlsStrategy tlsStrategy = ClientTlsStrategyBuilder.create()
|
final TlsStrategy tlsStrategy = ClientTlsStrategyBuilder.create()
|
||||||
.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
|
.setHostnameVerifier(NoopHostnameVerifier.INSTANCE)
|
||||||
.setSslContext(sslContext)
|
.setSslContext(sslContext)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -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>
|
||||||
|
@ -7,14 +7,14 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
public class CustomWebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
@Autowired private RestAuthenticationEntryPoint authenticationEntryPoint;
|
@Autowired private RestAuthenticationEntryPoint authenticationEntryPoint;
|
||||||
|
|
||||||
@ -27,8 +27,8 @@ public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAda
|
|||||||
.authorities("ROLE_USER");
|
.authorities("ROLE_USER");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Bean
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers("/securityNone")
|
.antMatchers("/securityNone")
|
||||||
@ -40,6 +40,8 @@ public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAda
|
|||||||
.authenticationEntryPoint(authenticationEntryPoint);
|
.authenticationEntryPoint(authenticationEntryPoint);
|
||||||
|
|
||||||
http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class);
|
http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class);
|
||||||
|
|
||||||
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -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,13 +52,19 @@
|
|||||||
<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>
|
||||||
<jna.version>5.7.0</jna.version>
|
<jna.version>5.7.0</jna.version>
|
||||||
<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.19.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>
|
@ -181,8 +181,9 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
<jackson.version>2.13.4</jackson.version>
|
||||||
<kafka.version>3.4.0</kafka.version>
|
<kafka.version>3.4.0</kafka.version>
|
||||||
<testcontainers-kafka.version>1.15.3</testcontainers-kafka.version>
|
<testcontainers-kafka.version>1.19.3</testcontainers-kafka.version>
|
||||||
<testcontainers-jupiter.version>1.15.3</testcontainers-jupiter.version>
|
<testcontainers-jupiter.version>1.15.3</testcontainers-jupiter.version>
|
||||||
<flink.version>1.16.1</flink.version>
|
<flink.version>1.16.1</flink.version>
|
||||||
<awaitility.version>3.0.0</awaitility.version>
|
<awaitility.version>3.0.0</awaitility.version>
|
||||||
|
@ -29,7 +29,7 @@ import java.util.List;
|
|||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class BackupCreatorIntegrationTest {
|
public class BackupCreatorUnitTest {
|
||||||
public static ObjectMapper mapper;
|
public static ObjectMapper mapper;
|
||||||
|
|
||||||
@Before
|
@Before
|
@ -10,7 +10,7 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class WordCapitalizerIntegrationTest {
|
public class WordCapitalizerUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenDataSet_whenExecuteWordCapitalizer_thenReturnCapitalizedWords() throws Exception {
|
public void givenDataSet_whenExecuteWordCapitalizer_thenReturnCapitalizedWords() throws Exception {
|
@ -21,8 +21,7 @@ import org.junit.Ignore;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class KafkaStreamsLiveTest {
|
public class KafkaStreamsLiveTest {
|
||||||
private String bootstrapServers = "localhost:9092";
|
private final String bootstrapServers = "localhost:9092";
|
||||||
private Path stateDirectory;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("it needs to have kafka broker running on local")
|
@Ignore("it needs to have kafka broker running on local")
|
||||||
@ -44,8 +43,8 @@ public class KafkaStreamsLiveTest {
|
|||||||
|
|
||||||
// Use a temporary directory for storing state, which will be automatically removed after the test.
|
// Use a temporary directory for storing state, which will be automatically removed after the test.
|
||||||
try {
|
try {
|
||||||
this.stateDirectory = Files.createTempDirectory("kafka-streams");
|
Path stateDirectory = Files.createTempDirectory("kafka-streams");
|
||||||
streamsConfiguration.put(StreamsConfig.STATE_DIR_CONFIG, this.stateDirectory.toAbsolutePath()
|
streamsConfiguration.put(StreamsConfig.STATE_DIR_CONFIG, stateDirectory.toAbsolutePath()
|
||||||
.toString());
|
.toString());
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new UncheckedIOException("Cannot create temporary directory", e);
|
throw new UncheckedIOException("Cannot create temporary directory", e);
|
||||||
|
@ -191,7 +191,7 @@
|
|||||||
<bval.version>2.0.6</bval.version>
|
<bval.version>2.0.6</bval.version>
|
||||||
<javax.validation.validation-api.version>2.0.1.Final</javax.validation.validation-api.version>
|
<javax.validation.validation-api.version>2.0.1.Final</javax.validation.validation-api.version>
|
||||||
<meecrowave-junit.version>1.2.15</meecrowave-junit.version>
|
<meecrowave-junit.version>1.2.15</meecrowave-junit.version>
|
||||||
<okhttp.version>3.10.0</okhttp.version>
|
<okhttp.version>4.12.0</okhttp.version>
|
||||||
<meecrowave-jpa.version>1.2.15</meecrowave-jpa.version>
|
<meecrowave-jpa.version>1.2.15</meecrowave-jpa.version>
|
||||||
<meecrowave-core.version>1.2.15</meecrowave-core.version>
|
<meecrowave-core.version>1.2.15</meecrowave-core.version>
|
||||||
<meecrowave-maven-plugin.version>1.2.15</meecrowave-maven-plugin.version>
|
<meecrowave-maven-plugin.version>1.2.15</meecrowave-maven-plugin.version>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>apache-poi-3</artifactId>
|
<artifactId>apache-poi-3</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
## ASM
|
|
||||||
|
|
||||||
This module contains articles about ASM
|
|
||||||
|
|
||||||
### Relevant Articles:
|
|
||||||
|
|
||||||
- [A Guide to Java Bytecode Manipulation with ASM](https://www.baeldung.com/java-asm)
|
|
54
asm/pom.xml
54
asm/pom.xml
@ -1,54 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<groupId>com.baeldung.examples</groupId>
|
|
||||||
<artifactId>asm</artifactId>
|
|
||||||
<version>1.0</version>
|
|
||||||
<name>asm</name>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>com.baeldung</groupId>
|
|
||||||
<artifactId>parent-modules</artifactId>
|
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ow2.asm</groupId>
|
|
||||||
<artifactId>asm</artifactId>
|
|
||||||
<version>${asm.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ow2.asm</groupId>
|
|
||||||
<artifactId>asm-util</artifactId>
|
|
||||||
<version>${asm.version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
<version>${maven-jar-plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifestEntries>
|
|
||||||
<Premain-Class>
|
|
||||||
com.baeldung.examples.asm.instrumentation.Premain
|
|
||||||
</Premain-Class>
|
|
||||||
</manifestEntries>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<asm.version>5.2</asm.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
</project>
|
|
@ -1,7 +1,7 @@
|
|||||||
<?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>aws-dynamodb</artifactId>
|
<artifactId>aws-dynamodb</artifactId>
|
||||||
<version>0.1.0-SNAPSHOT</version>
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
@ -40,22 +40,6 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
|
||||||
<version>${maven-shade-plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>shade</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
@ -79,7 +63,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>
|
||||||
|
@ -5,6 +5,5 @@ This module contains articles about various Amazon Web Services (AWS) such as EC
|
|||||||
### Relevant articles
|
### Relevant articles
|
||||||
|
|
||||||
- [Managing EC2 Instances in Java](https://www.baeldung.com/ec2-java)
|
- [Managing EC2 Instances in Java](https://www.baeldung.com/ec2-java)
|
||||||
- [Integration Testing with a Local DynamoDB Instance](https://www.baeldung.com/dynamodb-local-integration-tests)
|
|
||||||
- [Managing Amazon SQS Queues in Java](https://www.baeldung.com/aws-queues-java)
|
- [Managing Amazon SQS Queues in Java](https://www.baeldung.com/aws-queues-java)
|
||||||
- [Guide to AWS Aurora RDS with Java](https://www.baeldung.com/aws-aurora-rds-java)
|
- [Guide to AWS Aurora RDS with Java](https://www.baeldung.com/aws-aurora-rds-java)
|
||||||
|
@ -34,22 +34,6 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
|
||||||
<version>${maven-shade-plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>shade</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
@ -73,9 +57,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>
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.projectreactor</groupId>
|
<groupId>io.projectreactor</groupId>
|
||||||
<artifactId>reactor-test</artifactId>
|
<artifactId>reactor-test</artifactId>
|
||||||
|
<version>${reactor.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -94,6 +95,7 @@
|
|||||||
<spring.version>2.2.1.RELEASE</spring.version>
|
<spring.version>2.2.1.RELEASE</spring.version>
|
||||||
<awssdk.version>2.17.283</awssdk.version>
|
<awssdk.version>2.17.283</awssdk.version>
|
||||||
<lombok.version>1.18.20</lombok.version>
|
<lombok.version>1.18.20</lombok.version>
|
||||||
|
<reactor.version>3.6.0</reactor.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -1,43 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<artifactId>aws-s3-update-object</artifactId>
|
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
|
||||||
<name>aws-s3-update-object</name>
|
|
||||||
<description>Project demonstrating overwriting of S3 objects</description>
|
|
||||||
<parent>
|
|
||||||
<groupId>com.baeldung</groupId>
|
|
||||||
<artifactId>parent-boot-2</artifactId>
|
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
|
||||||
<relativePath>../../parent-boot-2</relativePath>
|
|
||||||
</parent>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.amazonaws</groupId>
|
|
||||||
<artifactId>aws-java-sdk</artifactId>
|
|
||||||
<version>${aws-java-sdk-version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
<properties>
|
|
||||||
<aws-java-sdk-version>1.12.523</aws-java-sdk-version>
|
|
||||||
</properties>
|
|
||||||
</project>
|
|
@ -1,13 +0,0 @@
|
|||||||
package com.baeldung.awss3updateobject;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
public class AwsS3UpdateObjectApplication {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(AwsS3UpdateObjectApplication.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package com.baeldung.awss3updateobject.controller;
|
|
||||||
|
|
||||||
import com.baeldung.awss3updateobject.service.FileService;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("api/v1/file")
|
|
||||||
public class FileController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
FileService fileService;
|
|
||||||
|
|
||||||
@PostMapping("/upload")
|
|
||||||
public String uploadFile(@RequestParam("file") MultipartFile multipartFile) throws Exception {
|
|
||||||
return this.fileService.uploadFile(multipartFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/update")
|
|
||||||
public String updateFile(@RequestParam("file") MultipartFile multipartFile, @RequestParam("filePath") String exitingFilePath) throws Exception {
|
|
||||||
return this.fileService.updateFile(multipartFile, exitingFilePath);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
package com.baeldung.awss3updateobject.service;
|
|
||||||
|
|
||||||
import com.amazonaws.auth.AWSCredentials;
|
|
||||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
|
||||||
import com.amazonaws.auth.BasicAWSCredentials;
|
|
||||||
import com.amazonaws.regions.Regions;
|
|
||||||
import com.amazonaws.services.s3.AmazonS3;
|
|
||||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
|
||||||
import com.amazonaws.services.s3.model.*;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class FileService {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(FileService.class);
|
|
||||||
|
|
||||||
public AmazonS3 amazonS3;
|
|
||||||
|
|
||||||
@Value("${aws.s3bucket}")
|
|
||||||
public String awsS3Bucket;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
private void init(){
|
|
||||||
AWSCredentials credentials = new BasicAWSCredentials(
|
|
||||||
"AWS AccessKey",
|
|
||||||
"AWS secretKey"
|
|
||||||
);
|
|
||||||
this.amazonS3 = AmazonS3ClientBuilder.standard()
|
|
||||||
.withRegion(Regions.fromName("us-east-1"))
|
|
||||||
.withCredentials(new AWSStaticCredentialsProvider(credentials))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String uploadFile(MultipartFile multipartFile) throws Exception {
|
|
||||||
String key = "/documents/" + multipartFile.getOriginalFilename();
|
|
||||||
return this.uploadDocument(this.awsS3Bucket, key, multipartFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String updateFile(MultipartFile multipartFile, String key) throws Exception {
|
|
||||||
return this.uploadDocument(this.awsS3Bucket, key, multipartFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String uploadDocument(String s3bucket, String key, MultipartFile multipartFile) throws Exception {
|
|
||||||
try {
|
|
||||||
ObjectMetadata metadata = new ObjectMetadata();
|
|
||||||
metadata.setContentType(multipartFile.getContentType());
|
|
||||||
Map<String, String> attributes = new HashMap<>();
|
|
||||||
attributes.put("document-content-size", String.valueOf(multipartFile.getSize()));
|
|
||||||
metadata.setUserMetadata(attributes);
|
|
||||||
InputStream documentStream = multipartFile.getInputStream();
|
|
||||||
PutObjectResult putObjectResult = this.amazonS3.putObject(new PutObjectRequest(s3bucket, key, documentStream, metadata));
|
|
||||||
|
|
||||||
S3Object s3Object = this.amazonS3.getObject(s3bucket, key);
|
|
||||||
logger.info("Last Modified: " + s3Object.getObjectMetadata().getLastModified());
|
|
||||||
return key;
|
|
||||||
} catch (AmazonS3Exception ex) {
|
|
||||||
if (ex.getErrorCode().equalsIgnoreCase("NoSuchBucket")) {
|
|
||||||
String msg = String.format("No bucket found with name %s", s3bucket);
|
|
||||||
throw new Exception(msg);
|
|
||||||
} else if (ex.getErrorCode().equalsIgnoreCase("AccessDenied")) {
|
|
||||||
String msg = String.format("Access denied to S3 bucket %s", s3bucket);
|
|
||||||
throw new Exception(msg);
|
|
||||||
}
|
|
||||||
throw ex;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
String msg = String.format("Error saving file %s to AWS S3 bucket %s", key, s3bucket);
|
|
||||||
throw new Exception(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
aws.s3bucket=baeldung-documents;
|
|
@ -1,62 +0,0 @@
|
|||||||
package com.baeldung.awss3updateobject.controller;
|
|
||||||
|
|
||||||
import com.baeldung.awss3updateobject.service.FileService;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.mockito.InjectMocks;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.springframework.mock.web.MockMultipartFile;
|
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
|
||||||
|
|
||||||
public class FileControllerUnitTest {
|
|
||||||
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private FileService fileService;
|
|
||||||
|
|
||||||
@InjectMocks
|
|
||||||
private FileController fileController;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.openMocks(this);
|
|
||||||
this.mockMvc = MockMvcBuilders.standaloneSetup(fileController).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenValidMultipartFile_whenUploadedViaEndpoint_thenCorrectPathIsReturned() throws Exception {
|
|
||||||
MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt", "text/plain", "sample file content".getBytes());
|
|
||||||
String expectedResult = "File Uploaded Successfully";
|
|
||||||
|
|
||||||
when(fileService.uploadFile(multipartFile)).thenReturn(expectedResult);
|
|
||||||
|
|
||||||
mockMvc.perform(multipart("/api/v1/file/upload").file(multipartFile))
|
|
||||||
.andExpect(status().isOk())
|
|
||||||
.andExpect(content().string(expectedResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenValidMultipartFileAndExistingPath_whenUpdatedViaEndpoint_thenSamePathIsReturned() throws Exception {
|
|
||||||
MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt", "text/plain", "updated file content".getBytes());
|
|
||||||
String filePath = "some/path/to/file";
|
|
||||||
String expectedResult = "File Updated Successfully";
|
|
||||||
|
|
||||||
when(fileService.updateFile(multipartFile, filePath)).thenReturn(expectedResult);
|
|
||||||
|
|
||||||
mockMvc.perform(multipart("/api/v1/file/update")
|
|
||||||
.file(multipartFile)
|
|
||||||
.param("filePath", filePath))
|
|
||||||
.andExpect(status().isOk())
|
|
||||||
.andExpect(content().string(expectedResult));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
package com.baeldung.awss3updateobject.service;
|
|
||||||
|
|
||||||
import com.amazonaws.services.s3.AmazonS3;
|
|
||||||
import com.amazonaws.services.s3.model.AmazonS3Exception;
|
|
||||||
import com.amazonaws.services.s3.model.PutObjectRequest;
|
|
||||||
import com.amazonaws.services.s3.model.S3Object;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.mockito.InjectMocks;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
public class FileServiceUnitTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private AmazonS3 amazonS3;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private MultipartFile multipartFile;
|
|
||||||
|
|
||||||
@InjectMocks
|
|
||||||
private FileService fileService;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() {
|
|
||||||
MockitoAnnotations.openMocks(this);
|
|
||||||
fileService = new FileService();
|
|
||||||
fileService.awsS3Bucket = "test-bucket";
|
|
||||||
fileService.amazonS3 = amazonS3;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenValidFile_whenUploaded_thenKeyMatchesDocumentPath() throws Exception {
|
|
||||||
when(multipartFile.getName()).thenReturn("testFile");
|
|
||||||
when(multipartFile.getOriginalFilename()).thenReturn("testFile");
|
|
||||||
when(multipartFile.getContentType()).thenReturn("application/pdf");
|
|
||||||
when(multipartFile.getSize()).thenReturn(1024L);
|
|
||||||
when(multipartFile.getInputStream()).thenReturn(mock(InputStream.class));
|
|
||||||
|
|
||||||
S3Object s3Object = new S3Object();
|
|
||||||
when(amazonS3.putObject(any())).thenReturn(null);
|
|
||||||
when(amazonS3.getObject(anyString(), anyString())).thenReturn(s3Object);
|
|
||||||
|
|
||||||
String key = fileService.uploadFile(multipartFile);
|
|
||||||
|
|
||||||
assertEquals("/documents/testFile", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenValidFile_whenUploadFailsDueToNoBucket_thenExceptionIsThrown() throws Exception {
|
|
||||||
when(multipartFile.getName()).thenReturn("testFile");
|
|
||||||
when(multipartFile.getOriginalFilename()).thenReturn("testFile");
|
|
||||||
when(multipartFile.getContentType()).thenReturn("application/pdf");
|
|
||||||
when(multipartFile.getSize()).thenReturn(1024L);
|
|
||||||
when(multipartFile.getInputStream()).thenReturn(mock(InputStream.class));
|
|
||||||
|
|
||||||
AmazonS3Exception exception = new AmazonS3Exception("Test exception");
|
|
||||||
exception.setErrorCode("NoSuchBucket");
|
|
||||||
when(amazonS3.putObject(any(PutObjectRequest.class))).thenThrow(exception);
|
|
||||||
|
|
||||||
assertThrows(Exception.class, () -> fileService.uploadFile(multipartFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenExistingFile_whenUpdated_thenSameKeyIsReturned() throws Exception {
|
|
||||||
when(multipartFile.getName()).thenReturn("testFile");
|
|
||||||
when(multipartFile.getContentType()).thenReturn("application/pdf");
|
|
||||||
when(multipartFile.getSize()).thenReturn(1024L);
|
|
||||||
when(multipartFile.getInputStream()).thenReturn(mock(InputStream.class));
|
|
||||||
|
|
||||||
S3Object s3Object = new S3Object();
|
|
||||||
when(amazonS3.putObject(any(PutObjectRequest.class))).thenReturn(null);
|
|
||||||
when(amazonS3.getObject(anyString(), anyString())).thenReturn(s3Object);
|
|
||||||
|
|
||||||
String key = "/documents/existingFile";
|
|
||||||
String resultKey = fileService.updateFile(multipartFile, key);
|
|
||||||
|
|
||||||
assertEquals(key, resultKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenFileWithIOException_whenUpdated_thenExceptionIsThrown() throws Exception {
|
|
||||||
when(multipartFile.getName()).thenReturn("testFile");
|
|
||||||
when(multipartFile.getContentType()).thenReturn("application/pdf");
|
|
||||||
when(multipartFile.getSize()).thenReturn(1024L);
|
|
||||||
when(multipartFile.getInputStream()).thenThrow(new IOException("Test IO Exception"));
|
|
||||||
|
|
||||||
assertThrows(Exception.class, () -> fileService.updateFile(multipartFile, "/documents/existingFile"));
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,3 +11,4 @@ This module contains articles about Simple Storage Service (S3) on AWS
|
|||||||
- [Listing All AWS S3 Objects in a Bucket Using Java](https://www.baeldung.com/java-aws-s3-list-bucket-objects)
|
- [Listing All AWS S3 Objects in a Bucket Using Java](https://www.baeldung.com/java-aws-s3-list-bucket-objects)
|
||||||
- [Update an Existing Amazon S3 Object Using Java](https://www.baeldung.com/java-update-amazon-s3-object)
|
- [Update an Existing Amazon S3 Object Using Java](https://www.baeldung.com/java-update-amazon-s3-object)
|
||||||
- [How To Rename Files and Folders in Amazon S3](https://www.baeldung.com/java-amazon-s3-rename-files-folders)
|
- [How To Rename Files and Folders in Amazon S3](https://www.baeldung.com/java-amazon-s3-rename-files-folders)
|
||||||
|
- [Update an Existing Amazon S3 Object Using Java](https://www.baeldung.com/java-update-amazon-s3-object)
|
||||||
|
@ -39,27 +39,6 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
|
||||||
<version>${maven-shade-plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>shade</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<aws.java.sdk.version>2.20.52</aws.java.sdk.version>
|
<aws.java.sdk.version>2.20.52</aws.java.sdk.version>
|
||||||
<commons-codec-version>1.10.L001</commons-codec-version>
|
<commons-codec-version>1.10.L001</commons-codec-version>
|
||||||
|
@ -45,6 +45,13 @@ public class S3Application {
|
|||||||
new File("/Users/user/Document/hello.txt")
|
new File("/Users/user/Document/hello.txt")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
s3Service.updateObject(
|
||||||
|
AWS_BUCKET,
|
||||||
|
"Document/hello2.txt",
|
||||||
|
new File("/Users/user/Document/hello2.txt")
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
//listing objects
|
//listing objects
|
||||||
s3Service.listObjects(AWS_BUCKET);
|
s3Service.listObjects(AWS_BUCKET);
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import software.amazon.awssdk.services.s3.model.GetObjectRequest;
|
|||||||
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
|
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
|
||||||
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
|
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
|
||||||
import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
|
import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
|
||||||
|
import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
|
||||||
import software.amazon.awssdk.services.s3.model.ListBucketsResponse;
|
import software.amazon.awssdk.services.s3.model.ListBucketsResponse;
|
||||||
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
|
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
|
||||||
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
|
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
|
||||||
@ -99,7 +100,13 @@ class S3Service {
|
|||||||
.key(key)
|
.key(key)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return s3Client.putObject(request, Path.of(file.toURI()) );
|
|
||||||
|
return s3Client.putObject(request, Path.of(file.toURI()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//updating object
|
||||||
|
public PutObjectResponse updateObject(String bucketName, String key, java.io.File file) {
|
||||||
|
return this.putObject(bucketName, key, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
//listing objects
|
//listing objects
|
||||||
@ -110,6 +117,7 @@ class S3Service {
|
|||||||
ListObjectsV2Response listObjectsV2Response = s3Client.listObjectsV2(listObjectsV2Request);
|
ListObjectsV2Response listObjectsV2Response = s3Client.listObjectsV2(listObjectsV2Request);
|
||||||
|
|
||||||
for(S3Object os : listObjectsV2Response.contents()) {
|
for(S3Object os : listObjectsV2Response.contents()) {
|
||||||
|
|
||||||
System.out.println(os.key());
|
System.out.println(os.key());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import software.amazon.awssdk.services.s3.S3Client;
|
import software.amazon.awssdk.services.s3.S3Client;
|
||||||
@ -23,6 +25,7 @@ import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
|
|||||||
import software.amazon.awssdk.services.s3.model.ListBucketsResponse;
|
import software.amazon.awssdk.services.s3.model.ListBucketsResponse;
|
||||||
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
|
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
|
||||||
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
|
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
|
||||||
|
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
||||||
|
|
||||||
class S3ServiceIntegrationTest {
|
class S3ServiceIntegrationTest {
|
||||||
|
|
||||||
@ -38,6 +41,8 @@ class S3ServiceIntegrationTest {
|
|||||||
|
|
||||||
private final String AWS_BUCKET = "baeldung-tutorial-s3";
|
private final String AWS_BUCKET = "baeldung-tutorial-s3";
|
||||||
|
|
||||||
|
private File file = new File("/Users/user/Document/hello2.txt");
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.openMocks(this);
|
MockitoAnnotations.openMocks(this);
|
||||||
@ -75,6 +80,17 @@ class S3ServiceIntegrationTest {
|
|||||||
verify(s3Client).createBucket(bucketRequest);
|
verify(s3Client).createBucket(bucketRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenVerifyingUploadOfS3Object_thenCorrect() {
|
||||||
|
PutObjectRequest request = PutObjectRequest.builder()
|
||||||
|
.bucket(BUCKET_NAME)
|
||||||
|
.key(KEY_NAME)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
s3Service.putObject(BUCKET_NAME, KEY_NAME, file);
|
||||||
|
verify(s3Client).putObject(request, Path.of(file.toURI()) );
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void whenVerifyingListBuckets_thenCorrect() {
|
void whenVerifyingListBuckets_thenCorrect() {
|
||||||
when(s3Client.listBuckets()).thenReturn(ListBucketsResponse.builder().buckets(Collections.emptyList()).build());
|
when(s3Client.listBuckets()).thenReturn(ListBucketsResponse.builder().buckets(Collections.emptyList()).build());
|
||||||
|
@ -5,6 +5,13 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>aws-modules</artifactId>
|
<artifactId>aws-modules</artifactId>
|
||||||
<name>aws-modules</name>
|
<name>aws-modules</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.amazonaws</groupId>
|
<groupId>com.amazonaws</groupId>
|
||||||
@ -15,12 +22,6 @@
|
|||||||
</dependencies>
|
</dependencies>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>com.baeldung</groupId>
|
|
||||||
<artifactId>parent-modules</artifactId>
|
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>aws-app-sync</module>
|
<module>aws-app-sync</module>
|
||||||
<module>aws-dynamodb</module>
|
<module>aws-dynamodb</module>
|
||||||
@ -28,7 +29,6 @@
|
|||||||
<module>aws-miscellaneous</module>
|
<module>aws-miscellaneous</module>
|
||||||
<module>aws-reactive</module>
|
<module>aws-reactive</module>
|
||||||
<module>aws-s3</module>
|
<module>aws-s3</module>
|
||||||
<module>aws-s3-update-object</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -167,7 +167,6 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<commons-lang3.version>3.9</commons-lang3.version>
|
<commons-lang3.version>3.9</commons-lang3.version>
|
||||||
<java.version>1.8</java.version>
|
|
||||||
<logback.version>1.2.3</logback.version>
|
<logback.version>1.2.3</logback.version>
|
||||||
</properties>
|
</properties>
|
||||||
</project>
|
</project>
|
||||||
|
@ -13,6 +13,13 @@
|
|||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
<pluginRepositories>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>groovy-plugins-release</id>
|
||||||
|
<url>https://groovy.jfrog.io/artifactory/plugins-release-local</url>
|
||||||
|
</pluginRepository>
|
||||||
|
</pluginRepositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
@ -156,8 +163,8 @@
|
|||||||
<groovy-wslite.version>1.1.3</groovy-wslite.version>
|
<groovy-wslite.version>1.1.3</groovy-wslite.version>
|
||||||
<assembly.plugin.version>3.4.2</assembly.plugin.version>
|
<assembly.plugin.version>3.4.2</assembly.plugin.version>
|
||||||
<compiler.plugin.version>3.8.1</compiler.plugin.version>
|
<compiler.plugin.version>3.8.1</compiler.plugin.version>
|
||||||
<groovy.compiler.version>3.7.0</groovy.compiler.version>
|
<groovy.compiler.version>3.9.0</groovy.compiler.version>
|
||||||
<groovy-eclipse-batch.version>3.0.8-01</groovy-eclipse-batch.version>
|
<groovy-eclipse-batch.version>3.0.9-03</groovy-eclipse-batch.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -5,7 +5,6 @@ This module contains articles about Java 11 core features
|
|||||||
### Relevant articles
|
### Relevant articles
|
||||||
- [Guide To Java 8 Optional](https://www.baeldung.com/java-optional)
|
- [Guide To Java 8 Optional](https://www.baeldung.com/java-optional)
|
||||||
- [Guide to Java Reflection](http://www.baeldung.com/java-reflection)
|
- [Guide to Java Reflection](http://www.baeldung.com/java-reflection)
|
||||||
- [Guide to Java 8’s Collectors](https://www.baeldung.com/java-8-collectors)
|
|
||||||
- [New Features in Java 11](https://www.baeldung.com/java-11-new-features)
|
- [New Features in Java 11](https://www.baeldung.com/java-11-new-features)
|
||||||
- [Getting the Java Version at Runtime](https://www.baeldung.com/get-java-version-runtime)
|
- [Getting the Java Version at Runtime](https://www.baeldung.com/get-java-version-runtime)
|
||||||
- [Invoking a SOAP Web Service in Java](https://www.baeldung.com/java-soap-web-service)
|
- [Invoking a SOAP Web Service in Java](https://www.baeldung.com/java-soap-web-service)
|
||||||
|
@ -1,237 +0,0 @@
|
|||||||
package com.baeldung.collectors;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.BinaryOperator;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collector;
|
|
||||||
|
|
||||||
import static com.google.common.collect.Sets.newHashSet;
|
|
||||||
import static java.util.stream.Collectors.*;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
||||||
|
|
||||||
public class Java8CollectorsUnitTest {
|
|
||||||
|
|
||||||
private final List<String> givenList = Arrays.asList("a", "bb", "ccc", "dd");
|
|
||||||
private final List<String> listWithDuplicates = Arrays.asList("a", "bb", "c", "d", "bb");
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingToList_shouldCollectToList() throws Exception {
|
|
||||||
final List<String> result = givenList.stream().collect(toList());
|
|
||||||
|
|
||||||
assertThat(result).containsAll(givenList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingToUnmodifiableList_shouldCollectToUnmodifiableList() {
|
|
||||||
final List<String> result = givenList.stream().collect(toUnmodifiableList());
|
|
||||||
|
|
||||||
assertThatThrownBy(() -> result.add("foo"))
|
|
||||||
.isInstanceOf(UnsupportedOperationException.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingToSet_shouldCollectToSet() throws Exception {
|
|
||||||
final Set<String> result = givenList.stream().collect(toSet());
|
|
||||||
|
|
||||||
assertThat(result).containsAll(givenList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingToUnmodifiableSet_shouldCollectToUnmodifiableSet() {
|
|
||||||
final Set<String> result = givenList.stream().collect(toUnmodifiableSet());
|
|
||||||
|
|
||||||
assertThatThrownBy(() -> result.add("foo"))
|
|
||||||
.isInstanceOf(UnsupportedOperationException.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenContainsDuplicateElements_whenCollectingToSet_shouldAddDuplicateElementsOnlyOnce() throws Exception {
|
|
||||||
final Set<String> result = listWithDuplicates.stream().collect(toSet());
|
|
||||||
|
|
||||||
assertThat(result).hasSize(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingToCollection_shouldCollectToCollection() throws Exception {
|
|
||||||
final List<String> result = givenList.stream().collect(toCollection(LinkedList::new));
|
|
||||||
|
|
||||||
assertThat(result).containsAll(givenList).isInstanceOf(LinkedList.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingToImmutableCollection_shouldThrowException() throws Exception {
|
|
||||||
assertThatThrownBy(() -> {
|
|
||||||
givenList.stream().collect(toCollection(ImmutableList::of));
|
|
||||||
}).isInstanceOf(UnsupportedOperationException.class);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingToMap_shouldCollectToMap() throws Exception {
|
|
||||||
final Map<String, Integer> result = givenList.stream().collect(toMap(Function.identity(), String::length));
|
|
||||||
|
|
||||||
assertThat(result).containsEntry("a", 1).containsEntry("bb", 2).containsEntry("ccc", 3).containsEntry("dd", 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingToUnmodifiableMap_shouldCollectToUnmodifiableMap() {
|
|
||||||
final Map<String, Integer> result = givenList.stream()
|
|
||||||
.collect(toUnmodifiableMap(Function.identity(), String::length));
|
|
||||||
|
|
||||||
assertThatThrownBy(() -> result.put("foo", 3))
|
|
||||||
.isInstanceOf(UnsupportedOperationException.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingToMapwWithDuplicates_shouldCollectToMapMergingTheIdenticalItems() throws Exception {
|
|
||||||
final Map<String, Integer> result = listWithDuplicates.stream().collect(
|
|
||||||
toMap(
|
|
||||||
Function.identity(),
|
|
||||||
String::length,
|
|
||||||
(item, identicalItem) -> item
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
assertThat(result).containsEntry("a", 1).containsEntry("bb", 2).containsEntry("c", 1).containsEntry("d", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenContainsDuplicateElements_whenCollectingToMap_shouldThrowException() throws Exception {
|
|
||||||
assertThatThrownBy(() -> {
|
|
||||||
listWithDuplicates.stream().collect(toMap(Function.identity(), String::length));
|
|
||||||
}).isInstanceOf(IllegalStateException.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCollectingAndThen_shouldCollect() throws Exception {
|
|
||||||
final List<String> result = givenList.stream().collect(collectingAndThen(toList(), ImmutableList::copyOf));
|
|
||||||
|
|
||||||
assertThat(result).containsAll(givenList).isInstanceOf(ImmutableList.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenJoining_shouldJoin() throws Exception {
|
|
||||||
final String result = givenList.stream().collect(joining());
|
|
||||||
|
|
||||||
assertThat(result).isEqualTo("abbcccdd");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenJoiningWithSeparator_shouldJoinWithSeparator() throws Exception {
|
|
||||||
final String result = givenList.stream().collect(joining(" "));
|
|
||||||
|
|
||||||
assertThat(result).isEqualTo("a bb ccc dd");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenJoiningWithSeparatorAndPrefixAndPostfix_shouldJoinWithSeparatorPrePost() throws Exception {
|
|
||||||
final String result = givenList.stream().collect(joining(" ", "PRE-", "-POST"));
|
|
||||||
|
|
||||||
assertThat(result).isEqualTo("PRE-a bb ccc dd-POST");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenPartitioningBy_shouldPartition() throws Exception {
|
|
||||||
final Map<Boolean, List<String>> result = givenList.stream().collect(partitioningBy(s -> s.length() > 2));
|
|
||||||
|
|
||||||
assertThat(result).containsKeys(true, false).satisfies(booleanListMap -> {
|
|
||||||
assertThat(booleanListMap.get(true)).contains("ccc");
|
|
||||||
|
|
||||||
assertThat(booleanListMap.get(false)).contains("a", "bb", "dd");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCounting_shouldCount() throws Exception {
|
|
||||||
final Long result = givenList.stream().collect(counting());
|
|
||||||
|
|
||||||
assertThat(result).isEqualTo(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenSummarizing_shouldSummarize() throws Exception {
|
|
||||||
final DoubleSummaryStatistics result = givenList.stream().collect(summarizingDouble(String::length));
|
|
||||||
|
|
||||||
assertThat(result.getAverage()).isEqualTo(2);
|
|
||||||
assertThat(result.getCount()).isEqualTo(4);
|
|
||||||
assertThat(result.getMax()).isEqualTo(3);
|
|
||||||
assertThat(result.getMin()).isEqualTo(1);
|
|
||||||
assertThat(result.getSum()).isEqualTo(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenAveraging_shouldAverage() throws Exception {
|
|
||||||
final Double result = givenList.stream().collect(averagingDouble(String::length));
|
|
||||||
|
|
||||||
assertThat(result).isEqualTo(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenSumming_shouldSum() throws Exception {
|
|
||||||
final Double result = givenList.stream().filter(i -> true).collect(summingDouble(String::length));
|
|
||||||
|
|
||||||
assertThat(result).isEqualTo(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenMaxingBy_shouldMaxBy() throws Exception {
|
|
||||||
final Optional<String> result = givenList.stream().collect(maxBy(Comparator.naturalOrder()));
|
|
||||||
|
|
||||||
assertThat(result).isPresent().hasValue("dd");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenGroupingBy_shouldGroupBy() throws Exception {
|
|
||||||
final Map<Integer, Set<String>> result = givenList.stream().collect(groupingBy(String::length, toSet()));
|
|
||||||
|
|
||||||
assertThat(result).containsEntry(1, newHashSet("a")).containsEntry(2, newHashSet("bb", "dd")).containsEntry(3, newHashSet("ccc"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void whenCreatingCustomCollector_shouldCollect() throws Exception {
|
|
||||||
final ImmutableSet<String> result = givenList.stream().collect(toImmutableSet());
|
|
||||||
|
|
||||||
assertThat(result).isInstanceOf(ImmutableSet.class).contains("a", "bb", "ccc", "dd");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> ImmutableSetCollector<T> toImmutableSet() {
|
|
||||||
return new ImmutableSetCollector<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ImmutableSetCollector<T> implements Collector<T, ImmutableSet.Builder<T>, ImmutableSet<T>> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Supplier<ImmutableSet.Builder<T>> supplier() {
|
|
||||||
return ImmutableSet::builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BiConsumer<ImmutableSet.Builder<T>, T> accumulator() {
|
|
||||||
return ImmutableSet.Builder::add;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BinaryOperator<ImmutableSet.Builder<T>> combiner() {
|
|
||||||
return (left, right) -> left.addAll(right.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Function<ImmutableSet.Builder<T>, ImmutableSet<T>> finisher() {
|
|
||||||
return ImmutableSet.Builder::build;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Characteristics> characteristics() {
|
|
||||||
return Sets.immutableEnumSet(Characteristics.UNORDERED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -45,7 +45,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source.version>11</maven.compiler.source.version>
|
<maven.compiler.source.version>11</maven.compiler.source.version>
|
||||||
<maven.compiler.target.version>11</maven.compiler.target.version>
|
<maven.compiler.target.version>11</maven.compiler.target.version>
|
||||||
<jackson.version>2.14.1</jackson.version>
|
<jackson.version>2.16.0</jackson.version>
|
||||||
<gson.version>2.10</gson.version>
|
<gson.version>2.10</gson.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung.core-java-modules</groupId>
|
||||||
<artifactId>parent-modules</artifactId>
|
<artifactId>core-java-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -21,30 +21,4 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>${maven-compiler-plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<source>${maven.compiler.source.version}</source>
|
|
||||||
<target>${maven.compiler.target.version}</target>
|
|
||||||
<compilerArgs>--enable-preview</compilerArgs>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<argLine>--enable-preview</argLine>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source.version>12</maven.compiler.source.version>
|
|
||||||
<maven.compiler.target.version>12</maven.compiler.target.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -19,19 +19,6 @@ public class SwitchUnitTest {
|
|||||||
Assert.assertEquals(value, 2);
|
Assert.assertEquals(value, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void switchLocalVariable(){
|
|
||||||
var month = Month.AUG;
|
|
||||||
int i = switch (month){
|
|
||||||
case JAN,JUN, JUL -> 3;
|
|
||||||
case FEB,SEP, OCT, NOV, DEC -> 1;
|
|
||||||
case MAR,MAY, APR, AUG -> {
|
|
||||||
int j = month.toString().length() * 4;
|
|
||||||
break j;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Assert.assertEquals(12, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Month {JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC}
|
enum Month {JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC}
|
||||||
}
|
}
|
||||||
|
@ -8,39 +8,9 @@
|
|||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung.core-java-modules</groupId>
|
||||||
<artifactId>parent-modules</artifactId>
|
<artifactId>core-java-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>${maven-compiler-plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<source>${maven.compiler.source.version}</source>
|
|
||||||
<target>${maven.compiler.target.version}</target>
|
|
||||||
<release>13</release>
|
|
||||||
<compilerArgs>--enable-preview</compilerArgs>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<version>${surefire.plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<argLine>--enable-preview</argLine>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source.version>13</maven.compiler.source.version>
|
|
||||||
<maven.compiler.target.version>13</maven.compiler.target.version>
|
|
||||||
<surefire.plugin.version>3.0.0-M3</surefire.plugin.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -7,7 +7,6 @@ import org.junit.Test;
|
|||||||
public class SwitchExpressionsWithYieldUnitTest {
|
public class SwitchExpressionsWithYieldUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("preview")
|
|
||||||
public void whenSwitchingOnOperationSquareMe_thenWillReturnSquare() {
|
public void whenSwitchingOnOperationSquareMe_thenWillReturnSquare() {
|
||||||
var me = 4;
|
var me = 4;
|
||||||
var operation = "squareMe";
|
var operation = "squareMe";
|
||||||
|
@ -8,7 +8,6 @@ public class TextBlocksUnitTest {
|
|||||||
|
|
||||||
private static final String JSON_STRING = "{\r\n" + "\"name\" : \"Baeldung\",\r\n" + "\"website\" : \"https://www.%s.com/\"\r\n" + "}";
|
private static final String JSON_STRING = "{\r\n" + "\"name\" : \"Baeldung\",\r\n" + "\"website\" : \"https://www.%s.com/\"\r\n" + "}";
|
||||||
|
|
||||||
@SuppressWarnings("preview")
|
|
||||||
private static final String TEXT_BLOCK_JSON = """
|
private static final String TEXT_BLOCK_JSON = """
|
||||||
{
|
{
|
||||||
"name" : "Baeldung",
|
"name" : "Baeldung",
|
||||||
@ -25,7 +24,6 @@ public class TextBlocksUnitTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
@Test
|
@Test
|
||||||
public void whenTextBlocks_thenFormattedWorksAsFormat() {
|
public void whenTextBlocks_thenFormattedWorksAsFormat() {
|
||||||
assertThat(TEXT_BLOCK_JSON.formatted("baeldung")
|
assertThat(TEXT_BLOCK_JSON.formatted("baeldung")
|
||||||
|
@ -13,7 +13,6 @@ import org.junit.Test;
|
|||||||
public class SwitchExpressionsUnitTest {
|
public class SwitchExpressionsUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings ("preview")
|
|
||||||
public void whenSwitchingOverMonthJune_thenWillReturn3() {
|
public void whenSwitchingOverMonthJune_thenWillReturn3() {
|
||||||
|
|
||||||
var month = JUNE;
|
var month = JUNE;
|
||||||
@ -29,7 +28,6 @@ public class SwitchExpressionsUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings ("preview")
|
|
||||||
public void whenSwitchingOverMonthAugust_thenWillReturn24() {
|
public void whenSwitchingOverMonthAugust_thenWillReturn24() {
|
||||||
var month = AUGUST;
|
var month = AUGUST;
|
||||||
|
|
||||||
@ -47,7 +45,6 @@ public class SwitchExpressionsUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings ("preview")
|
|
||||||
public void whenSwitchingOverMonthJanuary_thenWillReturn3() {
|
public void whenSwitchingOverMonthJanuary_thenWillReturn3() {
|
||||||
|
|
||||||
Function<Month, Integer> func = (month) -> {
|
Function<Month, Integer> func = (month) -> {
|
||||||
@ -61,7 +58,6 @@ public class SwitchExpressionsUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings ("preview")
|
|
||||||
public void whenSwitchingOverMonthAugust_thenWillReturn2() {
|
public void whenSwitchingOverMonthAugust_thenWillReturn2() {
|
||||||
var month = AUGUST;
|
var month = AUGUST;
|
||||||
|
|
||||||
|
@ -8,10 +8,9 @@
|
|||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung.core-java-modules</groupId>
|
||||||
<artifactId>parent-modules</artifactId>
|
<artifactId>core-java-modules</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -27,33 +26,4 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>${maven-compiler-plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<release>${maven.compiler.release}</release>
|
|
||||||
<compilerArgs>--enable-preview</compilerArgs>
|
|
||||||
<source>14</source>
|
|
||||||
<target>14</target>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<version>${surefire.plugin.version}</version>
|
|
||||||
<configuration>
|
|
||||||
<argLine>--enable-preview</argLine>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.release>15</maven.compiler.release>
|
|
||||||
<surefire.plugin.version>3.0.0-M3</surefire.plugin.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -4,5 +4,4 @@
|
|||||||
- [Guide to mapMulti in Stream API](https://www.baeldung.com/java-mapmulti)
|
- [Guide to mapMulti in Stream API](https://www.baeldung.com/java-mapmulti)
|
||||||
- [Collecting Stream Elements into a List in Java](https://www.baeldung.com/java-stream-to-list-collecting)
|
- [Collecting Stream Elements into a List in Java](https://www.baeldung.com/java-stream-to-list-collecting)
|
||||||
- [New Features in Java 16](https://www.baeldung.com/java-16-new-features)
|
- [New Features in Java 16](https://www.baeldung.com/java-16-new-features)
|
||||||
- [Guide to Java 8 groupingBy Collector](https://www.baeldung.com/java-groupingby-collector)
|
|
||||||
- [Value-Based Classes in Java](https://www.baeldung.com/java-value-based-classes)
|
- [Value-Based Classes in Java](https://www.baeldung.com/java-value-based-classes)
|
||||||
|
@ -8,3 +8,5 @@
|
|||||||
- [Sealed Classes and Interfaces in Java](https://www.baeldung.com/java-sealed-classes-interfaces)
|
- [Sealed Classes and Interfaces in Java](https://www.baeldung.com/java-sealed-classes-interfaces)
|
||||||
- [Migrate From Java 8 to Java 17](https://www.baeldung.com/java-migrate-8-to-17)
|
- [Migrate From Java 8 to Java 17](https://www.baeldung.com/java-migrate-8-to-17)
|
||||||
- [Format Multiple ‘or’ Conditions in an If Statement in Java](https://www.baeldung.com/java-multiple-or-conditions-if-statement)
|
- [Format Multiple ‘or’ Conditions in an If Statement in Java](https://www.baeldung.com/java-multiple-or-conditions-if-statement)
|
||||||
|
- [Get All Record Fields and Its Values via Reflection](https://www.baeldung.com/java-reflection-record-fields-values)
|
||||||
|
- [Context-Specific Deserialization Filters in Java 17](https://www.baeldung.com/java-context-specific-deserialization-filters)
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package com.baeldung.recordproperties;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.RecordComponent;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
record Player(String name, int age, Long score) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReadRecordPropertiesByReflectionUnitTest {
|
||||||
|
private static final Player ERIC = new Player("Eric", 28, 4242L);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenUsingRecordComponent_thenGetExpectedResult() {
|
||||||
|
var fields = new ArrayList<Field>();
|
||||||
|
RecordComponent[] components = Player.class.getRecordComponents();
|
||||||
|
for (var comp : components) {
|
||||||
|
try {
|
||||||
|
Field field = ERIC.getClass()
|
||||||
|
.getDeclaredField(comp.getName());
|
||||||
|
field.setAccessible(true);
|
||||||
|
fields.add(field);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// for simplicity, error handling is skipped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(3, fields.size());
|
||||||
|
|
||||||
|
var nameField = fields.get(0);
|
||||||
|
var ageField = fields.get(1);
|
||||||
|
var scoreField = fields.get(2);
|
||||||
|
try {
|
||||||
|
assertEquals("name", nameField.getName());
|
||||||
|
assertEquals(String.class, nameField.getType());
|
||||||
|
assertEquals("Eric", nameField.get(ERIC));
|
||||||
|
|
||||||
|
assertEquals("age", ageField.getName());
|
||||||
|
assertEquals(int.class, ageField.getType());
|
||||||
|
assertEquals(28, ageField.get(ERIC));
|
||||||
|
|
||||||
|
assertEquals("score", scoreField.getName());
|
||||||
|
assertEquals(Long.class, scoreField.getType());
|
||||||
|
assertEquals(4242L, scoreField.get(ERIC));
|
||||||
|
} catch (IllegalAccessException exception) {
|
||||||
|
// for simplicity, error handling is skipped
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenUsingClassGetDeclaredField_thenGetExpectedResult() {
|
||||||
|
// record has no public fields
|
||||||
|
assertEquals(0, Player.class.getFields().length);
|
||||||
|
|
||||||
|
var fields = new ArrayList<Field>();
|
||||||
|
for (var field : Player.class.getDeclaredFields()) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
fields.add(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(3, fields.size());
|
||||||
|
var nameField = fields.get(0);
|
||||||
|
var ageField = fields.get(1);
|
||||||
|
var scoreField = fields.get(2);
|
||||||
|
|
||||||
|
try {
|
||||||
|
assertEquals("name", nameField.getName());
|
||||||
|
assertEquals(String.class, nameField.getType());
|
||||||
|
assertEquals("Eric", nameField.get(ERIC));
|
||||||
|
|
||||||
|
assertEquals("age", ageField.getName());
|
||||||
|
assertEquals(int.class, ageField.getType());
|
||||||
|
assertEquals(28, ageField.get(ERIC));
|
||||||
|
|
||||||
|
assertEquals("score", scoreField.getName());
|
||||||
|
assertEquals(Long.class, scoreField.getType());
|
||||||
|
assertEquals(4242L, scoreField.get(ERIC));
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
// for simplicity, error handling is skipped
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
## Relevant Articles
|
||||||
|
- [Deprecate Finalization in Java 18](https://www.baeldung.com/java-18-deprecate-finalization)
|
@ -3,19 +3,34 @@
|
|||||||
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-20</artifactId>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung.core-java-modules</groupId>
|
<groupId>com.baeldung.core-java-modules</groupId>
|
||||||
<artifactId>core-java-modules</artifactId>
|
<artifactId>core-java-modules</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>core-java-20</artifactId>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
<properties>
|
<groupId>jakarta.servlet</groupId>
|
||||||
<maven.compiler.source>20</maven.compiler.source>
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
<maven.compiler.target>20</maven.compiler.target>
|
<version>6.0.0</version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<scope>provided</scope>
|
||||||
</properties>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>3.24.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
|
<version>5.2.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
@ -41,25 +56,10 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<dependencies>
|
<properties>
|
||||||
<dependency>
|
<maven.compiler.source>20</maven.compiler.source>
|
||||||
<groupId>jakarta.servlet</groupId>
|
<maven.compiler.target>20</maven.compiler.target>
|
||||||
<artifactId>jakarta.servlet-api</artifactId>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<version>6.0.0</version>
|
</properties>
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.assertj</groupId>
|
|
||||||
<artifactId>assertj-core</artifactId>
|
|
||||||
<version>3.24.2</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mockito</groupId>
|
|
||||||
<artifactId>mockito-junit-jupiter</artifactId>
|
|
||||||
<version>5.2.0</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.baeldung.deserialization;
|
||||||
|
|
||||||
|
public record Contact(String email, String phone) {
|
||||||
|
// Constructor, getters, and other methods are automatically generated
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.baeldung.deserialization;
|
||||||
|
|
||||||
|
public record Person(String name, int age, String address, Contact contact) {
|
||||||
|
// Constructor, getters, and other methods are automatically generated
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.baeldung.deserialization;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
public class DeserializationUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenJsonString_whenDeserialized_thenPersonRecordCreated() {
|
||||||
|
String json = "{\"name\":\"John Doe\",\"age\":30,\"address\":\"123 Main St\"}";
|
||||||
|
|
||||||
|
Person person = new Gson().fromJson(json, Person.class);
|
||||||
|
|
||||||
|
assertEquals("John Doe", person.name());
|
||||||
|
assertEquals(30, person.age());
|
||||||
|
assertEquals("123 Main St", person.address());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenNestedJsonString_whenDeserialized_thenPersonRecordCreated() {
|
||||||
|
String json = "{\"name\":\"John Doe\",\"age\":30,\"address\":\"123 Main St\",\"contact\":{\"email\":\"john.doe@example.com\",\"phone\":\"555-1234\"}}";
|
||||||
|
|
||||||
|
Person person = new Gson().fromJson(json, Person.class);
|
||||||
|
|
||||||
|
assertNotNull(person);
|
||||||
|
assertEquals("John Doe", person.name());
|
||||||
|
assertEquals(30, person.age());
|
||||||
|
assertEquals("123 Main St", person.address());
|
||||||
|
|
||||||
|
Contact contact = person.contact();
|
||||||
|
|
||||||
|
assertNotNull(contact);
|
||||||
|
assertEquals("john.doe@example.com", contact.email());
|
||||||
|
assertEquals("555-1234", contact.phone());
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<?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-21</artifactId>
|
<artifactId>core-java-21</artifactId>
|
||||||
<name>core-java-21</name>
|
<name>core-java-21</name>
|
||||||
@ -12,12 +12,6 @@
|
|||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source.version>21</maven.compiler.source.version>
|
|
||||||
<maven.compiler.target.version>21</maven.compiler.target.version>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
@ -44,4 +38,10 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source.version>21</maven.compiler.source.version>
|
||||||
|
<maven.compiler.target.version>21</maven.compiler.target.version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -6,4 +6,7 @@
|
|||||||
- [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)
|
||||||
|
- [Retrieving Unix Time in Java](https://www.baeldung.com/java-retrieve-unix-time)
|
||||||
- [[<-- 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,113 @@
|
|||||||
|
package com.baeldung.rounddate;
|
||||||
|
|
||||||
|
import java.time.DayOfWeek;
|
||||||
|
import java.time.temporal.ChronoField;
|
||||||
|
import java.time.temporal.TemporalAdjusters;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
|
public class RoundDate {
|
||||||
|
public static Date getDate(int year, int month, int day, int hour, int minute) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.set(Calendar.YEAR, year);
|
||||||
|
calendar.set(Calendar.MONTH, month);
|
||||||
|
calendar.set(Calendar.DAY_OF_MONTH, day);
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||||
|
calendar.set(Calendar.MINUTE, minute);
|
||||||
|
calendar.set(Calendar.SECOND, 0);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0);
|
||||||
|
|
||||||
|
return calendar.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date roundToDay(Date date) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.setTime(date);
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
calendar.set(Calendar.MINUTE, 0);
|
||||||
|
calendar.set(Calendar.SECOND, 0);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0);
|
||||||
|
return calendar.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date roundToNearestUnit(Date date, int unit) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.setTime(date);
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case Calendar.HOUR:
|
||||||
|
int minute = calendar.get(Calendar.MINUTE);
|
||||||
|
if (minute >= 0 && minute < 15) {
|
||||||
|
calendar.set(Calendar.MINUTE, 0);
|
||||||
|
} else if (minute >= 15 && minute < 45) {
|
||||||
|
calendar.set(Calendar.MINUTE, 30);
|
||||||
|
} else {
|
||||||
|
calendar.set(Calendar.MINUTE, 0);
|
||||||
|
calendar.add(Calendar.HOUR_OF_DAY, 1);
|
||||||
|
}
|
||||||
|
calendar.set(Calendar.SECOND, 0);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Calendar.DAY_OF_MONTH:
|
||||||
|
int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
||||||
|
if (hour >= 12) {
|
||||||
|
calendar.add(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
}
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
calendar.set(Calendar.MINUTE, 0);
|
||||||
|
calendar.set(Calendar.SECOND, 0);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Calendar.MONTH:
|
||||||
|
int day = calendar.get(Calendar.DAY_OF_MONTH);
|
||||||
|
if (day >= 15) {
|
||||||
|
calendar.add(Calendar.MONTH, 1);
|
||||||
|
}
|
||||||
|
calendar.set(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
calendar.set(Calendar.MINUTE, 0);
|
||||||
|
calendar.set(Calendar.SECOND, 0);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return calendar.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime roundToStartOfMonthUsingLocalDateTime(LocalDateTime dateTime) {
|
||||||
|
return dateTime.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime roundToEndOfWeekUsingLocalDateTime(LocalDateTime dateTime) {
|
||||||
|
return dateTime.with(TemporalAdjusters.next(DayOfWeek.SATURDAY))
|
||||||
|
.withHour(23)
|
||||||
|
.withMinute(59)
|
||||||
|
.withSecond(59)
|
||||||
|
.withNano(999);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ZonedDateTime roundToStartOfMonthUsingZonedDateTime(ZonedDateTime dateTime) {
|
||||||
|
return dateTime.withDayOfMonth(1)
|
||||||
|
.withHour(0)
|
||||||
|
.withMinute(0)
|
||||||
|
.withSecond(0)
|
||||||
|
.with(ChronoField.MILLI_OF_SECOND, 0)
|
||||||
|
.with(ChronoField.MICRO_OF_SECOND, 0)
|
||||||
|
.with(ChronoField.NANO_OF_SECOND, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ZonedDateTime roundToEndOfWeekUsingZonedDateTime(ZonedDateTime dateTime) {
|
||||||
|
return dateTime.with(TemporalAdjusters.next(DayOfWeek.SATURDAY))
|
||||||
|
.withHour(23)
|
||||||
|
.withMinute(59)
|
||||||
|
.withSecond(59)
|
||||||
|
.with(ChronoField.MILLI_OF_SECOND, 999)
|
||||||
|
.with(ChronoField.MICRO_OF_SECOND, 999)
|
||||||
|
.with(ChronoField.NANO_OF_SECOND, 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package com.baeldung.rounddate;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class DateRoundingUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenDate_whenRoundToDay_thenBeginningOfDay() {
|
||||||
|
Date date = RoundDate.getDate(2023, Calendar.JANUARY, 27, 14, 30);
|
||||||
|
Date result = RoundDate.roundToDay(date);
|
||||||
|
assertEquals(RoundDate.getDate(2023, Calendar.JANUARY, 27, 0, 0), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenDate_whenRoundToNearestUnit_thenNearestUnit() {
|
||||||
|
Date date = RoundDate.getDate(2023, Calendar.JANUARY, 27, 14, 12);
|
||||||
|
Date result = RoundDate.roundToNearestUnit(date, Calendar.DAY_OF_MONTH);
|
||||||
|
Date expected = RoundDate.getDate(2023, Calendar.JANUARY, 28, 0, 0);
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenLocalDateTime_whenRoundToStartOfMonth_thenStartOfMonth() {
|
||||||
|
LocalDateTime dateTime = LocalDateTime.of(2023, 1, 27, 14, 12);
|
||||||
|
LocalDateTime result = RoundDate.roundToStartOfMonthUsingLocalDateTime(dateTime);
|
||||||
|
LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 0, 0, 0);
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenZonedDateTime_whenRoundToStartOfMonth_thenStartOfMonth() {
|
||||||
|
ZonedDateTime dateTime = ZonedDateTime.of(2023, 1, 27, 14, 12, 0, 0, ZoneId.systemDefault());
|
||||||
|
ZonedDateTime result = RoundDate.roundToStartOfMonthUsingZonedDateTime(dateTime);
|
||||||
|
ZonedDateTime expected = ZonedDateTime.of(2023, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault());
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenLocalDateTime_whenRoundToEndOfWeek_thenEndOfWeek() {
|
||||||
|
LocalDateTime dateTime = LocalDateTime.of(2023, 1, 27, 14, 12);
|
||||||
|
LocalDateTime result = RoundDate.roundToEndOfWeekUsingLocalDateTime(dateTime);
|
||||||
|
LocalDateTime expected = LocalDateTime.of(2023, 1, 28, 23, 59, 59, 999);
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenZonedDateTime_whenRoundToEndOfWeek_thenEndOfWeek() {
|
||||||
|
ZonedDateTime dateTime = ZonedDateTime.of(2023, 1, 27, 14, 12, 0, 0, ZoneId.systemDefault());
|
||||||
|
ZonedDateTime result = RoundDate.roundToEndOfWeekUsingZonedDateTime(dateTime);
|
||||||
|
ZonedDateTime expected = ZonedDateTime.of(2023, 1, 28, 23, 59, 59, 999, ZoneId.systemDefault());
|
||||||
|
assertEquals(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.baeldung.unixtime;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
import java.time.Month;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class UnixTimeUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenTimeUsingDateApi_whenConvertedToUnixTime_thenMatch() {
|
||||||
|
Date date = new Date(2023 - 1900, 1, 15, 0, 0, 0);
|
||||||
|
long expected = 1676419200;
|
||||||
|
long actual = date.getTime() / 1000L;
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenTimeUsingJodaTime_whenConvertedToUnixTime_thenMatch() {
|
||||||
|
DateTime dateTime = new DateTime(2023, 2, 15, 00, 00, 00, 0);
|
||||||
|
long expected = 1676419200;
|
||||||
|
long actual = dateTime.getMillis() / 1000L;
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenTimeUsingLocalDate_whenConvertedToUnixTime_thenMatch() {
|
||||||
|
LocalDate date = LocalDate.of(2023, Month.FEBRUARY, 15);
|
||||||
|
Instant instant = date.atStartOfDay().atZone(ZoneId.of("UTC")).toInstant();
|
||||||
|
long expected = 1676419200;
|
||||||
|
long actual = instant.getEpochSecond();
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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>
|
@ -11,5 +11,5 @@ This module contains articles about Java 8 core features
|
|||||||
- [Finding Min/Max in an Array with Java](https://www.baeldung.com/java-array-min-max)
|
- [Finding Min/Max in an Array with Java](https://www.baeldung.com/java-array-min-max)
|
||||||
- [Internationalization and Localization in Java 8](https://www.baeldung.com/java-8-localization)
|
- [Internationalization and Localization in Java 8](https://www.baeldung.com/java-8-localization)
|
||||||
- [Generalized Target-Type Inference in Java](https://www.baeldung.com/java-generalized-target-type-inference)
|
- [Generalized Target-Type Inference in Java](https://www.baeldung.com/java-generalized-target-type-inference)
|
||||||
- [Monads in Java](https://www.baeldung.com/java-monads)
|
- [Monads in Java – Optional](https://www.baeldung.com/java-monads)
|
||||||
- [[More -->]](/core-java-modules/core-java-8-2)
|
- [[More -->]](/core-java-modules/core-java-8-2)
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.baeldung.spliterator;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class CustomSpliterator implements Spliterator<Integer> {
|
||||||
|
private final List<Integer> elements;
|
||||||
|
private int currentIndex;
|
||||||
|
|
||||||
|
public CustomSpliterator(List<Integer> elements) {
|
||||||
|
this.elements = elements;
|
||||||
|
this.currentIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tryAdvance(Consumer<? super Integer> action) {
|
||||||
|
if (currentIndex < elements.size()) {
|
||||||
|
action.accept(elements.get(currentIndex));
|
||||||
|
currentIndex++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spliterator<Integer> trySplit() {
|
||||||
|
int currentSize = elements.size() - currentIndex;
|
||||||
|
if (currentSize < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int splitIndex = currentIndex + currentSize / 2;
|
||||||
|
CustomSpliterator newSpliterator = new CustomSpliterator(elements.subList(currentIndex, splitIndex));
|
||||||
|
currentIndex = splitIndex;
|
||||||
|
return newSpliterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long estimateSize() {
|
||||||
|
return elements.size() - currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int characteristics() {
|
||||||
|
return ORDERED | SIZED | SUBSIZED | NONNULL;
|
||||||
|
}
|
||||||
|
}
|
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