Skip to content

Commit 1ebfd84

Browse files
committed
convert java 6 console api notes to executable example
1 parent 2dcbda5 commit 1ebfd84

11 files changed

Lines changed: 259 additions & 77 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ This project uses simple release notes rather than a strict changelog taxonomy.
2929
- Converted Java 4 integrated security APIs from notes-only material into executable local JCA/JCE examples.
3030
- Converted Java 17 strong encapsulation from notes-only material into an executable child-JVM access-boundary example.
3131
- Converted Java 2 `strictfp` from notes-only material into an executable example that also captures the Java 17+ compiler warning.
32+
- Converted Java 6 Console API from notes-only material into an executable boundary example with deterministic fake-console tests.
3233

3334
## v1.4.1
3435

‎docs/feature-map.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Learning notes: [java06 README](../src/main/java/net/jrodolfo/java_evolution/jav
8888
| Navigable collections and deques | `NavigableCollectionExamples` | `NavigableCollectionExamplesTest` |
8989
| Scripting support | [`scripting/ScriptingSupportNotes`](../src/main/java/net/jrodolfo/java_evolution/java06/scripting/ScriptingSupportNotes.java) | [`ScriptingSupportNotesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/scripting/ScriptingSupportNotesTest.java) |
9090
| Compiler API | [`compiler_api/CompilerApiExamples`](../src/main/java/net/jrodolfo/java_evolution/java06/compiler_api/CompilerApiExamples.java) | [`CompilerApiExamplesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/compiler_api/CompilerApiExamplesTest.java) |
91-
| Console API | [`console_api/ConsoleApiNotes`](../src/main/java/net/jrodolfo/java_evolution/java06/console_api/ConsoleApiNotes.java) | [`ConsoleApiNotesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/console_api/ConsoleApiNotesTest.java) |
91+
| Console API | [`console_api/ConsoleApiExamples`](../src/main/java/net/jrodolfo/java_evolution/java06/console_api/ConsoleApiExamples.java) | [`ConsoleApiExamplesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/console_api/ConsoleApiExamplesTest.java) |
9292
| Monitoring and management | [`monitoring_management/MonitoringManagementExamples`](../src/main/java/net/jrodolfo/java_evolution/java06/monitoring_management/MonitoringManagementExamples.java) | [`MonitoringManagementExamplesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/monitoring_management/MonitoringManagementExamplesTest.java) |
9393
| Web-service support | [`web_services/WebServiceSupportNotes`](../src/main/java/net/jrodolfo/java_evolution/java06/web_services/WebServiceSupportNotes.java) | [`WebServiceSupportNotesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/web_services/WebServiceSupportNotesTest.java) |
9494

‎docs/jep-index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Java 1-7 predate the modern JEP process used elsewhere in this index. Their entr
3434
| [Oracle Java SE 6 features](https://www.oracle.com/java/technologies/javase/features.html) | Navigable collections, scripting, compiler API, console, monitoring, web-service support | Final, Examples and Explanatory Modules |
3535
| [Java SE 6 scripting guide](https://docs.oracle.com/javase/6/docs/technotes/guides/scripting/) | Scripting support | Final, Explanatory Module |
3636
| [JavaCompiler API](https://docs.oracle.com/javase/6/docs/api/javax/tools/JavaCompiler.html) | Compiler API | Final, Tooling, Executable Example |
37-
| [Console API](https://docs.oracle.com/javase/6/docs/api/java/io/Console.html) | Console API | Final, Tooling, Explanatory Module |
37+
| [Console API](https://docs.oracle.com/javase/6/docs/api/java/io/Console.html) | Console API | Final, Tooling, Executable Example |
3838
| [Project Coin / JSR 334](https://cr.openjdk.org/~darcy/ProjectCoin/ProjectCoin-Documentation-v0.9375.html) | Try-with-resources, multi-catch, diamond operator, strings in switch, binary literals, numeric underscores | Final, Example |
3939
| [JDK 7 adoption guide](https://docs.oracle.com/javase/7/docs/webnotes/adoptionGuide/) | NIO.2 and Java 7 migration features | Final, Example |
4040
| [Java SE 7 concurrency enhancements](https://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/changes7.html) | Fork/join framework | Final, Example |

‎docs/status-matrix.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Status values may be combined, such as `final, security, executable example`, wh
7070
| 6 | Navigable collections and deques | final, executable example | `NavigableCollectionExamples` | `NavigableCollectionExamplesTest` | [Oracle Java SE 6 features](https://www.oracle.com/java/technologies/javase/features.html) |
7171
| 6 | Scripting support | final, explanatory module | [`ScriptingSupportNotes`](../src/main/java/net/jrodolfo/java_evolution/java06/scripting/ScriptingSupportNotes.java) | [`ScriptingSupportNotesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/scripting/ScriptingSupportNotesTest.java) | [Java SE 6 scripting guide](https://docs.oracle.com/javase/6/docs/technotes/guides/scripting/) |
7272
| 6 | Compiler API | final, tooling, executable example | [`CompilerApiExamples`](../src/main/java/net/jrodolfo/java_evolution/java06/compiler_api/CompilerApiExamples.java) | [`CompilerApiExamplesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/compiler_api/CompilerApiExamplesTest.java) | [JavaCompiler API](https://docs.oracle.com/javase/6/docs/api/javax/tools/JavaCompiler.html) |
73-
| 6 | Console API | final, tooling, explanatory module | [`ConsoleApiNotes`](../src/main/java/net/jrodolfo/java_evolution/java06/console_api/ConsoleApiNotes.java) | [`ConsoleApiNotesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/console_api/ConsoleApiNotesTest.java) | [Console API](https://docs.oracle.com/javase/6/docs/api/java/io/Console.html) |
73+
| 6 | Console API | final, tooling, executable example | [`ConsoleApiExamples`](../src/main/java/net/jrodolfo/java_evolution/java06/console_api/ConsoleApiExamples.java) | [`ConsoleApiExamplesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/console_api/ConsoleApiExamplesTest.java) | [Console API](https://docs.oracle.com/javase/6/docs/api/java/io/Console.html) |
7474
| 6 | Monitoring and management | runtime, tooling, executable example | [`MonitoringManagementExamples`](../src/main/java/net/jrodolfo/java_evolution/java06/monitoring_management/MonitoringManagementExamples.java) | [`MonitoringManagementExamplesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/monitoring_management/MonitoringManagementExamplesTest.java) | [Java SE 6 monitoring and management](https://docs.oracle.com/javase/6/docs/technotes/guides/management/index.html) |
7575
| 6 | Web-service support | final, explanatory module | [`WebServiceSupportNotes`](../src/main/java/net/jrodolfo/java_evolution/java06/web_services/WebServiceSupportNotes.java) | [`WebServiceSupportNotesTest`](../src/test/java/net/jrodolfo/java_evolution/java06/web_services/WebServiceSupportNotesTest.java) | [Oracle Java SE 6 features](https://www.oracle.com/java/technologies/javase/features.html) |
7676
| 7 | Try-with-resources | final, executable example | `TryWithResourcesStatementExamples` | `TryWithResourcesStatementExamplesTest` | [Project Coin / JSR 334](https://cr.openjdk.org/~darcy/ProjectCoin/ProjectCoin-Documentation-v0.9375.html) |

‎src/main/java/net/jrodolfo/java_evolution/java06/README.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Released: December 2006 as Java SE 6.
44

55
Java 6 was more of a platform and tooling release than a language release. It added standard scripting integration, a compiler API, better monitoring and management support, console password prompting, web-service APIs, desktop improvements, and library refinements such as deques and navigable collections.
66

7-
This repository keeps several Java 6 topics as explanatory modules because a faithful demonstration often depends on interactive terminals, optional engines, removed bundled APIs, or runtime attachment behavior. The executable examples focus on collection APIs, compiler invocation, and local MXBean monitoring APIs that still compile and run naturally on JDK 25.
7+
This repository keeps several Java 6 topics as explanatory modules because a faithful demonstration often depends on optional engines, removed bundled APIs, or runtime attachment behavior. The executable examples focus on collection APIs, compiler invocation, console boundary design, and local MXBean monitoring APIs that still compile and run naturally on JDK 25.
88

99
## Navigable Collections And Deques
1010

@@ -40,11 +40,11 @@ Test: `CompilerApiExamplesTest`
4040

4141
Java 6 added `java.io.Console`, including password-reading methods that avoid echoing sensitive input.
4242

43-
This is an explanatory module because `System.console()` is commonly `null` when code runs in Maven, IDEs, CI, or redirected processes.
43+
This module demonstrates how to isolate `System.console()` at the process boundary, handle the normal `null` result, and test command-line behavior without requiring Maven to run inside an interactive terminal.
4444

45-
Explanatory module: [`console_api`](console_api/README.md)
45+
Example module: [`console_api`](console_api/README.md)
4646

47-
Test: `ConsoleApiNotesTest`
47+
Test: `ConsoleApiExamplesTest`
4848

4949
## Monitoring And Management
5050

@@ -68,13 +68,13 @@ Test: `WebServiceSupportNotesTest`
6868

6969
## How To Read This Package
7070

71-
Start with `NavigableCollectionExamples`, `compiler_api/CompilerApiExamples`, and `monitoring_management/MonitoringManagementExamples` for executable library, tooling, and runtime-observability features. Then read the explanatory modules to understand the Java 6 platform direction without forcing obsolete or environment-sensitive APIs into the Maven test suite.
71+
Start with `NavigableCollectionExamples`, `compiler_api/CompilerApiExamples`, `console_api/ConsoleApiExamples`, and `monitoring_management/MonitoringManagementExamples` for executable library, tooling, command-line boundary, and runtime-observability features. Then read the explanatory modules to understand the Java 6 platform direction without forcing obsolete APIs into the Maven test suite.
7272

7373
Run the focused tests:
7474

7575
```bash
7676
mvn -Dtest=NavigableCollectionExamplesTest test
77-
mvn -Dtest=ScriptingSupportNotesTest,CompilerApiExamplesTest,ConsoleApiNotesTest test
77+
mvn -Dtest=ScriptingSupportNotesTest,CompilerApiExamplesTest,ConsoleApiExamplesTest test
7878
mvn -Dtest=MonitoringManagementExamplesTest,WebServiceSupportNotesTest test
7979
```
8080

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package net.jrodolfo.java_evolution.java06.console_api;
2+
3+
import java.io.Console;
4+
import java.util.Arrays;
5+
6+
/**
7+
* Demonstrates the Java 6 {@link Console} API at a testable process boundary.
8+
*/
9+
public class ConsoleApiExamples {
10+
11+
public ConsoleAvailability currentConsoleAvailability() {
12+
return consoleAvailability(JavaConsoleSession.current());
13+
}
14+
15+
public ConsoleAvailability consoleAvailability(ConsoleSession session) {
16+
if (session == null) {
17+
return new ConsoleAvailability(false, "System.console() is not available in this process");
18+
}
19+
return new ConsoleAvailability(true, "interactive console is available");
20+
}
21+
22+
public String greetUser(ConsoleSession session) {
23+
if (session == null) {
24+
return "console unavailable";
25+
}
26+
27+
String name = session.readLine("name: ");
28+
session.printf("hello, %s%n", name);
29+
return "hello, " + name;
30+
}
31+
32+
public int readPasswordLengthAndClear(ConsoleSession session) {
33+
if (session == null) {
34+
return 0;
35+
}
36+
37+
char[] password = session.readPassword("password: ");
38+
if (password == null) {
39+
return 0;
40+
}
41+
42+
try {
43+
return password.length;
44+
} finally {
45+
clearSecret(password);
46+
}
47+
}
48+
49+
public void clearSecret(char[] secret) {
50+
if (secret != null) {
51+
Arrays.fill(secret, '\0');
52+
}
53+
}
54+
55+
public String passwordGuidance() {
56+
return "Console.readPassword returns char[] so callers can clear sensitive data after use";
57+
}
58+
59+
public interface ConsoleSession {
60+
61+
String readLine(String prompt, Object... args);
62+
63+
char[] readPassword(String prompt, Object... args);
64+
65+
void printf(String format, Object... args);
66+
}
67+
68+
public static final class JavaConsoleSession implements ConsoleSession {
69+
70+
private final Console console;
71+
72+
private JavaConsoleSession(Console console) {
73+
this.console = console;
74+
}
75+
76+
static ConsoleSession current() {
77+
Console console = System.console();
78+
if (console == null) {
79+
return null;
80+
}
81+
return new JavaConsoleSession(console);
82+
}
83+
84+
@Override
85+
public String readLine(String prompt, Object... args) {
86+
return console.readLine(prompt, args);
87+
}
88+
89+
@Override
90+
public char[] readPassword(String prompt, Object... args) {
91+
return console.readPassword(prompt, args);
92+
}
93+
94+
@Override
95+
public void printf(String format, Object... args) {
96+
console.printf(format, args);
97+
}
98+
}
99+
100+
public static final class ConsoleAvailability {
101+
102+
private final boolean available;
103+
private final String message;
104+
105+
private ConsoleAvailability(boolean available, String message) {
106+
this.available = available;
107+
this.message = message;
108+
}
109+
110+
public boolean available() {
111+
return available;
112+
}
113+
114+
public String message() {
115+
return message;
116+
}
117+
}
118+
}

‎src/main/java/net/jrodolfo/java_evolution/java06/console_api/ConsoleApiNotes.java‎

Lines changed: 0 additions & 28 deletions
This file was deleted.

‎src/main/java/net/jrodolfo/java_evolution/java06/console_api/README.md‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Java 6 added `java.io.Console`.
44

5-
This is an explanatory learning module. It does not call `System.console()` as a required test fixture because Maven, IDEs, CI jobs, background processes, and redirected streams commonly have no attached console.
5+
This module uses an executable boundary example. It calls `System.console()` only to detect whether this process has an attached console, then keeps the command-line behavior behind a small testable interface. Maven, IDEs, CI jobs, background processes, and redirected streams commonly have no attached console, so the tests do not require one.
66

77
## 1. What Problem Does This Feature Solve?
88

@@ -23,11 +23,15 @@ The API provides:
2323
- console writers
2424
- `readPassword(...)`, which disables echoing where a real console is available
2525

26-
## 3. Why This Repository Uses Notes
26+
## 3. What Does The Example Show?
2727

28-
`System.console()` may return `null`.
28+
`ConsoleApiExamples` demonstrates three practical lessons:
2929

30-
That is normal when a Java program is launched without an interactive terminal. A Maven test that expects a console would fail for reasons unrelated to Java 6.
30+
- `System.console()` may return `null`.
31+
- Console logic should sit at the process boundary so application behavior remains testable.
32+
- Passwords should be read as `char[]` and cleared after use.
33+
34+
The tests use a fake console session for line input, formatted output, and password handling. That keeps the example deterministic while still teaching the shape of the Java 6 API.
3135

3236
## 4. Remember This
3337

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
/**
2-
* Explanatory module for the Java 6 Console API.
2+
* Executable examples for the Java 6 Console API.
33
*/
44
package net.jrodolfo.java_evolution.java06.console_api;
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package net.jrodolfo.java_evolution.java06.console_api;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
class ConsoleApiExamplesTest {
8+
9+
private final ConsoleApiExamples examples = new ConsoleApiExamples();
10+
11+
@Test
12+
void currentProcessCanReportConsoleAvailabilityWithoutRequiringATerminal() {
13+
ConsoleApiExamples.ConsoleAvailability availability = examples.currentConsoleAvailability();
14+
15+
assertThat(availability.message())
16+
.as("System.console() may be present in a terminal and absent under Maven, IDEs, CI, or redirected processes")
17+
.isNotBlank();
18+
}
19+
20+
@Test
21+
void nullConsoleUsesSafeUnavailablePath() {
22+
ConsoleApiExamples.ConsoleAvailability availability = examples.consoleAvailability(null);
23+
24+
assertThat(availability.available())
25+
.as("code using System.console() must handle the normal null result")
26+
.isFalse();
27+
assertThat(availability.message())
28+
.as("the unavailable result should explain the process-boundary condition")
29+
.contains("System.console()")
30+
.contains("not available");
31+
assertThat(examples.greetUser(null))
32+
.as("application code should have a deterministic fallback when no console is attached")
33+
.isEqualTo("console unavailable");
34+
}
35+
36+
@Test
37+
void fakeConsoleMakesLineInputAndPromptingTestable() {
38+
FakeConsoleSession console = new FakeConsoleSession("Rodolfo", new char[] { 's', 'e', 'c', 'r', 'e', 't' });
39+
40+
String greeting = examples.greetUser(console);
41+
42+
assertThat(greeting)
43+
.as("command-line behavior can be tested once Console is isolated at the edge")
44+
.isEqualTo("hello, Rodolfo");
45+
assertThat(console.output())
46+
.as("Console supports formatted output alongside line input")
47+
.contains("hello, Rodolfo");
48+
assertThat(console.lastLinePrompt())
49+
.as("Console.readLine can include formatted prompts")
50+
.isEqualTo("name: ");
51+
}
52+
53+
@Test
54+
void passwordInputUsesCharArrayThatCanBeCleared() {
55+
char[] password = new char[] { 's', 'e', 'c', 'r', 'e', 't' };
56+
FakeConsoleSession console = new FakeConsoleSession("ignored", password);
57+
58+
int length = examples.readPasswordLengthAndClear(console);
59+
60+
assertThat(length)
61+
.as("the example uses the password before clearing it")
62+
.isEqualTo(6);
63+
assertThat(password)
64+
.as("char[] secrets can be overwritten after use")
65+
.containsOnly('\0');
66+
assertThat(console.lastPasswordPrompt())
67+
.as("Console.readPassword supports a prompt and avoids echoing in a real terminal")
68+
.isEqualTo("password: ");
69+
}
70+
71+
@Test
72+
void passwordGuidanceMentionsTheJavaSixSecurityHabit() {
73+
assertThat(examples.passwordGuidance())
74+
.as("the API lesson is to prefer readPassword char arrays over immutable String secrets")
75+
.contains("Console.readPassword")
76+
.contains("char[]")
77+
.contains("clear");
78+
}
79+
80+
private static final class FakeConsoleSession implements ConsoleApiExamples.ConsoleSession {
81+
82+
private final String line;
83+
private final char[] password;
84+
private final StringBuilder output = new StringBuilder();
85+
private String lastLinePrompt;
86+
private String lastPasswordPrompt;
87+
88+
private FakeConsoleSession(String line, char[] password) {
89+
this.line = line;
90+
this.password = password;
91+
}
92+
93+
@Override
94+
public String readLine(String prompt, Object... args) {
95+
lastLinePrompt = String.format(prompt, args);
96+
return line;
97+
}
98+
99+
@Override
100+
public char[] readPassword(String prompt, Object... args) {
101+
lastPasswordPrompt = String.format(prompt, args);
102+
return password;
103+
}
104+
105+
@Override
106+
public void printf(String format, Object... args) {
107+
output.append(String.format(format, args));
108+
}
109+
110+
private String output() {
111+
return output.toString();
112+
}
113+
114+
private String lastLinePrompt() {
115+
return lastLinePrompt;
116+
}
117+
118+
private String lastPasswordPrompt() {
119+
return lastPasswordPrompt;
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)