System.getenv() - Behavior depends on whether debug mode is enabled - java

I try to retrieve the windows directory of my OS.
To get the correct path I tried the following 2 commands:
System.getenv().get("WINDIR")
System.getenv().get("SystemRoot")
Both commands work, but the strange thing is, that the first command (WINDIR) returns the path only, if I
run the program in debug mode. The latter command (SystemRoot) returns the path only if I run the program not in debug mode.
So this program
public static void main(String[] args) {
System.out.println(System.getenv().get("WINDIR"));
System.out.println(System.getenv().get("SystemRoot"));
}
evaluates to
// Debug mode
C:\Windows
null
// No Debug mode
null
C:\Windows
Is this a defined behavior?
(My application is Windows specific, and if i speak of debug mode I mean the default Eclipse "Debug as Java Applicaton" run configiguration)

System.getEnv() is an overloaded method, one implementation having no parameters and one having a String parameter.
static Map getenv​() Returns an unmodifiable string map view of the current system environment.
static String getenv​(String name) Gets the value of the specified environment variable.
You are calling the implementatation with no parameters, and then calling get() on the returned Map. From the Javadoc for System.getEnv():
For getEnv(): The returned map is typically case-sensitive on all platforms.
For getEnv(String): On UNIX systems the alphabetic case of name is typically significant, while on Microsoft Windows systems it is typically not.
So it is essential for your code to be providing the name of the environment variable in the correct case, specifying windir in all lower case, not upper case.
That said, I can't explain the differences you see when running in debug mode at all. If I run the program below - which is just an enhanced version of yours - I get identical results (as expected) regardless of whether it is run in debug mode or not:
System.getenv().get() windir=C:\WINDOWS
System.getenv().get() WINDIR=null
System.getenv().get() systemroot=null
System.getenv().get() SystemRoot=C:\WINDOWS
System.getenv() windir=C:\WINDOWS
System.getenv() WINDIR=C:\WINDOWS
System.getenv() systemroot=C:\WINDOWS
System.getenv() SystemRoot=C:\WINDOWS
Could you run the code below twice, once in debug mode and once in normal mode, and advise of the results? Also, advise of your environment: Windows version, Eclipse version and Java version.
[This is more of a request for further information than a final answer, but I couldn't fit it all into a comment.]
import java.lang.management.ManagementFactory;
import java.util.regex.Pattern;
public class App {
private final static Pattern debugPattern = Pattern.compile("-Xdebug|jdwp");
public static boolean isDebugging() {
// https://stackoverflow.com/questions/7397584/how-to-know-my-code-is-running-in-debug-mode-in-ide
// Taken from the code provided by SO user AlexR
for (String arg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
if (debugPattern.matcher(arg).find()) {
return true;
}
}
return false;
}
public static void main(String[] args) {
System.out.println("Running in debug mode? " + App.isDebugging());
System.out.println("System.getenv().get() windir=" + System.getenv().get("windir"));
System.out.println("System.getenv().get() WINDIR=" + System.getenv().get("WINDIR"));
System.out.println("System.getenv().get() systemroot=" + System.getenv().get("systemroot"));
System.out.println("System.getenv().get() SystemRoot=" + System.getenv().get("SystemRoot"));
System.out.println("System.getenv() windir=" + System.getenv("windir"));
System.out.println("System.getenv() WINDIR=" + System.getenv("WINDIR"));
System.out.println("System.getenv() systemroot=" + System.getenv("systemroot"));
System.out.println("System.getenv() SystemRoot=" + System.getenv("SystemRoot"));
}
}

Related

What exactly does Locale.getDefault() retrieve? [duplicate]

I want to set the default Locale for my JVM to fr_CA. What are the possible options to do this?
I know of only one option Locale.setDefault()
You can set it on the command line via JVM parameters:
java -Duser.country=CA -Duser.language=fr ... com.x.Main
For further information look at Internationalization: Understanding Locale in the Java Platform - Using Locale
From the Oracle Reference:
The default locale of your application is determined in three ways.
First, unless you have explicitly changed the default, the
Locale.getDefault() method returns the locale that was initially determined
by the Java Virtual Machine (JVM) when it first loaded. That is, the
JVM determines the default locale from the host environment. The host
environment's locale is determined by the host operating system and
the user preferences established on that system.
Second, on some Java runtime implementations, the application user can
override the host's default locale by providing this information on
the command line by setting the user.language, user.country, and
user.variant system properties.
Third, your application can call the Locale.setDefault(Locale)
method. The setDefault(Locale aLocale) method lets your application
set a systemwide (actually VM-wide) resource. After you set the default locale with this
method, subsequent calls to Locale.getDefault() will return the newly
set locale.
In the answers here, up to now, we find two ways of changing the JRE locale setting:
Programatically, using Locale.setDefault() (which, in my case, was the solution, since I didn't want to require any action of the user):
Locale.setDefault(new Locale("pt", "BR"));
Via arguments to the JVM:
java -jar anApp.jar -Duser.language=pt-BR
But, just as reference, I want to note that, on Windows, there is one more way of changing the locale used by the JRE, as documented here: changing the system-wide language.
Note: You must be logged in with an account that has Administrative Privileges.
Click Start > Control Panel.
Windows 7 and Vista: Click Clock, Language and Region > Region and Language.
Windows XP: Double click the Regional and Language Options
icon.
The Regional and Language Options dialog box appears.
Windows 7: Click the Administrative tab.
Windows XP and Vista: Click the Advanced tab.
(If there is no Advanced tab, then you are not logged in with
administrative privileges.)
Under the Language for non-Unicode programs section, select the desired language from the drop down menu.
Click OK.
The system displays a dialog box asking whether to use existing
files or to install from the operating system CD. Ensure that you have
the CD ready.
Follow the guided instructions to install the files.
Restart the computer after the installation is complete.
Certainly on Linux the JRE also uses the system settings to determine which locale to use, but the instructions to set the system-wide language change from distro to distro.
You can use JVM args
java -Duser.country=ES -Duser.language=es -Duser.variant=Traditional_WIN
There is another away if you don't like to change System locale but the JVM. you can setup a System (or user) Environment variable JAVA_TOOL_OPTIONS and set its value to -Duser.language=en-US or any other language-REGION you want.
You can do this:
And to capture locale. You can do this:
private static final String LOCALE = LocaleContextHolder.getLocale().getLanguage()
+ "-" + LocaleContextHolder.getLocale().getCountry();
You can enforce VM arguments for a JAR file with the following code:
import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
public class JVMArgumentEnforcer
{
private String argument;
public JVMArgumentEnforcer(String argument)
{
this.argument = argument;
}
public static long getTotalPhysicalMemory()
{
com.sun.management.OperatingSystemMXBean bean =
(com.sun.management.OperatingSystemMXBean)
java.lang.management.ManagementFactory.getOperatingSystemMXBean();
return bean.getTotalPhysicalMemorySize();
}
public static boolean isUsing64BitJavaInstallation()
{
String bitVersion = System.getProperty("sun.arch.data.model");
return bitVersion.equals("64");
}
private boolean hasTargetArgument()
{
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
List<String> inputArguments = runtimeMXBean.getInputArguments();
return inputArguments.contains(argument);
}
public void forceArgument() throws Exception
{
if (!hasTargetArgument())
{
// This won't work from IDEs
if (JARUtilities.isRunningFromJARFile())
{
// Supply the desired argument
restartApplication();
} else
{
throw new IllegalStateException("Please supply the VM argument with your IDE: " + argument);
}
}
}
private void restartApplication() throws Exception
{
String javaBinary = getJavaBinaryPath();
ArrayList<String> command = new ArrayList<>();
command.add(javaBinary);
command.add("-jar");
command.add(argument);
String currentJARFilePath = JARUtilities.getCurrentJARFilePath();
command.add(currentJARFilePath);
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.start();
// Kill the current process
System.exit(0);
}
private String getJavaBinaryPath()
{
return System.getProperty("java.home")
+ File.separator + "bin"
+ File.separator + "java";
}
public static class JARUtilities
{
static boolean isRunningFromJARFile() throws URISyntaxException
{
File currentJarFile = getCurrentJARFile();
return currentJarFile.getName().endsWith(".jar");
}
static String getCurrentJARFilePath() throws URISyntaxException
{
File currentJarFile = getCurrentJARFile();
return currentJarFile.getPath();
}
private static File getCurrentJARFile() throws URISyntaxException
{
return new File(JVMArgumentEnforcer.class.getProtectionDomain().getCodeSource().getLocation().toURI());
}
}
}
It is used as follows:
JVMArgumentEnforcer jvmArgumentEnforcer = new JVMArgumentEnforcer("-Duser.language=pt-BR"); // For example
jvmArgumentEnforcer.forceArgument();

Why does Files.isHidden(Path) return false for directories on Windows?

From the documentation of Files.isHidden(Path) (emphasis mine):
Tells whether or not a file is considered hidden. The exact definition of hidden is platform or provider dependent. On UNIX for example a file is considered to be hidden if its name begins with a period character ('.'). On Windows a file is considered hidden if it isn't a directory and the DOS hidden attribute is set.
Depending on the implementation this method may require to access the file system to determine if the file is considered hidden.
From this I can understand what the expected behavior is. However, why is this the expected behavior?
The reason I'm wondering is because of the difference in behavior between Files.isHidden, DosFileAttributes.isHidden, and Windows' File Explorer. For instance, I can go into File Explorer and set a directory to be hidden and it will no longer show up (unless I configure it to show hidden items). If I test if said directory is hidden with Java then Files.isHidden returns false and DosFileAttributes.isHidden returns true. You can test this with the following code:
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.DosFileAttributes;
public class Main {
public static void main(String[] args) throws Exception {
final var directory = Path.of(args[0]).toAbsolutePath().normalize();
final var store = Files.getFileStore(directory);
final var dosAttrs = Files.readAttributes(directory, DosFileAttributes.class);
System.out.println("Directory : " + directory);
System.out.println("FileStore : " + store.name() + " [" + store.type() + "]");
System.out.println("Hidden (Files): " + Files.isHidden(directory));
System.out.println("Hidden (Dos) : " + dosAttrs.isHidden());
}
}
Note: I'm using Windows 10 and OpenJDK 11.0.1. My file system is NTFS.
Running this with:
java Main.java C:\path\to\hidden\directory
I get:
Directory : C:\path\to\hidden\directory
FileStore : OS [NTFS]
Hidden (Files): false
Hidden (Dos) : true
Note: This behavior appears to be part of WindowsFileSystemProvider. The method Files.isHidden(Path) simply forwards the call to the argument's FileSystem's provider. The implementation is basically:
DosFileAttributes attrs = ...; // get attributes
return !attrs.isDirectory() && attrs.isHidden();
I found this (non)-issue (JDK-8170334) where a comment says:
I don't think we have a bug here because the hidden attribute is meaningless on directories.
Yet File Explorer, which is core software on Windows, behaves like the hidden attribute is not meaningless on directories. So again, why does the Java implementation on Windows take into account whether or not the Path points to a directory? Or is Java correct and File Explorer is doing non-standard things?
I'm inclined to think File Explorer is correct because both CMD (via dir) and PowerShell (via Get-ChildItem) won't list hidden directories either; not unless the appropriate options are specified.
I checked documentation for file attributes provided by Microsoft for Windows platform. It says that if attribute FILE_ATTRIBUTE_HIDDEN = 2 (0x2) is set
The file or directory is hidden. It is not included in an ordinary directory listing.
As I can see in the class sun.nio.fs.WindowsConstants there is the same value definition used by DosFileAttributes.isHidden() method - public static final int FILE_ATTRIBUTE_HIDDEN = 0x00000002; which for my understanding should be mapped one to one with the attribute available for Windows, so in general hidden flag for a directory should be working in the same way as for a regular file.
In relation to operating system/file system integration, this behaviour seems to be incorrect.

Java is not trowing Arithmetic exception

I have this Java code:
public class Calc {
public int quotient(int a, int b){
return a/b;
}
}
and TestNG unit test for this method:
#Test ()
public void testingMethod3() {
Assert.assertEquals(0, calc.quotient(5,0));
}
On my work computer I successfully get
java.lang.ArithmeticException: / by zero
message, as expected.
But when my colleague runs this test on home computer, then mentioned exception is not throwing and test passes.
How this magic could occur?
P.S.
Environment
OS: Windows 10
TestNG version: 6.13.1
Java version: 8 (don't know exact build version)
P.P.S.
Deletion of target folder and rebuilding of the project was that very helpful solution. Seem like IDE cashed old project sources, and didn't flush them after changes in the code.
In the past, I experienced something like yours. Because different JDK compile environment and/or JRE runtime environment. And need check the different of version of TestNG.
check by add few line of code to print Java properties.
Properties p = System.getProperties();
Enumeration keys = p.keys();
while (keys.hasMoreElements()) {
String key = (String)keys.nextElement();
String value = (String)p.get(key);
System.out.println(key + ": " + value);
}
then comparing

Can command-line set Java system properties be distinguished from the defaults?

Is there a way to distinguish a Java system property which has been set from the command line using a -D option from a system property which got the same value by default?
E.g., this program
class Test {
public static void main(String[] args) {
System.out.println(System.getProperty("user.home"));
}
}
prints
/home/uckelman
for me regardless of whether I run it as
java Test
or
java -Duser.home=/home/uckelman Test
Is there anything which the JDK provides which I could test to distinguish these two situations?
One way is to get the command line arguments used to start the JVM and check the returned List if a given system property is set or not.
RuntimeMXBean mx = ManagementFactory.getRuntimeMXBean();
System.out.println("COMMAND LINE ARGS:\n" + mx.getInputArguments());

Is *this* really the best way to start a second JVM from Java code?

This is a followup to my own previous question and I'm kind of embarassed to ask this... But anyway: how would you start a second JVM from a standalone Java program in a system-independent way? And without relying on for instance an env variable like JAVA_HOME as that might point to a different JRE than the one that is currently running. I came up with the following code which actually works but feels just a little awkward:
public static void startSecondJVM() throws Exception {
String separator = System.getProperty("file.separator");
String classpath = System.getProperty("java.class.path");
String path = System.getProperty("java.home")
+ separator + "bin" + separator + "java";
ProcessBuilder processBuilder =
new ProcessBuilder(path, "-cp",
classpath,
AnotherClassWithMainMethod.class.getName());
Process process = processBuilder.start();
process.waitFor();
}
Also, the currently running JVM might have been started with some other parameters (-D, -X..., ...) that the second JVM would not know about.
I think that the answer is "Yes". This probably as good as you can do in Java using system independent code. But be aware that even this is only relatively system independent. For example, in some systems:
the JAVA_HOME variable may not have been set,
the command name used to launch a JVM might be different (e.g. if it is not a Sun JVM), or
the command line options might be different (e.g. if it is not a Sun JVM).
If I was aiming for maximum portability in launching a (second) JVM, I think I would do it using wrapper scripts.
It's not clear to me that you would always want to use exactly the same parameters, classpath or whatever (especially -X kind of stuff - for example, why would the child need the same heap settings as its parents) when starting a secondary process.
I would prefer to use an external configuration of some sort to define these properties for the children. It's a bit more work, but I think in the end you will need the flexibility.
To see the extent of possible configuration settings you might look at thye "Run Configurations" settings in Eclipse. Quite a few tabs worth of configuration there.
To find the java executable that your code is currently running under (i.e. the 'path' variable in your question's sample code) there is a utility method within apache ant that can help you. You don't have to build your code with ant - just use it as a library, for this one method.
It is:
org.apache.tools.ant.util.JavaEnvUtils.getJreExecutable("java")
It takes care of the sort of special cases with different JVM vendors that others have mentioned. (And looking at the source code for it, there are more special cases than I would have imagined.)
It's in ant.jar. ant is distributed under the Apache license so hopefully you can use it how you want without hassle.
Here's a way that determines the java executable which runs the current JVM using ProcessHandle.current().info().command().
The ProcessHandle API also should allow to get the arguments. This code uses them for the new JVM if available, only replacing the current class name with another sample class. (Finding the current main class inside the arguments gets harder if you don't know its name, but in this demo it's simply "this" class. And maybe you want to reuse the same JVM options or some of them, but not the program arguments.)
However, for me (openjdk version 11.0.2, Windows 10), the ProcessInfo.arguments() is empty, so the fallback else path gets executed.
package test;
import java.lang.ProcessBuilder.Redirect;
import java.lang.management.ManagementFactory;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestStartJvm {
public static void main(String[] args) throws Exception {
ProcessHandle.Info currentProcessInfo = ProcessHandle.current().info();
List<String> newProcessCommandLine = new LinkedList<>();
newProcessCommandLine.add(currentProcessInfo.command().get());
Optional<String[]> currentProcessArgs = currentProcessInfo.arguments();
if (currentProcessArgs.isPresent()) { // I know about orElse, but sometimes isPresent + get is handy
for (String arg: currentProcessArgs.get()) {
newProcessCommandLine.add(TestStartJvm.class.getName().equals(arg) ? TargetMain.class.getName() : arg);
}
} else {
System.err.println("don't know all process arguments, falling back to passed args array");
newProcessCommandLine.add("-classpath");
newProcessCommandLine.add(ManagementFactory.getRuntimeMXBean().getClassPath());
newProcessCommandLine.add(TargetMain.class.getName());
newProcessCommandLine.addAll(List.of(args));
}
ProcessBuilder newProcessBuilder = new ProcessBuilder(newProcessCommandLine).redirectOutput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT);
Process newProcess = newProcessBuilder.start();
System.out.format("%s: process %s started%n", TestStartJvm.class.getName(), newProcessBuilder.command());
System.out.format("process exited with status %s%n", newProcess.waitFor());
}
static class TargetMain {
public static void main(String[] args) {
System.out.format("in %s: PID %s, args: %s%n", TargetMain.class.getName(), ProcessHandle.current().pid(),
Stream.of(args).collect(Collectors.joining(", ")));
}
}
}
Before ProcessHandle was added in Java 9, I did something like this to query the current JVM's command-line:
Let the user pass or configure a "PID to command-line" command template; under Windows, this could be wmic process where 'processid=%s' get commandline /format:list.
Determine PID using java.lang.management.ManagementFactory.getRuntimeMXBean().getPid().
Expand command template; execute; parse its output.

Categories