You are so unlucky.

For the first time in his life, YDJSIR encountered the difficulties of cross-platform development.

It is well known that JAVA is a cross-platform language, various JVMs enable it to run smoothly on a billion devices, but is its cross-platform nature impeccable?

Due to my lack of knowledge, I can only describe the phenomenon rather than analyze the specific reasons. YDJSIR would be very grateful if someone could help explain the reasons.

Here is an example.

**References: **https://github.com/XZ-X/2020SE1-FAQ/issues/13

Advanced https://zhuanlan.zhihu.com/p/46294360

Problem Description

Expected Result

AC

Actual Result

Pass locally, TLE on the OJ.

Code Analysis

Original Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//Determine whether the given string is a legal directory relative path in Linux: whether it is a set of/concatenated strings consisting only of numbers,  letters,  and underscores (ideally,  of course)
String patternDir = "^(\\w+\\/?)+$";

//Determine whether the given string is a legal ZIP/ zip/ JAR/ jar file relative path under Linux
String patternArchive = "^(\\w+\\/?)+(.)+((ZIP)|(zip)|(JAR)|(jar))+$";

//Path with wildcard
String patternWildEntry = "^(\\w+\\/?)+\\*";

//Composite: the set of paths that belong to one of the three kinds above joined by PATH_SEPARATOR
boolean isMatchComposite = false;
String[] composite = classpath.split(PATH_SEPARATOR);
if(composite.length > 1) {
for (String item : composite
) {
if (Pattern.matches(patternArchive, classpath) || Pattern.matches(patternWildEntry, classpath) ||
Pattern.matches(patternDir, item)) {
continue;
}
isMatchComposite = true;
}
}
if(isMatchComposite){
return new CompositeEntry(classpath);
}
else if(Pattern.matches(patternArchive, classpath)){
return new ArchivedEntry(classpath);
}
else if(Pattern.matches(patternWildEntry, classpath)){
return new WildEntry(classpath);
}
else if(Pattern.matches(patternDir, classpath)){
return new DirEntry(classpath);
}
return null;

There seems to be no problem. At this point, YDJSIR also takes a good dig at YDJSIR’s roommate to use case-oriented programming.

Test Results

Local Tests-Windows 10 x64

Windows10 Version

image-20200521090551533

LocalJDKVersion
1
2
3
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.242-b08, mixed mode)
IDEA Tests Results

IDEA-Tests-(Junit4)-Results. Fine. 94ms.

mvn test Results
1
Tests run: 9,  Failures: 0,  Errors: 0,  Skipped: 0,  Time elapsed: 0.133 sec

本处省略大量e.printbacktrace()的内容.

1
2
3
4
5
6
7
8
9
10
11
Results :

Tests run: 9, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13.387 s
[INFO] Finished at: 2020-05-17T13:12:00+08:00
[INFO] ------------------------------------------------------------------------

Here are tests from CZG.

Linux AdoptOpenJDK 1.8 TestsResults

image-20200522112750868

118ms, Fine(Linux - Arch)

Oracle JDK8 TestsResults

JDKVersion
1
jdk1.8.0_251
IDEA TestsResults

image-20200522112324100

There seems to be something wrong…

mvn test TestsResults
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running edu.nju.ClassFileReaderTest
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 81.642 sec

Results :

Tests run: 9, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:23 min
[INFO] Finished at: 2020-05-22T11:23:02+08:00
[INFO] ------------------------------------------------------------------------

Fine. YDJSIR is going to push my code.

OJ-CentOS x64 TestsResults

CentOS

image-20200521161941359

JDKVersion
1
jdk-8u161-linux-x64
OJ-mvn test Results
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=utf-8
Picked up _JAVA_OPTIONS: -Djdk.net.URLClassPath.disableClassPathURLCheck=true
Running edu.nju.ClassFileReaderTest
Cancelling nested steps due to timeout
Sending interrupt signal to process
Terminated

Results :

Tests run: 0, Failures: 0, Errors: 0, Skipped: 0

script returned exit code 143
Timeout has been exceeded

According to the explanation of the TA, the server gives all test cases 1 minute at most. YDJSIR’ i5-9300 - H, is powerful, and so as the performance of the server. More than one order of magnitude between 94 ms and 1 min is not reasonable. This error is not a compile error, which adds more difficulties to the problem. If it is a compile error, then YDJSIR finds out where the error is. However, it is not.

