Skip to content

Commit ce1c77b

Browse files
mark-vieiraastefan
authored andcommitted
Use Java 8 for JDBC driver target compatibility version (elastic#82274)
1 parent cd74112 commit ce1c77b

File tree

8 files changed

+247
-8
lines changed

8 files changed

+247
-8
lines changed

x-pack/plugin/sql/jdbc/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ tasks.named("dependencyLicenses").configure {
3333
mapping from: /jackson-.*/, to: 'jackson'
3434
}
3535

36+
tasks.named("compileJava").configure {
37+
targetCompatibility = JavaVersion.VERSION_1_8
38+
sourceCompatibility = JavaVersion.VERSION_1_8
39+
}
40+
41+
3642
tasks.named("shadowJar").configure {
3743
relocate 'com.fasterxml', 'shadow.fasterxml'
3844
relocate('org.elasticsearch', 'shadow.org.elasticsearch') {

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcConfiguration.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
import java.net.URI;
1414
import java.net.URLDecoder;
15-
import java.nio.charset.StandardCharsets;
1615
import java.sql.DriverPropertyInfo;
1716
import java.time.ZoneId;
1817
import java.util.ArrayList;
@@ -148,8 +147,8 @@ private static Properties parseProperties(URI uri, String u) throws JdbcSQLExcep
148147
if (args.size() != 2) {
149148
throw new JdbcSQLException("Invalid parameter [" + param + "], format needs to be key=value");
150149
}
151-
final String key = URLDecoder.decode(args.get(0), StandardCharsets.UTF_8).trim();
152-
final String val = URLDecoder.decode(args.get(1), StandardCharsets.UTF_8);
150+
final String key = URLDecoder.decode(args.get(0), "UTF-8").trim();
151+
final String val = URLDecoder.decode(args.get(1), "UTF-8");
153152
// further validation happens in the constructor (since extra properties might be specified either way)
154153
props.setProperty(key, val);
155154
}

x-pack/plugin/sql/sql-client/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ dependencies {
1313
testImplementation(testArtifact(project(xpackModule('core'))))
1414
}
1515

16-
tasks.named("dependencyLicenses").configure {
17-
mapping from: /jackson-.*/, to: 'jackson'
16+
tasks.named("compileJava").configure {
17+
targetCompatibility = JavaVersion.VERSION_1_8
18+
sourceCompatibility = JavaVersion.VERSION_1_8
1819
}
1920

2021
tasks.named('forbiddenApisMain').configure {

x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/StringUtils.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,4 +301,14 @@ public static String asHexString(byte[] content, int offset, int length) {
301301
return buf.toString();
302302
}
303303

304+
public static String repeatString(String in, int count) {
305+
if (count < 0) {
306+
throw new IllegalArgumentException("negative count: " + count);
307+
}
308+
StringBuffer sb = new StringBuffer(in.length() * count);
309+
for (int i = 0; i < count; i++) {
310+
sb.append(in);
311+
}
312+
return sb.toString();
313+
}
304314
}

x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/UriUtils.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import java.util.Locale;
1616
import java.util.Map;
1717

18+
import static org.elasticsearch.xpack.sql.client.StringUtils.repeatString;
19+
1820
public final class UriUtils {
1921
private UriUtils() {
2022

@@ -109,7 +111,7 @@ private static String redactAttributeInString(String string, String attrName, Ch
109111
int attrIdx = string.toLowerCase(Locale.ROOT).indexOf(needle); // note: won't catch "valid" `=password[%20]+=` cases
110112
if (attrIdx >= 0) { // ex: `...=[value]password=foo...`
111113
int attrEndIdx = attrIdx + needle.length();
112-
return string.substring(0, attrEndIdx) + String.valueOf(replacement).repeat(string.length() - attrEndIdx);
114+
return string.substring(0, attrEndIdx) + repeatString(String.valueOf(replacement), string.length() - attrEndIdx);
113115
}
114116
return string;
115117
}
@@ -124,7 +126,7 @@ private static void redactValueForSimilarKey(
124126
for (String k : similar) {
125127
for (Map.Entry<String, String> e : attrs) {
126128
if (e.getKey().equals(k)) {
127-
e.setValue(String.valueOf(replacement).repeat(e.getValue().length()));
129+
e.setValue(repeatString(String.valueOf(replacement), e.getValue().length()));
128130
}
129131
}
130132
}
@@ -178,7 +180,7 @@ private static String editURI(URI uri, List<Map.Entry<Integer, Character>> fault
178180
sb.append("://");
179181
}
180182
if (uri.getRawUserInfo() != null) {
181-
sb.append("\0".repeat(uri.getRawUserInfo().length()));
183+
sb.append(repeatString("\0", uri.getRawUserInfo().length()));
182184
if (uri.getHost() != null) {
183185
sb.append('@');
184186
}

x-pack/plugin/sql/sql-proto/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ dependencies {
1818
testImplementation project(":test:framework")
1919
}
2020

21+
tasks.named("compileJava").configure {
22+
targetCompatibility = JavaVersion.VERSION_1_8
23+
sourceCompatibility = JavaVersion.VERSION_1_8
24+
}
25+
2126
tasks.named('forbiddenApisMain').configure {
2227
//sql does not depend on server, so only jdk signatures should be checked
2328
replaceSignatureFiles 'jdk-signatures'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.sql.proto.xcontent;
9+
10+
/**
11+
* NB: Light-clone from XContent library to keep JDBC driver independent.
12+
*
13+
* Extension point to customize the error message for unknown fields. We expect
14+
* Elasticsearch to plug a fancy implementation that uses Lucene's spelling
15+
* correction infrastructure to suggest corrections.
16+
*/
17+
public interface ErrorOnUnknown {
18+
/**
19+
* The implementation of this interface that was loaded from SPI.
20+
*/
21+
ErrorOnUnknown IMPLEMENTATION = findImplementation();
22+
23+
/**
24+
* Build the error message to use when {@link ObjectParser} encounters an unknown field.
25+
* @param parserName the name of the thing we're parsing
26+
* @param unknownField the field that we couldn't recognize
27+
* @param candidates the possible fields
28+
*/
29+
String errorMessage(String parserName, String unknownField, Iterable<String> candidates);
30+
31+
/**
32+
* Priority that this error message handler should be used.
33+
*/
34+
int priority();
35+
36+
static ErrorOnUnknown findImplementation() {
37+
ErrorOnUnknown best = new ErrorOnUnknown() {
38+
@Override
39+
public String errorMessage(String parserName, String unknownField, Iterable<String> candidates) {
40+
return "[" + parserName + "] unknown field [" + unknownField + "]";
41+
}
42+
43+
@Override
44+
public int priority() {
45+
return Integer.MIN_VALUE;
46+
}
47+
};
48+
return best;
49+
}
50+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.sql.proto.xcontent;
9+
10+
import java.util.Collections;
11+
import java.util.HashMap;
12+
import java.util.Locale;
13+
import java.util.Map;
14+
import java.util.regex.Pattern;
15+
import java.util.stream.Collectors;
16+
17+
/**
18+
* NB: Light-clone from XContent library to keep JDBC driver independent.
19+
*
20+
* A raw result of parsing media types from Accept or Content-Type headers.
21+
* It follow parsing and validates as per rules defined in https://tools.ietf.org/html/rfc7231#section-3.1.1.1
22+
* Can be resolved to <code>MediaType</code>
23+
* @see MediaType
24+
* @see MediaTypeRegistry
25+
*/
26+
public class ParsedMediaType {
27+
private final String originalHeaderValue;
28+
private final String type;
29+
private final String subType;
30+
private final Map<String, String> parameters;
31+
// tchar pattern as defined by RFC7230 section 3.2.6
32+
private static final Pattern TCHAR_PATTERN = Pattern.compile("[a-zA-z0-9!#$%&'*+\\-.\\^_`|~]+");
33+
34+
private ParsedMediaType(String originalHeaderValue, String type, String subType, Map<String, String> parameters) {
35+
this.originalHeaderValue = originalHeaderValue;
36+
this.type = type;
37+
this.subType = subType;
38+
this.parameters = Collections.unmodifiableMap(parameters);
39+
}
40+
41+
/**
42+
* The parsed mime type without the associated parameters. Will always return lowercase.
43+
*/
44+
public String mediaTypeWithoutParameters() {
45+
return type + "/" + subType;
46+
}
47+
48+
public Map<String, String> getParameters() {
49+
return parameters;
50+
}
51+
52+
/**
53+
* Parses a header value into it's parts.
54+
* follows https://tools.ietf.org/html/rfc7231#section-3.1.1.1
55+
* but allows only single media type. Media ranges will be ignored (treated as not provided)
56+
* Note: parsing can return null, but it will throw exceptions once https://github.com/elastic/elasticsearch/issues/63080 is done
57+
* TODO Do not rely on nulls
58+
*
59+
* @return a {@link ParsedMediaType} if the header could be parsed.
60+
* @throws IllegalArgumentException if the header is malformed
61+
*/
62+
public static ParsedMediaType parseMediaType(String headerValue) {
63+
if (headerValue != null) {
64+
if (isMediaRange(headerValue) || "*/*".equals(headerValue)) {
65+
return null;
66+
}
67+
final String[] elements = headerValue.toLowerCase(Locale.ROOT).split("[\\s\\t]*;");
68+
69+
final String[] splitMediaType = elements[0].split("/");
70+
if ((splitMediaType.length == 2
71+
&& TCHAR_PATTERN.matcher(splitMediaType[0].trim()).matches()
72+
&& TCHAR_PATTERN.matcher(splitMediaType[1].trim()).matches()) == false) {
73+
throw new IllegalArgumentException("invalid media-type [" + headerValue + "]");
74+
}
75+
if (elements.length == 1) {
76+
return new ParsedMediaType(headerValue, splitMediaType[0].trim(), splitMediaType[1].trim(), new HashMap<>());
77+
} else {
78+
Map<String, String> parameters = new HashMap<>();
79+
for (int i = 1; i < elements.length; i++) {
80+
String paramsAsString = elements[i].trim();
81+
if (paramsAsString.isEmpty()) {
82+
continue;
83+
}
84+
// spaces are allowed between parameters, but not between '=' sign
85+
String[] keyValueParam = paramsAsString.split("=");
86+
if (keyValueParam.length != 2 || hasTrailingSpace(keyValueParam[0]) || hasLeadingSpace(keyValueParam[1])) {
87+
throw new IllegalArgumentException("invalid parameters for header [" + headerValue + "]");
88+
}
89+
String parameterName = keyValueParam[0].toLowerCase(Locale.ROOT).trim();
90+
String parameterValue = keyValueParam[1].toLowerCase(Locale.ROOT).trim();
91+
parameters.put(parameterName, parameterValue);
92+
}
93+
return new ParsedMediaType(
94+
headerValue,
95+
splitMediaType[0].trim().toLowerCase(Locale.ROOT),
96+
splitMediaType[1].trim().toLowerCase(Locale.ROOT),
97+
parameters
98+
);
99+
}
100+
}
101+
return null;
102+
}
103+
104+
// simplistic check for media ranges. do not validate if this is a correct header
105+
private static boolean isMediaRange(String headerValue) {
106+
return headerValue.contains(",");
107+
}
108+
109+
private static boolean hasTrailingSpace(String s) {
110+
return s.length() == 0 || Character.isWhitespace(s.charAt(s.length() - 1));
111+
}
112+
113+
private static boolean hasLeadingSpace(String s) {
114+
return s.length() == 0 || Character.isWhitespace(s.charAt(0));
115+
}
116+
117+
/**
118+
* Resolves this instance to a MediaType instance defined in given MediaTypeRegistry.
119+
* Performs validation against parameters.
120+
* @param mediaTypeRegistry a registry where a mapping between a raw media type to an instance MediaType is defined
121+
* @return a MediaType instance or null if no media type could be found or if a known parameter do not passes validation
122+
*/
123+
public <T extends MediaType> T toMediaType(MediaTypeRegistry<T> mediaTypeRegistry) {
124+
T someType = mediaTypeRegistry.typeWithSubtypeToMediaType(mediaTypeWithoutParameters());
125+
126+
if (someType != null) {
127+
Map<String, Pattern> registeredParams = mediaTypeRegistry.parametersFor(mediaTypeWithoutParameters());
128+
for (Map.Entry<String, String> givenParamEntry : parameters.entrySet()) {
129+
if (isValidParameter(givenParamEntry.getKey(), givenParamEntry.getValue(), registeredParams) == false) {
130+
return null;
131+
}
132+
}
133+
return someType;
134+
}
135+
return null;
136+
}
137+
138+
private boolean isValidParameter(String paramName, String value, Map<String, Pattern> registeredParams) {
139+
if (registeredParams.containsKey(paramName)) {
140+
Pattern regex = registeredParams.get(paramName);
141+
return regex.matcher(value).matches();
142+
}
143+
// TODO undefined parameters are allowed until https://github.com/elastic/elasticsearch/issues/63080
144+
return true;
145+
}
146+
147+
@Override
148+
public String toString() {
149+
return originalHeaderValue;
150+
}
151+
152+
public String responseContentTypeHeader() {
153+
return mediaTypeWithoutParameters() + formatParameters(parameters);
154+
}
155+
156+
// used in testing
157+
public String responseContentTypeHeader(Map<String, String> params) {
158+
return mediaTypeWithoutParameters() + formatParameters(params);
159+
}
160+
161+
private String formatParameters(Map<String, String> params) {
162+
String joined = params.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(";"));
163+
return joined.isEmpty() ? "" : ";" + joined;
164+
}
165+
166+
}

0 commit comments

Comments
 (0)