diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ObjectSubstitutionBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ObjectSubstitutionBuildItem.java index ef465c6f3a84a7..92d3b8602d1d25 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ObjectSubstitutionBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ObjectSubstitutionBuildItem.java @@ -30,8 +30,9 @@ public Holder(Class from, Class to, Class> substi public final Holder holder; - public ObjectSubstitutionBuildItem(Class from, Class to, Class> substitution) { - holder = new Holder<>(from, to, substitution); + public ObjectSubstitutionBuildItem(Class from, Class to, + Class> substitution) { + holder = new Holder(from, to, substitution); } public ObjectSubstitutionBuildItem(Holder holder) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/recording/substitutions/AdditionalSubstitutionsBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/recording/substitutions/AdditionalSubstitutionsBuildStep.java new file mode 100644 index 00000000000000..cc6396b13ad200 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/recording/substitutions/AdditionalSubstitutionsBuildStep.java @@ -0,0 +1,33 @@ +package io.quarkus.deployment.recording.substitutions; + +import java.time.ZoneId; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.ObjectSubstitutionBuildItem; + +public class AdditionalSubstitutionsBuildStep { + + @BuildStep + public void additionalSubstitutions(BuildProducer producer) { + zoneIdSubstitutions(producer); + } + + @SuppressWarnings("unchecked") + private void zoneIdSubstitutions(BuildProducer producer) { + try { + /* + * We can't refer to these classes as they are package private but we need a handle on need + * because the bytecode recorder needs to have the actual class registered and not a super class + */ + + Class zoneRegionClass = (Class) Class.forName("java.time.ZoneRegion"); + producer.produce(new ObjectSubstitutionBuildItem(zoneRegionClass, String.class, ZoneIdSubstitution.class)); + + Class zoneOffsetClass = (Class) Class.forName("java.time.ZoneRegion"); + producer.produce(new ObjectSubstitutionBuildItem(zoneOffsetClass, String.class, ZoneIdSubstitution.class)); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Improper registration of ZoneId substitution", e); + } + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/recording/substitutions/ZoneIdSubstitution.java b/core/deployment/src/main/java/io/quarkus/deployment/recording/substitutions/ZoneIdSubstitution.java new file mode 100644 index 00000000000000..f9e7105247a8da --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/recording/substitutions/ZoneIdSubstitution.java @@ -0,0 +1,18 @@ +package io.quarkus.deployment.recording.substitutions; + +import java.time.ZoneId; + +import io.quarkus.runtime.ObjectSubstitution; + +public class ZoneIdSubstitution implements ObjectSubstitution { + + @Override + public String serialize(ZoneId obj) { + return obj.getId(); + } + + @Override + public ZoneId deserialize(String str) { + return ZoneId.of(str); + } +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ZoneIdConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ZoneIdConverter.java new file mode 100644 index 00000000000000..d584e2d72ccf62 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ZoneIdConverter.java @@ -0,0 +1,24 @@ +package io.quarkus.runtime.configuration; + +import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; + +import java.io.Serializable; +import java.time.ZoneId; + +import javax.annotation.Priority; + +import org.eclipse.microprofile.config.spi.Converter; + +/** + * A converter to support locales. + */ +@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) +public class ZoneIdConverter implements Converter, Serializable { + + private static final long serialVersionUID = -439010527617997936L; + + @Override + public ZoneId convert(final String value) { + return ZoneId.of(value); + } +} diff --git a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java index 88a544abe5ae64..4b0d32c66a07a8 100755 --- a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java +++ b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java @@ -7,7 +7,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.TimeZone; import javax.inject.Inject; import javax.inject.Singleton; @@ -179,23 +178,6 @@ private void registerModuleIfOnClassPath(String moduleClassName, @Record(ExecutionTime.STATIC_INIT) SyntheticBeanBuildItem pushConfigurationBean(JacksonRecorder jacksonRecorder, JacksonBuildTimeConfig jacksonBuildTimeConfig) { - - if (jacksonBuildTimeConfig.timezone.isPresent()) { - /* - * We need to make timezone a String instead of a java.util.TimeZone class - * because: - * 1) TimeZone cannot automatically be handled by the BytecodeRecorder - * 2) Handling it would require us to add non-JDK classes (i.e. sun.util.calendar.ZoneInfo) to bytecode recording - */ - String timeZoneStr = jacksonBuildTimeConfig.timezone.get(); - TimeZone timeZone = TimeZone.getTimeZone(timeZoneStr); - if ("GMT".equals(timeZone.getID()) && !timeZoneStr.startsWith("GMT")) { - // Parsing an illegal TZ string value results in falling back to GMT... - throw new IllegalArgumentException( - "Value '" + timeZoneStr + "' is an invalid value for the 'quarkus.jackson.timezone' property"); - } - } - return SyntheticBeanBuildItem.configure(JacksonConfigSupport.class) .scope(Singleton.class) .supplier(jacksonRecorder.jacksonConfigSupport(jacksonBuildTimeConfig)) diff --git a/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/JacksonBuildTimeConfig.java b/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/JacksonBuildTimeConfig.java index aae6c1b716559b..a99e7a57a6d983 100644 --- a/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/JacksonBuildTimeConfig.java +++ b/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/JacksonBuildTimeConfig.java @@ -1,5 +1,6 @@ package io.quarkus.jackson.runtime; +import java.time.ZoneId; import java.util.Optional; import io.quarkus.runtime.annotations.ConfigItem; @@ -28,5 +29,5 @@ public class JacksonBuildTimeConfig { * If not set, Jackson will use its own default. */ @ConfigItem - public Optional timezone; + public Optional timezone; } diff --git a/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/JacksonConfigSupport.java b/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/JacksonConfigSupport.java index 75cc56e52519d2..82ba9ffcbe55a4 100644 --- a/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/JacksonConfigSupport.java +++ b/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/JacksonConfigSupport.java @@ -1,14 +1,16 @@ package io.quarkus.jackson.runtime; +import java.time.ZoneId; + public class JacksonConfigSupport { private final boolean failOnUnknownProperties; private final boolean writeDatesAsTimestamps; - private final String timeZone; + private final ZoneId timeZone; - public JacksonConfigSupport(boolean failOnUnknownProperties, boolean writeDatesAsTimestamps, String timeZone) { + public JacksonConfigSupport(boolean failOnUnknownProperties, boolean writeDatesAsTimestamps, ZoneId timeZone) { this.failOnUnknownProperties = failOnUnknownProperties; this.writeDatesAsTimestamps = writeDatesAsTimestamps; this.timeZone = timeZone; @@ -22,7 +24,7 @@ public boolean isWriteDatesAsTimestamps() { return writeDatesAsTimestamps; } - public String getTimeZone() { + public ZoneId getTimeZone() { return timeZone; } } diff --git a/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/ObjectMapperProducer.java b/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/ObjectMapperProducer.java index 16f4fa74554eae..621cdaeb1eb878 100644 --- a/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/ObjectMapperProducer.java +++ b/extensions/jackson/runtime/src/main/java/io/quarkus/jackson/runtime/ObjectMapperProducer.java @@ -1,5 +1,6 @@ package io.quarkus.jackson.runtime; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -34,10 +35,9 @@ public ObjectMapper objectMapper(Instance customizers, // this feature is enabled by default, so we disable it objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); } - String timeZoneStr = jacksonConfigSupport.getTimeZone(); - if (timeZoneStr != null) { - TimeZone timeZone = TimeZone.getTimeZone(timeZoneStr); - objectMapper.setTimeZone(timeZone); + ZoneId zoneId = jacksonConfigSupport.getTimeZone(); + if (zoneId != null) { + objectMapper.setTimeZone(TimeZone.getTimeZone(zoneId)); } List sortedCustomizers = sortCustomizersInDescendingPriorityOrder(customizers); for (ObjectMapperCustomizer customizer : sortedCustomizers) { diff --git a/integration-tests/container-image/quarkus-standard-way/src/test/java/io/quarkus/it/container/image/GreetingResourceIT.java b/integration-tests/container-image/quarkus-standard-way/src/test/java/io/quarkus/it/container/image/GreetingResourceIT.java index 073a95b145d720..633a970b92fd30 100644 --- a/integration-tests/container-image/quarkus-standard-way/src/test/java/io/quarkus/it/container/image/GreetingResourceIT.java +++ b/integration-tests/container-image/quarkus-standard-way/src/test/java/io/quarkus/it/container/image/GreetingResourceIT.java @@ -3,12 +3,11 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusIntegrationTest; -@Disabled("This test seems flaky") +//@Disabled("This test seems flaky") @QuarkusIntegrationTest public class GreetingResourceIT {