Skip to content

Commit ed044b1

Browse files
authored
Merge branch 'master' into BAEL-8822-Introduction-to-Objenesis
2 parents e1d33b6 + ae5f02f commit ed044b1

File tree

369 files changed

+8242
-802
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

369 files changed

+8242
-802
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package com.baeldung.apache.avro;
2+
3+
import org.apache.avro.Conversion;
4+
import org.apache.avro.LogicalTypes;
5+
import org.apache.avro.Schema;
6+
import org.apache.avro.data.TimeConversions;
7+
import org.apache.avro.generic.GenericData;
8+
import org.apache.avro.generic.GenericDatumReader;
9+
import org.apache.avro.generic.GenericDatumWriter;
10+
import org.apache.avro.generic.GenericRecord;
11+
import org.apache.avro.io.DatumReader;
12+
import org.apache.avro.io.DatumWriter;
13+
import org.apache.avro.io.Decoder;
14+
import org.apache.avro.io.DecoderFactory;
15+
import org.apache.avro.io.Encoder;
16+
import org.apache.avro.io.EncoderFactory;
17+
import org.apache.commons.lang3.tuple.Pair;
18+
import org.junit.jupiter.api.Test;
19+
20+
import java.io.ByteArrayOutputStream;
21+
import java.io.IOException;
22+
import java.time.Instant;
23+
import java.time.LocalDate;
24+
import java.time.ZoneId;
25+
import java.util.Date;
26+
27+
import static org.junit.jupiter.api.Assertions.assertEquals;
28+
29+
public class SerializeAndDeserializeDateUnitTest {
30+
31+
@Test
32+
void whenSerializingDateWithLogicalType_thenDeserializesCorrectly() throws IOException {
33+
34+
LocalDate expectedDate = LocalDate.now();
35+
Instant expectedTimestamp = Instant.now();
36+
37+
byte[] serialized = serializeDateWithLogicalType(expectedDate, expectedTimestamp);
38+
Pair<LocalDate, Instant> deserialized = deserializeDateWithLogicalType(serialized);
39+
40+
assertEquals(expectedDate, deserialized.getLeft());
41+
42+
// This is perfectly valid when using logical types
43+
assertEquals(expectedTimestamp.toEpochMilli(), deserialized.getRight().toEpochMilli(),
44+
"Timestamps should match exactly at millisecond precision");
45+
}
46+
47+
@Test
48+
void whenSerializingWithConversionApi_thenDeserializesCorrectly() throws IOException {
49+
50+
LocalDate expectedDate = LocalDate.now();
51+
Instant expectedTimestamp = Instant.now();
52+
53+
byte[] serialized = serializeWithConversionApi(expectedDate, expectedTimestamp);
54+
Pair<LocalDate, Instant> deserialized = deserializeWithConversionApi(serialized);
55+
56+
assertEquals(expectedDate, deserialized.getLeft());
57+
assertEquals(expectedTimestamp.toEpochMilli(), deserialized.getRight().toEpochMilli(),
58+
"Timestamps should match at millisecond precision");
59+
}
60+
61+
@Test
62+
void whenSerializingLegacyDate_thenConvertsCorrectly() throws IOException {
63+
64+
Date legacyDate = new Date();
65+
LocalDate expectedLocalDate = legacyDate.toInstant()
66+
.atZone(ZoneId.systemDefault())
67+
.toLocalDate();
68+
69+
byte[] serialized = serializeLegacyDateAsModern(legacyDate);
70+
LocalDate deserialized = deserializeDateWithLogicalType(serialized).getKey();
71+
72+
assertEquals(expectedLocalDate, deserialized);
73+
}
74+
75+
public static Schema createDateSchema() {
76+
String schemaJson =
77+
"{"
78+
+ "\"type\": \"record\","
79+
+ "\"name\": \"DateRecord\","
80+
+ "\"fields\": ["
81+
+ " {\"name\": \"date\", \"type\": {\"type\": \"int\", \"logicalType\": \"date\"}},"
82+
+ " {\"name\": \"timestamp\", \"type\": {\"type\": \"long\", \"logicalType\": \"timestamp-millis\"}}"
83+
+ "]"
84+
+ "}";
85+
return new Schema.Parser().parse(schemaJson);
86+
}
87+
88+
public static byte[] serializeDateWithLogicalType(LocalDate date, Instant timestamp) throws IOException {
89+
Schema schema = createDateSchema();
90+
GenericRecord record = new GenericData.Record(schema);
91+
92+
// Convert LocalDate to days since epoch
93+
record.put("date", (int) date.toEpochDay());
94+
95+
// Convert Instant to milliseconds since epoch
96+
record.put("timestamp", timestamp.toEpochMilli());
97+
98+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
99+
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(schema);
100+
Encoder encoder = EncoderFactory.get().binaryEncoder(baos, null);
101+
102+
datumWriter.write(record, encoder);
103+
encoder.flush();
104+
105+
return baos.toByteArray();
106+
}
107+
108+
public static Pair<LocalDate, Instant> deserializeDateWithLogicalType(byte[] bytes) throws IOException {
109+
Schema schema = createDateSchema();
110+
DatumReader<GenericRecord> datumReader = new GenericDatumReader<>(schema);
111+
Decoder decoder = DecoderFactory.get().binaryDecoder(bytes, null);
112+
113+
GenericRecord record = datumReader.read(null, decoder);
114+
115+
// Convert days since epoch back to LocalDate
116+
LocalDate date = LocalDate.ofEpochDay((int) record.get("date"));
117+
118+
// Convert milliseconds since epoch back to Instant
119+
Instant timestamp = Instant.ofEpochMilli((long) record.get("timestamp"));
120+
121+
return Pair.of(date, timestamp);
122+
}
123+
124+
public static byte[] serializeWithConversionApi(LocalDate date, Instant timestamp) throws IOException {
125+
Schema schema = createDateSchema();
126+
GenericRecord record = new GenericData.Record(schema);
127+
128+
// Use LogicalTypes.date() for conversion
129+
Conversion<LocalDate> dateConversion = new org.apache.avro.data.TimeConversions.DateConversion();
130+
LogicalTypes.date().addToSchema(schema.getField("date").schema());
131+
132+
// Use LogicalTypes.timestampMillis() for conversion
133+
Conversion<Instant> timestampConversion = new org.apache.avro.data.TimeConversions.TimestampMillisConversion();
134+
LogicalTypes.timestampMillis().addToSchema(schema.getField("timestamp").schema());
135+
136+
record.put("date", dateConversion.toInt(date, schema.getField("date").schema(), LogicalTypes.date()));
137+
record.put("timestamp", timestampConversion.toLong(timestamp, schema.getField("timestamp").schema(), LogicalTypes.timestampMillis()));
138+
139+
// Serialize as before
140+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
141+
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(schema);
142+
Encoder encoder = EncoderFactory.get().binaryEncoder(baos, null);
143+
144+
datumWriter.write(record, encoder);
145+
encoder.flush();
146+
147+
return baos.toByteArray();
148+
}
149+
150+
public static Pair<LocalDate, Instant> deserializeWithConversionApi(byte[] bytes) throws IOException {
151+
Schema schema = createDateSchema();
152+
DatumReader<GenericRecord> datumReader = new GenericDatumReader<>(schema);
153+
Decoder decoder = DecoderFactory.get().binaryDecoder(bytes, null);
154+
155+
GenericRecord record = datumReader.read(null, decoder);
156+
157+
// Use LogicalTypes.date() for conversion
158+
Conversion<LocalDate> dateConversion = new TimeConversions.DateConversion();
159+
LogicalTypes.date().addToSchema(schema.getField("date").schema());
160+
161+
// Use LogicalTypes.timestampMillis() for conversion
162+
Conversion<Instant> timestampConversion = new TimeConversions.TimestampMillisConversion();
163+
LogicalTypes.timestampMillis().addToSchema(schema.getField("timestamp").schema());
164+
165+
// Get the primitive values from the record
166+
int daysSinceEpoch = (int) record.get("date");
167+
long millisSinceEpoch = (long) record.get("timestamp");
168+
169+
// Convert back to Java types using the conversion API
170+
LocalDate date = dateConversion.fromInt(
171+
daysSinceEpoch,
172+
schema.getField("date").schema(),
173+
LogicalTypes.date()
174+
);
175+
176+
Instant timestamp = timestampConversion.fromLong(
177+
millisSinceEpoch,
178+
schema.getField("timestamp").schema(),
179+
LogicalTypes.timestampMillis()
180+
);
181+
182+
return Pair.of(date, timestamp);
183+
}
184+
185+
public static byte[] serializeLegacyDateAsModern(Date legacyDate) throws IOException {
186+
// Convert java.util.Date to java.time.Instant
187+
Instant instant = legacyDate.toInstant();
188+
189+
// Convert to LocalDate if you need date-only information
190+
LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
191+
192+
// Then use one of our modern date serialization methods
193+
return serializeDateWithLogicalType(localDate, instant);
194+
}
195+
}

