Setting Java VM line.separator - java

Has anybody found a way how to specify the Java line.separator property on VM startup? I was thinking of something like this:
java -Dline.separator="\n"
But this doesn't interprete the "\n" as linefeed character. Any ideas?

Try using java -Dline.separator=$'\n'. That should do the trick, at least in bash.
Here is a test-run:
aioobe#r60:~/tmp$ cat Test.java
public class Test {
public static void main(String[] args) {
System.out.println("\"" + System.getProperty("line.separator") + "\"");
}
}
aioobe#r60:~/tmp$ javac Test.java && java -Dline.separator=$'\n' Test
"
"
aioobe#r60:~/tmp$
Note:
The expression $'' uses the Bash feature ANSI-C Quoting. It expands backslash-escaped characters, thus $'\n' produces a line feed (ASCII code 10) character, enclosed in single quotes. See Bash manual, section 3.1.2.4 ANSI-C Quoting.

To bridge the gap between aioobe and Bozho's answers, I also would advise against setting the line.separator parameter at JVM startup, as this potentially breaks many fundamental assumptions the JVM and library code makes about the environment being run in. For instance, if a library you depend on relies on line.separator in order to store a config file in a cross-platform way, you've just broken that behavior. Yes, it's an edge case, but that makes it all the more nefarious when, years from now, a problem does crop up, and now all your code is dependent on this tweak being in place, while your libraries are (correctly) assuming it isn't.
That said, sometimes these things are out of your control, like when a library relies on line.separator and provides no way for you to override that behavior explicitly. In such a case, you're stuck overriding the value, or something more painful like re-implementing or patching the code manually.
For those limited cases, the it's acceptable to override line.separator, but we've got to follow two rules:
Minimize the scope of the override
Revert the override no matter what
Both of these requirements are well served by AutoCloseable and the try-with-resources syntax, so I've implemented a PropertiesModifier class that cleanly provides both.
/**
* Class which enables temporary modifications to the System properties,
* via an AutoCloseable. Wrap the behavior that needs your modification
* in a try-with-resources block in order to have your properties
* apply only to code within that block. Generally, alternatives
* such as explicitly passing in the value you need, rather than pulling
* it from System.getProperties(), should be preferred to using this class.
*/
public class PropertiesModifier implements AutoCloseable {
private final String original;
public PropertiesModifier(String key, String value) {
this(ImmutableMap.of(key, value));
}
public PropertiesModifier(Map<String, String> map) {
StringWriter sw = new StringWriter();
try {
System.getProperties().store(sw, "");
} catch (IOException e) {
throw new AssertionError("Impossible with StringWriter", e);
}
original = sw.toString();
for(Map.Entry<String, String> e : map.entrySet()) {
System.setProperty(e.getKey(), e.getValue());
}
}
#Override
public void close() {
Properties set = new Properties();
try {
set.load(new StringReader(original));
} catch (IOException e) {
throw new AssertionError("Impossible with StringWriter", e);
}
System.setProperties(set);
}
}
My use case was with Files.write(), which is a very convenient method, except it explicitly relies on line.separator. By wrapping the call to Files.write() I can cleanly specify the line separator I want to use, without risking exposing this to any other parts of my application (take note of course, that this still isn't thread-safe).
try(PropertiesModifier pm = new PropertiesModifier("line.separator", "\n")) {
Files.write(file, ImmutableList.of(line), Charsets.UTF_8);
}

I wouldn't do that if I were you. The line-separator is platform specific, and should remain so. If you want to write windows-only or linux-only files, define a UNIX_LINE_SEPARATOR constant somewhere and use it instead.

Related

Possible to have code run just by importing class in Java?

Working on this one-class library where I need to disable buffering on stderr and stdout, if client imports that class, using something like
// stderr
FileOutputStream fderr = new FileOutputStream(FileDescriptor.err);
// stdout
FileOutputStream fdout = new FileOutputStream(FileDescriptor.out);
// stderr buffer of size 1
BufferedOutputStream errBuf = new BufferedOutputStream(fderr, 1);
// stdout buffer of size 1
BufferedOutputStream outBuf = new BufferedOutputStream(fdout, 1);
// add more features and functionality to stderr and stdout
PrintStream errStream = new PrintStream(errBuf);
PrintStream outStream = new PrintStream(outBuf);
// update stderr and stderr
System.setErr(errStream);
System.setOut(outStream);
Came across static initializers, but unfortunately, they only run in particular circumstances, none of which is of interest to me. Is there any way in Java to have the code above run only by importing the containing class (i.e., just the import statement; without invoking any methods, etc)?
You are looking into the wrong place.
If you want that your application sends all stdout/stderr messages into a specifc stream; then you "simply" want to control that on the "JVM startup" level. In other words: you should look into means to manipulate the JVM as early as possible.
You don't want that redirection to be put in place when some of your classes gets "imported"; you want to have a robust way of directly telling the JVM on startup to do that.
But of course: the really sane way of doing such things is to use a logging framework which allows for much better control of what is going on. Logging to stdout/stderr isn't a good approach in the first place!
Edit: given the fact that this is about "training wheels" for students; then simply put a static setup method in some of your library classes (ideally taking to strings that denote file names where stdout/stderr should go to); and instruct your students to call that method as very first thing in their main methods.
Imports are only used at compile time.
You say that static initializers are not of interest. However, static initializers run when your class is loaded. There is no way around it you cannot run code from your class without it being loaded, therefore I would say static initializers are exactly what you want.
The client can load a class without having to instantiate it, for example by using Class.forName
Example
public class Foo
{
static
{
System.out.println("Class foo loaded");
}
}
// different file...
public class Client
{
static
{
try {
Class.forName("Foo");
}catch (ClassNotFoundException e) {
// TODO
}
}
public static void main (String args[]) {
// Nothing to do in this demo. The static intitizlier in
// this class causes Foo to be loaded and thus invokes Foo's
// static initializer
}
}

how to get ExePath/MainClassPath/MainJarPath of java App at runtime?

The "Path of the running EXE" is well defined for Windows Applications written in C#, C++, VB, etc.
-
Java Applications on Windows are not "EXE-Applications" but a class-file or a jar-file is started rather than an EXE-file.
So for java Applications the term "ExePath" should be translated to "MainClassPath" or to "JarPath" resp.
-
In some cases a programmer needs to know the physical Path of the Application's jar or MainClass.
(e.g. when you develop a large project both in java and in c# with identical classes and identical methods)
-
thanks to other stackoverflow users this statement does the job:
String exePath = URLDecoder.decode(this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF-8")
and now comes my question:
If I put the same code into any helper/utils jar-library then it will return the path of the helperlib.jar, it will NOT return the path of my MainClass/AppJar !
-
So the final getExePath() helper method should look something like:
return(URLDecoder.decode(Thread.currentThread().getStartingThread().getMainClass().getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF-8"));
(if there only were methods like getStartingThread() and getMainClass() in java ...)
-
please, point me to the final solution, how can I implement these steps:
get the starting Thread
get the Main Class of the starting Thread
get the Path of the Main Class
if I get you right, just put the method with a parameter of the desired class into your helperlib-class ...
e.g.:
public static String getExePath(Object main) {
String path = "";
try {
path = URLDecoder.decode(main.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return path;
}
then you can call it e.g. from inside your main-jar with the parameter 'this' ...
System.out.println("AppPath:\n" + helperlib.getExePath(this));
... and you got the path of the class specified in you parameter
hope it helps and sorry for my bad english ... ;)

Is -Djava.library.path=... equivalent to System.setProperty("java.library.path", ...)

I load an external library that is placed in ./lib. Are these two solutions to set the java.library.path equivalent?
Set path in console when executing jar:
java -Djava.library.path=./lib -jar myApplication.jar
Set path in the code before loading library:
System.setProperty("java.library.path", "./lib");
If they are equivalent, why in the second solution can Java not find the library while the first one is ok?
If not, is there a way the set the path in the code?
Although it is not well documented, the java.library.path system property is a "read-only" property as far as the System.loadLibrary() method is concerned. This is a reported bug but it was closed by Sun as opposed to getting fixed. The problem is that the JVM's ClassLoader reads this property once at startup and then caches it, not allowing us to change it programatically afterward. The line System.setProperty("java.library.path", anyVal); will have no effect except for System.getProperty() method calls.
Luckily, someone posted a workaround on the Sun forums. Unfortunately, that link no longer works but I did find the code on another source. Here is the code you can use to work around not being able to set the java.library.path system property:
public static void addDir(String s) throws IOException {
try {
// This enables the java.library.path to be modified at runtime
// From a Sun engineer at http://forums.sun.com/thread.jspa?threadID=707176
//
Field field = ClassLoader.class.getDeclaredField("usr_paths");
field.setAccessible(true);
String[] paths = (String[])field.get(null);
for (int i = 0; i < paths.length; i++) {
if (s.equals(paths[i])) {
return;
}
}
String[] tmp = new String[paths.length+1];
System.arraycopy(paths,0,tmp,0,paths.length);
tmp[paths.length] = s;
field.set(null,tmp);
System.setProperty("java.library.path", System.getProperty("java.library.path") + File.pathSeparator + s);
} catch (IllegalAccessException e) {
throw new IOException("Failed to get permissions to set library path");
} catch (NoSuchFieldException e) {
throw new IOException("Failed to get field handle to set library path");
}
}
WARNING: This may not work on all platforms and/or JVMs.
Generally speaking, both approaches have the same net effect in that the system property java.library.path is set to the value ./lib.
However, some system properties are only evaluated at specific points in time, such as the startup of the JVM. If java.library.path is among those properties (and your experiment seems to indicate that), then using the second approach will have no noticeable effect except for returning the new value on future invocations of getProperty().
As a rule of thumb, using the -D command line property works on all system properties, while System.setProperty() only works on properties that are not only checked during startup.
you can add three lines
System.setProperty("java.library.path", "/path/to/libs" );
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
and also import java.lang.reflect.Field
It's ok to solve the problem
This is an addendum to this answer to Jesse Webb's amazing answer above: https://stackoverflow.com/a/6408467/257299
For Java 17:
import jdk.internal.loader.NativeLibraries;
final Class<?>[] declClassArr = NativeLibraries.class.getDeclaredClasses();
final Class<?> libraryPaths =
Arrays.stream(declClassArr)
.filter(klass -> klass.getSimpleName().equals("LibraryPaths"))
.findFirst()
.get();
final Field field = libraryPaths.getDeclaredField("USER_PATHS");
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
final VarHandle varHandle = lookup.findVarHandle(Field.class, "modifiers", int.class);
varHandle.set(field, field.getModifiers() & ~Modifier.FINAL);
Since package jdk.internal.loader from module java.base is not normally accessible, you will need to add "exports" and "opens" to both the compiler and JVM runtime args.
--add-exports=java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
Read more here:
--add-exports: https://stackoverflow.com/a/53647605/257299
--add-opens: https://stackoverflow.com/a/61663667/257299
Remove final modifier on Java12+: https://stackoverflow.com/a/56043252/257299

Parse string as if it was a command with params and options

I have this Java enum:
public enum Commands
{
RESIZE_WINDOW("size -size"),
CREATE_CHARACTER("create-char -name"),
NEW_SCENE("scene -image"),
DIALOG("d -who -words"),
PLAY_SOUND("sound -file --blocking=false"),
FADE_TO("fade-to -file"),
WAIT("w -length=auto");
}
I want to be able to parse those strings and extract:
command name (e.g. create-char)
required parameters (e.g. -name)
optional options, with default value (e.g. --blocking=false)
I looked at org.apache.commons.cli, but that appears to fail on the first criteria (different command names) and is very verbose.
Any library suggestions?
(This is going to be used to parse a scripting "language", if that context helps.)
Edit: An example input in the scripting language would be d John "Hello World" -- multi-word text goes in quotes.
It appears that you want to build numerous CLI commands based simply on their "help descriptors"; a DSL of sorts. Instead of doing this string parsing, consider building the commands programmatically, for which there are numerous libraries (CLI being but one) and advantages.
Your example is already significantly complex enough that a second look at CLI (or one of the others) is warranted. You show required and optional args, each having either no-value or a default (though no means of indicating 'required' arg-values without a default, command descriptions, etc), and you still need to build a parser for the command line itself, verification, means of invoking handlers, etc...
Below is a list of command parsers I've found. None will parse your specific DSL, but they will allow you to build your commands programmatically, parse it, often verify and provide meaningful warnings, help handling, etc. Some even use annotations on objects to define commands, theoretically making maintenance easier.
Most are designed (and show examples) for parsing the arguments to your program, rather than numerous commands (natural-cli being an exception) but all should be able to do this - a simple class could wrap the parsing and options together:
static class CommandLine {
HashMap<String,Options> options = new HashMap<String,Options>();
public void register(String cmd, Options opts) {
options.put(cmd, opts);
}
public void parse(String line) {
// a better parser here would handle quoted strings
String[] split = line.split("\\s");
String cmd = split[0];
String[] args = new String[split.length-1];
if (args.length > 0)
System.arraycopy(split, 1, args, 0, args.length);
Options opts = options.get(cmd);
if (opts == null)
; // handle unknown command
else {
opts.parse(args);
// handle results...
opts.reset(); // required?
}
}
}
public static void main(String[] args) throws Exception {
CommandLine cl = new CommandLine();
cl.register("size", new Options()); // This will vary based on library Some
cl.register("create-char", new Options()); // require subclasses, others use builder
//... pattern, or other means.
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (true) {
cl.parse(in.readLine());
}
}
Other Libraries
CLI
JewelCli: also uses annotations, and allows lists and enums for values. examples look promising
natural-cli: allows cli with "human readable sentances". Seems geared towards your goal of multiple commands and their options, albiet with a different DSL and syntax.
GNU Java Getopt: Used same way as above, though you initialize the Getopt object with the name of the command.
JOpt Simple: Claims "simplicity over all others"
JArgs
args4j: uses annotations on separate classes created for each command
JSAP
CLAJR: uses reflection
CmdLn
ArgParser
JCommando: uses XML config files.
parse-cmd: uses Builder-pattern and method chaining to build parameters
cli-parser: annotation based

How to Find the Default Charset/Encoding in Java?

The obvious answer is to use Charset.defaultCharset() but we recently found out that this might not be the right answer. I was told that the result is different from real default charset used by java.io classes in several occasions. Looks like Java keeps 2 sets of default charset. Does anyone have any insights on this issue?
We were able to reproduce one fail case. It's kind of user error but it may still expose the root cause of all other problems. Here is the code,
public class CharSetTest {
public static void main(String[] args) {
System.out.println("Default Charset=" + Charset.defaultCharset());
System.setProperty("file.encoding", "Latin-1");
System.out.println("file.encoding=" + System.getProperty("file.encoding"));
System.out.println("Default Charset=" + Charset.defaultCharset());
System.out.println("Default Charset in Use=" + getDefaultCharSet());
}
private static String getDefaultCharSet() {
OutputStreamWriter writer = new OutputStreamWriter(new ByteArrayOutputStream());
String enc = writer.getEncoding();
return enc;
}
}
Our server requires default charset in Latin-1 to deal with some mixed encoding (ANSI/Latin-1/UTF-8) in a legacy protocol. So all our servers run with this JVM parameter,
-Dfile.encoding=ISO-8859-1
Here is the result on Java 5,
Default Charset=ISO-8859-1
file.encoding=Latin-1
Default Charset=UTF-8
Default Charset in Use=ISO8859_1
Someone tries to change the encoding runtime by setting the file.encoding in the code. We all know that doesn't work. However, this apparently throws off defaultCharset() but it doesn't affect the real default charset used by OutputStreamWriter.
Is this a bug or feature?
EDIT: The accepted answer shows the root cause of the issue. Basically, you can't trust defaultCharset() in Java 5, which is not the default encoding used by I/O classes. Looks like Java 6 corrects this issue.
This is really strange... Once set, the default Charset is cached and it isn't changed while the class is in memory. Setting the "file.encoding" property with System.setProperty("file.encoding", "Latin-1"); does nothing. Every time Charset.defaultCharset() is called it returns the cached charset.
Here are my results:
Default Charset=ISO-8859-1
file.encoding=Latin-1
Default Charset=ISO-8859-1
Default Charset in Use=ISO8859_1
I'm using JVM 1.6 though.
(update)
Ok. I did reproduce your bug with JVM 1.5.
Looking at the source code of 1.5, the cached default charset isn't being set. I don't know if this is a bug or not but 1.6 changes this implementation and uses the cached charset:
JVM 1.5:
public static Charset defaultCharset() {
synchronized (Charset.class) {
if (defaultCharset == null) {
java.security.PrivilegedAction pa =
new GetPropertyAction("file.encoding");
String csn = (String) AccessController.doPrivileged(pa);
Charset cs = lookup(csn);
if (cs != null)
return cs;
return forName("UTF-8");
}
return defaultCharset;
}
}
JVM 1.6:
public static Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
java.security.PrivilegedAction pa =
new GetPropertyAction("file.encoding");
String csn = (String) AccessController.doPrivileged(pa);
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
else
defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}
When you set the file encoding to file.encoding=Latin-1 the next time you call Charset.defaultCharset(), what happens is, because the cached default charset isn't set, it will try to find the appropriate charset for the name Latin-1. This name isn't found, because it's incorrect, and returns the default UTF-8.
As for why the IO classes such as OutputStreamWriter return an unexpected result,
the implementation of sun.nio.cs.StreamEncoder (witch is used by these IO classes) is different as well for JVM 1.5 and JVM 1.6. The JVM 1.6 implementation is based in the Charset.defaultCharset() method to get the default encoding, if one is not provided to IO classes. The JVM 1.5 implementation uses a different method Converters.getDefaultEncodingName(); to get the default charset. This method uses its own cache of the default charset that is set upon JVM initialization:
JVM 1.6:
public static StreamEncoder forOutputStreamWriter(OutputStream out,
Object lock,
String charsetName)
throws UnsupportedEncodingException
{
String csn = charsetName;
if (csn == null)
csn = Charset.defaultCharset().name();
try {
if (Charset.isSupported(csn))
return new StreamEncoder(out, lock, Charset.forName(csn));
} catch (IllegalCharsetNameException x) { }
throw new UnsupportedEncodingException (csn);
}
JVM 1.5:
public static StreamEncoder forOutputStreamWriter(OutputStream out,
Object lock,
String charsetName)
throws UnsupportedEncodingException
{
String csn = charsetName;
if (csn == null)
csn = Converters.getDefaultEncodingName();
if (!Converters.isCached(Converters.CHAR_TO_BYTE, csn)) {
try {
if (Charset.isSupported(csn))
return new CharsetSE(out, lock, Charset.forName(csn));
} catch (IllegalCharsetNameException x) { }
}
return new ConverterSE(out, lock, csn);
}
But I agree with the comments. You shouldn't rely on this property. It's an implementation detail.
Is this a bug or feature?
Looks like undefined behaviour. I know that, in practice, you can change the default encoding using a command-line property, but I don't think what happens when you do this is defined.
Bug ID: 4153515 on problems setting this property:
This is not a bug. The "file.encoding" property is not required by the J2SE
platform specification; it's an internal detail of Sun's implementations and
should not be examined or modified by user code. It's also intended to be
read-only; it's technically impossible to support the setting of this property
to arbitrary values on the command line or at any other time during program
execution.
The preferred way to change the default encoding used by the VM and the runtime
system is to change the locale of the underlying platform before starting your
Java program.
I cringe when I see people setting the encoding on the command line - you don't know what code that is going to affect.
If you do not want to use the default encoding, set the encoding you do want explicitly via the appropriate method/constructor.
The behaviour is not really that strange. Looking into the implementation of the classes, it is caused by:
Charset.defaultCharset() is not caching the determined character set in Java 5.
Setting the system property "file.encoding" and invoking Charset.defaultCharset() again causes a second evaluation of the system property, no character set with the name "Latin-1" is found, so Charset.defaultCharset() defaults to "UTF-8".
The OutputStreamWriter is however caching the default character set and is probably used already during VM initialization, so that its default character set diverts from Charset.defaultCharset() if the system property "file.encoding" has been changed at runtime.
As already pointed out, it is not documented how the VM must behave in such a situation. The Charset.defaultCharset() API documentation is not very precise on how the default character set is determined, only mentioning that it is usually done on VM startup, based on factors like the OS default character set or default locale.
First, Latin-1 is the same as ISO-8859-1, so, the default was already OK for you. Right?
You successfully set the encoding to ISO-8859-1 with your command line parameter. You also set it programmatically to "Latin-1", but, that's not a recognized value of a file encoding for Java. See http://java.sun.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
When you do that, looks like Charset resets to UTF-8, from looking at the source. That at least explains most of the behavior.
I don't know why OutputStreamWriter shows ISO8859_1. It delegates to closed-source sun.misc.* classes. I'm guessing it isn't quite dealing with encoding via the same mechanism, which is weird.
But of course you should always be specifying what encoding you mean in this code. I'd never rely on the platform default.
I have set the vm argument in WAS server as -Dfile.encoding=UTF-8 to change the servers' default character set.
check
System.getProperty("sun.jnu.encoding")
it seems to be the same encoding as the one used in your system's command line.

Categories

Resources