JDK-8200243 : System error message is decoded as invalid encoding in Windows.
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 9.0.4,10,11
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2018-03-25
  • Updated: 2018-10-09
  • Resolved: 2018-06-27
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 12
11 b20Fixed 12Fixed
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)

Also,
java version "10" 2018-03-20
Java(TM) SE Runtime Environment 18.3 (build 10+46)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+46, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.16299.309]

A DESCRIPTION OF THE PROBLEM :
In Japanese environment, error messages by Windows
(e.g. "��������������������������������� ������������������������������������������������������������������������������",
in English: "An established connection was aborted by the software in your host machine.")
are decoded as wrong encoding. This causes mojibake, and users can't understand the error message.

It seems that the original messages are in UTF-8 but they are decoded as CP932(Shift-JIS).

REGRESSION.  Last worked in version 8u162

ADDITIONAL REGRESSION INFORMATION: 
In jre1.8.0_162, error messages are decoded correctly.

java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Cause IOException with nio object.
Then, get error message from the exception with getMessage() or getLocalizedMessage().
Example: attached source code.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Output:
----------------------------
��������������������������������� ������������������������������������������������������������������������������
��������������������������������� ������������������������������������������������������������������������������
false
true
----------------------------
or
----------------------------
An established connection was aborted by the software in your host machine.
��������������������������������� ������������������������������������������������������������������������������
false
true
----------------------------
ACTUAL -
Output:
----------------------------
���������������������������������������������������? �����������������������������������������������������������������������������������������������������������������������������������������?
���������������������������������������������������? �����������������������������������������������������������������������������������������������������������������������������������������?
true
false
----------------------------

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;

public class Test {
    public static void main(String[] args) throws IOException {
        int port = 12345;
        ServerSocket server = new ServerSocket(port);
        new Thread(() -> {
            try
            {
                server.accept().close(); // socket close imediately
            }
            catch (IOException e)
            {
            }
        }).start();
        
        try {
            SocketChannel channel = SocketChannel.open(new InetSocketAddress("localhost", port));
            Thread.sleep(1000);
            // write data to the closed connection
            for (int i = 0; i < 100; i++)
                channel.write(ByteBuffer.wrap("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\n".getBytes()));
        } catch (InterruptedException|IOException e) {
            System.out.println(e.getMessage());
            System.out.println(e.getLocalizedMessage());
            String msg = e.getLocalizedMessage();
            System.out.println(msg.startsWith("������")); // mojibake, should be false
            System.out.println(msg.startsWith("������������������������")); // japanese message, should be true
        }
        server.close();
    }    
}
---------- END SOURCE ----------


Comments
The "newly" introduced getLastErrorString() for windows platform uses "utf8", which is an "incompatible" and unexpected change.
22-05-2018

From submitter: I have inspected source codes of openjdk-10 to determine the cause. The bug is in JNU_ThrowByNameWithLastError@src/java.base/share/native/libjava/jni_util.c: ------------------- char buf[256]; size_t n = getLastErrorString(buf, sizeof(buf)); size_t messagelen = message == NULL ? 0 : strlen(message); if (n > 0) { jstring s = JNU_NewStringPlatform(env, buf); ------------------- getLastErrorString always outputs UTF-8 string, but JNU_ThrowByNameWithLastError decodes as platform's encoding with JNU_NewStringPlatform. libnio uses JNU_ThrowIOExceptionWithLastError (e.g. Java_sun_nio_ch_SocketDispatcher_write0@src/java.base/windows/native/libnio/ch/SocketDispatcher.c), and JNU_ThrowIOExceptionWithLastError calls JNU_ThrowByNameWithLastError.
23-04-2018

To reproduce the issue, run the attached test case. JDK 8u162 - Pass JDK 9.0.4 +11 - Fail JDK 10 +46 - Fail Attached are the screenshots with pass and fail cases.
26-03-2018