Therefore, YDJSIR proposed an issue. TA thought it was the problem of the Maven package at first, and let YDJSIR run MVN test. YDJSIR at first lacked the package due to network problems, but after the package was installed, my code just doesn’t work on the OJ.

After that, the feedback from the platform teaching assistant was that the system was not deployed/built successfully, so it was impossible to judge which tests failed.

At the same time, there are two ultra-realistic statements.

There is someone who AC, so we can’t modify the OJ for you alone, yes?

If the problem cannot be solved, please let the teaching assistants run your code locally and give you scores.

image-20200521163633986

Then YDJSIR was trapped into the loop of DEBUG.

image-20200521140229918

MAC- TestsResults

MAC

img

Local JDK
1
jdk-8u251-macosx-x64
IDEA TestsResults

image-20200521163300684

MVN TestsResults

More than 20s. The test is interrupted.

image-20200521163358536

Solutions

Basic: No Regular Expressions

The code here comes from YDJSIR’s roommate.

1
2
3
4
5
6
7
8
9
10
String substring = classpath.substring(classpath.length() - 4,  classpath.length());
if(classpath.contains(PATH_SEPARATOR)){
return new CompositeEntry(classpath);
}
else if(classpath.charAt(classpath.length()-1)=='*')
return new WildEntry(classpath);
else if(substring.equals(".JAR")|| substring.equals(".jar")|| substring.equals(".zip")|| substring.equals(".ZIP"))
return new ArchivedEntry(classpath);
else
return new DirEntry(classpath);
LocalIDEA Results

image-20200521154828526

AC should go first!

OJ Results
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
T E S T S
-------------------------------------------------------
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=utf-8
Picked up _JAVA_OPTIONS: -Djdk.net.URLClassPath.disableClassPathURLCheck=true
Running edu.nju.ClassFileReaderTest
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.215 sec - in edu.nju.ClassFileReaderTest

Results :

Tests run: 9, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.471 s
[INFO] Finished at: 2020-05-17T07:39:24+00:00
[INFO] Final Memory: 15M/36M
[INFO] ------------------------------------------------------------------------

Advanced: Improve Regular Expression

After my AC, YDJSIR went back and read the Zhihu column again and again (the link at the beginning) and seemed to find something. Thus, the regular expression of YDJSIR became as follows.

1
2
3
String patternDir = "^(\\w+\\/?)+$";
String patternArchive = "^(\\w+\\/?)+(\\.)+((ZIP)|(zip)|(JAR)|(jar))$";
String patternWildEntry = "^(\\w+\\/?)+\\*$";
LocalIDEA Results

Fine. 94ms

OJ Results

AC.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 T E S T S
-------------------------------------------------------
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=utf-8
Picked up _JAVA_OPTIONS: -Djdk.net.URLClassPath.disableClassPathURLCheck=true
Running edu.nju.ClassFileReaderTest
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 16.383 sec - in edu.nju.ClassFileReaderTest

Results :

Tests run: 9, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 22.397 s
[INFO] Finished at: 2020-05-21T07:41:27+00:00
[INFO] Final Memory: 14M/36M
[INFO] ------------------------------------------------------------------------

What’s the problem?

. Matching any single char except ‘\n’. To match ‘.’,use ‘\. ‘ please.
https://www.runoob.com/regexp/regexp-syntax.html

image-20200521154503284

This gap in time… Well, it’s still scary.

Results

Summary

Environment Original Modified
YDJSIR-IDEA-OpenJDK 1.8.0_242-b08 Win_x64 94ms 58ms
YDJSIR-MVN-OpenJDK 1.8.0_242-b08 Win_x64 133ms 201ms
YDJSIR-IDEA-OracleJDK jdk-1.8.0_251 Win_x64 81sec 14sec
YDJSIR-MVN-OracleJDK jdk-1.8.0_251 Win_x64 81sec 15sec
YDJSIR -IDEA-OracleJDK jdk-14.0.1 Win_x64 115ms 106ms
YDJSIR -IDEA-OracleJDK jdk-14.0.1 Win_x64 123ms 73ms
MAC-IDEA-OracleJDK jdk-1.8.0_251 Mac_x64 ==TLE== 17sec
CZG VM-MVN-OpenJDK 1.8.0_242-b08 Linux_x64(Arch) 118ms ==Not Tested==
OJ ECS-MVN-OracleJDK jdk-1.8.0_251 Linux_x64(CentOS 7.7) ==TLE== 16s
CX MAC-IDEA-OracleJDK jdk-11 Mac_x64 90ms 40ms
CX MAC-MVN-OracleJDK jdk-11 Mac_x64 180ms 107ms