aws-modules/amazon-athena/pom.xml

Lines changed: 82 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,90 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<project xmlns="http://maven.apache.org/POM/4.0.0"
3-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5-
<modelVersion>4.0.0</modelVersion>
6-
<artifactId>amazon-athena</artifactId>
7-
<version>0.0.1</version>
8-
<packaging>jar</packaging>
9-
<name>amazon-athena</name>
10-
<description>codebase demonstrating the integration of Amazon Athena in Spring Boot to query data stored in a S3 bucket</description>
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<artifactId>amazon-athena</artifactId>
7+
<version>0.0.1</version>
8+
<packaging>jar</packaging>
9+
<name>amazon-athena</name>
10+
<description>codebase demonstrating the integration of Amazon Athena in Spring Boot to query data stored in a S3 bucket</description>
1111

12-
<parent>
13-
<groupId>com.baeldung</groupId>
14-
<artifactId>parent-boot-3</artifactId>
15-
<version>0.0.1-SNAPSHOT</version>
16-
<relativePath>../../parent-boot-3</relativePath>
17-
</parent>
12+
<parent>
13+
<groupId>com.baeldung</groupId>
14+
<artifactId>aws-modules</artifactId>
15+
<version>1.0.0-SNAPSHOT</version>
16+
</parent>
1817

