Skip to content

Commit 45b3392

Browse files
authored
[Core] Fix NoSuchMethodError PrintWriter(OutputStream, boolean, Charset) (#2578)
The `PrintWriter(OutputStream, boolean, Charset)` method was introduced in Java 10 and can't yet be used on Java 8. And while support for Open JDK 8 has ended 2 months ago, Adoptium and Corretto are receiving support until 2026 and Azul and Oracle have support until 2030. As such we're replacing the `PrintWriter` implementation with our own "good enough" version. Fixes: #2575
1 parent 2d41214 commit 45b3392

File tree

7 files changed

+127
-13
lines changed

7 files changed

+127
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1616
### Removed
1717

1818
### Fixed
19+
* [Core] Fix NoSuchMethodError `PrintWriter(OutputStream, boolean, Charset)` ([#2578](https://github.com/cucumber/cucumber-jvm/pull/2578) M.P. Korstanje)
1920

2021
## [7.4.0] (2022-06-22)
2122

core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.io.File;
2323
import java.io.IOException;
2424
import java.io.OutputStream;
25-
import java.io.PrintWriter;
2625
import java.io.StringReader;
2726
import java.net.URI;
2827
import java.net.URISyntaxException;
@@ -52,7 +51,7 @@ public final class PrettyFormatter implements ConcurrentEventListener, ColorAwar
5251

5352
private final Map<UUID, Integer> commentStartIndex = new HashMap<>();
5453

55-
private final PrintWriter out;
54+
private final UTF8PrintWriter out;
5655
private Formats formats = ansi();
5756

5857
public PrettyFormatter(OutputStream out) {

core/src/main/java/io/cucumber/core/plugin/ProgressFormatter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import io.cucumber.plugin.event.TestStepFinished;
1010

1111
import java.io.OutputStream;
12-
import java.io.PrintWriter;
1312
import java.util.HashMap;
1413
import java.util.Map;
1514

@@ -36,7 +35,7 @@ public final class ProgressFormatter implements ConcurrentEventListener, ColorAw
3635
}
3736
};
3837

39-
private final PrintWriter out;
38+
private final UTF8PrintWriter out;
4039
private boolean monochrome = false;
4140

4241
public ProgressFormatter(OutputStream out) {

core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import io.cucumber.plugin.event.TestRunFinished;
99

1010
import java.io.OutputStream;
11-
import java.io.PrintWriter;
1211
import java.net.URI;
1312
import java.util.ArrayList;
1413
import java.util.Collection;
@@ -24,7 +23,7 @@
2423
*/
2524
public final class RerunFormatter implements ConcurrentEventListener {
2625

27-
private final PrintWriter out;
26+
private final UTF8PrintWriter out;
2827
private final Map<URI, Collection<Integer>> featureAndFailedLinesMapping = new HashMap<>();
2928

3029
public RerunFormatter(OutputStream out) {
@@ -46,7 +45,7 @@ private void handleTestCaseFinished(TestCaseFinished event) {
4645
private void finishReport() {
4746
for (Map.Entry<URI, Collection<Integer>> entry : featureAndFailedLinesMapping.entrySet()) {
4847
FeatureWithLines featureWithLines = create(relativize(entry.getKey()), entry.getValue());
49-
out.println(featureWithLines);
48+
out.println(featureWithLines.toString());
5049
}
5150

5251
out.close();
Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,82 @@
11
package io.cucumber.core.plugin;
22

3+
import java.io.Closeable;
4+
import java.io.Flushable;
5+
import java.io.IOException;
36
import java.io.OutputStream;
4-
import java.io.PrintWriter;
5-
import java.nio.charset.StandardCharsets;
7+
import java.io.OutputStreamWriter;
68

7-
final class UTF8PrintWriter extends PrintWriter {
9+
/**
10+
* A "good enough" PrintWriter implementation that writes UTF-8 and rethrows all
11+
* exceptions as runtime exceptions.
12+
*/
13+
final class UTF8PrintWriter implements Appendable, Closeable, Flushable {
14+
15+
private final OutputStreamWriter out;
816

917
UTF8PrintWriter(OutputStream out) {
10-
super(out, false, StandardCharsets.UTF_8);
18+
this.out = new UTF8OutputStreamWriter(out);
19+
}
20+
21+
public void println() {
22+
try {
23+
out.write(System.lineSeparator());
24+
} catch (IOException e) {
25+
throw new RuntimeException(e);
26+
}
27+
}
28+
29+
public void println(String s) {
30+
try {
31+
out.write(s);
32+
out.write(System.lineSeparator());
33+
} catch (IOException e) {
34+
throw new RuntimeException(e);
35+
}
36+
}
37+
38+
public void flush() {
39+
try {
40+
out.flush();
41+
} catch (IOException e) {
42+
throw new RuntimeException(e);
43+
}
44+
}
45+
46+
@Override
47+
public void close() {
48+
try {
49+
out.close();
50+
} catch (IOException e) {
51+
throw new RuntimeException(e);
52+
}
53+
}
54+
55+
@Override
56+
public Appendable append(CharSequence csq) {
57+
try {
58+
return out.append(csq);
59+
} catch (IOException e) {
60+
throw new RuntimeException(e);
61+
}
62+
}
63+
64+
@Override
65+
public Appendable append(CharSequence csq, int start, int end) {
66+
try {
67+
return out.append(csq, start, end);
68+
} catch (IOException e) {
69+
throw new RuntimeException(e);
70+
}
71+
}
72+
73+
@Override
74+
public Appendable append(char c) {
75+
try {
76+
return out.append(c);
77+
} catch (IOException e) {
78+
throw new RuntimeException(e);
79+
}
1180
}
1281

1382
}

core/src/main/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import io.cucumber.plugin.event.TestStepFinished;
1010

1111
import java.io.OutputStream;
12-
import java.io.PrintWriter;
1312
import java.util.Map;
1413
import java.util.Map.Entry;
1514
import java.util.Set;
@@ -24,7 +23,7 @@ public final class UnusedStepsSummaryPrinter implements ColorAware, ConcurrentEv
2423

2524
private final Map<String, String> registeredSteps = new TreeMap<>();
2625
private final Set<String> usedSteps = new TreeSet<>();
27-
private final PrintWriter out;
26+
private final UTF8PrintWriter out;
2827
private Formats formats = ansi();
2928

3029
public UnusedStepsSummaryPrinter(OutputStream out) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.cucumber.core.plugin;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.io.ByteArrayOutputStream;
6+
7+
import static io.cucumber.core.plugin.BytesEqualTo.isBytesEqualTo;
8+
import static org.hamcrest.MatcherAssert.assertThat;
9+
10+
class UTF8PrintWriterTest {
11+
12+
final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
13+
final UTF8PrintWriter out = new UTF8PrintWriter(bytes);
14+
15+
@Test
16+
void println() {
17+
out.println();
18+
out.println("Hello ");
19+
out.close();
20+
assertThat(bytes, isBytesEqualTo(
21+
System.lineSeparator() + "Hello " + System.lineSeparator()));
22+
}
23+
24+
@Test
25+
void append() {
26+
out.append("Hello");
27+
out.append("Hello World", 5, 11);
28+
out.close();
29+
assertThat(bytes, isBytesEqualTo("Hello World"));
30+
}
31+
32+
@Test
33+
void flush() {
34+
out.append("Hello");
35+
assertThat(bytes, isBytesEqualTo(""));
36+
out.flush();
37+
assertThat(bytes, isBytesEqualTo("Hello"));
38+
}
39+
40+
@Test
41+
void close() {
42+
out.append("Hello");
43+
assertThat(bytes, isBytesEqualTo(""));
44+
out.close();
45+
assertThat(bytes, isBytesEqualTo("Hello"));
46+
}
47+
48+
}

0 commit comments

Comments
 (0)