Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.x] Adopt SnakeYAML 2.0; add integration tests for reactive and Nima #6535

Merged
merged 2 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -39,6 +39,7 @@
import io.helidon.config.MutabilitySupport;

import org.eclipse.microprofile.config.spi.ConfigSource;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

Expand Down Expand Up @@ -380,7 +381,7 @@ public String getName() {
static Map toMap(Reader reader) {
// the default of Snake YAML is a Map, safe constructor makes sure we never deserialize into anything
// harmful
Yaml yaml = new Yaml(new SafeConstructor());
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
return (Map) yaml.loadAs(reader, Object.class);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022 Oracle and/or its affiliates.
* Copyright (c) 2020, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,6 +32,7 @@
import io.helidon.config.spi.ConfigParser;
import io.helidon.config.spi.ConfigParserException;

import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

Expand Down Expand Up @@ -113,7 +114,7 @@ public ObjectNode parse(Content content) throws ConfigParserException {
static Map toMap(Reader reader) {
// the default of Snake YAML is a Map, safe constructor makes sure we never deserialize into anything
// harmful
Yaml yaml = new Yaml(new SafeConstructor());
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
return (Map) yaml.loadAs(reader, Object.class);
}

Expand Down
2 changes: 1 addition & 1 deletion dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
<version.lib.prometheus>0.9.0</version.lib.prometheus>
<version.lib.slf4j>2.0.0</version.lib.slf4j>
<version.lib.smallrye-openapi>2.1.16</version.lib.smallrye-openapi>
<version.lib.snakeyaml>1.32</version.lib.snakeyaml>
<version.lib.snakeyaml>2.0</version.lib.snakeyaml>
<version.lib.typesafe-config>1.4.2</version.lib.typesafe-config>
<version.lib.tyrus>2.0.4</version.lib.tyrus>
<version.lib.weld-api>5.0.SP3</version.lib.weld-api>
Expand Down
6 changes: 6 additions & 0 deletions examples/todo-app/backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<version.datastax.driver.core>4.9.0</version.datastax.driver.core>
<version.datastax.driver.query.builder>4.9.0</version.datastax.driver.query.builder>
<version.codahale.metrics.core>3.0.2</version.codahale.metrics.core>
<version.lib.snakeyaml.override>1.32</version.lib.snakeyaml.override>
</properties>

<dependencyManagement>
Expand All @@ -57,6 +58,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${version.lib.snakeyaml.override}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
16 changes: 16 additions & 0 deletions microprofile/tests/tck/tck-openapi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@
<artifactId>tck-openapi</artifactId>
<name>Helidon Microprofile Tests TCK OpenAPI</name>

<properties>
<selectedSnakeYamlVersion>1.32</selectedSnakeYamlVersion>
</properties>

<!-- The TCK uses Jackson which in turn uses SnakeYAML APIs that have disappeared in 2.0. So, for running the TCK
use the earlier SnakeYAML release (until an updated Jackson release becomes available). -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${selectedSnakeYamlVersion}</version>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.helidon.microprofile.tests</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,9 @@ private OpenApiStaticFile getExplicitStaticFile() {
+ " is not one of recognized types: "
+ OpenAPIMediaType.recognizedFileTypes()));

try (InputStream is = new BufferedInputStream(Files.newInputStream(path))) {
// DO NOT use try-with-close; the stream needs to remain open because it is not consumed until later.
try {
InputStream is = new BufferedInputStream(Files.newInputStream(path));
LOGGER.log(Level.DEBUG,
() -> String.format(
OPENAPI_EXPLICIT_STATIC_FILE_LOG_MESSAGE_FORMAT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.eclipse.microprofile.openapi.models.responses.APIResponse;
import org.eclipse.microprofile.openapi.models.responses.APIResponses;
import org.eclipse.microprofile.openapi.models.security.SecurityRequirement;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.error.Mark;
Expand Down Expand Up @@ -148,7 +149,7 @@ record ChildMapType<P, C>(Class<P> parentType,
Function<Class<?>, ExpandedTypeDescription.MapLikeTypeDescription<P, C>> typeDescriptionFactory) { }

/**
* Type information about a map-resembling interface in which a child can have 0, 1, or more values i.e., the child is
* Type information about a map-resembling interface in which a child can have 0, 1, or more values (i.e., the child is
* a list).
*
* @param <P> parent type
Expand All @@ -165,7 +166,7 @@ record ChildMapListType<P, C>(
private static final System.Logger LOGGER = System.getLogger(CustomConstructor.class.getName());

CustomConstructor(TypeDescription td) {
super(td);
super(td, new LoaderOptions());
yamlClassConstructors.put(NodeId.mapping, new ConstructMapping());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2021 Oracle and/or its affiliates.
* Copyright (c) 2020, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -61,46 +61,18 @@ public Tag getTag() {
return delegate.getTag();
}

@Override
public void setTag(Tag tag) {
delegate.setTag(tag);
}

@Override
public void setTag(String tag) {
delegate.setTag(tag);
}

@Override
@Deprecated
public void putListPropertyType(String property, Class<?> type) {
delegate.putListPropertyType(property, type);
}

@Override
@Deprecated
public Class<? extends Object> getListPropertyType(String property) {
return delegate.getListPropertyType(property);
}

@Override
@Deprecated
public void putMapPropertyType(String property, Class<?> key, Class<?> value) {
delegate.putMapPropertyType(property, key, value);
}

@Override
@Deprecated
public Class<? extends Object> getMapKeyType(String property) {
return delegate.getMapKeyType(property);
}

@Override
@Deprecated
public Class<? extends Object> getMapValueType(String property) {
return delegate.getMapValueType(property);
}

@Override
public void addPropertyParameters(String pName, Class<?>... classes) {
delegate.addPropertyParameters(pName, classes);
Expand Down
13 changes: 12 additions & 1 deletion openapi/src/main/java/io/helidon/openapi/ParserHelper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,13 +32,24 @@
* Wraps generated parser and uses {@link io.helidon.openapi.ExpandedTypeDescription} as its type.
*/
public class ParserHelper {

// Temporary to suppress SnakeYAML warnings.
// As a static we keep a reference to the logger, thereby making sure any changes we make are persistent. (JUL holds
// only weak references to loggers internally.)
private static final java.util.logging.Logger SNAKE_YAML_INTROSPECTOR_LOGGER =
java.util.logging.Logger.getLogger(org.yaml.snakeyaml.introspector.PropertySubstitute.class.getPackage().getName());

/**
* The SnakeYAMLParserHelper is generated by a maven plug-in.
*/
private final SnakeYAMLParserHelper<ExpandedTypeDescription> generatedHelper;

private ParserHelper(SnakeYAMLParserHelper<ExpandedTypeDescription> generatedHelper) {
this.generatedHelper = generatedHelper;
boolean warningsEnabled = Boolean.getBoolean("openapi.parsing.warnings.enabled");
if (SNAKE_YAML_INTROSPECTOR_LOGGER.isLoggable(java.util.logging.Level.WARNING) && !warningsEnabled) {
SNAKE_YAML_INTROSPECTOR_LOGGER.setLevel(java.util.logging.Level.SEVERE);
}
}

/**
Expand Down
3 changes: 1 addition & 2 deletions openapi/src/main/java/io/helidon/openapi/Serializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,15 @@ static class CustomRepresenter extends Representer {

private static final String EXTENSIONS = "extensions";

private final DumperOptions dumperOptions;
private final DumperOptions.ScalarStyle stringStyle;

private final Map<Class<?>, ExpandedTypeDescription> implsToTypes;

CustomRepresenter(Map<Class<?>, ExpandedTypeDescription> types,
Map<Class<?>, ExpandedTypeDescription> implsToTypes, DumperOptions dumperOptions,
DumperOptions.ScalarStyle stringStyle) {
super(dumperOptions);
this.implsToTypes = implsToTypes;
this.dumperOptions = dumperOptions;
this.stringStyle = stringStyle;
types.values().stream()
.map(ImplTypeDescription::new)
Expand Down
1 change: 1 addition & 0 deletions openapi/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
requires transitive microprofile.openapi.api;

requires static io.helidon.config.metadata;
requires java.logging; // temporary to adjust SnakeYAML logger level

exports io.helidon.openapi;
exports io.helidon.openapi.internal to io.helidon.microprofile.openapi, io.helidon.reactive.openapi, io.helidon.nima.openapi;
Expand Down
21 changes: 21 additions & 0 deletions tests/integration/gh-5792-nima/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

# helidon-tests-integration-yaml-parsing

Sample Helidon Níma project to make sure that we can build and run using an older release of SnakeYAML in case users need to fall back.

Note that the static OpenAPI document packaged into the application JAR file intentionally _does not_ describe the API for this service.
It contains a much richer definition to exercise YAML parsing a bit more.

## Build and run

With JDK19+
```bash
mvn package
java --enable-preview -jar target/helidon-tests-integration-yaml-parsing.jar
```

## Try OpenAPI

```
curl -s -X GET http://localhost:8080/openapi
```
117 changes: 117 additions & 0 deletions tests/integration/gh-5792-nima/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright (c) 2019, 2023 Oracle and/or its affiliates.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

-->

<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>
<parent>
<groupId>io.helidon.applications</groupId>
<artifactId>helidon-nima</artifactId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../../../applications/nima/pom.xml</relativePath>
</parent>
<groupId>io.helidon.tests.integration</groupId>
<artifactId>helidon-tests-integration-yaml-parsing-nima</artifactId>
<version>4.0.0-SNAPSHOT</version>

<name>Helidon SnakeYAML Earlier Version Test - Nima</name>

<properties>
<mainClass>io.helidon.tests.integration.yamlparsing.Main</mainClass>
<selectedSnakeYamlVersion>1.32</selectedSnakeYamlVersion>
</properties>

<dependencyManagement>
<dependencies>
<!-- Verify that the app builds correctly and that config and OpenAPI document YAML parsing work with the older
SnakeYAML release. -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${selectedSnakeYamlVersion}</version>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.helidon.nima.webserver</groupId>
<artifactId>helidon-nima-webserver</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.nima.webclient</groupId>
<artifactId>helidon-nima-webclient</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.nima.http.media</groupId>
<artifactId>helidon-nima-http-media-jsonp</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.nima.openapi</groupId>
<artifactId>helidon-nima-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.helidon.nima.testing.junit5</groupId>
<artifactId>helidon-nima-testing-junit5-webserver</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<selected-snakeyaml-version>${selectedSnakeYamlVersion}</selected-snakeyaml-version>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-libs</id>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Loading