19-
<dependencies>
20-
<dependency>
21-
<groupId>org.springframework.boot</groupId>
22-
<artifactId>spring-boot-starter-web</artifactId>
23-
</dependency>
24-
<dependency>
25-
<groupId>org.springframework.boot</groupId>
26-
<artifactId>spring-boot-starter-validation</artifactId>
27-
</dependency>
28-
<dependency>
29-
<groupId>org.springframework.boot</groupId>
30-
<artifactId>spring-boot-configuration-processor</artifactId>
31-
</dependency>
32-
<dependency>
33-
<groupId>software.amazon.awssdk</groupId>
34-
<artifactId>athena</artifactId>
35-
<version>${amazon-athena.version}</version>
36-
</dependency>
37-
<dependency>
38-
<groupId>commons-io</groupId>
39-
<artifactId>commons-io</artifactId>
40-
<version>${commons-io.version}</version>
41-
</dependency>
42-
<dependency>
43-
<groupId>org.json</groupId>
44-
<artifactId>json</artifactId>
45-
<version>${org-json.version}</version>
46-
</dependency>
47-
<dependency>
48-
<groupId>com.fasterxml.jackson.datatype</groupId>
49-
<artifactId>jackson-datatype-json-org</artifactId>
50-
</dependency>
51-
<dependency>
52-
<groupId>org.projectlombok</groupId>
53-
<artifactId>lombok</artifactId>
54-
<optional>true</optional>
55-
</dependency>
56-
</dependencies>
18+
<dependencyManagement>
19+
<dependencies>
20+
<dependency>
21+
<groupId>org.springframework.boot</groupId>
22+
<artifactId>spring-boot-dependencies</artifactId>
23+
<version>${spring-boot.version}</version>
24+
<type>pom</type>
25+
<scope>import</scope>
26+
</dependency>
27+
</dependencies>
28+
</dependencyManagement>
5729

58-
<build>
59-
<plugins>
60-
<plugin>
61-
<groupId>org.springframework.boot</groupId>
62-
<artifactId>spring-boot-maven-plugin</artifactId>
63-
<configuration>
64-
<excludes>
65-
<exclude>
66-
<groupId>org.projectlombok</groupId>
67-
<artifactId>lombok</artifactId>
68-
</exclude>
69-
</excludes>
70-
</configuration>
71-
</plugin>
72-
</plugins>
73-
</build>
30+
<dependencies>
31+
<dependency>
32+
<groupId>org.springframework.boot</groupId>
33+
<artifactId>spring-boot-starter-web</artifactId>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.springframework.boot</groupId>
37+
<artifactId>spring-boot-starter-validation</artifactId>
38+
</dependency>
39+
<dependency>
40+
<groupId>org.springframework.boot</groupId>
41+
<artifactId>spring-boot-configuration-processor</artifactId>
42+
</dependency>
43+
<dependency>
44+
<groupId>software.amazon.awssdk</groupId>
45+
<artifactId>athena</artifactId>
46+
</dependency>
47+
<dependency>
48+
<groupId>commons-io</groupId>
49+
<artifactId>commons-io</artifactId>
50+
<version>${commons-io.version}</version>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.json</groupId>
54+
<artifactId>json</artifactId>
55+
<version>${org-json.version}</version>
56+
</dependency>
57+
<dependency>
58+
<groupId>com.fasterxml.jackson.datatype</groupId>
59+
<artifactId>jackson-datatype-json-org</artifactId>
60+
</dependency>
61+
<dependency>
62+
<groupId>org.projectlombok</groupId>
63+
<artifactId>lombok</artifactId>
64+
<optional>true</optional>
65+
</dependency>
66+
</dependencies>
7467

75-
<properties>
76-
<org-json.version>20240303</org-json.version>
77-
<commons-io.version>2.16.1</commons-io.version>
78-
<amazon-athena.version>2.26.0</amazon-athena.version>
79-
</properties>
68+
<build>
69+
<plugins>
70+
<plugin>
71+
<groupId>org.springframework.boot</groupId>
72+
<artifactId>spring-boot-maven-plugin</artifactId>
73+
<configuration>
74+
<excludes>
75+
<exclude>
76+
<groupId>org.projectlombok</groupId>
77+
<artifactId>lombok</artifactId>
78+
</exclude>
79+
</excludes>
80+
</configuration>
81+
</plugin>
82+
</plugins>
83+
</build>
84+
85+
<properties>
86+
<org-json.version>20240303</org-json.version>
87+
<commons-io.version>2.16.1</commons-io.version>
88+
</properties>
8089

8190
</project>

0 commit comments

Comments
 (0)