The Time limit here is 1min in one thread.

  • Different JDKs have different implementation for the same syntax, so the application of language features should be extremely cautious. Even for JDK 1.8, its implementation between OpenJDK and that of Oracle JDK is probably not the same. From 58 ms to 16 seconds, nearly two orders of magnitude of the gap cause great confusion. However, this problem is challenging to be found unless the production environment and the actual environment are of the same configuration.
  • Small mistakes, not a Critical error, may lead to considerable confusion. There is no escape. The brought from 58 ms to 94 ms changes, and in OracleJDK, the brought from 16 s to 81 s, the dramatic differences between the two can be amazing. However, the former takes a difference close to 2.8 times, while the latter nearly five times.
  • It makes sense to adopt newer technologies as much as possible in development. JAVA 11 is also an LTSVersion, and although the older version may have more resources and less unknown bugs, the improvements made by the new technology from the bottom should be recognized.

image-20200522113959911

From Wiki

Postscripts

YDJSIR finds that IDEA may at first using its own JDK to run Tests, rather than an MVN test. The YDJSIR thinks this statement is reasonable. YDJSIR IDEA Local Tests initially passed even before installing the pack needed by MVN. The characteristic feature of this phenomenon is that the SDK Settings have never been changed by default. However, the results obtained by YDJSIR in the official IDEA document is that the JDK is only used for the operation of the IDE itself, not for development, and you need to set the JDK by yourself.

References: https://www.jetbrains.com/help/idea/sdk.html#

1
2
3
4
Important notice
The bundled JRE is used for running the IDE itself, and it's not sufficient for developing Java applications. Before you start developing in Java, download and install a standalone JDK build.

Due to the changes in the Oracle Java License, you might not have the right to use Oracle's Java SE for free. We recommend that you use one of the OpenJDK builds to avoid potential compliance failures.

YDJSIR doesn’t know how to explain these words.

References: https://intellij-support.jetbrains.com/hc/en-us/community/posts/360006922240-Do-I-have-to-install-JDK-

The experiment proved that IDEA would compile and run the program with its own JDK(based on the OpenJDK, called JBR) by default. After setting up its JDK, YDJSIR did not find a way to use only the native JDK. To reduce the situation, YDJSIR selects the Oracle JDK to complete the YDJSIR’s job (DDL)

1
2
JetBrains Runtime is a runtime environment for running IntelliJ Platform-based products on Windows,  Mac OS X,  and Linux. JetBrains Runtime is based on the OpenJDK project with some modifications. These modifications include Subpixel Anti-Aliasing,  enhanced font rendering on Linux,  HiDPI support,  ligatures,  some fixes for native crashes not presented in official builds,  and other small enhancements. 

From the website of JBR.

YDJSIR is too inexperienced to address these issues. To aid in this, here is the information about the Local IDEA Version.

image-20200522115930944

References: https://www.quora.com/As-IntelliJ-has-its-own-embedded-JDK-can-it-run-Java-programs-without-having-any-Java-installed-elsewhere-on-the-system

Conclusions

After a series of twists and turns, YDJSIR came to the following conclusion:

  • In programming, any character that can be typed directly from a keyboard should be treated with caution! Even a Numpad might have 114,514 strange functions!

  • Regular expressions have many holes that you can fall into if you’re not careful enough.

  • Case oriented programming is sometimes a proper technique. However, YDJSIR is willing to play around when possible;

  • It is dangerous not to learn something well. For example, the missing escape character YDJSIR leads to a timeout on the server, which can be disastrous in a production environment.

  • YDJSIR has been learning to code so long that the feeling of programming ability has not improved, but the DEBUG experience does increase indeed. YDJSIR will abandon the way of printing all and use more IDE’s features to DEBUG.