Skip to content

Commit b89efa3

Browse files
authored
4.x Fix warnings logged from SnakeYAML; update SnakeYAML to 2.5 (#10754)
* Avoid innocuous but alarming warning messages * Slight clean-up in the new test
1 parent 5719cda commit b89efa3

File tree

7 files changed

+232
-38
lines changed

7 files changed

+232
-38
lines changed

dependencies/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
<version.lib.reactivestreams>1.0.4</version.lib.reactivestreams>
162162
<version.lib.slf4j>2.0.16</version.lib.slf4j>
163163
<version.lib.smallrye-openapi>3.3.4</version.lib.smallrye-openapi>
164-
<version.lib.snakeyaml>2.4</version.lib.snakeyaml>
164+
<version.lib.snakeyaml>2.5</version.lib.snakeyaml>
165165
<version.lib.testcontainers>1.19.8</version.lib.testcontainers>
166166
<version.lib.typesafe-config>1.4.4</version.lib.typesafe-config>
167167
<version.lib.tyrus>2.1.5</version.lib.tyrus>

microprofile/openapi/pom.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,29 @@
310310
<sun.net.http.allowRestrictedHeaders>true</sun.net.http.allowRestrictedHeaders>
311311
</systemPropertyVariables>
312312
</configuration>
313+
<executions>
314+
<execution>
315+
<id>default-test</id>
316+
<configuration>
317+
<excludes>
318+
<exclude>**/TestLogWarningFix.java</exclude>
319+
</excludes>
320+
</configuration>
321+
</execution>
322+
<execution>
323+
<id>log-warning-test</id>
324+
<goals>
325+
<goal>test</goal>
326+
</goals>
327+
<phase>test</phase>
328+
<configuration>
329+
<!-- Run this separately so it uses a distinct OpenApiHelper with SnakeYAML warning logging on. -->
330+
<includes>
331+
<include>**/TestLogWarningFix.java</include>
332+
</includes>
333+
</configuration>
334+
</execution>
335+
</executions>
313336
</plugin>
314337
</plugins>
315338
</build>

microprofile/openapi/src/main/java/io/helidon/microprofile/openapi/ExpandedTypeDescription.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2019, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,7 +31,6 @@
3131
import org.yaml.snakeyaml.error.YAMLException;
3232
import org.yaml.snakeyaml.introspector.MethodProperty;
3333
import org.yaml.snakeyaml.introspector.Property;
34-
import org.yaml.snakeyaml.introspector.PropertySubstitute;
3534
import org.yaml.snakeyaml.introspector.PropertyUtils;
3635
import org.yaml.snakeyaml.nodes.MappingNode;
3736
import org.yaml.snakeyaml.nodes.Node;
@@ -78,7 +77,7 @@
7877
*/
7978
public class ExpandedTypeDescription extends TypeDescription {
8079

81-
static final PropertyUtils PROPERTY_UTILS = new PropertyUtils();
80+
static final PropertyUtils PROPERTY_UTILS = new ExtendedPropertyUtils();
8281

8382
private static final String EXTENSION_PROPERTY_PREFIX = "x-";
8483

@@ -87,6 +86,7 @@ public class ExpandedTypeDescription extends TypeDescription {
8786
private ExpandedTypeDescription(Class<?> clazz, Class<?> impl) {
8887
super(clazz, null, impl);
8988
this.impl = impl;
89+
setPropertyUtils(PROPERTY_UTILS);
9090
}
9191

9292
/**
@@ -110,39 +110,33 @@ public static ExpandedTypeDescription create(Class<?> clazz, Class<?> impl) {
110110
} else {
111111
result = new ExpandedTypeDescription(clazz, impl);
112112
}
113-
result.setPropertyUtils(PROPERTY_UTILS);
114113
return result;
115114
}
116115

117116
/**
118117
* Adds property handling for a {@code $ref} reference.
119118
*
120119
* @return this type description
120+
* @deprecated No need to invoke addRef any longer; refs are handled by the custom property utils implementation.
121121
*/
122+
@Deprecated(since = "4.3.2", forRemoval = true)
122123
public ExpandedTypeDescription addRef() {
123-
PropertySubstitute sub = new PropertySubstitute("ref", String.class, "getRef", "setRef");
124-
sub.setTargetType(impl);
125-
substituteProperty(sub);
126124
return this;
127125
}
128126

129127
/**
130128
* Adds property handling for extensions.
131129
*
132130
* @return this type description
131+
* @deprecated No need to invoke addExtensions any longer; extensions are handled by the custom property utils implementation.
133132
*/
133+
@Deprecated(since = "4.3.2", forRemoval = true)
134134
public ExpandedTypeDescription addExtensions() {
135-
PropertySubstitute sub = new PropertySubstitute("extensions", Map.class, "getExtensions", "setExtensions");
136-
sub.setTargetType(impl);
137-
substituteProperty(sub);
138135
return this;
139136
}
140137

141138
@Override
142139
public Property getProperty(String name) {
143-
if (isExtension(name)) {
144-
return new ExtensionProperty(name);
145-
}
146140
if (isRef(name)) {
147141
return new RenamedProperty(this.getType(), "ref");
148142
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.microprofile.openapi;
18+
19+
import java.beans.IntrospectionException;
20+
import java.beans.PropertyDescriptor;
21+
import java.util.Map;
22+
import java.util.Set;
23+
24+
import org.eclipse.microprofile.openapi.models.Extensible;
25+
import org.eclipse.microprofile.openapi.models.Reference;
26+
import org.yaml.snakeyaml.introspector.BeanAccess;
27+
import org.yaml.snakeyaml.introspector.MethodProperty;
28+
import org.yaml.snakeyaml.introspector.Property;
29+
import org.yaml.snakeyaml.introspector.PropertyUtils;
30+
31+
/**
32+
* Specialized SnakeYAML implementation of {@link org.yaml.snakeyaml.introspector.PropertyUtils} that essentially adds ad hoc
33+
* properties to the SnakeYAML type if the underlying type meets certain criteria.
34+
*/
35+
class ExtendedPropertyUtils extends PropertyUtils {
36+
37+
private static final Map<Class<?>, InferredProperty> TYPE_INFO = Map.of(
38+
Extensible.class, InferredProperty.create("extensions"),
39+
Reference.class, InferredProperty.create("ref"));
40+
41+
@Override
42+
public Set<Property> getProperties(Class<?> type, BeanAccess bAccess) {
43+
var result = super.getProperties(type, bAccess);
44+
TYPE_INFO.forEach((t, info) -> {
45+
if (t.isAssignableFrom(type)) {
46+
result.add(info.property(type));
47+
}
48+
});
49+
return result;
50+
}
51+
52+
@Override
53+
protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess) {
54+
var result = super.getPropertiesMap(type, bAccess);
55+
TYPE_INFO.forEach((t, info) -> {
56+
if (t.isAssignableFrom(type)) {
57+
result.put(info.propertyName, info.property(type));
58+
}
59+
});
60+
return result;
61+
}
62+
63+
private record InferredProperty(String propertyName) {
64+
65+
static InferredProperty create(String propertyName) {
66+
return new InferredProperty(propertyName);
67+
}
68+
69+
Property property(Class<?> type) {
70+
String capitalizedPropertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
71+
try {
72+
return new MethodProperty(new PropertyDescriptor(propertyName,
73+
type,
74+
"get" + capitalizedPropertyName,
75+
"set" + capitalizedPropertyName));
76+
} catch (IntrospectionException e) {
77+
throw new RuntimeException(e);
78+
}
79+
}
80+
}
81+
}

microprofile/openapi/src/main/java/io/helidon/microprofile/openapi/OpenApiHelper.java

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,23 +22,26 @@
2222

2323
import io.helidon.common.LazyValue;
2424

25-
import org.eclipse.microprofile.openapi.models.Extensible;
2625
import org.eclipse.microprofile.openapi.models.Operation;
2726
import org.eclipse.microprofile.openapi.models.PathItem;
28-
import org.eclipse.microprofile.openapi.models.Reference;
2927
import org.eclipse.microprofile.openapi.models.media.Schema;
3028
import org.eclipse.microprofile.openapi.models.servers.ServerVariable;
31-
import org.yaml.snakeyaml.TypeDescription;
3229
import org.yaml.snakeyaml.introspector.Property;
3330

3431
/**
3532
* Wraps generated parser and uses {@link ExpandedTypeDescription} as its type.
3633
*/
3734
final class OpenApiHelper {
3835

36+
@Deprecated(since = "4.3.2", forRemoval = true)
37+
private static final String WARNINGS_ENABLED_PROPERTY_NAME = "openapi.parsing.warnings.enabled";
38+
39+
private static final System.Logger LOGGER = System.getLogger(OpenApiHelper.class.getName());
40+
3941
// Temporary to suppress SnakeYAML warnings.
4042
// As a static we keep a reference to the logger, thereby making sure any changes we make are persistent. (JUL holds
4143
// only weak references to loggers internally.)
44+
@Deprecated(since = "4.3.2", forRemoval = true)
4245
private static final java.util.logging.Logger SNAKE_YAML_INTROSPECTOR_LOGGER =
4346
java.util.logging.Logger.getLogger(org.yaml.snakeyaml.introspector.PropertySubstitute.class.getPackage().getName());
4447

@@ -48,10 +51,7 @@ final class OpenApiHelper {
4851
private final SnakeYAMLParserHelper<ExpandedTypeDescription> generatedHelper;
4952

5053
private OpenApiHelper() {
51-
boolean warningsEnabled = Boolean.getBoolean("openapi.parsing.warnings.enabled");
52-
if (SNAKE_YAML_INTROSPECTOR_LOGGER.isLoggable(java.util.logging.Level.WARNING) && !warningsEnabled) {
53-
SNAKE_YAML_INTROSPECTOR_LOGGER.setLevel(java.util.logging.Level.SEVERE);
54-
}
54+
suppressWarningsIfRequested();
5555
this.generatedHelper = SnakeYAMLParserHelper.create(ExpandedTypeDescription::create);
5656
adjustTypeDescriptions(generatedHelper.types());
5757
}
@@ -65,6 +65,28 @@ static Map<Class<?>, ExpandedTypeDescription> types() {
6565
return INSTANCE.get().generatedHelper.types();
6666
}
6767

68+
69+
@Deprecated(since = "4.3.2", forRemoval = true)
70+
private static void suppressWarningsIfRequested() {
71+
// Previously, Helidon by default forced a SnakeYAML logger's level to SEVERE in order to suppress innocuous but alarming
72+
// warnings. (Helidon used SnakeYAML property substitutes for properties that did not already exist on
73+
// certain types, and although that did what we wanted functionally it also triggered warning messages.)
74+
// Users could set openapi.parsing.warnings.enabled to true to re-enable warnings for that logger.
75+
// Helidon no longer uses the property substitute approach for those purposes, so Helidon no longer suppresses the
76+
// warnings by default so there is no need for the property. Helidon still honors it if specified but logs its use as
77+
// deprecated.
78+
String warningsEnabledText = System.getProperty(WARNINGS_ENABLED_PROPERTY_NAME);
79+
if (warningsEnabledText != null) {
80+
LOGGER.log(System.Logger.Level.INFO, String.format("""
81+
Use of the property %s + " is deprecated. \
82+
Helidon logs parsing warnings by default but honors the property setting.""", WARNINGS_ENABLED_PROPERTY_NAME));
83+
boolean warningsEnabled = Boolean.parseBoolean(warningsEnabledText);
84+
if (SNAKE_YAML_INTROSPECTOR_LOGGER.isLoggable(java.util.logging.Level.WARNING) && !warningsEnabled) {
85+
SNAKE_YAML_INTROSPECTOR_LOGGER.setLevel(java.util.logging.Level.SEVERE);
86+
}
87+
}
88+
}
89+
6890
private static void adjustTypeDescriptions(Map<Class<?>, ExpandedTypeDescription> types) {
6991
// We need to adjust the {@code TypeDescription} objects set up by the generated {@code SnakeYAMLParserHelper} class
7092
// because there are some OpenAPI-specific issues that the general-purpose helper generator cannot know about.
@@ -91,27 +113,12 @@ private static void adjustTypeDescriptions(Map<Class<?>, ExpandedTypeDescription
91113
// SnakeYAML derives properties only from methods declared directly by each OpenAPI interface, not from methods defined
92114
// on other interfaces which the original one extends. Those we have to handle explicitly.
93115
for (ExpandedTypeDescription td : types.values()) {
94-
if (Extensible.class.isAssignableFrom(td.getType())) {
95-
td.addExtensions();
96-
}
97116
Property defaultProperty = td.defaultProperty();
98117
if (defaultProperty != null) {
99118
td.substituteProperty("default", defaultProperty.getType(), "getDefaultValue", "setDefaultValue");
100119
td.addExcludes("defaultValue");
101120
}
102-
if (isRef(td)) {
103-
td.addRef();
104-
}
105-
}
106-
}
107-
108-
private static boolean isRef(TypeDescription td) {
109-
for (Class<?> c : td.getType().getInterfaces()) {
110-
if (c.equals(Reference.class)) {
111-
return true;
112-
}
113121
}
114-
return false;
115122
}
116123

117124
private static String getter(PathItem.HttpMethod method) {

microprofile/openapi/src/main/java/io/helidon/microprofile/openapi/OpenApiSerializer.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2020, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -118,6 +118,7 @@ static class CustomRepresenter extends Representer {
118118

119119
CustomRepresenter(Map<Class<?>, ExpandedTypeDescription> types, DumperOptions dumperOptions, ScalarStyle stringStyle) {
120120
super(dumperOptions);
121+
setPropertyUtils(ExpandedTypeDescription.PROPERTY_UTILS);
121122
this.stringStyle = stringStyle;
122123
types.values().stream()
123124
.map(ImplTypeDescription::new)
@@ -149,6 +150,15 @@ protected Node representMapping(Tag tag, Map<?, ?> mapping, DumperOptions.FlowSt
149150
});
150151
}
151152
}
153+
if (mapping instanceof Reference<?> reference) {
154+
List<NodeTuple> tuples = ((MappingNode) result).getValue();
155+
String ref = reference.getRef();
156+
if (ref != null) {
157+
NodeTuple refTuple = new NodeTuple(new ScalarNode(Tag.STR, "$ref", null, null, stringStyle),
158+
represent(ref));
159+
tuples.add(refTuple);
160+
}
161+
}
152162
return result;
153163
}
154164

0 commit comments

Comments
 (0)